stratal 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{base-email.provider-CfQCA08m.mjs → base-email.provider-BWZHIjt8.mjs} +1 -1
- package/dist/{base-email.provider-CfQCA08m.mjs.map → base-email.provider-BWZHIjt8.mjs.map} +1 -1
- package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
- package/dist/bin/quarry.mjs +46 -109
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +6 -46
- package/dist/cache/index.d.mts.map +1 -1
- package/dist/cache/index.mjs +22 -67
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{cache.service-DsnKuNyO.d.mts → cache.service-e34gV6tz.d.mts} +8 -8
- package/dist/{cache.service-DsnKuNyO.d.mts.map → cache.service-e34gV6tz.d.mts.map} +1 -1
- package/dist/{cache.tokens-B7Rw1C9Q.mjs → cache.tokens-ovi_c52J.mjs} +1 -1
- package/dist/{cache.tokens-B7Rw1C9Q.mjs.map → cache.tokens-ovi_c52J.mjs.map} +1 -1
- package/dist/{colors-DJaRDXoS.mjs → colors-axmupKdp.mjs} +1 -1
- package/dist/{colors-DJaRDXoS.mjs.map → colors-axmupKdp.mjs.map} +1 -1
- package/dist/{command-BgSlsS4M.mjs → command-BU4ApTo5.mjs} +2 -3
- package/dist/command-BU4ApTo5.mjs.map +1 -0
- package/dist/{command-Bu-PjJrX.d.mts → command-wXfvHbBZ.d.mts} +3 -2
- package/dist/command-wXfvHbBZ.d.mts.map +1 -0
- package/dist/config/index.d.mts +24 -11
- package/dist/config/index.d.mts.map +1 -1
- package/dist/config/index.mjs +33 -57
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-B7yUNh0q.d.mts → consumer-registry-DHQtypr1.d.mts} +1 -1
- package/dist/{consumer-registry-B7yUNh0q.d.mts.map → consumer-registry-DHQtypr1.d.mts.map} +1 -1
- package/dist/container-storage-GpNNz79X.mjs +52 -0
- package/dist/container-storage-GpNNz79X.mjs.map +1 -0
- package/dist/{controller.decorator-DQzenvSN.mjs → controller.decorator-DIUazNU7.mjs} +8 -8
- package/dist/controller.decorator-DIUazNU7.mjs.map +1 -0
- package/dist/cron/index.d.mts +26 -5
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +1 -1
- package/dist/{cron-manager-7Symz_TE.mjs → cron-manager-9bpN9bu4.mjs} +42 -16
- package/dist/cron-manager-9bpN9bu4.mjs.map +1 -0
- package/dist/{cron-manager-BEsH1mjW.d.mts → cron-manager-CSTIBPcM.d.mts} +6 -13
- package/dist/cron-manager-CSTIBPcM.d.mts.map +1 -0
- package/dist/decorate-HgTKAYK8.mjs +16 -0
- package/dist/deep-merge-C8NgcXw4.mjs +18 -0
- package/dist/deep-merge-C8NgcXw4.mjs.map +1 -0
- package/dist/di/index.d.mts +2 -2
- package/dist/di/index.mjs +4 -3
- package/dist/di-BO1QIb5H.mjs +415 -0
- package/dist/di-BO1QIb5H.mjs.map +1 -0
- package/dist/email/index.d.mts +14 -89
- package/dist/email/index.d.mts.map +1 -1
- package/dist/email/index.mjs +30 -125
- package/dist/email/index.mjs.map +1 -1
- package/dist/en-BPP6h6y5.mjs +202 -0
- package/dist/en-BPP6h6y5.mjs.map +1 -0
- package/dist/{env-D1rcZ8_r.d.mts → env-DKSbuBi5.d.mts} +1 -1
- package/dist/env-DKSbuBi5.d.mts.map +1 -0
- package/dist/errors/index.d.mts +2 -2
- package/dist/errors/index.mjs +4 -2
- package/dist/errors-BBZTnjdq.mjs +333 -0
- package/dist/errors-BBZTnjdq.mjs.map +1 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.d.mts.map +1 -1
- package/dist/events/index.mjs +1 -1
- package/dist/{events-COKixqnG.mjs → events-D1KdDaiP.mjs} +13 -11
- package/dist/events-D1KdDaiP.mjs.map +1 -0
- package/dist/exception-context-B4kM-M53.mjs +429 -0
- package/dist/exception-context-B4kM-M53.mjs.map +1 -0
- package/dist/{gateway-context-CdJjpUCW.mjs → gateway-context-CFe6a9gz.mjs} +20 -31
- package/dist/gateway-context-CFe6a9gz.mjs.map +1 -0
- package/dist/guards/index.d.mts +3 -3
- package/dist/guards/index.d.mts.map +1 -1
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-DUk_Kzst.mjs → guards-Ced-uNIF.mjs} +7 -5
- package/dist/guards-Ced-uNIF.mjs.map +1 -0
- package/dist/{http-method.decorator-DXwxAfb_.mjs → http-method.decorator-CdjKFJZZ.mjs} +7 -6
- package/dist/http-method.decorator-CdjKFJZZ.mjs.map +1 -0
- package/dist/i18n/index.d.mts +238 -3
- package/dist/i18n/index.d.mts.map +1 -0
- package/dist/i18n/index.mjs +39 -3
- package/dist/i18n/index.mjs.map +1 -0
- package/dist/i18n/messages/en/index.d.mts +2 -2
- package/dist/i18n/messages/en/index.mjs +2 -2
- package/dist/i18n/utils/index.d.mts +4 -26
- package/dist/i18n/utils/index.d.mts.map +1 -1
- package/dist/i18n/utils/index.mjs +2 -2
- package/dist/i18n/validation/index.d.mts +3 -2
- package/dist/i18n/validation/index.mjs +4 -2
- package/dist/i18n.module-BlXrtAlV.mjs +219 -0
- package/dist/i18n.module-BlXrtAlV.mjs.map +1 -0
- package/dist/i18n.tokens-hwRpmjRq.mjs +19 -0
- package/dist/i18n.tokens-hwRpmjRq.mjs.map +1 -0
- package/dist/{index-7-hU3GTV.d.mts → index-B4UBK-2T.d.mts} +1 -1
- package/dist/{index-7-hU3GTV.d.mts.map → index-B4UBK-2T.d.mts.map} +1 -1
- package/dist/index-BtlE9RuO.d.mts +124 -0
- package/dist/index-BtlE9RuO.d.mts.map +1 -0
- package/dist/{index-CjaQ6_tZ.d.mts → index-CW1YHSft.d.mts} +71 -167
- package/dist/index-CW1YHSft.d.mts.map +1 -0
- package/dist/{index-D0US0X14.d.mts → index-DEncMcC6.d.mts} +559 -2239
- package/dist/index-DEncMcC6.d.mts.map +1 -0
- package/dist/index-Dj5IMwtr.d.mts +44 -0
- package/dist/index-Dj5IMwtr.d.mts.map +1 -0
- package/dist/{index-C1KvMncZ.d.mts → index-KMgSCSM7.d.mts} +3 -108
- package/dist/index-KMgSCSM7.d.mts.map +1 -0
- package/dist/index.d.mts +5 -43
- package/dist/index.mjs +1 -1
- package/dist/{is-command-C6a7WTPw.mjs → is-command-CX5rAfZW.mjs} +2 -2
- package/dist/{is-command-C6a7WTPw.mjs.map → is-command-CX5rAfZW.mjs.map} +1 -1
- package/dist/{is-seeder-CebjZCDn.mjs → is-seeder-CYCtELlm.mjs} +1 -1
- package/dist/{is-seeder-CebjZCDn.mjs.map → is-seeder-CYCtELlm.mjs.map} +1 -1
- package/dist/logger/index.d.mts +2 -2
- package/dist/logger/index.mjs +170 -2
- package/dist/logger/index.mjs.map +1 -0
- package/dist/macroable/index.d.mts +1 -1
- package/dist/macroable/index.mjs +1 -1
- package/dist/{macroable-BmufBshB.mjs → macroable-DzlfzT50.mjs} +1 -1
- package/dist/{macroable-BmufBshB.mjs.map → macroable-DzlfzT50.mjs.map} +1 -1
- package/dist/metadata-BVkc4aUu.mjs +39 -0
- package/dist/metadata-BVkc4aUu.mjs.map +1 -0
- package/dist/module/index.d.mts +6 -24
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +2 -2
- package/dist/module-xYoHba6B.mjs +422 -0
- package/dist/module-xYoHba6B.mjs.map +1 -0
- package/dist/openapi/index.d.mts +3 -3
- package/dist/openapi/index.d.mts.map +1 -1
- package/dist/openapi/index.mjs +1 -2
- package/dist/openapi-C6lm0RmV.mjs +483 -0
- package/dist/openapi-C6lm0RmV.mjs.map +1 -0
- package/dist/{openapi.service-BLgvn3hJ.d.mts → openapi.service-CrLlsXAd.d.mts} +3 -3
- package/dist/openapi.service-CrLlsXAd.d.mts.map +1 -0
- package/dist/quarry/index.d.mts +5 -163
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +5 -5
- package/dist/quarry/runner.d.mts +184 -0
- package/dist/quarry/runner.d.mts.map +1 -0
- package/dist/quarry/runner.mjs +775 -0
- package/dist/quarry/runner.mjs.map +1 -0
- package/dist/quarry-registry-D4hIGScf.d.mts +69 -0
- package/dist/quarry-registry-D4hIGScf.d.mts.map +1 -0
- package/dist/quarry-registry-DkraZNwn.mjs +311 -0
- package/dist/quarry-registry-DkraZNwn.mjs.map +1 -0
- package/dist/queue/index.d.mts +3 -3
- package/dist/queue/index.mjs +27 -28
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-BCdCiySt.mjs → queue.module-DeWJ0tQM.mjs} +67 -112
- package/dist/queue.module-DeWJ0tQM.mjs.map +1 -0
- package/dist/{r2-storage.provider-Co6F0ZYV.mjs → r2-storage.provider-Hfm6LdZQ.mjs} +8 -5
- package/dist/r2-storage.provider-Hfm6LdZQ.mjs.map +1 -0
- package/dist/{rate-limit.decorator--o6Q6p9w.mjs → rate-limit.decorator-D69zdZbp.mjs} +6 -11
- package/dist/rate-limit.decorator-D69zdZbp.mjs.map +1 -0
- package/dist/rate-limiter/index.d.mts +11 -50
- package/dist/rate-limiter/index.d.mts.map +1 -1
- package/dist/rate-limiter/index.mjs +25 -30
- package/dist/rate-limiter/index.mjs.map +1 -1
- package/dist/{resend.provider-M6qRLrcy.mjs → resend.provider-Ur6tU7fK.mjs} +8 -7
- package/dist/resend.provider-Ur6tU7fK.mjs.map +1 -0
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +8 -7
- package/dist/{i18n.module-BBlNNlcG.mjs → router-Cy6DjkvP.mjs} +215 -855
- package/dist/router-Cy6DjkvP.mjs.map +1 -0
- package/dist/seeder/index.d.mts +6 -11
- package/dist/seeder/index.d.mts.map +1 -1
- package/dist/seeder/index.mjs +3 -3
- package/dist/{seeder-CJAOHEIo.mjs → seeder-BADTig4n.mjs} +17 -22
- package/dist/seeder-BADTig4n.mjs.map +1 -0
- package/dist/{signed-url-BQPbv2In.mjs → signed-url-BqUqt5dF.mjs} +1 -1
- package/dist/{signed-url-BQPbv2In.mjs.map → signed-url-BqUqt5dF.mjs.map} +1 -1
- package/dist/{smtp.provider-w0Ve52Xg.mjs → smtp.provider-C129sNBT.mjs} +7 -6
- package/dist/smtp.provider-C129sNBT.mjs.map +1 -0
- package/dist/storage/index.d.mts +15 -39
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +3 -3
- package/dist/storage/providers/index.d.mts +2 -2
- package/dist/storage/providers/index.mjs +1 -1
- package/dist/{storage-1zw-6Yiz.mjs → storage-BA3ppVYM.mjs} +70 -59
- package/dist/storage-BA3ppVYM.mjs.map +1 -0
- package/dist/{storage-provider.interface-Bd6vA4ak.d.mts → storage-provider.interface-DQMtT42e.d.mts} +2 -3
- package/dist/storage-provider.interface-DQMtT42e.d.mts.map +1 -0
- package/dist/storage.error-C6FY037a.mjs +8 -0
- package/dist/storage.error-C6FY037a.mjs.map +1 -0
- package/dist/{stratal-DeEcGgdq.mjs → stratal-Bdq4IdB3.mjs} +31 -183
- package/dist/stratal-Bdq4IdB3.mjs.map +1 -0
- package/dist/stratal-BsKmvP6J.d.mts +43 -0
- package/dist/stratal-BsKmvP6J.d.mts.map +1 -0
- package/dist/{types-cySNS_lp.d.mts → types-BaeHi67f.d.mts} +1 -1
- package/dist/types-BaeHi67f.d.mts.map +1 -0
- package/dist/{usage-generator-BUdlhnCK.mjs → usage-generator-DTqaUMR9.mjs} +6 -3
- package/dist/usage-generator-DTqaUMR9.mjs.map +1 -0
- package/dist/validation-DUzcjb8Q.mjs +49 -0
- package/dist/validation-DUzcjb8Q.mjs.map +1 -0
- package/dist/validation.context-XTysWJ3b.mjs +117 -0
- package/dist/validation.context-XTysWJ3b.mjs.map +1 -0
- package/dist/websocket/index.d.mts +7 -14
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +2 -2
- package/dist/workers/index.d.mts +2 -2
- package/dist/workers/index.mjs +3 -2
- package/dist/workers/index.mjs.map +1 -1
- package/dist/{index-Bnpfq6uk.d.mts → zod-DvWTfRpI.d.mts} +58 -133
- package/dist/zod-DvWTfRpI.d.mts.map +1 -0
- package/dist/zod-hMa3rSHV.mjs +72 -0
- package/dist/zod-hMa3rSHV.mjs.map +1 -0
- package/package.json +20 -20
- package/dist/command-BgSlsS4M.mjs.map +0 -1
- package/dist/command-Bu-PjJrX.d.mts.map +0 -1
- package/dist/controller.decorator-DQzenvSN.mjs.map +0 -1
- package/dist/cron-manager-7Symz_TE.mjs.map +0 -1
- package/dist/cron-manager-BEsH1mjW.d.mts.map +0 -1
- package/dist/en-DSH_bhh6.mjs +0 -308
- package/dist/en-DSH_bhh6.mjs.map +0 -1
- package/dist/env-D1rcZ8_r.d.mts.map +0 -1
- package/dist/errors-BdyV5PnY.mjs +0 -1725
- package/dist/errors-BdyV5PnY.mjs.map +0 -1
- package/dist/errors-Da3Pz2X7.mjs +0 -74
- package/dist/errors-Da3Pz2X7.mjs.map +0 -1
- package/dist/events-COKixqnG.mjs.map +0 -1
- package/dist/gateway-context-CdJjpUCW.mjs.map +0 -1
- package/dist/guards-DUk_Kzst.mjs.map +0 -1
- package/dist/http-method.decorator-DXwxAfb_.mjs.map +0 -1
- package/dist/i18n.module-BBlNNlcG.mjs.map +0 -1
- package/dist/index-Bnpfq6uk.d.mts.map +0 -1
- package/dist/index-C1KvMncZ.d.mts.map +0 -1
- package/dist/index-CjaQ6_tZ.d.mts.map +0 -1
- package/dist/index-D0US0X14.d.mts.map +0 -1
- package/dist/index-DBd_2wv8.d.mts +0 -263
- package/dist/index-DBd_2wv8.d.mts.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/logger-V6Ms3QnQ.mjs +0 -436
- package/dist/logger-V6Ms3QnQ.mjs.map +0 -1
- package/dist/module-Dk2qTa77.mjs +0 -860
- package/dist/module-Dk2qTa77.mjs.map +0 -1
- package/dist/openapi-tools.service-Zs-Ewv7F.mjs +0 -200
- package/dist/openapi-tools.service-Zs-Ewv7F.mjs.map +0 -1
- package/dist/openapi.service-BLgvn3hJ.d.mts.map +0 -1
- package/dist/quarry-registry-DNEej-Db.mjs +0 -688
- package/dist/quarry-registry-DNEej-Db.mjs.map +0 -1
- package/dist/queue.module-BCdCiySt.mjs.map +0 -1
- package/dist/r2-storage.provider-Co6F0ZYV.mjs.map +0 -1
- package/dist/rate-limit.decorator--o6Q6p9w.mjs.map +0 -1
- package/dist/resend.provider-M6qRLrcy.mjs.map +0 -1
- package/dist/seeder-CJAOHEIo.mjs.map +0 -1
- package/dist/setup-CefZKV_e.mjs +0 -37
- package/dist/setup-CefZKV_e.mjs.map +0 -1
- package/dist/smtp.provider-w0Ve52Xg.mjs.map +0 -1
- package/dist/storage-1zw-6Yiz.mjs.map +0 -1
- package/dist/storage-provider.interface-Bd6vA4ak.d.mts.map +0 -1
- package/dist/stratal-DeEcGgdq.mjs.map +0 -1
- package/dist/types-cySNS_lp.d.mts.map +0 -1
- package/dist/usage-generator-BUdlhnCK.mjs.map +0 -1
- package/dist/validation-DtJwAv7O.mjs +0 -248
- package/dist/validation-DtJwAv7O.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.decorator-D69zdZbp.mjs","names":[],"sources":["../src/rate-limiter/decorators/rate-limit.decorator.ts"],"sourcesContent":["import { defineMetadata, getMetadata } from '../../di/metadata'\nimport { ROUTE_METADATA_KEYS } from '../../router/constants'\n\nconst KEY = ROUTE_METADATA_KEYS.RATE_LIMIT\n\n/**\n * Apply a named rate limiter to a controller class or a single route method.\n *\n * Stacks: multiple `@RateLimit` decorators on the same target push onto\n * the metadata array — every named limiter is enforced. Class-level limits\n * run before method-level limits in the resulting middleware chain.\n *\n * The named limiter must be registered separately via\n * `RateLimiterRegistry.for('name', resolver)` (typically inside a\n * module's `onInitialize` hook) and the user must import\n * `RateLimiterModule.forRoot({ store: ... })` in their AppModule.\n *\n * @example\n * ```typescript\n * @Controller('/api/v1/users')\n * @RateLimit('api')\n * export class UsersController {\n * @Get('/')\n * list(ctx: RouterContext) { ... }\n *\n * @Post('/')\n * @RateLimit('writes') // stacks with class-level 'api'\n * create(ctx: RouterContext) { ... }\n * }\n * ```\n */\nexport function RateLimit(name: string): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n if (propertyKey === undefined) {\n const existing = getMetadata<string[]>(KEY, target) ?? []\n defineMetadata(KEY, [...existing, name], target)\n } else {\n const existing = getMetadata<string[]>(KEY, target, propertyKey as string) ?? []\n defineMetadata(KEY, [...existing, name], target, propertyKey as string)\n }\n }\n}\n\n/**\n * Read the rate-limit names attached to a class or method via `@RateLimit`.\n * Returns an empty array when no decorator was applied.\n *\n * @param target - For class metadata, pass the controller constructor.\n * For method metadata, pass `Controller.prototype` and the method name.\n */\nexport function getRateLimits(target: object, propertyKey?: string): string[] {\n const meta = propertyKey === undefined\n ? getMetadata<string[]>(KEY, target)\n : getMetadata<string[]>(KEY, target, propertyKey)\n return meta ?? []\n}\n"],"mappings":";;;AAGA,MAAM,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BhC,SAAgB,UAAU,MAAgD;CACxE,QAAQ,QAAgB,gBAAkC;EACxD,IAAI,gBAAgB,KAAA,GAElB,eAAe,KAAK,CAAC,GADJ,YAAsB,KAAK,OAAO,IAAI,EAAE,EACvB,KAAK,EAAE,OAAO;OAGhD,eAAe,KAAK,CAAC,GADJ,YAAsB,KAAK,QAAQ,YAAsB,IAAI,EAAE,EAC9C,KAAK,EAAE,QAAQ,YAAsB;;;;;;;;;;AAY7E,SAAgB,cAAc,QAAgB,aAAgC;CAI5E,QAHa,gBAAgB,KAAA,IACzB,YAAsB,KAAK,OAAO,GAClC,YAAsB,KAAK,QAAQ,YAAY,KACpC,EAAE"}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
3
|
-
import { t as
|
|
4
|
-
import { t as
|
|
5
|
-
import { t as CacheService } from "../cache.service-
|
|
1
|
+
import { An as OnException, On as ModuleContext, Sn as AsyncModuleOptions, Y as Container, c as HttpException, d as ExceptionHandler, fn as Middleware, jn as OnInitialize, pn as Next, rt as RouterContext, wn as DynamicModule, xr as ApplicationError } from "../index-DEncMcC6.mjs";
|
|
2
|
+
import { t as Macroable } from "../index-B4UBK-2T.mjs";
|
|
3
|
+
import { t as StratalEnv } from "../env-DKSbuBi5.mjs";
|
|
4
|
+
import { t as Constructor } from "../types-BaeHi67f.mjs";
|
|
5
|
+
import { t as CacheService } from "../cache.service-e34gV6tz.mjs";
|
|
6
6
|
|
|
7
7
|
//#region src/rate-limiter/errors.d.ts
|
|
8
8
|
/**
|
|
9
9
|
* Thrown when a request exceeds a configured rate limit.
|
|
10
10
|
*
|
|
11
11
|
* HTTP Status: 429 Too Many Requests
|
|
12
|
-
* Error Code: 4290
|
|
13
12
|
*
|
|
14
13
|
* The {@link ExceptionHandler} renders the body via content negotiation
|
|
15
14
|
* (HTML for HTML clients, JSON for everything else). Standard rate-limit
|
|
@@ -28,37 +27,7 @@ declare class TooManyRequestsError extends HttpException {
|
|
|
28
27
|
resetAt: number;
|
|
29
28
|
});
|
|
30
29
|
}
|
|
31
|
-
|
|
32
|
-
* Thrown when `RateLimiterRegistry.handle(name, ...)` is invoked for a
|
|
33
|
-
* name that was never registered via `.for()`.
|
|
34
|
-
*
|
|
35
|
-
* Most likely cause: a typo in `router.throttle('foo')` or `@RateLimit('foo')`,
|
|
36
|
-
* or the module that registers the limiter is missing from imports.
|
|
37
|
-
*/
|
|
38
|
-
declare class RateLimiterNotDefinedError extends ApplicationError {
|
|
39
|
-
readonly limiterName: string;
|
|
40
|
-
constructor(limiterName: string);
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Thrown by `RateLimiterStoreFactory.create()` (during the module's eager
|
|
44
|
-
* `onInitialize` validation) when the user imported `RateLimiterModule`
|
|
45
|
-
* without calling `.forRoot({ store: ... })`. There is no implicit default
|
|
46
|
-
* store — the user must pick one.
|
|
47
|
-
*/
|
|
48
|
-
declare class RateLimiterNotConfiguredError extends HttpException {
|
|
49
|
-
constructor();
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Thrown when a throttled route fires but `RateLimiterModule` was never
|
|
53
|
-
* imported in the user's AppModule (so the registry token is unbound).
|
|
54
|
-
*
|
|
55
|
-
* Distinct from {@link RateLimiterNotConfiguredError}, which fires when
|
|
56
|
-
* the module IS imported but `forRoot` was not called.
|
|
57
|
-
*/
|
|
58
|
-
declare class RateLimiterModuleNotImportedError extends ApplicationError {
|
|
59
|
-
readonly limiterName: string;
|
|
60
|
-
constructor(limiterName: string);
|
|
61
|
-
}
|
|
30
|
+
declare class RateLimiterError extends ApplicationError {}
|
|
62
31
|
//#endregion
|
|
63
32
|
//#region src/rate-limiter/limit.d.ts
|
|
64
33
|
/**
|
|
@@ -269,7 +238,7 @@ declare class RateLimiterStoreFactory {
|
|
|
269
238
|
* The module:
|
|
270
239
|
* - eagerly validates the store at app boot (`onInitialize` resolves the
|
|
271
240
|
* factory and calls `create()`); a missing `forRoot` surfaces
|
|
272
|
-
* `
|
|
241
|
+
* `RateLimiterError` before any request is served.
|
|
273
242
|
* - registers a `respond()` callback on the `ExceptionHandler` (via
|
|
274
243
|
* `onException`) that injects `Retry-After` and `X-RateLimit-*` headers
|
|
275
244
|
* on every {@link TooManyRequestsError} response, regardless of whether
|
|
@@ -315,12 +284,7 @@ declare const RATE_LIMITER_TOKENS: {
|
|
|
315
284
|
readonly Options: symbol;
|
|
316
285
|
/**
|
|
317
286
|
* Per-app marker registered by RateLimiterModule.onInitialize. Used by
|
|
318
|
-
* ThrottleMiddleware to detect "module not imported"
|
|
319
|
-
* decorator globally registers providers via tsyringe's registry(),
|
|
320
|
-
* so the Registry/Store tokens are globally bound the moment the module
|
|
321
|
-
* file is loaded. The only way to confirm the module was actually wired
|
|
322
|
-
* into the *user's* AppModule is to look for an artifact registered
|
|
323
|
-
* inside the user's app container (not the root container).
|
|
287
|
+
* ThrottleMiddleware to detect "module not imported".
|
|
324
288
|
*/
|
|
325
289
|
readonly ModuleMarker: symbol;
|
|
326
290
|
};
|
|
@@ -333,11 +297,8 @@ type RateLimiterToken = (typeof RATE_LIMITER_TOKENS)[keyof typeof RATE_LIMITER_T
|
|
|
333
297
|
* — important for `Router.middleware` deduplication via class identity.
|
|
334
298
|
*
|
|
335
299
|
* Detection of "module not imported" works against a per-app marker
|
|
336
|
-
* registered by `RateLimiterModule.onInitialize
|
|
337
|
-
*
|
|
338
|
-
* inject would explode with a less-actionable tsyringe wrapping). We hold
|
|
339
|
-
* the user's container, then check `isRegistered(marker, recursive=true)`
|
|
340
|
-
* at request time before resolving Registry.
|
|
300
|
+
* registered by `RateLimiterModule.onInitialize`. We check
|
|
301
|
+
* `isRegistered(marker)` at request time before resolving Registry.
|
|
341
302
|
*/
|
|
342
303
|
declare function createThrottleMiddleware(name: string): Constructor<Middleware>;
|
|
343
304
|
//#endregion
|
|
@@ -416,5 +377,5 @@ declare class InMemoryRateLimiterStore implements IRateLimiterStore {
|
|
|
416
377
|
delete(key: string): Promise<void>;
|
|
417
378
|
}
|
|
418
379
|
//#endregion
|
|
419
|
-
export { type IRateLimiterStore, InMemoryRateLimiterStore, KvRateLimiterStore, Limit, type LimitResolver, RATE_LIMITER_TOKENS, RateLimit, type RateLimitHeaders, type RateLimitHit, type RateLimitResponseHandler,
|
|
380
|
+
export { type IRateLimiterStore, InMemoryRateLimiterStore, KvRateLimiterStore, Limit, type LimitResolver, RATE_LIMITER_TOKENS, RateLimit, type RateLimitHeaders, type RateLimitHit, type RateLimitResponseHandler, RateLimiterError, RateLimiterModule, type RateLimiterModuleOptions, RateLimiterRegistry, RateLimiterStoreFactory, type RateLimiterToken, TooManyRequestsError, createThrottleMiddleware, getRateLimits };
|
|
420
381
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/rate-limiter/errors.ts","../../src/rate-limiter/limit.ts","../../src/rate-limiter/stores/rate-limiter-store.interface.ts","../../src/rate-limiter/rate-limiter-registry.ts","../../src/rate-limiter/stores/store-factory.ts","../../src/rate-limiter/rate-limiter.module.ts","../../src/rate-limiter/rate-limiter.tokens.ts","../../src/rate-limiter/throttle.middleware.ts","../../src/rate-limiter/decorators/rate-limit.decorator.ts","../../src/rate-limiter/stores/kv-store.ts","../../src/rate-limiter/stores/memory-store.ts"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/rate-limiter/errors.ts","../../src/rate-limiter/limit.ts","../../src/rate-limiter/stores/rate-limiter-store.interface.ts","../../src/rate-limiter/rate-limiter-registry.ts","../../src/rate-limiter/stores/store-factory.ts","../../src/rate-limiter/rate-limiter.module.ts","../../src/rate-limiter/rate-limiter.tokens.ts","../../src/rate-limiter/throttle.middleware.ts","../../src/rate-limiter/decorators/rate-limit.decorator.ts","../../src/rate-limiter/stores/kv-store.ts","../../src/rate-limiter/stores/memory-store.ts"],"mappings":";;;;;;;;;;;;;AAYA;;;;cAAa,oBAAA,SAA6B,aAAA;EAAA,SAEtB,IAAA;IAAQ,UAAA;IAAoB,KAAA;IAAe,OAAA;EAAA;cAA3C,IAAA;IAAQ,UAAA;IAAoB,KAAA;IAAe,OAAA;EAAA;AAAA;AAAA,cAMlD,gBAAA,SAAyB,gBAAA;;;;;;;UCdrB,gBAAA;EACf,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,mBAAA;AAAA;;;;;;;KASU,wBAAA,IACV,GAAA,EAAK,aAAA,EACL,OAAA,EAAS,gBAAA,KACN,QAAA,GAAW,OAAA,CAAQ,QAAA;;;;;;ADFxB;;;;;;;;ACdA;;;;cAmCa,KAAA;EAAA,SAKO,aAAA;EAAA,SACA,GAAA;EAAA,SACA,QAAA;EAAA,QANV,IAAA;EAAA,QACA,eAAA;EAAA,QAED,WAAA,CAAA;EAAA,OAMA,SAAA,CAAU,GAAA,WAAc,KAAA;EAAA,OAIxB,UAAA,CAAW,OAAA,UAAiB,GAAA,WAAc,KAAA;EAAA,OAI1C,SAAA,CAAU,GAAA,WAAc,KAAA;EAAA,OAIxB,UAAA,CAAW,OAAA,UAAiB,GAAA,WAAc,KAAA;EAAA,OAI1C,OAAA,CAAQ,GAAA,WAAc,KAAA;EAAA,OAItB,MAAA,CAAO,GAAA,WAAc,KAAA;EAjDd;EAAA,OAsDP,IAAA,CAAA,GAAQ,KAAA;EAtDM;EA2DrB,EAAA,CAAG,GAAA;EA7DH;;;;;EAuEA,QAAA,CAAS,OAAA,EAAS,wBAAA;EAAA,IAKd,GAAA,CAAA;EAAA,IAIA,cAAA,CAAA,GAAkB,wBAAA;AAAA;;;;;;;;;;UC7FP,YAAA;EFKiB;EEHhC,KAAA;EFGqD;EEDrD,OAAA;AAAA;;;;;;;;;;;AFSF;;UEMiB,iBAAA;EFNqB;;;;EEWpC,GAAA,cAAiB,GAAA,WAAc,OAAA,CAAQ,CAAA;EDzBxB;;;;;ECgCf,GAAA,cAAiB,GAAA,UAAa,KAAA,EAAO,CAAA,EAAG,UAAA,WAAqB,OAAA;ED7B7D;;;ECkCA,MAAA,CAAO,GAAA,WAAc,OAAA;AAAA;;;;;AF/BvB;;;KGGY,aAAA,IACV,GAAA,EAAK,aAAA,KACF,KAAA,GAAQ,KAAA,KAAU,OAAA,CAAQ,KAAA,GAAQ,KAAA;;;;;;;;;;;;;AHGvC;;;;;;;cG8Ba,mBAAA,SAA4B,SAAA;EAAA,iBAIe,KAAA;EAAA,iBAHrC,SAAA;cAGqC,KAAA,EAAO,iBAAA;EFhD9B;;;;;EE0D/B,GAAA,CAAI,IAAA,UAAc,QAAA,EAAU,aAAA;EAI5B,GAAA,CAAI,IAAA;EFjDM;;;;;;;;EE6DJ,MAAA,CAAO,IAAA,UAAc,GAAA,EAAK,aAAA,EAAe,IAAA,EAAM,IAAA,GAAO,OAAA,CAAQ,QAAA;EF1D/C;;;;EAAA,QE4HP,GAAA;EAAA,QAgBN,OAAA;EAAA,QAKA,WAAA;AAAA;;;;;AH3JV;;;;;;;KIWY,wBAAA;EACN,KAAA;EAAa,OAAA,QAAe,UAAA;AAAA;EAC5B,KAAA;AAAA;EACA,KAAA;IAAS,QAAA,EAAU,WAAA,CAAY,iBAAA;EAAA;AAAA;AAAA,cAOxB,uBAAA;EAAA,iBAEyC,GAAA;EAAA,iBACE,KAAA;EAAA,iBACV,SAAA;EAAA,iBAEzB,OAAA;cAJiC,GAAA,EAAK,UAAA,EACH,KAAA,EAAO,YAAA,EACjB,SAAA,EAAW,SAAA,EAEpC,OAAA,GAAU,wBAAA;EAG7B,MAAA,CAAA,GAAU,iBAAA;AAAA;;;;;;;AJ9BZ;;;;;;;;;;;;;;;;AAQA;;;;cKwBa,iBAAA,YAA6B,YAAA,EAAc,WAAA;;;;AJtCxD;;SI4CS,OAAA,CAAQ,OAAA,EAAS,wBAAA,GAA2B,aAAA;EJ5CpB;;;;EAAA,OIyDxB,YAAA,CAAa,OAAA,EAAS,kBAAA,CAAmB,wBAAA,IAA4B,aAAA;EJrDzD;;AASrB;;;;;;EIiEE,YAAA,CAAA;IAAe;EAAA,GAAa,aAAA;EJ9Dd;;;;;;EIyEd,WAAA,CAAY,OAAA,EAAS,gBAAA;AAAA;;;cC/FV,mBAAA;EAAA;;;;;;ANYb;;;;KMAY,gBAAA,WAA2B,mBAAA,eAAkC,mBAAA;;;;;;;;ANAzE;;;;iBOSgB,wBAAA,CAAyB,IAAA,WAAe,WAAA,CAAY,UAAA;;;;;;;;;;APTpE;;;;;;;;;;;;;;;;AAQA;;;iBQWgB,SAAA,CAAU,IAAA,WAAe,cAAA,GAAiB,eAAA;;;;;APzB1D;;;iBO4CgB,aAAA,CAAc,MAAA,UAAgB,WAAA;;;;;;;;ARtC9C;;;;;;;cSGa,kBAAA,YAA8B,iBAAA;EAAA,iBACZ,KAAA;cAAA,KAAA,EAAO,YAAA;EAE9B,GAAA,GAAA,CAAO,GAAA,WAAc,OAAA,CAAQ,CAAA;EAI7B,GAAA,GAAA,CAAO,GAAA,UAAa,KAAA,EAAO,CAAA,EAAG,UAAA,WAAqB,OAAA;EAKnD,MAAA,CAAO,GAAA,WAAc,OAAA;AAAA;;;;;;;;;ATf7B;;;cUIa,wBAAA,YAAoC,iBAAA;EAAA,iBAC9B,OAAA;EAEjB,GAAA,GAAA,CAAO,GAAA,WAAc,OAAA,CAAQ,CAAA;EAU7B,GAAA,GAAA,CAAO,GAAA,UAAa,KAAA,EAAO,CAAA,EAAG,UAAA,WAAqB,OAAA;EAKnD,MAAA,CAAO,GAAA,WAAc,OAAA;AAAA"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as Macroable } from "../macroable-
|
|
4
|
-
import {
|
|
5
|
-
import { t as CACHE_TOKENS } from "../cache.tokens-
|
|
6
|
-
import { n as getRateLimits, t as RateLimit } from "../rate-limit.decorator
|
|
7
|
-
import { inject } from "tsyringe";
|
|
1
|
+
import { c as DI_TOKENS, m as inject, s as CONTAINER_TOKEN, u as Singleton } from "../di-BO1QIb5H.mjs";
|
|
2
|
+
import { n as __decorateParam, t as __decorate } from "../decorate-HgTKAYK8.mjs";
|
|
3
|
+
import { t as Macroable } from "../macroable-DzlfzT50.mjs";
|
|
4
|
+
import { c as RATE_LIMITER_TOKENS, f as Module, l as RateLimiterError, s as createThrottleMiddleware, u as TooManyRequestsError } from "../module-xYoHba6B.mjs";
|
|
5
|
+
import { t as CACHE_TOKENS } from "../cache.tokens-ovi_c52J.mjs";
|
|
6
|
+
import { n as getRateLimits, t as RateLimit } from "../rate-limit.decorator-D69zdZbp.mjs";
|
|
8
7
|
//#region src/rate-limiter/limit.ts
|
|
9
8
|
/**
|
|
10
9
|
* A single rate limit window.
|
|
@@ -24,6 +23,9 @@ import { inject } from "tsyringe";
|
|
|
24
23
|
* ```
|
|
25
24
|
*/
|
|
26
25
|
var Limit = class Limit {
|
|
26
|
+
windowSeconds;
|
|
27
|
+
max;
|
|
28
|
+
disabled;
|
|
27
29
|
_key;
|
|
28
30
|
_customResponse;
|
|
29
31
|
constructor(windowSeconds, max, disabled = false) {
|
|
@@ -77,6 +79,7 @@ var Limit = class Limit {
|
|
|
77
79
|
//#endregion
|
|
78
80
|
//#region src/rate-limiter/rate-limiter-registry.ts
|
|
79
81
|
let RateLimiterRegistry = class RateLimiterRegistry extends Macroable {
|
|
82
|
+
store;
|
|
80
83
|
resolvers = /* @__PURE__ */ new Map();
|
|
81
84
|
constructor(store) {
|
|
82
85
|
super();
|
|
@@ -103,7 +106,7 @@ let RateLimiterRegistry = class RateLimiterRegistry extends Macroable {
|
|
|
103
106
|
*/
|
|
104
107
|
async handle(name, ctx, next) {
|
|
105
108
|
const resolver = this.resolvers.get(name);
|
|
106
|
-
if (!resolver) throw new
|
|
109
|
+
if (!resolver) throw new RateLimiterError(`Rate limiter "${name}" is not defined. Register it with limiter.for("${name}", ...) in a module's onInitialize hook.`);
|
|
107
110
|
const resolved = await resolver(ctx);
|
|
108
111
|
const active = (Array.isArray(resolved) ? resolved : [resolved]).filter((l) => !l.disabled);
|
|
109
112
|
if (active.length === 0) return next();
|
|
@@ -177,11 +180,7 @@ let RateLimiterRegistry = class RateLimiterRegistry extends Macroable {
|
|
|
177
180
|
};
|
|
178
181
|
}
|
|
179
182
|
};
|
|
180
|
-
RateLimiterRegistry = __decorate([
|
|
181
|
-
Transient(),
|
|
182
|
-
__decorateParam(0, inject(RATE_LIMITER_TOKENS.Store)),
|
|
183
|
-
__decorateMetadata("design:paramtypes", [Object])
|
|
184
|
-
], RateLimiterRegistry);
|
|
183
|
+
RateLimiterRegistry = __decorate([Singleton(), __decorateParam(0, inject(RATE_LIMITER_TOKENS.Store))], RateLimiterRegistry);
|
|
185
184
|
//#endregion
|
|
186
185
|
//#region src/rate-limiter/stores/kv-store.ts
|
|
187
186
|
/**
|
|
@@ -197,6 +196,7 @@ RateLimiterRegistry = __decorate([
|
|
|
197
196
|
* accuracy across edges.
|
|
198
197
|
*/
|
|
199
198
|
var KvRateLimiterStore = class {
|
|
199
|
+
cache;
|
|
200
200
|
constructor(cache) {
|
|
201
201
|
this.cache = cache;
|
|
202
202
|
}
|
|
@@ -248,6 +248,10 @@ var InMemoryRateLimiterStore = class {
|
|
|
248
248
|
//#endregion
|
|
249
249
|
//#region src/rate-limiter/stores/store-factory.ts
|
|
250
250
|
let RateLimiterStoreFactory = class RateLimiterStoreFactory {
|
|
251
|
+
env;
|
|
252
|
+
cache;
|
|
253
|
+
container;
|
|
254
|
+
options;
|
|
251
255
|
constructor(env, cache, container, options) {
|
|
252
256
|
this.env = env;
|
|
253
257
|
this.cache = cache;
|
|
@@ -255,29 +259,23 @@ let RateLimiterStoreFactory = class RateLimiterStoreFactory {
|
|
|
255
259
|
this.options = options;
|
|
256
260
|
}
|
|
257
261
|
create() {
|
|
258
|
-
if (!this.options) throw new
|
|
262
|
+
if (!this.options) throw new RateLimiterError("RateLimiterModule is not configured. Call RateLimiterModule.forRoot({ store: ... }) to configure a backing store.");
|
|
259
263
|
const { store } = this.options;
|
|
260
264
|
if (store === "memory") return new InMemoryRateLimiterStore();
|
|
261
265
|
if (store === "kv") {
|
|
262
266
|
const binding = this.env[this.options.binding];
|
|
263
|
-
if (!binding) throw new
|
|
267
|
+
if (!binding) throw new RateLimiterError(`KV binding "${String(this.options.binding)}" is not available in the environment.`);
|
|
264
268
|
return new KvRateLimiterStore(this.cache.withBinding(binding));
|
|
265
269
|
}
|
|
266
270
|
return this.container.resolve(store.useClass);
|
|
267
271
|
}
|
|
268
272
|
};
|
|
269
273
|
RateLimiterStoreFactory = __decorate([
|
|
270
|
-
|
|
274
|
+
Singleton(),
|
|
271
275
|
__decorateParam(0, inject(DI_TOKENS.CloudflareEnv)),
|
|
272
276
|
__decorateParam(1, inject(CACHE_TOKENS.CacheService)),
|
|
273
277
|
__decorateParam(2, inject(CONTAINER_TOKEN)),
|
|
274
|
-
__decorateParam(3, inject(RATE_LIMITER_TOKENS.Options, { isOptional: true }))
|
|
275
|
-
__decorateMetadata("design:paramtypes", [
|
|
276
|
-
Object,
|
|
277
|
-
Object,
|
|
278
|
-
Object,
|
|
279
|
-
Object
|
|
280
|
-
])
|
|
278
|
+
__decorateParam(3, inject(RATE_LIMITER_TOKENS.Options, { isOptional: true }))
|
|
281
279
|
], RateLimiterStoreFactory);
|
|
282
280
|
//#endregion
|
|
283
281
|
//#region src/rate-limiter/rate-limiter.module.ts
|
|
@@ -344,22 +342,19 @@ let RateLimiterModule = _RateLimiterModule = class RateLimiterModule {
|
|
|
344
342
|
RateLimiterModule = _RateLimiterModule = __decorate([Module({ providers: [
|
|
345
343
|
{
|
|
346
344
|
provide: RATE_LIMITER_TOKENS.Registry,
|
|
347
|
-
useClass: RateLimiterRegistry
|
|
348
|
-
scope: Scope.Singleton
|
|
345
|
+
useClass: RateLimiterRegistry
|
|
349
346
|
},
|
|
350
347
|
{
|
|
351
348
|
provide: RATE_LIMITER_TOKENS.StoreFactory,
|
|
352
|
-
useClass: RateLimiterStoreFactory
|
|
353
|
-
scope: Scope.Singleton
|
|
349
|
+
useClass: RateLimiterStoreFactory
|
|
354
350
|
},
|
|
355
351
|
{
|
|
356
352
|
provide: RATE_LIMITER_TOKENS.Store,
|
|
357
353
|
useFactory: (factory) => factory.create(),
|
|
358
|
-
inject: [RATE_LIMITER_TOKENS.StoreFactory]
|
|
359
|
-
scope: Scope.Singleton
|
|
354
|
+
inject: [RATE_LIMITER_TOKENS.StoreFactory]
|
|
360
355
|
}
|
|
361
356
|
] })], RateLimiterModule);
|
|
362
357
|
//#endregion
|
|
363
|
-
export { InMemoryRateLimiterStore, KvRateLimiterStore, Limit, RATE_LIMITER_TOKENS, RateLimit,
|
|
358
|
+
export { InMemoryRateLimiterStore, KvRateLimiterStore, Limit, RATE_LIMITER_TOKENS, RateLimit, RateLimiterError, RateLimiterModule, RateLimiterRegistry, RateLimiterStoreFactory, TooManyRequestsError, createThrottleMiddleware, getRateLimits };
|
|
364
359
|
|
|
365
360
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/rate-limiter/limit.ts","../../src/rate-limiter/rate-limiter-registry.ts","../../src/rate-limiter/stores/kv-store.ts","../../src/rate-limiter/stores/memory-store.ts","../../src/rate-limiter/stores/store-factory.ts","../../src/rate-limiter/rate-limiter.module.ts"],"sourcesContent":["import type { RouterContext } from '../router/router-context'\n\n/**\n * Standard rate-limit response headers passed to custom response handlers.\n * Keys are canonical HTTP header names; values are the stringified counts.\n */\nexport interface RateLimitHeaders {\n 'Retry-After': string\n 'X-RateLimit-Limit': string\n 'X-RateLimit-Remaining': string\n 'X-RateLimit-Reset': string\n}\n\n/**\n * Custom response handler invoked when a limit is exceeded and the user\n * supplied `.response()` on the {@link Limit}. Receives the request context\n * and the precomputed standard headers (which the handler can spread, drop,\n * or override).\n */\nexport type RateLimitResponseHandler = (\n ctx: RouterContext,\n headers: RateLimitHeaders,\n) => Response | Promise<Response>\n\n/**\n * A single rate limit window.\n *\n * Build via the static factories (`perSecond`, `perMinute`, `perMinutes`,\n * `perHour`, `perDay`, `none`). Chain `.by(key)` to scope per-actor and\n * `.response(handler)` to override the default 429 response.\n *\n * Returned (singly or as an array) by limiter resolvers registered via\n * `RateLimiterRegistry.for()`.\n *\n * @example\n * ```typescript\n * Limit.perMinute(60).by(ctx.header('cf-connecting-ip') ?? 'global')\n * Limit.perHour(100).by(userId)\n * Limit.none() // bypass\n * ```\n */\nexport class Limit {\n private _key?: string\n private _customResponse?: RateLimitResponseHandler\n\n private constructor(\n public readonly windowSeconds: number,\n public readonly max: number,\n public readonly disabled = false,\n ) {}\n\n static perSecond(max: number): Limit {\n return new Limit(1, max)\n }\n\n static perSeconds(seconds: number, max: number): Limit {\n return new Limit(seconds, max)\n }\n\n static perMinute(max: number): Limit {\n return new Limit(60, max)\n }\n\n static perMinutes(minutes: number, max: number): Limit {\n return new Limit(minutes * 60, max)\n }\n\n static perHour(max: number): Limit {\n return new Limit(60 * 60, max)\n }\n\n static perDay(max: number): Limit {\n return new Limit(24 * 60 * 60, max)\n }\n\n /** Bypass the limiter entirely for this request. */\n static none(): Limit {\n return new Limit(0, 0, true)\n }\n\n /** Scope this limit to a specific actor (user id, IP, tenant, etc.). */\n by(key: string | number): this {\n this._key = String(key)\n return this\n }\n\n /**\n * Override the default 429 response. The handler receives the standard\n * `RateLimitHeaders` so it can spread them onto its own Response or omit\n * them as it sees fit.\n */\n response(handler: RateLimitResponseHandler): this {\n this._customResponse = handler\n return this\n }\n\n get key(): string | undefined {\n return this._key\n }\n\n get customResponse(): RateLimitResponseHandler | undefined {\n return this._customResponse\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { Macroable } from '../macroable'\nimport type { Next } from '../router/middleware.interface'\nimport type { RouterContext } from '../router/router-context'\nimport { RateLimiterNotDefinedError, TooManyRequestsError } from './errors'\nimport type { Limit, RateLimitHeaders } from './limit'\nimport { RATE_LIMITER_TOKENS } from './rate-limiter.tokens'\nimport type { IRateLimiterStore, RateLimitHit } from './stores/rate-limiter-store.interface'\n\n/**\n * Resolver function registered via {@link RateLimiterRegistry.for}. Receives\n * the request context and returns the limit (or limits) that apply to this\n * request. Return `Limit.none()` to bypass for the current request.\n */\nexport type LimitResolver = (\n ctx: RouterContext,\n) => Limit | Limit[] | Promise<Limit | Limit[]>\n\ninterface StoredHit {\n count: number\n resetAt: number\n}\n\n/**\n * Central registry of named rate limiters and the request-time enforcement\n * pipeline. Resolved as a singleton; consumed by `ThrottleMiddleware`.\n *\n * Register limiters in a module's `onInitialize` hook:\n * ```typescript\n * @Module({})\n * export class RateLimitsModule implements OnInitialize {\n * onInitialize({ container }: ModuleContext): void {\n * const limiter = container.resolve<RateLimiterRegistry>(RATE_LIMITER_TOKENS.Registry)\n * limiter.for('api', (ctx) => Limit.perMinute(60).by(ctx.header('cf-connecting-ip') ?? '*'))\n * }\n * }\n * ```\n *\n * Extensible via `Macroable`: adapter packages (e.g. `@stratal/framework/auth`)\n * can attach extra registration methods such as `forPath()` for better-auth\n * `customRules` interop.\n */\n// IMPORTANT: do not pass a token to @Transient — that would self-register\n// the class globally at module-load time, making the Registry resolvable\n// even when the user never imported RateLimiterModule. We rely on\n// RateLimiterModule providers being the only binding source, so\n// `{ isOptional: true }` in ThrottleMiddleware correctly returns undefined\n// when the module is missing.\n@Transient()\nexport class RateLimiterRegistry extends Macroable {\n private readonly resolvers = new Map<string, LimitResolver>()\n\n constructor(\n @inject(RATE_LIMITER_TOKENS.Store) private readonly store: IRateLimiterStore,\n ) {\n super()\n }\n\n /**\n * Register a named limiter. Names must be unique; calling `for()` again\n * with the same name overwrites the previous resolver (matches Laravel\n * `RateLimiter::for` semantics — last definition wins).\n */\n for(name: string, resolver: LimitResolver): void {\n this.resolvers.set(name, resolver)\n }\n\n has(name: string): boolean {\n return this.resolvers.has(name)\n }\n\n /**\n * Enforce the named limiter for the current request. Called by\n * `ThrottleMiddleware` (the per-name class produced by\n * `createThrottleMiddleware`). Resolves the limiter, increments the store\n * for each non-bypassed limit, sets `X-RateLimit-*` headers on success, and\n * either invokes the limit's custom `.response()` or throws\n * {@link TooManyRequestsError} when a limit is exceeded.\n */\n async handle(name: string, ctx: RouterContext, next: Next): Promise<Response | void> {\n const resolver = this.resolvers.get(name)\n if (!resolver) {\n throw new RateLimiterNotDefinedError(name)\n }\n\n const resolved = await resolver(ctx)\n const limits = Array.isArray(resolved) ? resolved : [resolved]\n const active = limits.filter((l) => !l.disabled)\n\n if (active.length === 0) {\n return next()\n }\n\n let mostRestrictive: { limit: Limit; remaining: number; resetAt: number } | undefined\n let exceeded: { limit: Limit; resetAt: number } | undefined\n\n for (const limit of active) {\n const key = this.makeKey(name, limit.windowSeconds, limit.key)\n const hit = await this.hit(key, limit.windowSeconds)\n\n if (hit.count > limit.max) {\n if (!exceeded || hit.resetAt > exceeded.resetAt) {\n exceeded = { limit, resetAt: hit.resetAt }\n }\n continue\n }\n\n const remaining = limit.max - hit.count\n if (!mostRestrictive || remaining < mostRestrictive.remaining) {\n mostRestrictive = { limit, remaining, resetAt: hit.resetAt }\n }\n }\n\n if (exceeded) {\n const headers = this.makeHeaders(exceeded.limit.max, 0, exceeded.resetAt)\n if (exceeded.limit.customResponse) {\n return exceeded.limit.customResponse(ctx, headers)\n }\n throw new TooManyRequestsError({\n retryAfter: Number(headers['Retry-After']),\n limit: exceeded.limit.max,\n resetAt: exceeded.resetAt,\n })\n }\n\n await next()\n\n if (mostRestrictive) {\n const headers = this.makeHeaders(\n mostRestrictive.limit.max,\n mostRestrictive.remaining,\n mostRestrictive.resetAt,\n )\n // Hono populates ctx.c.res after next() — same pattern as logger.middleware.ts.\n const downstream = ctx.c.res\n downstream.headers.set('X-RateLimit-Limit', headers['X-RateLimit-Limit'])\n downstream.headers.set('X-RateLimit-Remaining', headers['X-RateLimit-Remaining'])\n downstream.headers.set('X-RateLimit-Reset', headers['X-RateLimit-Reset'])\n }\n }\n\n /**\n * Get-modify-set increment over the typed KV store. Not atomic across\n * concurrent edge requests on KV — see `KvRateLimiterStore`'s caveat.\n */\n private async hit(key: string, windowSeconds: number): Promise<RateLimitHit> {\n const now = Date.now()\n const existing = await this.store.get<StoredHit>(key)\n\n let next: StoredHit\n if (!existing || existing.resetAt <= now) {\n next = { count: 1, resetAt: now + windowSeconds * 1000 }\n } else {\n next = { count: existing.count + 1, resetAt: existing.resetAt }\n }\n\n const ttlSeconds = Math.max(1, Math.ceil((next.resetAt - now) / 1000))\n await this.store.set(key, next, ttlSeconds)\n return next\n }\n\n private makeKey(name: string, windowSeconds: number, by: string | undefined): string {\n const actor = by ?? '*'\n return `rl:${name}:${windowSeconds}:${actor}`\n }\n\n private makeHeaders(limit: number, remaining: number, resetAt: number): RateLimitHeaders {\n const retryAfter = Math.max(1, Math.ceil((resetAt - Date.now()) / 1000))\n return {\n 'Retry-After': String(retryAfter),\n 'X-RateLimit-Limit': String(limit),\n 'X-RateLimit-Remaining': String(Math.max(0, remaining)),\n 'X-RateLimit-Reset': String(Math.ceil(resetAt / 1000)),\n }\n }\n}\n","import type { CacheService } from '../../cache/services/cache.service'\nimport type { IRateLimiterStore } from './rate-limiter-store.interface'\n\n/**\n * Cloudflare KV-backed typed KV store.\n *\n * KV's minimum `expirationTtl` is 60 seconds; sub-60s windows are still\n * enforced by the registry's algorithm via the persisted `resetAt`, but\n * the key itself may live in KV longer than the logical window.\n *\n * KV has no native atomic increment, so concurrent writes from different\n * edge locations may undercount under high contention. That's an inherent\n * KV tradeoff — pick `{ useClass: MyDurableObjectStore }` for strict\n * accuracy across edges.\n */\nexport class KvRateLimiterStore implements IRateLimiterStore {\n constructor(private readonly cache: CacheService) {}\n\n async get<T>(key: string): Promise<T | null> {\n return (await this.cache.get<T>(key, 'json')) ?? null\n }\n\n async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n const ttl = Math.max(60, Math.ceil(ttlSeconds))\n await this.cache.put(key, JSON.stringify(value), { expirationTtl: ttl })\n }\n\n async delete(key: string): Promise<void> {\n await this.cache.delete(key)\n }\n}\n","import type { IRateLimiterStore } from './rate-limiter-store.interface'\n\ninterface Entry {\n value: unknown\n expiresAt: number\n}\n\n/**\n * In-process Map-backed typed KV store.\n *\n * Suitable for tests and single-isolate scenarios. Not safe across\n * Cloudflare Worker isolates — entries reset whenever a fresh isolate\n * spins up. Use `KvRateLimiterStore` (or a custom Durable Object store)\n * for production. Expiry is lazy: stale entries are dropped on the next\n * `get`.\n */\nexport class InMemoryRateLimiterStore implements IRateLimiterStore {\n private readonly entries = new Map<string, Entry>()\n\n get<T>(key: string): Promise<T | null> {\n const entry = this.entries.get(key)\n if (!entry) return Promise.resolve(null)\n if (entry.expiresAt <= Date.now()) {\n this.entries.delete(key)\n return Promise.resolve(null)\n }\n return Promise.resolve(entry.value as T)\n }\n\n set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n this.entries.set(key, { value, expiresAt: Date.now() + ttlSeconds * 1000 })\n return Promise.resolve()\n }\n\n delete(key: string): Promise<void> {\n this.entries.delete(key)\n return Promise.resolve()\n }\n}\n","import { inject } from 'tsyringe'\nimport { CACHE_TOKENS } from '../../cache/cache.tokens'\nimport type { CacheService } from '../../cache/services/cache.service'\nimport { CONTAINER_TOKEN, type Container } from '../../di'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport type { StratalEnv } from '../../env'\nimport type { Constructor } from '../../types'\nimport { RateLimiterNotConfiguredError } from '../errors'\nimport { RATE_LIMITER_TOKENS } from '../rate-limiter.tokens'\nimport { KvRateLimiterStore } from './kv-store'\nimport { InMemoryRateLimiterStore } from './memory-store'\nimport type { IRateLimiterStore } from './rate-limiter-store.interface'\n\n/**\n * Configuration for `RateLimiterModule.forRoot()`. Picks the backing store.\n *\n * - `'kv'`: Cloudflare KV. `binding` names the KVNamespace on `StratalEnv`.\n * - `'memory'`: in-process Map. Tests / single-isolate only.\n * - `{ useClass }`: any class implementing `IRateLimiterStore`. Resolved\n * from the DI container (so the class can declare its own `@inject` deps,\n * e.g. a Durable Object namespace from `StratalEnv`).\n */\nexport type RateLimiterModuleOptions =\n | { store: 'kv'; binding: keyof StratalEnv }\n | { store: 'memory' }\n | { store: { useClass: Constructor<IRateLimiterStore> } }\n\n// IMPORTANT: see RateLimiterRegistry — no token on @Transient so the\n// factory isn't globally bound at class-load time. Module providers are\n// the sole binding source, which keeps the \"module not imported\" detection\n// in ThrottleMiddleware working.\n@Transient()\nexport class RateLimiterStoreFactory {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(CACHE_TOKENS.CacheService) private readonly cache: CacheService,\n @inject(CONTAINER_TOKEN) private readonly container: Container,\n @inject(RATE_LIMITER_TOKENS.Options, { isOptional: true })\n private readonly options?: RateLimiterModuleOptions,\n ) {}\n\n create(): IRateLimiterStore {\n if (!this.options) {\n throw new RateLimiterNotConfiguredError()\n }\n\n const { store } = this.options\n\n if (store === 'memory') {\n return new InMemoryRateLimiterStore()\n }\n\n if (store === 'kv') {\n const binding = this.env[this.options.binding] as KVNamespace | undefined\n if (!binding) {\n throw new RateLimiterNotConfiguredError()\n }\n return new KvRateLimiterStore(this.cache.withBinding(binding))\n }\n\n return this.container.resolve<IRateLimiterStore>(store.useClass)\n }\n}\n","import { Scope } from '../di/types'\nimport type { ExceptionHandler } from '../errors/exception-handler'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule, ModuleContext, OnException, OnInitialize } from '../module/types'\nimport { TooManyRequestsError } from './errors'\nimport { RateLimiterRegistry } from './rate-limiter-registry'\nimport { RATE_LIMITER_TOKENS } from './rate-limiter.tokens'\nimport { RateLimiterStoreFactory, type RateLimiterModuleOptions } from './stores/store-factory'\nimport type { IRateLimiterStore } from './stores/rate-limiter-store.interface'\n\n/**\n * Rate limiter module — opt-in, NOT registered automatically by Application.\n *\n * Usage:\n * ```typescript\n * @Module({\n * imports: [RateLimiterModule.forRoot({ store: 'kv', binding: 'RATE_LIMITS' })],\n * })\n * export class AppModule {}\n * ```\n *\n * Define limiters in any module's `onInitialize` hook by resolving\n * `RATE_LIMITER_TOKENS.Registry` from the container. Apply them with\n * `router.throttle('name')` or `@RateLimit('name')`.\n *\n * The module:\n * - eagerly validates the store at app boot (`onInitialize` resolves the\n * factory and calls `create()`); a missing `forRoot` surfaces\n * `RateLimiterNotConfiguredError` before any request is served.\n * - registers a `respond()` callback on the `ExceptionHandler` (via\n * `onException`) that injects `Retry-After` and `X-RateLimit-*` headers\n * on every {@link TooManyRequestsError} response, regardless of whether\n * the body was rendered as JSON, HTML, or via Inertia.\n */\n@Module({\n providers: [\n { provide: RATE_LIMITER_TOKENS.Registry, useClass: RateLimiterRegistry, scope: Scope.Singleton },\n { provide: RATE_LIMITER_TOKENS.StoreFactory, useClass: RateLimiterStoreFactory, scope: Scope.Singleton },\n {\n provide: RATE_LIMITER_TOKENS.Store,\n useFactory: (factory: RateLimiterStoreFactory) => factory.create(),\n inject: [RATE_LIMITER_TOKENS.StoreFactory],\n scope: Scope.Singleton,\n },\n ],\n})\nexport class RateLimiterModule implements OnInitialize, OnException {\n /**\n * Configure the rate limiter with a store choice.\n *\n * @param options - `{ store: 'kv', binding }` | `{ store: 'memory' }` | `{ store: { useClass } }`\n */\n static forRoot(options: RateLimiterModuleOptions): DynamicModule {\n return {\n module: RateLimiterModule,\n providers: [\n { provide: RATE_LIMITER_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n /**\n * Async configuration. Use when store options depend on other services\n * (e.g. ConfigService).\n */\n static forRootAsync(options: AsyncModuleOptions<RateLimiterModuleOptions>): DynamicModule {\n return {\n module: RateLimiterModule,\n providers: [\n {\n provide: RATE_LIMITER_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n /**\n * Eagerly resolve the store so a missing `forRoot()` fails at boot,\n * not on the first throttled request. Also registers a per-app marker\n * value so ThrottleMiddleware can distinguish \"module imported into this\n * app\" from \"module class loaded somewhere in the process\" (the latter is\n * indistinguishable at the DI layer because @Module's `registry()` call\n * binds providers globally at decoration time).\n */\n onInitialize({ container }: ModuleContext): void {\n container.resolve<IRateLimiterStore>(RATE_LIMITER_TOKENS.Store)\n container.registerValue(RATE_LIMITER_TOKENS.ModuleMarker, { imported: true })\n }\n\n /**\n * Inject `Retry-After` + `X-RateLimit-*` headers on every 429 response.\n * The body itself is rendered by `ExceptionHandler.defaultRender` —\n * content-negotiated, so HTML clients (browsers, Inertia) and JSON\n * clients both get the right shape.\n */\n onException(handler: ExceptionHandler): void {\n handler.dontReport([TooManyRequestsError])\n handler.respond((response, error) => {\n if (!(error instanceof TooManyRequestsError)) return response\n response.headers.set('Retry-After', String(error.info.retryAfter))\n response.headers.set('X-RateLimit-Limit', String(error.info.limit))\n response.headers.set('X-RateLimit-Remaining', '0')\n response.headers.set('X-RateLimit-Reset', String(Math.ceil(error.info.resetAt / 1000)))\n return response\n })\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/rate-limiter/limit.ts","../../src/rate-limiter/rate-limiter-registry.ts","../../src/rate-limiter/stores/kv-store.ts","../../src/rate-limiter/stores/memory-store.ts","../../src/rate-limiter/stores/store-factory.ts","../../src/rate-limiter/rate-limiter.module.ts"],"sourcesContent":["import type { RouterContext } from '../router/router-context'\n\n/**\n * Standard rate-limit response headers passed to custom response handlers.\n * Keys are canonical HTTP header names; values are the stringified counts.\n */\nexport interface RateLimitHeaders {\n 'Retry-After': string\n 'X-RateLimit-Limit': string\n 'X-RateLimit-Remaining': string\n 'X-RateLimit-Reset': string\n}\n\n/**\n * Custom response handler invoked when a limit is exceeded and the user\n * supplied `.response()` on the {@link Limit}. Receives the request context\n * and the precomputed standard headers (which the handler can spread, drop,\n * or override).\n */\nexport type RateLimitResponseHandler = (\n ctx: RouterContext,\n headers: RateLimitHeaders,\n) => Response | Promise<Response>\n\n/**\n * A single rate limit window.\n *\n * Build via the static factories (`perSecond`, `perMinute`, `perMinutes`,\n * `perHour`, `perDay`, `none`). Chain `.by(key)` to scope per-actor and\n * `.response(handler)` to override the default 429 response.\n *\n * Returned (singly or as an array) by limiter resolvers registered via\n * `RateLimiterRegistry.for()`.\n *\n * @example\n * ```typescript\n * Limit.perMinute(60).by(ctx.header('cf-connecting-ip') ?? 'global')\n * Limit.perHour(100).by(userId)\n * Limit.none() // bypass\n * ```\n */\nexport class Limit {\n private _key?: string\n private _customResponse?: RateLimitResponseHandler\n\n private constructor(\n public readonly windowSeconds: number,\n public readonly max: number,\n public readonly disabled = false,\n ) {}\n\n static perSecond(max: number): Limit {\n return new Limit(1, max)\n }\n\n static perSeconds(seconds: number, max: number): Limit {\n return new Limit(seconds, max)\n }\n\n static perMinute(max: number): Limit {\n return new Limit(60, max)\n }\n\n static perMinutes(minutes: number, max: number): Limit {\n return new Limit(minutes * 60, max)\n }\n\n static perHour(max: number): Limit {\n return new Limit(60 * 60, max)\n }\n\n static perDay(max: number): Limit {\n return new Limit(24 * 60 * 60, max)\n }\n\n /** Bypass the limiter entirely for this request. */\n static none(): Limit {\n return new Limit(0, 0, true)\n }\n\n /** Scope this limit to a specific actor (user id, IP, tenant, etc.). */\n by(key: string | number): this {\n this._key = String(key)\n return this\n }\n\n /**\n * Override the default 429 response. The handler receives the standard\n * `RateLimitHeaders` so it can spread them onto its own Response or omit\n * them as it sees fit.\n */\n response(handler: RateLimitResponseHandler): this {\n this._customResponse = handler\n return this\n }\n\n get key(): string | undefined {\n return this._key\n }\n\n get customResponse(): RateLimitResponseHandler | undefined {\n return this._customResponse\n }\n}\n","import { inject } from '../di'\nimport { Singleton } from '../di/decorators'\nimport { Macroable } from '../macroable'\nimport type { Next } from '../router/middleware.interface'\nimport type { RouterContext } from '../router/router-context'\nimport { RateLimiterError, TooManyRequestsError } from './errors'\nimport type { Limit, RateLimitHeaders } from './limit'\nimport { RATE_LIMITER_TOKENS } from './rate-limiter.tokens'\nimport type { IRateLimiterStore, RateLimitHit } from './stores/rate-limiter-store.interface'\n\n/**\n * Resolver function registered via {@link RateLimiterRegistry.for}. Receives\n * the request context and returns the limit (or limits) that apply to this\n * request. Return `Limit.none()` to bypass for the current request.\n */\nexport type LimitResolver = (\n ctx: RouterContext,\n) => Limit | Limit[] | Promise<Limit | Limit[]>\n\ninterface StoredHit {\n count: number\n resetAt: number\n}\n\n/**\n * Central registry of named rate limiters and the request-time enforcement\n * pipeline. Resolved as a singleton; consumed by `ThrottleMiddleware`.\n *\n * Register limiters in a module's `onInitialize` hook:\n * ```typescript\n * @Module({})\n * export class RateLimitsModule implements OnInitialize {\n * onInitialize({ container }: ModuleContext): void {\n * const limiter = container.resolve<RateLimiterRegistry>(RATE_LIMITER_TOKENS.Registry)\n * limiter.for('api', (ctx) => Limit.perMinute(60).by(ctx.header('cf-connecting-ip') ?? '*'))\n * }\n * }\n * ```\n *\n * Extensible via `Macroable`: adapter packages (e.g. `@stratal/framework/auth`)\n * can attach extra registration methods such as `forPath()` for better-auth\n * `customRules` interop.\n */\n// IMPORTANT: do not pass a token to @Singleton — that would self-register\n// the class globally at module-load time, making the Registry resolvable\n// even when the user never imported RateLimiterModule. We rely on\n// RateLimiterModule providers being the only binding source, so\n// `{ isOptional: true }` in ThrottleMiddleware correctly returns undefined\n// when the module is missing.\n@Singleton()\nexport class RateLimiterRegistry extends Macroable {\n private readonly resolvers = new Map<string, LimitResolver>()\n\n constructor(\n @inject(RATE_LIMITER_TOKENS.Store) private readonly store: IRateLimiterStore,\n ) {\n super()\n }\n\n /**\n * Register a named limiter. Names must be unique; calling `for()` again\n * with the same name overwrites the previous resolver (matches Laravel\n * `RateLimiter::for` semantics — last definition wins).\n */\n for(name: string, resolver: LimitResolver): void {\n this.resolvers.set(name, resolver)\n }\n\n has(name: string): boolean {\n return this.resolvers.has(name)\n }\n\n /**\n * Enforce the named limiter for the current request. Called by\n * `ThrottleMiddleware` (the per-name class produced by\n * `createThrottleMiddleware`). Resolves the limiter, increments the store\n * for each non-bypassed limit, sets `X-RateLimit-*` headers on success, and\n * either invokes the limit's custom `.response()` or throws\n * {@link TooManyRequestsError} when a limit is exceeded.\n */\n async handle(name: string, ctx: RouterContext, next: Next): Promise<Response | void> {\n const resolver = this.resolvers.get(name)\n if (!resolver) {\n throw new RateLimiterError(`Rate limiter \"${name}\" is not defined. Register it with limiter.for(\"${name}\", ...) in a module's onInitialize hook.`)\n }\n\n const resolved = await resolver(ctx)\n const limits = Array.isArray(resolved) ? resolved : [resolved]\n const active = limits.filter((l) => !l.disabled)\n\n if (active.length === 0) {\n return next()\n }\n\n let mostRestrictive: { limit: Limit; remaining: number; resetAt: number } | undefined\n let exceeded: { limit: Limit; resetAt: number } | undefined\n\n for (const limit of active) {\n const key = this.makeKey(name, limit.windowSeconds, limit.key)\n const hit = await this.hit(key, limit.windowSeconds)\n\n if (hit.count > limit.max) {\n if (!exceeded || hit.resetAt > exceeded.resetAt) {\n exceeded = { limit, resetAt: hit.resetAt }\n }\n continue\n }\n\n const remaining = limit.max - hit.count\n if (!mostRestrictive || remaining < mostRestrictive.remaining) {\n mostRestrictive = { limit, remaining, resetAt: hit.resetAt }\n }\n }\n\n if (exceeded) {\n const headers = this.makeHeaders(exceeded.limit.max, 0, exceeded.resetAt)\n if (exceeded.limit.customResponse) {\n return exceeded.limit.customResponse(ctx, headers)\n }\n throw new TooManyRequestsError({\n retryAfter: Number(headers['Retry-After']),\n limit: exceeded.limit.max,\n resetAt: exceeded.resetAt,\n })\n }\n\n await next()\n\n if (mostRestrictive) {\n const headers = this.makeHeaders(\n mostRestrictive.limit.max,\n mostRestrictive.remaining,\n mostRestrictive.resetAt,\n )\n // Hono populates ctx.c.res after next() — same pattern as logger.middleware.ts.\n const downstream = ctx.c.res\n downstream.headers.set('X-RateLimit-Limit', headers['X-RateLimit-Limit'])\n downstream.headers.set('X-RateLimit-Remaining', headers['X-RateLimit-Remaining'])\n downstream.headers.set('X-RateLimit-Reset', headers['X-RateLimit-Reset'])\n }\n }\n\n /**\n * Get-modify-set increment over the typed KV store. Not atomic across\n * concurrent edge requests on KV — see `KvRateLimiterStore`'s caveat.\n */\n private async hit(key: string, windowSeconds: number): Promise<RateLimitHit> {\n const now = Date.now()\n const existing = await this.store.get<StoredHit>(key)\n\n let next: StoredHit\n if (!existing || existing.resetAt <= now) {\n next = { count: 1, resetAt: now + windowSeconds * 1000 }\n } else {\n next = { count: existing.count + 1, resetAt: existing.resetAt }\n }\n\n const ttlSeconds = Math.max(1, Math.ceil((next.resetAt - now) / 1000))\n await this.store.set(key, next, ttlSeconds)\n return next\n }\n\n private makeKey(name: string, windowSeconds: number, by: string | undefined): string {\n const actor = by ?? '*'\n return `rl:${name}:${windowSeconds}:${actor}`\n }\n\n private makeHeaders(limit: number, remaining: number, resetAt: number): RateLimitHeaders {\n const retryAfter = Math.max(1, Math.ceil((resetAt - Date.now()) / 1000))\n return {\n 'Retry-After': String(retryAfter),\n 'X-RateLimit-Limit': String(limit),\n 'X-RateLimit-Remaining': String(Math.max(0, remaining)),\n 'X-RateLimit-Reset': String(Math.ceil(resetAt / 1000)),\n }\n }\n}\n","import type { CacheService } from '../../cache/services/cache.service'\nimport type { IRateLimiterStore } from './rate-limiter-store.interface'\n\n/**\n * Cloudflare KV-backed typed KV store.\n *\n * KV's minimum `expirationTtl` is 60 seconds; sub-60s windows are still\n * enforced by the registry's algorithm via the persisted `resetAt`, but\n * the key itself may live in KV longer than the logical window.\n *\n * KV has no native atomic increment, so concurrent writes from different\n * edge locations may undercount under high contention. That's an inherent\n * KV tradeoff — pick `{ useClass: MyDurableObjectStore }` for strict\n * accuracy across edges.\n */\nexport class KvRateLimiterStore implements IRateLimiterStore {\n constructor(private readonly cache: CacheService) {}\n\n async get<T>(key: string): Promise<T | null> {\n return (await this.cache.get<T>(key, 'json')) ?? null\n }\n\n async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n const ttl = Math.max(60, Math.ceil(ttlSeconds))\n await this.cache.put(key, JSON.stringify(value), { expirationTtl: ttl })\n }\n\n async delete(key: string): Promise<void> {\n await this.cache.delete(key)\n }\n}\n","import type { IRateLimiterStore } from './rate-limiter-store.interface'\n\ninterface Entry {\n value: unknown\n expiresAt: number\n}\n\n/**\n * In-process Map-backed typed KV store.\n *\n * Suitable for tests and single-isolate scenarios. Not safe across\n * Cloudflare Worker isolates — entries reset whenever a fresh isolate\n * spins up. Use `KvRateLimiterStore` (or a custom Durable Object store)\n * for production. Expiry is lazy: stale entries are dropped on the next\n * `get`.\n */\nexport class InMemoryRateLimiterStore implements IRateLimiterStore {\n private readonly entries = new Map<string, Entry>()\n\n get<T>(key: string): Promise<T | null> {\n const entry = this.entries.get(key)\n if (!entry) return Promise.resolve(null)\n if (entry.expiresAt <= Date.now()) {\n this.entries.delete(key)\n return Promise.resolve(null)\n }\n return Promise.resolve(entry.value as T)\n }\n\n set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {\n this.entries.set(key, { value, expiresAt: Date.now() + ttlSeconds * 1000 })\n return Promise.resolve()\n }\n\n delete(key: string): Promise<void> {\n this.entries.delete(key)\n return Promise.resolve()\n }\n}\n","import { inject } from '../../di'\nimport { CACHE_TOKENS } from '../../cache/cache.tokens'\nimport type { CacheService } from '../../cache/services/cache.service'\nimport { CONTAINER_TOKEN, type Container } from '../../di'\nimport { Singleton } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport type { StratalEnv } from '../../env'\nimport type { Constructor } from '../../types'\nimport { RateLimiterError } from '../errors'\nimport { RATE_LIMITER_TOKENS } from '../rate-limiter.tokens'\nimport { KvRateLimiterStore } from './kv-store'\nimport { InMemoryRateLimiterStore } from './memory-store'\nimport type { IRateLimiterStore } from './rate-limiter-store.interface'\n\n/**\n * Configuration for `RateLimiterModule.forRoot()`. Picks the backing store.\n *\n * - `'kv'`: Cloudflare KV. `binding` names the KVNamespace on `StratalEnv`.\n * - `'memory'`: in-process Map. Tests / single-isolate only.\n * - `{ useClass }`: any class implementing `IRateLimiterStore`. Resolved\n * from the DI container (so the class can declare its own `@inject` deps,\n * e.g. a Durable Object namespace from `StratalEnv`).\n */\nexport type RateLimiterModuleOptions =\n | { store: 'kv'; binding: keyof StratalEnv }\n | { store: 'memory' }\n | { store: { useClass: Constructor<IRateLimiterStore> } }\n\n// IMPORTANT: see RateLimiterRegistry — no token on @Singleton so the\n// factory isn't globally bound at class-load time. Module providers are\n// the sole binding source, which keeps the \"module not imported\" detection\n// in ThrottleMiddleware working.\n@Singleton()\nexport class RateLimiterStoreFactory {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(CACHE_TOKENS.CacheService) private readonly cache: CacheService,\n @inject(CONTAINER_TOKEN) private readonly container: Container,\n @inject(RATE_LIMITER_TOKENS.Options, { isOptional: true })\n private readonly options?: RateLimiterModuleOptions,\n ) {}\n\n create(): IRateLimiterStore {\n if (!this.options) {\n throw new RateLimiterError('RateLimiterModule is not configured. Call RateLimiterModule.forRoot({ store: ... }) to configure a backing store.')\n }\n\n const { store } = this.options\n\n if (store === 'memory') {\n return new InMemoryRateLimiterStore()\n }\n\n if (store === 'kv') {\n const binding = this.env[this.options.binding] as KVNamespace | undefined\n if (!binding) {\n throw new RateLimiterError(`KV binding \"${String(this.options.binding)}\" is not available in the environment.`)\n }\n return new KvRateLimiterStore(this.cache.withBinding(binding))\n }\n\n return this.container.resolve<IRateLimiterStore>(store.useClass)\n }\n}\n","import type { ExceptionHandler } from '../errors/exception-handler'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule, ModuleContext, OnException, OnInitialize } from '../module/types'\nimport { TooManyRequestsError } from './errors'\nimport { RateLimiterRegistry } from './rate-limiter-registry'\nimport { RATE_LIMITER_TOKENS } from './rate-limiter.tokens'\nimport { RateLimiterStoreFactory, type RateLimiterModuleOptions } from './stores/store-factory'\nimport type { IRateLimiterStore } from './stores/rate-limiter-store.interface'\n\n/**\n * Rate limiter module — opt-in, NOT registered automatically by Application.\n *\n * Usage:\n * ```typescript\n * @Module({\n * imports: [RateLimiterModule.forRoot({ store: 'kv', binding: 'RATE_LIMITS' })],\n * })\n * export class AppModule {}\n * ```\n *\n * Define limiters in any module's `onInitialize` hook by resolving\n * `RATE_LIMITER_TOKENS.Registry` from the container. Apply them with\n * `router.throttle('name')` or `@RateLimit('name')`.\n *\n * The module:\n * - eagerly validates the store at app boot (`onInitialize` resolves the\n * factory and calls `create()`); a missing `forRoot` surfaces\n * `RateLimiterError` before any request is served.\n * - registers a `respond()` callback on the `ExceptionHandler` (via\n * `onException`) that injects `Retry-After` and `X-RateLimit-*` headers\n * on every {@link TooManyRequestsError} response, regardless of whether\n * the body was rendered as JSON, HTML, or via Inertia.\n */\n@Module({\n providers: [\n { provide: RATE_LIMITER_TOKENS.Registry, useClass: RateLimiterRegistry },\n { provide: RATE_LIMITER_TOKENS.StoreFactory, useClass: RateLimiterStoreFactory },\n {\n provide: RATE_LIMITER_TOKENS.Store,\n useFactory: (factory: RateLimiterStoreFactory) => factory.create(),\n inject: [RATE_LIMITER_TOKENS.StoreFactory],\n },\n ],\n})\nexport class RateLimiterModule implements OnInitialize, OnException {\n /**\n * Configure the rate limiter with a store choice.\n *\n * @param options - `{ store: 'kv', binding }` | `{ store: 'memory' }` | `{ store: { useClass } }`\n */\n static forRoot(options: RateLimiterModuleOptions): DynamicModule {\n return {\n module: RateLimiterModule,\n providers: [\n { provide: RATE_LIMITER_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n /**\n * Async configuration. Use when store options depend on other services\n * (e.g. ConfigService).\n */\n static forRootAsync(options: AsyncModuleOptions<RateLimiterModuleOptions>): DynamicModule {\n return {\n module: RateLimiterModule,\n providers: [\n {\n provide: RATE_LIMITER_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n /**\n * Eagerly resolve the store so a missing `forRoot()` fails at boot,\n * not on the first throttled request. Also registers a per-app marker\n * value so ThrottleMiddleware can distinguish \"module imported into this\n * app\" from \"module class loaded somewhere in the process\" (the latter is\n * indistinguishable at the DI layer because @Module's `registry()` call\n * binds providers globally at decoration time).\n */\n onInitialize({ container }: ModuleContext): void {\n container.resolve<IRateLimiterStore>(RATE_LIMITER_TOKENS.Store)\n container.registerValue(RATE_LIMITER_TOKENS.ModuleMarker, { imported: true })\n }\n\n /**\n * Inject `Retry-After` + `X-RateLimit-*` headers on every 429 response.\n * The body itself is rendered by `ExceptionHandler.defaultRender` —\n * content-negotiated, so HTML clients (browsers, Inertia) and JSON\n * clients both get the right shape.\n */\n onException(handler: ExceptionHandler): void {\n handler.dontReport([TooManyRequestsError])\n handler.respond((response, error) => {\n if (!(error instanceof TooManyRequestsError)) return response\n response.headers.set('Retry-After', String(error.info.retryAfter))\n response.headers.set('X-RateLimit-Limit', String(error.info.limit))\n response.headers.set('X-RateLimit-Remaining', '0')\n response.headers.set('X-RateLimit-Reset', String(Math.ceil(error.info.resetAt / 1000)))\n return response\n })\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as BaseEmailProvider } from "./base-email.provider-
|
|
1
|
+
import { EmailError } from "./email/index.mjs";
|
|
2
|
+
import { t as BaseEmailProvider } from "./base-email.provider-BWZHIjt8.mjs";
|
|
3
3
|
import { Resend } from "resend";
|
|
4
4
|
//#region src/email/providers/resend.provider.ts
|
|
5
5
|
/**
|
|
@@ -9,12 +9,13 @@ import { Resend } from "resend";
|
|
|
9
9
|
* Docs: https://resend.com/docs
|
|
10
10
|
*/
|
|
11
11
|
var ResendProvider = class extends BaseEmailProvider {
|
|
12
|
+
options;
|
|
12
13
|
client;
|
|
13
14
|
defaultFrom;
|
|
14
15
|
constructor(options) {
|
|
15
16
|
super();
|
|
16
17
|
this.options = options;
|
|
17
|
-
if (!this.options.apiKey) throw new
|
|
18
|
+
if (!this.options.apiKey) throw new EmailError("Resend API key is required");
|
|
18
19
|
this.client = new Resend(this.options.apiKey);
|
|
19
20
|
this.defaultFrom = this.options.from;
|
|
20
21
|
}
|
|
@@ -37,15 +38,15 @@ var ResendProvider = class extends BaseEmailProvider {
|
|
|
37
38
|
content: await this.toBuffer(attachment.content)
|
|
38
39
|
}))) }
|
|
39
40
|
});
|
|
40
|
-
if (response.error) throw new
|
|
41
|
+
if (response.error) throw new EmailError("Resend API request failed");
|
|
41
42
|
return {
|
|
42
43
|
messageId: response.data.id,
|
|
43
44
|
accepted: true,
|
|
44
45
|
metadata: { provider: "resend" }
|
|
45
46
|
};
|
|
46
47
|
} catch (error) {
|
|
47
|
-
if (error instanceof
|
|
48
|
-
throw new
|
|
48
|
+
if (error instanceof EmailError) throw error;
|
|
49
|
+
throw new EmailError("Resend API request failed");
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
@@ -64,4 +65,4 @@ var ResendProvider = class extends BaseEmailProvider {
|
|
|
64
65
|
//#endregion
|
|
65
66
|
export { ResendProvider };
|
|
66
67
|
|
|
67
|
-
//# sourceMappingURL=resend.provider-
|
|
68
|
+
//# sourceMappingURL=resend.provider-Ur6tU7fK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.provider-Ur6tU7fK.mjs","names":[],"sources":["../src/email/providers/resend.provider.ts"],"sourcesContent":["import { Resend } from 'resend'\nimport type { ResolvedEmailAttachment, ResolvedEmailMessage } from '../contracts'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EmailError } from '../email.error'\nimport { BaseEmailProvider } from './base-email.provider'\nimport type { EmailSendResult } from './email-provider.interface'\n\n/**\n * Resend Email Provider\n *\n * Implementation of IEmailProvider using Resend API\n * Docs: https://resend.com/docs\n */\nexport class ResendProvider extends BaseEmailProvider {\n private readonly client: Resend\n private readonly defaultFrom: { name: string; email: string }\n\n constructor(\n private readonly options: EmailModuleOptions\n ) {\n super()\n\n // Validate Resend API key\n if (!this.options.apiKey) {\n throw new EmailError('Resend API key is required')\n }\n\n this.client = new Resend(this.options.apiKey)\n this.defaultFrom = this.options.from\n }\n\n async send(message: ResolvedEmailMessage): Promise<EmailSendResult> {\n try {\n const from = message.from\n ? `${message.from.name} <${message.from.email}>`\n : `${this.defaultFrom.name} <${this.defaultFrom.email}>`\n\n const to = Array.isArray(message.to) ? message.to : [message.to]\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- Resend SDK types\n const response = await this.client.emails.send({\n from,\n to,\n subject: message.subject,\n html: message.html,\n text: message.text,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment -- Resend template extension\n ...((message as any).template && { template: (message as any).template }),\n ...(message.replyTo && { replyTo: message.replyTo }),\n ...(message.cc && { cc: message.cc }),\n ...(message.bcc && { bcc: message.bcc }),\n ...(message.attachments && {\n attachments: await Promise.all(\n message.attachments.map(async attachment => ({\n filename: attachment.filename,\n content: await this.toBuffer(attachment.content),\n }))\n ),\n }),\n })\n\n if (response.error) {\n throw new EmailError('Resend API request failed')\n }\n\n return {\n messageId: response.data.id,\n accepted: true,\n metadata: {\n provider: 'resend',\n },\n }\n } catch (error) {\n if (error instanceof EmailError) {\n throw error\n }\n\n throw new EmailError('Resend API request failed')\n }\n }\n\n /**\n * Convert attachment content to Buffer\n *\n * Resend SDK expects Buffer for attachment content.\n * If content is already a Buffer, return as-is.\n * If content is a ReadableStream, convert to Buffer.\n */\n private async toBuffer(content: ResolvedEmailAttachment['content']): Promise<Buffer> {\n if (Buffer.isBuffer(content)) {\n return content\n }\n // Convert ReadableStream to Buffer\n const response = new Response(content)\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n }\n}\n"],"mappings":";;;;;;;;;;AAaA,IAAa,iBAAb,cAAoC,kBAAkB;CAKjC;CAJnB;CACA;CAEA,YACE,SACA;EACA,OAAO;EAFU,KAAA,UAAA;EAKjB,IAAI,CAAC,KAAK,QAAQ,QAChB,MAAM,IAAI,WAAW,6BAA6B;EAGpD,KAAK,SAAS,IAAI,OAAO,KAAK,QAAQ,OAAO;EAC7C,KAAK,cAAc,KAAK,QAAQ;;CAGlC,MAAM,KAAK,SAAyD;EAClE,IAAI;GACF,MAAM,OAAO,QAAQ,OACjB,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,KAC5C,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,YAAY,MAAM;GAExD,MAAM,KAAK,MAAM,QAAQ,QAAQ,GAAG,GAAG,QAAQ,KAAK,CAAC,QAAQ,GAAG;GAGhE,MAAM,WAAW,MAAM,KAAK,OAAO,OAAO,KAAK;IAC7C;IACA;IACA,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,MAAM,QAAQ;IAEd,GAAK,QAAgB,YAAY,EAAE,UAAW,QAAgB,UAAU;IACxE,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;IACnD,GAAI,QAAQ,MAAM,EAAE,IAAI,QAAQ,IAAI;IACpC,GAAI,QAAQ,OAAO,EAAE,KAAK,QAAQ,KAAK;IACvC,GAAI,QAAQ,eAAe,EACzB,aAAa,MAAM,QAAQ,IACzB,QAAQ,YAAY,IAAI,OAAM,gBAAe;KAC3C,UAAU,WAAW;KACrB,SAAS,MAAM,KAAK,SAAS,WAAW,QAAQ;KACjD,EAAE,CACJ,EACF;IACF,CAAC;GAEF,IAAI,SAAS,OACX,MAAM,IAAI,WAAW,4BAA4B;GAGnD,OAAO;IACL,WAAW,SAAS,KAAK;IACzB,UAAU;IACV,UAAU,EACR,UAAU,UACX;IACF;WACM,OAAO;GACd,IAAI,iBAAiB,YACnB,MAAM;GAGR,MAAM,IAAI,WAAW,4BAA4B;;;;;;;;;;CAWrD,MAAc,SAAS,SAA8D;EACnF,IAAI,OAAO,SAAS,QAAQ,EAC1B,OAAO;EAIT,MAAM,cAAc,MAAM,IADL,SAAS,QACI,CAAC,aAAa;EAChD,OAAO,OAAO,KAAK,YAAY"}
|
package/dist/router/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
export { All, Controller, ControllerOptions,
|
|
1
|
+
import { $n as RouteResponseObject, $t as ROUTER_TOKENS, At as uuidParamSchema, Bt as Put, Ct as createDomainMiddleware, Dt as paginatedResponseSchema, Et as errorResponseSchema, Ft as All, Gn as ConventionRouteMetadata, Gt as createMiddlewareChain, Ht as getControllerOptions, It as Delete, Jn as RouteBody, Jt as generateConventionRouteName, Kn as ExplicitRouteMetadata, Kt as extractDomainParamNames, Lt as Get, Mt as Route, Nt as getRouteDecoratedMethods, Ot as paginationQuerySchema, Pt as getRouteMetadata, Qn as RouteResponse, Qt as route, Rt as Patch, St as VerifySignatureMiddleware, Tt as commonErrorSchemas, Ut as getControllerRoute, Vt as Controller, Wn as ControllerOptions, Wt as getControllerVersion, Xn as RouteConfig, Xt as sortRoutesBySpecificity, Yn as RouteBodyObject, Yt as getPathSpecificityScore, Zn as RouteMetadata, Zt as toOpenAPIPath, _r as StratalRouteMap, _t as RouteNotFoundError, ar as HTTP_METHODS, at as Uri, bt as signUrl, cn as RouterGroupConfig, cr as SECURITY_SCHEMES, dn as HonoApp, dr as RouteMatcher, dt as SSEMessage, en as RouteRegistrationService, er as RouterEnv, fn as Middleware, fr as RouteName, ft as SSEStreamingApi, gr as SerializedRoutes, gt as SchemaValidationError, hr as SerializedRoute, ht as ResponseValidationError, in as VersioningService, ir as VersioningOptions, it as SignedUriOptions, jt as validationErrorResponseSchema, kt as successMessageSchema, ln as LocalePathService, lr as VERSION_NEUTRAL, mn as IController, mr as RoutePrefixes, mt as InvalidSignatureError, nn as RouteRegistrationInput, nr as SecurityScheme, on as RouteConfigurable, or as ROUTER_CONTEXT_KEYS, ot as UriOptions, pn as Next, pr as RouteParams, pt as StreamingApi, qn as LocalePathConfig, qt as extractParamNames, rn as RouteRegistry, rr as TrailingSlashMode, rt as RouterContext, sn as Router, sr as ROUTE_METADATA_KEYS, st as buildRouteUrl, tn as RegisteredRoute, tr as RouterVariables, un as ResolvedPath, ur as CurrentRoute, vt as RouterError, wt as parseDomainPattern, xt as verifySignedUrl, yt as SignedUrlOptions, zt as Post } from "../index-DEncMcC6.mjs";
|
|
2
|
+
export { All, Controller, ControllerOptions, ConventionRouteMetadata, CurrentRoute, Delete, ExplicitRouteMetadata, Get, HTTP_METHODS, HonoApp, IController, InvalidSignatureError, LocalePathConfig, LocalePathService, Middleware, Next, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, RegisteredRoute, ResolvedPath, ResponseValidationError, Route, RouteBody, RouteBodyObject, RouteConfig, RouteConfigurable, RouteMatcher, RouteMetadata, RouteName, RouteNotFoundError, RouteParams, RoutePrefixes, RouteRegistrationInput, RouteRegistrationService, RouteRegistry, RouteResponse, RouteResponseObject, Router, RouterContext, RouterEnv, RouterError, RouterGroupConfig, RouterVariables, SECURITY_SCHEMES, SSEMessage, SSEStreamingApi, SchemaValidationError, SecurityScheme, SerializedRoute, SerializedRoutes, SignedUriOptions, SignedUrlOptions, StratalRouteMap, StreamingApi, TrailingSlashMode, Uri, UriOptions, VERSION_NEUTRAL, VerifySignatureMiddleware, VersioningOptions, VersioningService, buildRouteUrl, commonErrorSchemas, createDomainMiddleware, createMiddlewareChain, errorResponseSchema, extractDomainParamNames, extractParamNames, generateConventionRouteName, getControllerOptions, getControllerRoute, getControllerVersion, getPathSpecificityScore, getRouteDecoratedMethods, getRouteMetadata, paginatedResponseSchema, paginationQuerySchema, parseDomainPattern, route, signUrl, sortRoutesBySpecificity, successMessageSchema, toOpenAPIPath, uuidParamSchema, validationErrorResponseSchema, verifySignedUrl };
|
package/dist/router/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { i as
|
|
5
|
-
import {
|
|
6
|
-
import { n as
|
|
7
|
-
|
|
1
|
+
import { o as ROUTER_TOKENS } from "../di-BO1QIb5H.mjs";
|
|
2
|
+
import { a as RouterContext, d as SECURITY_SCHEMES, f as VERSION_NEUTRAL, l as ROUTER_CONTEXT_KEYS, s as HTTP_METHODS, u as ROUTE_METADATA_KEYS } from "../exception-context-B4kM-M53.mjs";
|
|
3
|
+
import { n as Router, o as RouterError } from "../module-xYoHba6B.mjs";
|
|
4
|
+
import { A as parseDomainPattern, C as uuidParamSchema, D as getRouteMetadata, E as getRouteDecoratedMethods, M as ResponseValidationError, N as SchemaValidationError, O as createMiddlewareChain, P as RouteNotFoundError, S as successMessageSchema, T as Route, _ as toOpenAPIPath, a as RouteRegistry, b as paginatedResponseSchema, d as RouteRegistrationService, f as extractDomainParamNames, g as sortRoutesBySpecificity, h as getPathSpecificityScore, i as route, j as InvalidSignatureError, k as createDomainMiddleware, m as generateConventionRouteName, n as Uri, o as VersioningService, p as extractParamNames, r as buildRouteUrl, s as LocalePathService, t as VerifySignatureMiddleware, u as HonoApp, v as commonErrorSchemas, w as validationErrorResponseSchema, x as paginationQuerySchema, y as errorResponseSchema } from "../router-Cy6DjkvP.mjs";
|
|
5
|
+
import { i as getControllerVersion, n as getControllerOptions, r as getControllerRoute, t as Controller } from "../controller.decorator-DIUazNU7.mjs";
|
|
6
|
+
import { a as Post, i as Patch, n as Delete, o as Put, r as Get, t as All } from "../http-method.decorator-CdjKFJZZ.mjs";
|
|
7
|
+
import { n as verifySignedUrl, t as signUrl } from "../signed-url-BqUqt5dF.mjs";
|
|
8
|
+
export { All, Controller, Delete, Get, HTTP_METHODS, HonoApp, InvalidSignatureError, LocalePathService, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, ResponseValidationError, Route, RouteNotFoundError, RouteRegistrationService, RouteRegistry, Router, RouterContext, RouterError, SECURITY_SCHEMES, SchemaValidationError, Uri, VERSION_NEUTRAL, VerifySignatureMiddleware, VersioningService, buildRouteUrl, commonErrorSchemas, createDomainMiddleware, createMiddlewareChain, errorResponseSchema, extractDomainParamNames, extractParamNames, generateConventionRouteName, getControllerOptions, getControllerRoute, getControllerVersion, getPathSpecificityScore, getRouteDecoratedMethods, getRouteMetadata, paginatedResponseSchema, paginationQuerySchema, parseDomainPattern, route, signUrl, sortRoutesBySpecificity, successMessageSchema, toOpenAPIPath, uuidParamSchema, validationErrorResponseSchema, verifySignedUrl };
|