diff --git a/.browserslistrc b/.browserslistrc index 4475c28..39961ab 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Jan 8, 2024 2:21 AM UTC. +# Last generated Jan 8, 2024 2:57 AM UTC. [production] node >= 20.9.0 diff --git a/.dockerignore b/.dockerignore index 275d3e9..c6e339c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Jan 8, 2024 2:21 AM UTC. +# Last generated Jan 8, 2024 2:57 AM UTC. # Locals diff --git a/.env.vault b/.env.vault index 4342d35..d299d3c 100644 --- a/.env.vault +++ b/.env.vault @@ -8,12 +8,12 @@ DOTENV_VAULT_MAIN="3rfi+ClkONvfHPiU9FTBcC1+Um/L8QeM9dRw1jFzEqzZXw==" DOTENV_VAULT_MAIN_VERSION=1 # dev -DOTENV_VAULT_DEV="y+f2F71mgD9qMUGboxpsN9L8PVUWv3dn8d5CqJtpnqZzywQGwUrm2V8YdhzIeDziY1F65m29RI+g1KqmCtJpRwEdB1SQyKiG5/FnrXMLhKWtU6y8tXEod8pMqyqdboe1z5mbkuQovANu23LeRtY1OuiXDDXkd8MnZzbrQG8TSDnrCaZGhnZJDL3crpqVWCpnSiV7QG1xCtfephzx4mP44095vXI7ytrNoFVGXp+i8wFzqb9NI8vf7V3vsvVP/Yj/BIbEy1UgrX5hcNF6BgrA9hwOBG4g6V3/ZaAc7YwGcwH/wBX78tkBQELxcADuEXVHLUAGUvJOx/jCvMm6SqLFaaw2fI8JMQZlsnF3+h8mkt38wC9FBHpNn1Kdq1i2jbU5gwweVoxOWUsBIfsLeZrAasnZLLJWCToHbQHcIS4X83XwdHPeg9hSO1VjNySuOp1how1ZB7HjtuVS468958bSb15+NmCv5Z1J4vmRKn+gxQvh63/Zo39SHbMk+0K1ZFjlz6wOxlAw2bmzkgTmbkY5dpSO4wa9v/m7B3RPuKlG8ZSu7q7TcV2t1mcf0K840YlxGAoRyoe0XDi9toiKxrhNnBn5/EI199CRjRbclptIdrRSxAZyMiJ7Vxf7mcMwUIxsPuTPOtCEPCsxC5EQvgzMKZFoxk/AObauvPP9uF85MlFAkBi2Igr6OiGZV+oAxqcT7tQ/crGBNtwaRN/4oK4tFFI86KaoRBuhxNy2W9OkpIAdRsFgHTWwOFlaqttg9w==" -DOTENV_VAULT_DEV_VERSION=19 +DOTENV_VAULT_DEV="OzvClLP2HQQTLhQ6MQp439uGnZE5z/tqLSWT9+UQNejbcmnvqMKGzHlxB4Mn/oR6+5L6eEwbJhVE/EE5ckgChPa+p4G+V0OCE+75zc81ftClSzg/ZBt6gPtpvEPymcNwq2bwn3r7vP5nN9jixpsSrkngD4liXAJlW2T2Kir+NPuTRrk46A3GBm88Cj7FOpNSVjDNkFzmdxwHdIbTitWrV+oDs88F/YkoKO8Ks+HdgF81h0rllKAUrTlm48QmWMM8KElHmyRAr8KQ4fuoq8CYmxpTrVSEloC+I+rDhoc/xJUEDKc+pjxNNVkzCSHI00AfbmSSXluSBA4d3A1345a9XKa1+G/JYcK4Hc6FxYG5lAAU5s/20RG7RetNG93P/u/1Te285zOtbXssP+cldV2fxTKYMCI0PvnB99VZUuPV8YbFxJoJqXT0wlMK74PhWtOmaKhpZZ/aHJyl6kIog0nTl3DfaGBDrzO4N4YuIVp21ywwGISoqhdp6M18LmrOb+R+UWYGTa00ms/kbr+gbHaFVgFQGz6JxBMq0oj7HRXY9yB2rluyf7bJ0uqvsEWutVAaTyNG3E1eZ15ln0SkXEEMx0OY4TxFneCWgsjkf69xI5wGPL83ThuK7MYEBGPIlZy7XDykaLHgnKIBy+DdUvZZLItMlQEBQXe4HpztiU1CgSZjzKbMbPOJqvzq6TD+Ha8mcaKNHChLE7CGI5iPPOiCT7wEPltznsO2hNDyfYrA6PrdF/1WZLckyRNChoXt+A==" +DOTENV_VAULT_DEV_VERSION=21 # ci -DOTENV_VAULT_CI="TTwj20a3CRvqdbRMCbM0jKeSRJHP1PFc8O7STB5gRUuLi9dJjAGNWBcbLlFgeA7ifWM2MJebD3NjgsBJoRqIywY+T/YTsd0jXXfT7RS/6Yk3pPark6d/RQe0PIlopdFmZe5EO7b7SlszEwXZrPsyEFL4wFNQcyNZD1HDOHJ/38FTTzutNDLgz18eTxchGKT5B2ZEFsz1WgtlGWoFvdsFKyqObDxCmec6LAevd9AML1XLetFc5Conuw1dhKUmi5BjuIQUEbOOB8/GiiluB9Z6RmJwmqrHorkB6rt1RMJSmj5P1TOGUt9TU/FAOlAuhQ88K5CSIR81d5YXd2PwaM2OjRFSl9vq77NXhVCbvICE7f3vd83oQifZdW68+2yBV1lhruB4+oyz2xon3M3XZJtv6rIjBIZgLkkhAY9y8cH1Hp/r/RXkZZljltJzlsLD5vHvgjmNJ6apSaK3csJbehaBv0UGzAdR2O6s/k2c2hfkluZjXJDUdtj+wwbCccK6ReitEYbLdb5mue6EcWc5PiTPStuZ3nFknPLy3jUg/1CWtud3Y7tjlDpThWL2UVq4pGnycCXFEt7YV18PEKZsTw/3sO+ZighajBWCPMQSJTnP3AD38Gw2fEHylEeEoT95tNN63G8uQcKFp3Jq/GU0HZHXT0KUmRugzy7vfwci1EZumr7CJIjDMBVTfZYu9sHqhktYI4sogM80Ms6QGPlWLgcbV6kqMyJ2kT1No9KyVth6B6V95ZB7sQrSoxbFBVObiA==" -DOTENV_VAULT_CI_VERSION=19 +DOTENV_VAULT_CI="YGQFYeu8F7vxH9ognz64iPhUDNa50cnDOcs7c1yaC3W07b1SZTfqGHJkyvxoxiW+ECH0E+o0i/81hFyQ+FXxfekw3A0Kc+a8rLs8vxynFr3OErJVK/mOU7fNNTV/LvoTEEKKtOmzjX1Fhsw6mCO8Apd991LzdzSMQNy5ktxKFhlxGYwdaqTF4GJjpqmRzDfGfLo5MKYPBSr4msxx8ITppgNxMUJeFZcKa/FW8LYvfeYYUr+XkNQaLRz9lPhdnizUAblXikDp0in9ghe4EC71L73rLn2wzpHs4fK476gstjP1crEk8WwQnFcnWPSsLw8GNaEsPMEHjLXFq2sZyn7HK6RTDvwJQIHtz5DEuLN8Ud3Q/T8cDgaxKP9c3U9fuFZbIh5zg3GPCSq2aLLKnI5QyF5IoZ7yCfBe+cNeQ/IkQYd6pG2gCIebQ0qvEMkYVF8Ppw5ZE6J+IWQatqBvDYb7fScSPBQcUIEC3qvwl8TWn+zcgwwMvHmwazU616Ai/w5Vt3k9HUd+KsPEadIsmPYC8x0iAKfkt21k1FTx0dZBpoNUzT4+EckP4WkbPP6CfjGAkaaBsCaat6lxvna/BqxfxeZOOb0jyhWI4XKAIDPYktabHBWb678xCqRe3LkI50DItFkm5Dn7eg/1db4EeCUsneQMvPB06tzqGO0AhIshG6VHeLvRewCYkntKiorq2OHeOnt2aWy3DEUeks+0oqD1S+tBEcBL5jC91JRgJ9Rjve/myr+e4HEnaAL6Wd3w6w==" +DOTENV_VAULT_CI_VERSION=21 # stage DOTENV_VAULT_STAGE="bfqZYD8oVLWMZ0JR5LICV+sB/btaXavszxZLxTfwSH8U5fg=" diff --git a/.gitattributes b/.gitattributes index 99b07f8..90c32f8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Jan 8, 2024 2:21 AM UTC. +# Last generated Jan 8, 2024 2:57 AM UTC. # Default diff --git a/.gitignore b/.gitignore index 6da87c9..e96a50f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Jan 8, 2024 2:21 AM UTC. +# Last generated Jan 8, 2024 2:57 AM UTC. # Locals diff --git a/.npmignore b/.npmignore index 6de2eaf..278aafb 100644 --- a/.npmignore +++ b/.npmignore @@ -25,7 +25,7 @@ # Generated data. # -# Last generated Jan 8, 2024 2:21 AM UTC. +# Last generated Jan 8, 2024 2:57 AM UTC. # Locals diff --git a/.prettierignore b/.prettierignore index 3506140..6d6bbd6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Jan 8, 2024 2:21 AM UTC. +# Last generated Jan 8, 2024 2:57 AM UTC. # Packages diff --git a/.vscode/settings.json b/.vscode/settings.json index 93eae28..f702ff3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./settings.mjs`. * - * Last generated using `./settings.mjs` Jan 8, 2024 2:21 AM UTC. + * Last generated using `./settings.mjs` Jan 8, 2024 2:57 AM UTC. */ { "editor.formatOnType": false, diff --git a/.vscodeignore b/.vscodeignore index f2469f0..a45f996 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Jan 8, 2024 2:21 AM UTC. +# Last generated Jan 8, 2024 2:57 AM UTC. # Locals diff --git a/package-lock.json b/package-lock.json index 98fe702..565d2ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@clevercanyon/utilities.cfw", - "version": "1.0.161", + "version": "1.0.162", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@clevercanyon/utilities.cfw", - "version": "1.0.161", + "version": "1.0.162", "cpu": [ "x64", "arm64" @@ -1103,9 +1103,9 @@ } }, "node_modules/@clevercanyon/utilities.cfw": { - "version": "1.0.161", - "resolved": "https://registry.npmjs.org/@clevercanyon/utilities.cfw/-/utilities.cfw-1.0.161.tgz", - "integrity": "sha512-hZSLTKOdhbEzauXOjduSstPgQ2C3fTlwExHb200S0enPxXU0QESz6VQq5jWOyPxLQqKSEWi2w+sdsCHA6AjvSw==", + "version": "1.0.162", + "resolved": "https://registry.npmjs.org/@clevercanyon/utilities.cfw/-/utilities.cfw-1.0.162.tgz", + "integrity": "sha512-m1noyMMGJU389wvBeHsu4Mdhj7ulVyeuwFWAv8AOzBlwHGiDQb854wXH924yrhx7Kz5M4JBjYkVnczwj/ssXvw==", "cpu": [ "x64", "arm64" @@ -1124,7 +1124,7 @@ "url": "https://github.com/sponsors/clevercanyon" }, "peerDependencies": { - "@clevercanyon/utilities": "^1.0.695", + "@clevercanyon/utilities": "^1.0.697", "@cloudflare/kv-asset-handler": "0.2.0", "@upstash/ratelimit": "1.0.0", "@upstash/redis": "1.28.0" diff --git a/package.json b/package.json index 31ed7c0..f44b45c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "1.0.162", + "version": "1.0.163", "license": "GPL-3.0-or-later", "name": "@clevercanyon/utilities.cfw", "description": "Utilities for JavaScript apps running in a Cloudflare Worker environment.", diff --git a/src/redis.ts b/src/redis.ts index e63350c..6eae63c 100644 --- a/src/redis.ts +++ b/src/redis.ts @@ -5,7 +5,7 @@ import '#@initialize.ts'; import { type StdFetchEventData } from '#cfw.ts'; -import { $class, $env, $fn, $http, $is, $mime, $obj } from '@clevercanyon/utilities'; +import { $class, $env, $fn, $http, $is, $json, $mime, $obj, $url } from '@clevercanyon/utilities'; import { Ratelimit as RateLimiterCore } from '@upstash/ratelimit'; import { Redis } from '@upstash/redis/cloudflare'; @@ -119,7 +119,7 @@ export const instance = $fn.memo( * @returns {@see RateLimiter} Instance. */ export const rateLimiter = (feData: StdFetchEventData, options?: RateLimiterOptions): RateLimiter => { - const { auditLogger, request, ctx } = feData, + const { ctx, url, request, auditLogger } = feData, limiter = rateLimiterCore(options); return { @@ -132,16 +132,27 @@ export const rateLimiter = (feData: StdFetchEventData, options?: RateLimiterOpti ctx.waitUntil(limiterResponse.pending); // e.g., Analytics, multiregion sync. } if (!limiterResponse.success) { - void auditLogger.info('429: ' + $http.responseStatusText('429'), { + void auditLogger.info('429: ' + $http.responseStatusText(429), { rateLimiter: limiter, rateLimiterMethod: 'limit', rateLimiterResponse: limiterResponse, }); - throw $http.prepareResponse(request, { - status: 429, // Too many requests in this scenario. - headers: { 'content-type': $mime.contentType('.txt') }, - body: $http.responseStatusText('429'), // Too many requests. - }); + if ( + /\b(?:application\/json)\b/iu.test(request.headers.get('accept') || '') || // + /^\/(?:api)(?:$|\/)/iu.test($url.removeAppBasePath(url).pathname) + ) { + throw $http.prepareResponse(request, { + status: 429, // Too many requests. + headers: { 'content-type': $json.contentType() }, + body: $json.stringify({ ok: false, error: { message: $http.responseStatusText(429) } }, { pretty: true }), + }); + } else { + throw $http.prepareResponse(request, { + status: 429, // Too many requests. + headers: { 'content-type': $mime.contentType('.txt') }, + body: $http.responseStatusText(429), + }); + } } return limiterResponse; }, @@ -152,16 +163,27 @@ export const rateLimiter = (feData: StdFetchEventData, options?: RateLimiterOpti ctx.waitUntil(limiterResponse.pending); // e.g., Analytics, multiregion sync. } if (!limiterResponse.success) { - void auditLogger.info('429: ' + $http.responseStatusText('429'), { + void auditLogger.info('429: ' + $http.responseStatusText(429), { rateLimiter: limiter, rateLimiterMethod: 'blockUntilReady', rateLimiterResponse: limiterResponse, }); - throw $http.prepareResponse(request, { - status: 429, // Too many requests in this scenario. - headers: { 'content-type': $mime.contentType('.txt') }, - body: $http.responseStatusText('429'), // Too many requests. - }); + if ( + /\b(?:application\/json)\b/iu.test(request.headers.get('accept') || '') || // + /^\/(?:api)(?:$|\/)/iu.test($url.removeAppBasePath(url).pathname) + ) { + throw $http.prepareResponse(request, { + status: 429, // Too many requests. + headers: { 'content-type': $json.contentType() }, + body: $json.stringify({ ok: false, error: { message: $http.responseStatusText(429) } }, { pretty: true }), + }); + } else { + throw $http.prepareResponse(request, { + status: 429, // Too many requests. + headers: { 'content-type': $mime.contentType('.txt') }, + body: $http.responseStatusText(429), + }); + } } return limiterResponse; }, diff --git a/tsconfig.json b/tsconfig.json index f205b6e..bb060a9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./tsconfig.mjs`. * - * Last generated using `./tsconfig.mjs` Jan 8, 2024 2:21 AM UTC. + * Last generated using `./tsconfig.mjs` Jan 8, 2024 2:57 AM UTC. */ { "include": ["./src/**/*", "./dev-types.d.ts"], diff --git a/wrangler.toml b/wrangler.toml index c564086..31c931b 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -7,7 +7,7 @@ # @note This entire file will be updated automatically. # @note Instead of editing here, please review `./wrangler.mjs`. # -# Last generated using `./wrangler.mjs` Jan 8, 2024 2:21 AM UTC. +# Last generated using `./wrangler.mjs` Jan 8, 2024 2:57 AM UTC. ## send_metrics = false