stratal 0.0.22 → 0.0.23
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/bin/cloudflare-workers-loader.mjs +80 -7
- package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
- package/dist/bin/quarry.mjs +41 -54
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +5 -3
- package/dist/cache/index.d.mts.map +1 -1
- package/dist/cache/index.mjs +123 -39
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{cache.service-e34gV6tz.d.mts → cache.service-uElmBtdS.d.mts} +24 -34
- package/dist/cache.service-uElmBtdS.d.mts.map +1 -0
- package/dist/{command-BU4ApTo5.mjs → command-BvmUAPPQ.mjs} +15 -3
- package/dist/command-BvmUAPPQ.mjs.map +1 -0
- package/dist/{command-wXfvHbBZ.d.mts → command-CPhFHjG3.d.mts} +2 -2
- package/dist/command-CPhFHjG3.d.mts.map +1 -0
- package/dist/command-not-found.error-ONAZ2Bpk.mjs +14 -0
- package/dist/command-not-found.error-ONAZ2Bpk.mjs.map +1 -0
- package/dist/config/index.d.mts +3 -3
- package/dist/config/index.d.mts.map +1 -1
- package/dist/config/index.mjs +7 -6
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-DHQtypr1.d.mts → consumer-registry-D3iMTSdy.d.mts} +54 -22
- package/dist/consumer-registry-D3iMTSdy.d.mts.map +1 -0
- package/dist/{container-storage-GpNNz79X.mjs → container-storage-BmOJ4_Na.mjs} +1 -1
- package/dist/{container-storage-GpNNz79X.mjs.map → container-storage-BmOJ4_Na.mjs.map} +1 -1
- package/dist/{controller.decorator-DIUazNU7.mjs → controller.decorator-C5UVeJS3.mjs} +4 -4
- package/dist/{controller.decorator-DIUazNU7.mjs.map → controller.decorator-C5UVeJS3.mjs.map} +1 -1
- package/dist/cron/index.d.mts +79 -4
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +2 -2
- package/dist/cron-job-NesZRk8F.d.mts +58 -0
- package/dist/cron-job-NesZRk8F.d.mts.map +1 -0
- package/dist/{cron-manager-9bpN9bu4.mjs → cron.module-Bgzq5hiT.mjs} +17 -7
- package/dist/cron.module-Bgzq5hiT.mjs.map +1 -0
- package/dist/{decorate-HgTKAYK8.mjs → decorate-CuAoSZvs.mjs} +2 -2
- package/dist/{deep-merge-C8NgcXw4.mjs → deep-merge-ByiAOZ3r.mjs} +1 -1
- package/dist/{deep-merge-C8NgcXw4.mjs.map → deep-merge-ByiAOZ3r.mjs.map} +1 -1
- package/dist/di/index.d.mts +2 -2
- package/dist/di/index.mjs +3 -3
- package/dist/{di-BO1QIb5H.mjs → di-DseMn-z9.mjs} +244 -135
- package/dist/di-DseMn-z9.mjs.map +1 -0
- package/dist/email/index.d.mts +33 -40
- package/dist/email/index.d.mts.map +1 -1
- package/dist/email/index.mjs +456 -41
- package/dist/email/index.mjs.map +1 -1
- package/dist/{en-BPP6h6y5.mjs → en-CDZBMcc1.mjs} +2 -2
- package/dist/{en-BPP6h6y5.mjs.map → en-CDZBMcc1.mjs.map} +1 -1
- package/dist/{env-DKSbuBi5.d.mts → env-ug22bJj7.d.mts} +1 -1
- package/dist/env-ug22bJj7.d.mts.map +1 -0
- package/dist/errors/index.d.mts +1 -1
- package/dist/errors/index.mjs +3 -3
- package/dist/{errors-BBZTnjdq.mjs → errors-mXYxG0XB.mjs} +5 -5
- package/dist/{errors-BBZTnjdq.mjs.map → errors-mXYxG0XB.mjs.map} +1 -1
- package/dist/events/index.d.mts +14 -3
- package/dist/events/index.d.mts.map +1 -1
- package/dist/events/index.mjs +2 -2
- package/dist/{events-D1KdDaiP.mjs → events-BXJGZjpG.mjs} +16 -6
- package/dist/events-BXJGZjpG.mjs.map +1 -0
- package/dist/{exception-context-B4kM-M53.mjs → exception-context-kEoMFwze.mjs} +3 -3
- package/dist/{exception-context-B4kM-M53.mjs.map → exception-context-kEoMFwze.mjs.map} +1 -1
- package/dist/{gateway-context-CFe6a9gz.mjs → gateway-context-TMu_AlJt.mjs} +25 -6
- package/dist/{gateway-context-CFe6a9gz.mjs.map → gateway-context-TMu_AlJt.mjs.map} +1 -1
- 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-Ced-uNIF.mjs → guards-DALPXy3_.mjs} +2 -2
- package/dist/{guards-Ced-uNIF.mjs.map → guards-DALPXy3_.mjs.map} +1 -1
- package/dist/hono-app-CvV3hOfT.mjs +161 -0
- package/dist/hono-app-CvV3hOfT.mjs.map +1 -0
- package/dist/{http-method.decorator-CdjKFJZZ.mjs → http-method.decorator-ByWZb9DO.mjs} +4 -4
- package/dist/{http-method.decorator-CdjKFJZZ.mjs.map → http-method.decorator-ByWZb9DO.mjs.map} +1 -1
- package/dist/i18n/index.d.mts +4 -4
- package/dist/i18n/index.d.mts.map +1 -1
- package/dist/i18n/index.mjs +5 -5
- package/dist/i18n/index.mjs.map +1 -1
- package/dist/i18n/messages/en/index.d.mts +1 -1
- package/dist/i18n/messages/en/index.mjs +1 -1
- package/dist/i18n/utils/index.mjs +1 -1
- package/dist/i18n/validation/index.d.mts +3 -3
- package/dist/i18n/validation/index.mjs +3 -3
- package/dist/{i18n.module-BlXrtAlV.mjs → i18n.module-DRQAZoSZ.mjs} +14 -11
- package/dist/{i18n.module-BlXrtAlV.mjs.map → i18n.module-DRQAZoSZ.mjs.map} +1 -1
- package/dist/{i18n.tokens-hwRpmjRq.mjs → i18n.tokens-CZ_v8oyS.mjs} +1 -1
- package/dist/{i18n.tokens-hwRpmjRq.mjs.map → i18n.tokens-CZ_v8oyS.mjs.map} +1 -1
- package/dist/{index-B4UBK-2T.d.mts → index-0ItCjaqw.d.mts} +1 -1
- package/dist/index-0ItCjaqw.d.mts.map +1 -0
- package/dist/{index-CW1YHSft.d.mts → index-B5JBRcWD.d.mts} +249 -103
- package/dist/index-B5JBRcWD.d.mts.map +1 -0
- package/dist/{index-BtlE9RuO.d.mts → index-BUt92sAE.d.mts} +1 -1
- package/dist/index-BUt92sAE.d.mts.map +1 -0
- package/dist/{index-DEncMcC6.d.mts → index-B_JoEl3V.d.mts} +221 -16
- package/dist/index-B_JoEl3V.d.mts.map +1 -0
- package/dist/{index-Dj5IMwtr.d.mts → index-DtBNIFuP.d.mts} +4 -6
- package/dist/index-DtBNIFuP.d.mts.map +1 -0
- package/dist/{index-KMgSCSM7.d.mts → index-HgOLNruQ.d.mts} +1 -1
- package/dist/{index-KMgSCSM7.d.mts.map → index-HgOLNruQ.d.mts.map} +1 -1
- package/dist/index.d.mts +6 -5
- package/dist/index.mjs +3 -2
- package/dist/{is-command-CX5rAfZW.mjs → is-command-CEPO9n8c.mjs} +2 -2
- package/dist/{is-command-CX5rAfZW.mjs.map → is-command-CEPO9n8c.mjs.map} +1 -1
- package/dist/{is-seeder-CYCtELlm.mjs → is-seeder-Gvh_AM71.mjs} +1 -1
- package/dist/{is-seeder-CYCtELlm.mjs.map → is-seeder-Gvh_AM71.mjs.map} +1 -1
- package/dist/lazy-module-loader-Ib383jH_.d.mts +60 -0
- package/dist/lazy-module-loader-Ib383jH_.d.mts.map +1 -0
- package/dist/locale-path.service-D-dHiIPc.mjs +165 -0
- package/dist/locale-path.service-D-dHiIPc.mjs.map +1 -0
- package/dist/locale-url-nZrZxqJP.mjs +44 -0
- package/dist/locale-url-nZrZxqJP.mjs.map +1 -0
- package/dist/locale-url.service-C2EWmGdq.mjs +41 -0
- package/dist/locale-url.service-C2EWmGdq.mjs.map +1 -0
- package/dist/logger/index.d.mts +1 -1
- package/dist/logger/index.mjs +2 -2
- package/dist/logger/index.mjs.map +1 -1
- package/dist/macroable/index.d.mts +2 -2
- package/dist/macroable/index.mjs +1 -1
- package/dist/{macroable-DzlfzT50.mjs → macroable-cvDTFZ_A.mjs} +1 -1
- package/dist/{macroable-DzlfzT50.mjs.map → macroable-cvDTFZ_A.mjs.map} +1 -1
- package/dist/{metadata-BVkc4aUu.mjs → metadata-DzzprcID.mjs} +1 -1
- package/dist/{metadata-BVkc4aUu.mjs.map → metadata-DzzprcID.mjs.map} +1 -1
- package/dist/module/index.d.mts +4 -3
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +10 -2
- package/dist/module/index.mjs.map +1 -0
- package/dist/{module-xYoHba6B.mjs → module-registry-Dm-pqHd3.mjs} +189 -57
- package/dist/module-registry-Dm-pqHd3.mjs.map +1 -0
- package/dist/module.decorator-CYHY6pG5.mjs +19 -0
- package/dist/module.decorator-CYHY6pG5.mjs.map +1 -0
- package/dist/openapi/index.d.mts +44 -8
- package/dist/openapi/index.d.mts.map +1 -1
- package/dist/openapi/index.mjs +3 -2
- package/dist/{openapi-C6lm0RmV.mjs → openapi-CstuTM8S.mjs} +55 -229
- package/dist/openapi-CstuTM8S.mjs.map +1 -0
- package/dist/openapi-tools.service-BC5EC3R3.mjs +206 -0
- package/dist/openapi-tools.service-BC5EC3R3.mjs.map +1 -0
- package/dist/{openapi.service-CrLlsXAd.d.mts → openapi.service-YhTiJ1bO.d.mts} +3 -3
- package/dist/{openapi.service-CrLlsXAd.d.mts.map → openapi.service-YhTiJ1bO.d.mts.map} +1 -1
- package/dist/quarry/index.d.mts +14 -5
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +6 -5
- package/dist/quarry/runner.d.mts +11 -11
- package/dist/quarry/runner.d.mts.map +1 -1
- package/dist/quarry/runner.mjs +192 -22
- package/dist/quarry/runner.mjs.map +1 -1
- package/dist/{quarry-registry-D4hIGScf.d.mts → quarry-registry-CXg0RFXq.d.mts} +4 -4
- package/dist/quarry-registry-CXg0RFXq.d.mts.map +1 -0
- package/dist/{quarry-registry-DkraZNwn.mjs → quarry.module-BuRPGMDm.mjs} +22 -21
- package/dist/quarry.module-BuRPGMDm.mjs.map +1 -0
- package/dist/queue/index.d.mts +3 -3
- package/dist/queue/index.mjs +42 -31
- package/dist/queue/index.mjs.map +1 -1
- package/dist/queue.module-nddvxzCB.mjs +613 -0
- package/dist/queue.module-nddvxzCB.mjs.map +1 -0
- package/dist/queue.tokens-DjHnFmre.mjs +11 -0
- package/dist/queue.tokens-DjHnFmre.mjs.map +1 -0
- package/dist/{r2-storage.provider-Hfm6LdZQ.mjs → r2-storage.provider-DCxQt9dD.mjs} +4 -4
- package/dist/{r2-storage.provider-Hfm6LdZQ.mjs.map → r2-storage.provider-DCxQt9dD.mjs.map} +1 -1
- package/dist/{rate-limit.decorator-D69zdZbp.mjs → rate-limit.decorator-BPAie_p3.mjs} +3 -3
- package/dist/{rate-limit.decorator-D69zdZbp.mjs.map → rate-limit.decorator-BPAie_p3.mjs.map} +1 -1
- package/dist/rate-limiter/index.d.mts +5 -5
- package/dist/rate-limiter/index.d.mts.map +1 -1
- package/dist/rate-limiter/index.mjs +26 -21
- package/dist/rate-limiter/index.mjs.map +1 -1
- package/dist/route-name-DGoBOfPg.mjs +171 -0
- package/dist/route-name-DGoBOfPg.mjs.map +1 -0
- package/dist/route-registration.service-D6vSwiKP.mjs +918 -0
- package/dist/route-registration.service-D6vSwiKP.mjs.map +1 -0
- package/dist/route-registry-CYqLp2Nj.mjs +123 -0
- package/dist/route-registry-CYqLp2Nj.mjs.map +1 -0
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +18 -8
- package/dist/router-CWGBD-Bg.mjs +78 -0
- package/dist/router-CWGBD-Bg.mjs.map +1 -0
- package/dist/router-resolver-D4YlPNlm.mjs +88 -0
- package/dist/router-resolver-D4YlPNlm.mjs.map +1 -0
- package/dist/seeder/index.d.mts +14 -4
- package/dist/seeder/index.d.mts.map +1 -1
- package/dist/seeder/index.mjs +5 -3
- package/dist/{seeder-BADTig4n.mjs → seeder-7ubkms-Y.mjs} +7 -56
- package/dist/seeder-7ubkms-Y.mjs.map +1 -0
- package/dist/seeder-registry-CyUmKsJq.mjs +57 -0
- package/dist/seeder-registry-CyUmKsJq.mjs.map +1 -0
- package/dist/seeder.module-CYYwk3Qk.mjs +15 -0
- package/dist/seeder.module-CYYwk3Qk.mjs.map +1 -0
- package/dist/{signed-url-BqUqt5dF.mjs → signed-url-DIU0sK_6.mjs} +1 -1
- package/dist/{signed-url-BqUqt5dF.mjs.map → signed-url-DIU0sK_6.mjs.map} +1 -1
- package/dist/storage/index.d.mts +3 -3
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +2 -2
- package/dist/storage/providers/index.d.mts +2 -2
- package/dist/storage/providers/index.d.mts.map +1 -1
- package/dist/storage/providers/index.mjs +1 -1
- package/dist/{storage-BA3ppVYM.mjs → storage-MDZypIE9.mjs} +12 -11
- package/dist/{storage-BA3ppVYM.mjs.map → storage-MDZypIE9.mjs.map} +1 -1
- package/dist/{storage-provider.interface-DQMtT42e.d.mts → storage-provider.interface-ClUwxz4S.d.mts} +2 -2
- package/dist/storage-provider.interface-ClUwxz4S.d.mts.map +1 -0
- package/dist/storage.error-Dnib4VHc.mjs +8 -0
- package/dist/{storage.error-C6FY037a.mjs.map → storage.error-Dnib4VHc.mjs.map} +1 -1
- package/dist/{stratal-Bdq4IdB3.mjs → stratal-DL9M38_s.mjs} +142 -140
- package/dist/stratal-DL9M38_s.mjs.map +1 -0
- package/dist/{stratal-BsKmvP6J.d.mts → stratal-DwDJPY9N.d.mts} +3 -3
- package/dist/{stratal-BsKmvP6J.d.mts.map → stratal-DwDJPY9N.d.mts.map} +1 -1
- package/dist/tiered-cache.service-Dv3BhxxE.d.mts +79 -0
- package/dist/tiered-cache.service-Dv3BhxxE.d.mts.map +1 -0
- package/dist/trailing-slash-CFyw8nYu.mjs +34 -0
- package/dist/trailing-slash-CFyw8nYu.mjs.map +1 -0
- package/dist/{types-BaeHi67f.d.mts → types-CmV_9xBD.d.mts} +1 -1
- package/dist/types-CmV_9xBD.d.mts.map +1 -0
- package/dist/uri-h7Q8Jug9.mjs +251 -0
- package/dist/uri-h7Q8Jug9.mjs.map +1 -0
- package/dist/{usage-generator-DTqaUMR9.mjs → usage-generator-DAWYasuP.mjs} +4 -4
- package/dist/usage-generator-DAWYasuP.mjs.map +1 -0
- package/dist/{validation-DUzcjb8Q.mjs → validation-CpOjviyT.mjs} +6 -6
- package/dist/{validation-DUzcjb8Q.mjs.map → validation-CpOjviyT.mjs.map} +1 -1
- package/dist/{validation.context-XTysWJ3b.mjs → validation.context-CRvmrhq7.mjs} +3 -3
- package/dist/{validation.context-XTysWJ3b.mjs.map → validation.context-CRvmrhq7.mjs.map} +1 -1
- package/dist/versioning.service-C6aHky8-.mjs +36 -0
- package/dist/versioning.service-C6aHky8-.mjs.map +1 -0
- package/dist/websocket/index.d.mts +11 -2
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +1 -1
- package/dist/workers/index.d.mts +2 -2
- package/dist/workers/index.d.mts.map +1 -1
- package/dist/workers/index.mjs +3 -3
- package/dist/workers/index.mjs.map +1 -1
- package/dist/{zod-hMa3rSHV.mjs → zod-eKqqhZ5_.mjs} +2 -2
- package/dist/{zod-hMa3rSHV.mjs.map → zod-eKqqhZ5_.mjs.map} +1 -1
- package/dist/{zod-DvWTfRpI.d.mts → zod-wecrEVAs.d.mts} +8 -3
- package/dist/zod-wecrEVAs.d.mts.map +1 -0
- package/package.json +19 -30
- package/dist/base-email.provider-BWZHIjt8.mjs +0 -42
- package/dist/base-email.provider-BWZHIjt8.mjs.map +0 -1
- package/dist/cache.service-e34gV6tz.d.mts.map +0 -1
- package/dist/cache.tokens-ovi_c52J.mjs +0 -6
- package/dist/cache.tokens-ovi_c52J.mjs.map +0 -1
- package/dist/colors-axmupKdp.mjs +0 -16
- package/dist/colors-axmupKdp.mjs.map +0 -1
- package/dist/command-BU4ApTo5.mjs.map +0 -1
- package/dist/command-wXfvHbBZ.d.mts.map +0 -1
- package/dist/consumer-registry-DHQtypr1.d.mts.map +0 -1
- package/dist/cron-manager-9bpN9bu4.mjs.map +0 -1
- package/dist/cron-manager-CSTIBPcM.d.mts +0 -124
- package/dist/cron-manager-CSTIBPcM.d.mts.map +0 -1
- package/dist/di-BO1QIb5H.mjs.map +0 -1
- package/dist/env-DKSbuBi5.d.mts.map +0 -1
- package/dist/events-D1KdDaiP.mjs.map +0 -1
- package/dist/index-B4UBK-2T.d.mts.map +0 -1
- package/dist/index-BtlE9RuO.d.mts.map +0 -1
- package/dist/index-CW1YHSft.d.mts.map +0 -1
- package/dist/index-DEncMcC6.d.mts.map +0 -1
- package/dist/index-Dj5IMwtr.d.mts.map +0 -1
- package/dist/module-xYoHba6B.mjs.map +0 -1
- package/dist/openapi-C6lm0RmV.mjs.map +0 -1
- package/dist/quarry-registry-D4hIGScf.d.mts.map +0 -1
- package/dist/quarry-registry-DkraZNwn.mjs.map +0 -1
- package/dist/queue.module-DeWJ0tQM.mjs +0 -355
- package/dist/queue.module-DeWJ0tQM.mjs.map +0 -1
- package/dist/resend.provider-Ur6tU7fK.mjs +0 -68
- package/dist/resend.provider-Ur6tU7fK.mjs.map +0 -1
- package/dist/router-Cy6DjkvP.mjs +0 -1852
- package/dist/router-Cy6DjkvP.mjs.map +0 -1
- package/dist/seeder-BADTig4n.mjs.map +0 -1
- package/dist/smtp.provider-C129sNBT.mjs +0 -76
- package/dist/smtp.provider-C129sNBT.mjs.map +0 -1
- package/dist/storage-provider.interface-DQMtT42e.d.mts.map +0 -1
- package/dist/storage.error-C6FY037a.mjs +0 -8
- package/dist/stratal-Bdq4IdB3.mjs.map +0 -1
- package/dist/types-BaeHi67f.d.mts.map +0 -1
- package/dist/usage-generator-DTqaUMR9.mjs.map +0 -1
- package/dist/zod-DvWTfRpI.d.mts.map +0 -1
- /package/dist/{chunk-D1SwGrFN.mjs → chunk-BBjsoOtd.mjs} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-Dj5IMwtr.d.mts","names":[],"sources":["../src/i18n/validation/with-zod-i18n.ts","../src/i18n/validation/validation.context.ts","../src/i18n/validation/index.ts"],"mappings":";;;;;;;;;;;AA0BA;;;;;;;;;;;;;;;;iBAAgB,WAAA,CACd,GAAA,EAAK,WAAA,EACL,MAAA,GAAS,MAAA;EACN,KAAA,GAAQ,MAAA,EAAQ,YAAA;AAAA;;;;;;;;cCtBR,WAAA,EAAW,KAAA,CAAA,WAAA,CAAuB,cAAA,CAAvB,SAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"module-xYoHba6B.mjs","names":["internal.getDefaultEntry","internal.getGroups","internal.getGlobalMiddleware"],"sources":["../src/module/module.error.ts","../src/module/module.decorator.ts","../src/rate-limiter/errors.ts","../src/rate-limiter/rate-limiter.tokens.ts","../src/rate-limiter/throttle.middleware.ts","../src/router/router.error.ts","../src/router/router.internals.ts","../src/router/router.ts","../src/module/module-registry.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class ModuleError extends ApplicationError {}\n","import { defineMetadata, getMetadata, hasMetadata } from '../di/metadata'\nimport type { Constructor } from '../types'\nimport type { ModuleOptions } from './types'\n\nexport const MODULE_OPTIONS_KEY = Symbol.for('stratal:module:options')\n\nexport function Module(options: ModuleOptions) {\n return <TFunction extends abstract new (...args: never[]) => unknown>(target: TFunction): TFunction => {\n defineMetadata(MODULE_OPTIONS_KEY, options, target)\n return target\n }\n}\n\nexport function getModuleOptions(target: Constructor): ModuleOptions | undefined {\n return getMetadata<ModuleOptions>(MODULE_OPTIONS_KEY, target)\n}\n\nexport function isModuleClass(target: unknown): target is Constructor {\n return typeof target === 'function' && hasMetadata(MODULE_OPTIONS_KEY, target)\n}\n","import { ApplicationError, HttpException } from '../errors'\n\n/**\n * Thrown when a request exceeds a configured rate limit.\n *\n * HTTP Status: 429 Too Many Requests\n *\n * The {@link ExceptionHandler} renders the body via content negotiation\n * (HTML for HTML clients, JSON for everything else). Standard rate-limit\n * headers (`Retry-After`, `X-RateLimit-*`) are injected by the\n * `respond()` callback registered via `RateLimiterModule.onException`.\n */\nexport class TooManyRequestsError extends HttpException {\n constructor(\n public readonly info: { retryAfter: number; limit: number; resetAt: number },\n ) {\n super(429, 'Too many requests')\n }\n}\n\nexport class RateLimiterError extends ApplicationError {}\n","export const RATE_LIMITER_TOKENS = {\n Registry: Symbol.for('stratal:rate-limiter:registry'),\n Store: Symbol.for('stratal:rate-limiter:store'),\n StoreFactory: Symbol.for('stratal:rate-limiter:store-factory'),\n Options: Symbol.for('stratal:rate-limiter:options'),\n /**\n * Per-app marker registered by RateLimiterModule.onInitialize. Used by\n * ThrottleMiddleware to detect \"module not imported\".\n */\n ModuleMarker: Symbol.for('stratal:rate-limiter:module-marker'),\n} as const\n\nexport type RateLimiterToken = (typeof RATE_LIMITER_TOKENS)[keyof typeof RATE_LIMITER_TOKENS]\n","import { inject } from '../di'\nimport { CONTAINER_TOKEN, type Container } from '../di'\nimport { Transient } from '../di/decorators'\nimport type { Middleware, Next } from '../router/middleware.interface'\nimport type { RouterContext } from '../router/router-context'\nimport type { Constructor } from '../types'\nimport { RateLimiterError } from './errors'\nimport type { RateLimiterRegistry } from './rate-limiter-registry'\nimport { RATE_LIMITER_TOKENS } from './rate-limiter.tokens'\n\nconst cache = new Map<string, Constructor<Middleware>>()\n\n/**\n * Memoized factory that produces a Stratal `Middleware` class bound to a\n * named limiter. Calling twice with the same name returns the *same* class\n * — important for `Router.middleware` deduplication via class identity.\n *\n * Detection of \"module not imported\" works against a per-app marker\n * registered by `RateLimiterModule.onInitialize`. We check\n * `isRegistered(marker)` at request time before resolving Registry.\n */\nexport function createThrottleMiddleware(name: string): Constructor<Middleware> {\n const existing = cache.get(name)\n if (existing) return existing\n\n @Transient()\n class ThrottleMiddleware implements Middleware {\n constructor(\n @inject(CONTAINER_TOKEN) private readonly container: Container,\n ) {}\n\n handle(ctx: RouterContext, next: Next): Promise<Response | void> {\n if (!this.container.isRegistered(RATE_LIMITER_TOKENS.ModuleMarker)) {\n throw new RateLimiterError(`RateLimiterModule was not imported. Cannot resolve throttle \"${name}\". Import RateLimiterModule.forRoot({ store: ... }) in your AppModule.`)\n }\n const registry = this.container.resolve<RateLimiterRegistry>(RATE_LIMITER_TOKENS.Registry)\n return registry.handle(name, ctx, next)\n }\n }\n\n Object.defineProperty(ThrottleMiddleware, 'name', { value: `Throttle(${name})` })\n cache.set(name, ThrottleMiddleware)\n return ThrottleMiddleware\n}\n\n/**\n * Test-only escape hatch: clear the per-name middleware class cache.\n * Production code never needs this.\n */\nexport function _resetThrottleMiddlewareCache(): void {\n cache.clear()\n}\n","import { ApplicationError } from '../errors'\n\nexport class RouterError extends ApplicationError {}\n","/**\n * Symbol keys for Router internal accessors.\n *\n * These symbols are NOT exported from the public `stratal/router` barrel.\n * Only internal modules (RouterResolver) import them, keeping the Router's\n * public API clean — users never see these methods.\n *\n * Declared as individual unique symbols so TypeScript can distinguish\n * their return types in computed property access.\n *\n * @internal\n */\n\n/** @internal */\nexport const getDefaultEntry: unique symbol = Symbol('Router.getDefaultEntry')\n/** @internal */\nexport const getGroups: unique symbol = Symbol('Router.getGroups')\n/** @internal */\nexport const getGlobalMiddleware: unique symbol = Symbol('Router.getGlobalMiddleware')\n","import type { ZodObject } from '../i18n/validation/zod'\nimport { createThrottleMiddleware } from '../rate-limiter/throttle.middleware'\nimport type { Constructor } from '../types'\nimport { RouterError } from './router.error'\nimport type { Middleware } from './middleware.interface'\nimport * as internal from './router.internals'\n\n/**\n * Configuration for a sub-group created via `router.group()`.\n */\nexport interface RouterGroupConfig {\n prefix?: string\n domain?: string\n name?: string\n middleware?: Constructor<Middleware>[]\n version?: string | string[]\n hideFromDocs?: boolean\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ZodObject generics require any for flexible shape parameter\n params?: ZodObject<any>\n}\n\n/**\n * Internal entry representing a sub-group or the default scope.\n * @internal — used by RouterResolver, not exported publicly.\n */\nexport interface RouterEntry {\n prefix?: string\n domain?: string\n name?: string\n middleware: Constructor<Middleware>[]\n version?: string | string[]\n hideFromDocs?: boolean\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ZodObject generics require any for flexible shape parameter\n params?: ZodObject<any>\n /** Controllers in this entry. undefined = all controllers not in any sub-group */\n controllers?: Constructor[]\n}\n\n/**\n * Modules implement this to configure routes and middleware.\n * Replaces `MiddlewareConfigurable`.\n *\n * @example\n * ```typescript\n * @Module({ controllers: [UsersController, PostsController] })\n * export class ApiModule implements RouteConfigurable {\n * configureRoutes(router: Router): void {\n * router\n * .name('api.')\n * .middleware(CorsMiddleware)\n * .version('1')\n * }\n * }\n * ```\n */\nexport interface RouteConfigurable {\n configureRoutes(router: Router): void\n}\n\n/**\n * Fluent builder for route and middleware configuration.\n *\n * Scoped methods (`middleware()`, `prefix()`, `domain()`, `name()`, `version()`, `hideFromDocs()`)\n * apply only to this module's controllers or the sub-group's controllers.\n *\n * `use()` registers global middleware (all routes in the entire app).\n * Only callable on the root Router — throws inside `group()` callbacks.\n *\n * `group()` creates sub-groups for specific controllers with a callback.\n * Controllers in a sub-group are excluded from the parent scope.\n */\nexport class Router {\n private readonly _isChild: boolean\n private readonly _defaultEntry: RouterEntry = { middleware: [] }\n private readonly _groups: RouterEntry[] = []\n private readonly _globalMiddleware: Constructor<Middleware>[] = []\n\n constructor(isChild = false) {\n this._isChild = isChild\n }\n\n /** Dynamic path prefix. For shared segments like `/:companyId` */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ZodObject generics require any for flexible shape parameter\n prefix(path: string, params?: ZodObject<any>): this {\n this._defaultEntry.prefix = path\n this._defaultEntry.params = params\n return this\n }\n\n /** Domain pattern for controllers in this scope */\n domain(pattern: string): this {\n this._defaultEntry.domain = pattern\n return this\n }\n\n /** Name prefix for routes in this scope */\n name(prefix: string): this {\n this._defaultEntry.name = prefix\n return this\n }\n\n /** Middleware applied to controllers in this scope */\n middleware(...middlewares: Constructor<Middleware>[]): this {\n this._defaultEntry.middleware.push(...middlewares)\n return this\n }\n\n /**\n * Apply a named rate limiter to controllers in this scope.\n *\n * The named limiter must be registered via `RateLimiterRegistry.for(name, ...)`\n * (typically inside a module's `onInitialize` hook), and the user must\n * import `RateLimiterModule.forRoot({ store: ... })` in their AppModule.\n *\n * @example\n * ```typescript\n * router.prefix('/uploads').throttle('uploads')\n * router.group([AdminController], (admin) => admin.throttle('admin'))\n * ```\n */\n throttle(name: string): this {\n this._defaultEntry.middleware.push(createThrottleMiddleware(name))\n return this\n }\n\n /** API version for controllers in this scope */\n version(version: string | string[]): this {\n this._defaultEntry.version = version\n return this\n }\n\n /** Hide/show routes in this scope from OpenAPI docs */\n hideFromDocs(hide = true): this {\n this._defaultEntry.hideFromDocs = hide\n return this\n }\n\n /**\n * Global middleware — applied to ALL routes in the entire app.\n * Only callable on the root Router. Throws if called inside `group()`.\n */\n use(...middlewares: Constructor<Middleware>[]): this {\n if (this._isChild) {\n throw new RouterError('router.use() is only allowed on the root Router. It cannot be called inside a group() callback.')\n }\n this._globalMiddleware.push(...middlewares)\n return this\n }\n\n /**\n * Create a sub-group for specific controllers/gateways.\n * Controllers in a sub-group are excluded from the parent (default) scope.\n * The callback receives a new Router (without `use()`) for fluent configuration.\n */\n group(controllers: Constructor[], callback: (router: Omit<Router, 'use'>) => void): this {\n const childRouter = new Router(true)\n callback(childRouter)\n\n this._groups.push({\n ...childRouter._defaultEntry,\n controllers,\n })\n return this\n }\n\n // --- Internal accessors via symbol keys (invisible to consumers) ---\n\n [internal.getDefaultEntry](): RouterEntry {\n return this._defaultEntry\n }\n\n [internal.getGroups](): RouterEntry[] {\n return this._groups\n }\n\n [internal.getGlobalMiddleware](): Constructor<Middleware>[] {\n return this._globalMiddleware\n }\n}\n","import type { Container } from '../di/container'\nimport { isListener } from '../events'\nimport type { LoggerService } from '../logger'\nimport { Router, type RouteConfigurable } from '../router/router'\nimport { isCommand } from '../quarry/is-command'\nimport { isSeeder } from '../seeder/is-seeder'\nimport type { Constructor } from '../types'\nimport { getModuleOptions } from './module.decorator'\nimport type { ExceptionHandler } from '../errors/exception-handler'\nimport type {\n DynamicModule,\n ModuleClass,\n ModuleContext,\n ModuleOptions,\n OnException,\n OnInitialize,\n OnShutdown,\n Provider,\n} from './types'\n\n\ninterface RegisteredModule {\n moduleClass: Constructor\n options: ModuleOptions\n instance: object | null\n hasLifecycle: boolean\n}\n\nexport class ModuleRegistry {\n private modules: RegisteredModule[] = []\n private registeredClasses = new Set<Constructor>()\n private initialized = false\n\n private allControllers: Constructor[] = []\n private allConsumers: Constructor[] = []\n private allJobs: Constructor[] = []\n private allListeners: Constructor[] = []\n private allCommands: Constructor[] = []\n private allSeeders: Constructor[] = []\n private allRouterConfigs: { router: Router; controllers: Constructor[] }[] = []\n\n constructor(\n private readonly container: Container,\n private readonly logger: LoggerService\n ) { }\n\n register(moduleOrDynamic: ModuleClass | DynamicModule): void {\n const { moduleClass, options } = this.resolveModule(moduleOrDynamic)\n const isDynamic = this.isDynamicModule(moduleOrDynamic)\n\n if (this.registeredClasses.has(moduleClass)) {\n if (isDynamic) {\n this.logger.debug(`Module ${moduleClass.name} already registered, registering DynamicModule providers`)\n const { module: _, ...dynamicRest } = moduleOrDynamic\n for (const provider of dynamicRest.providers ?? []) {\n this.registerProvider(provider)\n }\n } else {\n this.logger.debug(`Module ${moduleClass.name} already registered, skipping`)\n }\n return\n }\n\n this.registeredClasses.add(moduleClass)\n this.logger.info(`Registering module: ${moduleClass.name}`)\n\n for (const ImportedModule of options.imports ?? []) {\n this.register(ImportedModule)\n }\n\n for (const provider of options.providers ?? []) {\n this.registerProvider(provider)\n }\n\n for (const controller of options.controllers ?? []) {\n this.container.register(controller)\n this.allControllers.push(controller)\n }\n\n for (const consumer of options.consumers ?? []) {\n this.container.register(consumer)\n this.allConsumers.push(consumer)\n this.logger.info(`Collected consumer: ${consumer.name}`, { queueCount: this.allConsumers.length })\n }\n\n for (const job of options.jobs ?? []) {\n this.container.register(job)\n this.allJobs.push(job)\n }\n\n const hasLifecycle =\n 'onInitialize' in moduleClass.prototype ||\n 'onShutdown' in moduleClass.prototype ||\n 'onException' in moduleClass.prototype ||\n 'configureRoutes' in moduleClass.prototype\n\n this.modules.push({ moduleClass, options, instance: null, hasLifecycle })\n }\n\n registerAll(modules: (ModuleClass | DynamicModule)[]): void {\n for (const module of modules) {\n this.register(module)\n }\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return\n\n this.logger.info('Initializing modules...')\n\n const context: ModuleContext = {\n container: this.container,\n logger: this.logger,\n }\n\n for (const registered of this.modules) {\n if (!registered.hasLifecycle) continue\n\n const instance = new registered.moduleClass()\n registered.instance = instance\n\n if (this.hasOnInitialize(instance)) {\n this.logger.info(`Initializing: ${registered.moduleClass.name}`)\n await instance.onInitialize(context)\n }\n }\n\n this.initialized = true\n this.logger.info('All modules initialized')\n }\n\n getAllControllers(): Constructor[] {\n return this.allControllers\n }\n\n getAllConsumers(): Constructor[] {\n return this.allConsumers\n }\n\n getAllJobs(): Constructor[] {\n return this.allJobs\n }\n\n getAllListeners(): Constructor[] {\n return this.allListeners\n }\n\n getAllCommands(): Constructor[] {\n return this.allCommands\n }\n\n getAllSeeders(): Constructor[] {\n return this.allSeeders\n }\n\n getAllRouterConfigs(): { router: Router; controllers: Constructor[] }[] {\n if (this.allRouterConfigs.length === 0) {\n for (const { moduleClass, options, instance } of this.modules) {\n if (instance && this.hasRouteConfigurable(instance)) {\n this.logger.debug(`Configuring routes for: ${moduleClass.name}`)\n const router = new Router()\n instance.configureRoutes(router)\n const moduleControllers = options.controllers ?? []\n this.allRouterConfigs.push({ router, controllers: moduleControllers })\n }\n }\n }\n return this.allRouterConfigs\n }\n\n configureExceptionHandlers(handler: ExceptionHandler): void {\n for (const { moduleClass, instance } of this.modules) {\n if (instance && this.hasOnException(instance)) {\n this.logger.debug(`Configuring exception handlers for: ${moduleClass.name}`)\n instance.onException(handler)\n }\n }\n }\n\n async shutdown(): Promise<void> {\n this.logger.info('Shutting down modules...')\n\n const context: ModuleContext = {\n container: this.container,\n logger: this.logger,\n }\n\n const reversed = [...this.modules].reverse()\n\n for (const { moduleClass, instance } of reversed) {\n if (instance && this.hasOnShutdown(instance)) {\n try {\n await instance.onShutdown(context)\n } catch (error) {\n this.logger.error(`Error shutting down ${moduleClass.name}:`, error as Error)\n }\n }\n }\n\n this.logger.info('All modules shut down')\n }\n\n private hasRouteConfigurable(instance: unknown): instance is RouteConfigurable {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'configureRoutes' in instance &&\n typeof (instance as RouteConfigurable).configureRoutes === 'function'\n )\n }\n\n private hasOnInitialize(instance: unknown): instance is OnInitialize {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'onInitialize' in instance &&\n typeof (instance as OnInitialize).onInitialize === 'function'\n )\n }\n\n private hasOnShutdown(instance: unknown): instance is OnShutdown {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'onShutdown' in instance &&\n typeof (instance as OnShutdown).onShutdown === 'function'\n )\n }\n\n private hasOnException(instance: unknown): instance is OnException {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'onException' in instance &&\n typeof (instance as OnException).onException === 'function'\n )\n }\n\n private resolveModule(moduleOrDynamic: ModuleClass | DynamicModule): {\n moduleClass: Constructor\n options: ModuleOptions\n } {\n if (this.isDynamicModule(moduleOrDynamic)) {\n const { module: moduleClass, ...dynamicRest } = moduleOrDynamic\n\n const decoratorOptions = getModuleOptions(moduleClass) ?? {}\n const mergedOptions: ModuleOptions = {\n ...decoratorOptions,\n ...dynamicRest,\n providers: [...(decoratorOptions.providers ?? []), ...(dynamicRest.providers ?? [])],\n imports: [...(decoratorOptions.imports ?? [])],\n }\n\n return { moduleClass: moduleClass, options: mergedOptions }\n }\n\n const moduleClass = moduleOrDynamic as Constructor\n const options = getModuleOptions(moduleClass) ?? {}\n return { moduleClass, options }\n }\n\n private isDynamicModule(value: unknown): value is DynamicModule {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'module' in value &&\n typeof (value as DynamicModule).module === 'function'\n )\n }\n\n private registerProvider(provider: Provider): void {\n if (typeof provider === 'function') {\n this.container.register(provider as Constructor)\n this.collectIfListener(provider as Constructor)\n this.collectIfCommand(provider as Constructor)\n this.collectIfSeeder(provider as Constructor)\n } else if ('useClass' in provider) {\n this.container.register(provider.provide, provider.useClass as Constructor)\n this.collectIfListener(provider.useClass as Constructor)\n this.collectIfCommand(provider.useClass as Constructor)\n this.collectIfSeeder(provider.useClass as Constructor)\n } else if ('useValue' in provider) {\n this.container.registerValue(provider.provide, provider.useValue)\n } else if ('useFactory' in provider) {\n const { provide, useFactory, inject = [] } = provider\n this.container.registerFactory(provide, (c) => {\n const deps = inject.map((token) => c.resolve(token))\n return useFactory(...deps)\n })\n } else if ('useExisting' in provider) {\n this.container.registerExisting(provider.provide, provider.useExisting)\n }\n }\n\n private collectIfCommand(providerClass: Constructor): void {\n if (isCommand(providerClass) && !this.allCommands.includes(providerClass)) {\n this.allCommands.push(providerClass)\n this.logger.debug(`Collected command: ${providerClass.name}`)\n }\n }\n\n private collectIfSeeder(providerClass: Constructor): void {\n if (isSeeder(providerClass) && !this.allSeeders.includes(providerClass)) {\n this.allSeeders.push(providerClass)\n this.logger.debug(`Collected seeder: ${providerClass.name}`)\n }\n }\n\n private collectIfListener(providerClass: Constructor): void {\n if (isListener(providerClass)) {\n this.container.registerSingleton(providerClass)\n this.allListeners.push(providerClass)\n this.logger.debug(`Collected listener: ${providerClass.name}`)\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-C6lm0RmV.mjs","names":[],"sources":["../src/openapi/openapi.tokens.ts","../src/openapi/services/openapi-config.service.ts","../src/openapi/services/openapi.service.ts","../src/openapi/openapi.module.ts","../src/openapi/services/openapi-tools.service.ts"],"sourcesContent":["/**\n * OpenAPI Module DI Tokens\n */\nexport const OPENAPI_TOKENS = {\n /** Static options provided via forRoot() */\n Options: Symbol.for('stratal:openapi:options'),\n\n /** Request-scoped config service that supports runtime overrides */\n ConfigService: Symbol.for('stratal:openapi:config:service'),\n\n /** OpenAPI service that generates specs and serves endpoints */\n OpenAPIService: Symbol.for('stratal:openapi:service'),\n} as const\n","import { inject } from '../../di'\nimport { Request } from '../../di/decorators'\nimport { OPENAPI_TOKENS } from '../openapi.tokens'\nimport type {\n IOpenAPIConfigService,\n OpenAPIConfigOverride,\n OpenAPIEffectiveConfig,\n OpenAPIModuleOptions\n} from '../types'\n\n/**\n * OpenAPI Config Service\n *\n * Request-scoped service that manages OpenAPI configuration for the current request.\n * Supports runtime overrides via middleware while preserving base configuration.\n *\n * @example\n * ```typescript\n * // In middleware (e.g., RouteAccessMiddleware)\n * constructor(\n * @inject(OPENAPI_TOKENS.ConfigService) private openAPIConfig: IOpenAPIConfigService\n * ) {}\n *\n * async handle(ctx, next) {\n * this.openAPIConfig.override({\n * info: { title: 'Custom API' },\n * routeFilter: (path) => shouldInclude(path)\n * })\n * await next()\n * }\n * ```\n */\n@Request(OPENAPI_TOKENS.ConfigService)\nexport class OpenAPIConfigService implements IOpenAPIConfigService {\n private overrides: OpenAPIConfigOverride[] = []\n\n constructor(\n @inject(OPENAPI_TOKENS.Options, { isOptional: true }) private baseOptions?: OpenAPIModuleOptions\n ) { }\n\n /**\n * Add configuration override for this request.\n * Overrides are merged in the order they are added.\n */\n override(config: OpenAPIConfigOverride): void {\n this.overrides.push(config)\n }\n\n /** Get effective configuration (base merged with all overrides) */\n getEffectiveConfig(): OpenAPIEffectiveConfig {\n let effective: OpenAPIEffectiveConfig = {\n jsonPath: this.baseOptions?.jsonPath ?? '/api/openapi.json',\n ui: this.baseOptions?.ui,\n info: {\n title: this.baseOptions?.info?.title ?? 'API',\n version: this.baseOptions?.info?.version ?? '1.0.0',\n description: this.baseOptions?.info?.description\n },\n securitySchemes: this.baseOptions?.securitySchemes\n }\n\n for (const override of this.overrides) {\n effective = this.mergeConfig(effective, override)\n }\n\n return effective\n }\n\n /**\n * Merge override into effective config.\n * Info is shallow-merged, routeFilter is replaced.\n */\n private mergeConfig(\n base: OpenAPIEffectiveConfig,\n override: OpenAPIConfigOverride\n ): OpenAPIEffectiveConfig {\n return {\n ...base,\n info: {\n ...base.info,\n ...(override.info && {\n title: override.info.title ?? base.info.title,\n version: override.info.version ?? base.info.version,\n description: override.info.description ?? base.info.description\n })\n },\n routeFilter: override.routeFilter ?? base.routeFilter\n }\n }\n}\n","import type { Container } from '../../di/container'\nimport { Singleton } from '../../di/decorators'\nimport { I18N_TOKENS } from '../../i18n/i18n.tokens'\nimport type { II18nService } from '../../i18n/i18n.types'\nimport type { OpenAPIHono, OpenAPIObject, PathItemObject } from '../../i18n/validation/zod'\nimport { ROUTER_CONTEXT_KEYS, SECURITY_SCHEMES } from '../../router/constants'\nimport type { RouterEnv } from '../../router/types'\nimport { OPENAPI_TOKENS } from '../openapi.tokens'\nimport type { IOpenAPIConfigService, OpenAPIEffectiveConfig } from '../types'\n\n/**\n * OpenAPI Service\n *\n * Generates OpenAPI specifications with support for:\n * - Runtime configuration via OpenAPIConfigService\n * - Route filtering via custom routeFilter\n * - i18n support for titles and descriptions\n * - Security scheme definitions\n *\n * Hidden routes (hideFromDocs) are excluded at registration time via\n * @hono/zod-openapi's `hide` option and don't appear in the spec.\n *\n * Configuration is resolved per-request from OpenAPIConfigService,\n * allowing middleware to override config based on domain context.\n */\n@Singleton(OPENAPI_TOKENS.OpenAPIService)\nexport class OpenAPIService {\n\n /**\n * Generate a filtered OpenAPI spec using the user's config.\n * Usable from both HTTP handlers and CLI commands.\n */\n getSpec(app: OpenAPIHono<RouterEnv>, container: Container): OpenAPIObject {\n const configService = container.resolve<IOpenAPIConfigService>(OPENAPI_TOKENS.ConfigService)\n const i18n = container.resolve<II18nService>(I18N_TOKENS.I18nService)\n const config = configService.getEffectiveConfig()\n\n const fullSpec = app.getOpenAPIDocument({\n openapi: '3.0.0',\n info: {\n version: config.info.version,\n title: config.info.title,\n description: config.info.description,\n },\n })\n\n // Security schemes\n fullSpec.components ??= {}\n fullSpec.components.securitySchemes = this.getSecuritySchemeDefinitions(i18n)\n\n // Apply custom routeFilter if provided\n if (config.routeFilter) {\n fullSpec.paths = this.filterRoutes(\n fullSpec.paths as Record<string, PathItemObject>,\n config,\n )\n }\n\n // Filter unreferenced schemas\n if (fullSpec.components.schemas) {\n fullSpec.components.schemas = this.filterSchemas(fullSpec as unknown as Record<string, unknown>) as typeof fullSpec.components.schemas\n }\n\n return fullSpec\n }\n\n /**\n * Setup OpenAPI documentation endpoints\n */\n setupEndpoints(app: OpenAPIHono<RouterEnv>, container: Container): void {\n const configService = container.resolve<IOpenAPIConfigService>(OPENAPI_TOKENS.ConfigService)\n const config = configService.getEffectiveConfig()\n\n // OpenAPI JSON spec endpoint\n app.get(config.jsonPath, (c) => {\n const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n const fullSpec = this.getSpec(app, requestContainer)\n\n // Add servers (HTTP-specific — needs request URL context)\n const url = new URL(c.req.raw.url)\n const i18n = requestContainer.resolve<II18nService>(I18N_TOKENS.I18nService)\n fullSpec.servers = [{\n url: `${url.protocol}//${url.host}`,\n description: i18n.t('common.api.serverDescription'),\n }]\n\n return c.json(fullSpec)\n })\n this.nameLastHandler(app, 'OpenAPI', 'spec')\n\n // Docs UI endpoint\n if (config.ui !== false) {\n const uiPath = config.ui?.path ?? '/api/docs'\n const uiRenderer = config.ui?.renderer\n\n app.get(uiPath, async (c, next) => {\n const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n const requestConfigService = requestContainer.resolve<IOpenAPIConfigService>(\n OPENAPI_TOKENS.ConfigService\n )\n const effectiveConfig = requestConfigService.getEffectiveConfig()\n const uiContext = { specUrl: effectiveConfig.jsonPath, title: effectiveConfig.info.title }\n\n if (uiRenderer) {\n return uiRenderer(uiContext)(c, next)\n }\n\n const { swaggerUI } = await import('@hono/swagger-ui')\n return swaggerUI<RouterEnv>({ url: uiContext.specUrl })(c, next)\n })\n this.nameLastHandler(app, 'OpenAPI', 'docs')\n }\n }\n\n private nameLastHandler(app: OpenAPIHono<RouterEnv>, controller: string, method: string): void {\n const last = app.routes[app.routes.length - 1]\n Object.defineProperty(last.handler, 'name', { value: `http:${controller}.${method}` })\n }\n\n /**\n * Get localized security scheme definitions\n */\n private getSecuritySchemeDefinitions(i18n: II18nService) {\n return {\n [SECURITY_SCHEMES.BEARER_AUTH]: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: i18n.t('common.api.security.bearerAuth')\n },\n [SECURITY_SCHEMES.API_KEY]: {\n type: 'apiKey',\n in: 'header',\n name: 'X-API-Key',\n description: i18n.t('common.api.security.apiKey')\n },\n [SECURITY_SCHEMES.SESSION_COOKIE]: {\n type: 'apiKey',\n in: 'cookie',\n name: 'session',\n description: i18n.t('common.api.security.sessionCookie')\n }\n } as const\n }\n\n /**\n * Filter OpenAPI paths using custom routeFilter\n */\n private filterRoutes(\n paths: Record<string, PathItemObject>,\n config: OpenAPIEffectiveConfig\n ): Record<string, PathItemObject> {\n const filteredPaths: Record<string, PathItemObject> = {}\n\n for (const [path, pathItem] of Object.entries(paths)) {\n if (config.routeFilter && !config.routeFilter(path, pathItem)) {\n continue\n }\n\n filteredPaths[path] = pathItem\n }\n\n return filteredPaths\n }\n\n /**\n * Filter unreferenced schemas from OpenAPI spec\n */\n private filterSchemas(spec: Record<string, unknown>): Record<string, unknown> {\n const referencedSchemas = new Set<string>()\n\n // Collect all schema references from paths\n this.collectSchemaRefs(spec.paths, referencedSchemas)\n\n // Filter schemas to only include referenced ones\n const filteredSchemas: Record<string, unknown> = {}\n const components = spec.components as Record<string, unknown> | undefined\n if (components?.schemas) {\n const allSchemas = components.schemas as Record<string, unknown>\n let prevSize = 0\n while (referencedSchemas.size > prevSize) {\n prevSize = referencedSchemas.size\n for (const [schemaName, schemaValue] of Object.entries(allSchemas)) {\n if (referencedSchemas.has(schemaName) && !filteredSchemas[schemaName]) {\n filteredSchemas[schemaName] = schemaValue\n this.collectSchemaRefs(schemaValue, referencedSchemas)\n }\n }\n }\n }\n\n return filteredSchemas\n }\n\n /**\n * Recursively collect all schema references from an object\n */\n private collectSchemaRefs(obj: unknown, refs: Set<string>): void {\n if (!obj || typeof obj !== 'object') {\n return\n }\n\n const record = obj as Record<string, unknown>\n\n // Check if this object has a $ref property\n if (record.$ref && typeof record.$ref === 'string') {\n // Extract schema name from $ref (format: #/components/schemas/SchemaName)\n const match = /^#\\/components\\/schemas\\/(.+)$/.exec(record.$ref)\n if (match) {\n refs.add(match[1])\n }\n }\n\n // Recursively check all properties\n if (Array.isArray(obj)) {\n for (const item of obj) {\n this.collectSchemaRefs(item, refs)\n }\n } else {\n for (const value of Object.values(record)) {\n this.collectSchemaRefs(value, refs)\n }\n }\n }\n}\n","/**\n * OpenAPI Module\n *\n * Provides configurable OpenAPI documentation endpoints with runtime override support.\n *\n * Features:\n * - Configurable paths for /openapi.json and /docs\n * - Runtime config overrides via middleware\n * - i18n support for titles and descriptions\n * - Route filtering via hideFromDocs and custom routeFilter\n *\n * @example Basic usage\n * ```typescript\n * @Module({\n * imports: [\n * OpenAPIModule.forRoot({\n * info: { title: 'My API', version: '1.0.0' }\n * })\n * ]\n * })\n * export class AppModule {}\n * ```\n *\n * @example With runtime override in middleware\n * ```typescript\n * // In RouteAccessMiddleware\n * constructor(\n * @inject(OPENAPI_TOKENS.ConfigService) private openAPIConfig: IOpenAPIConfigService\n * ) {}\n *\n * async handle(ctx, next) {\n * this.openAPIConfig.override({\n * info: { title: 'Custom API' },\n * routeFilter: (path) => this.shouldInclude(path)\n * })\n * await next()\n * }\n * ```\n */\n\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport { OPENAPI_TOKENS } from './openapi.tokens'\nimport { OpenAPIConfigService, OpenAPIService } from './services'\nimport type { OpenAPIModuleOptions } from './types'\n\n/** Default options when none provided */\nconst DEFAULT_OPTIONS: OpenAPIModuleOptions = {\n jsonPath: '/api/openapi.json',\n info: {\n title: 'API',\n version: '1.0.0'\n }\n}\n\n@Module({\n providers: [\n // OpenAPI config service (request-scoped, supports runtime overrides)\n { provide: OPENAPI_TOKENS.ConfigService, useClass: OpenAPIConfigService },\n // OpenAPI service (singleton — serves spec and docs endpoints)\n { provide: OPENAPI_TOKENS.OpenAPIService, useClass: OpenAPIService },\n ],\n})\nexport class OpenAPIModule {\n /**\n * Configure OpenAPI module with static options\n *\n * @param options - OpenAPI configuration (paths, info, security schemes)\n * @returns DynamicModule with options provider\n */\n static forRoot(options: OpenAPIModuleOptions = {}): DynamicModule {\n // Merge with defaults\n const mergedOptions: OpenAPIModuleOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n info: {\n ...DEFAULT_OPTIONS.info,\n ...options.info,\n title: options.info?.title ?? DEFAULT_OPTIONS.info?.title ?? 'API',\n version: options.info?.version ?? DEFAULT_OPTIONS.info?.version ?? '1.0.0',\n }\n }\n\n return {\n module: OpenAPIModule,\n providers: [\n { provide: OPENAPI_TOKENS.Options, useValue: mergedOptions }\n ]\n }\n }\n\n static forRootAsync(options: AsyncModuleOptions<OpenAPIModuleOptions>): DynamicModule {\n return {\n module: OpenAPIModule,\n providers: [\n {\n provide: OPENAPI_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject\n },\n ]\n }\n }\n}\n","import type { OpenAPIObject } from '../../i18n/validation/zod'\nimport type { HttpMethod } from '../../router/types'\n\ntype JsonSchema = Record<string, unknown>\n\nexport interface ToolDefinition {\n name: string\n description: string\n inputSchema: JsonSchema\n method: string\n path: string\n pathParams: string[]\n}\n\nexport interface ToolExecutionResult {\n status: number\n body: string\n headers: Record<string, string>\n}\n\nexport interface ToolFilter {\n tags?: string[]\n pathPrefix?: string\n}\n\nexport type Dispatcher = (method: string, url: string, options?: {\n body?: unknown\n headers?: Record<string, string>\n}) => Promise<Response>\n\ninterface OperationObject {\n operationId?: string\n summary?: string\n description?: string\n tags?: string[]\n parameters?: ParameterObject[]\n requestBody?: RequestBodyObject\n}\n\ninterface ParameterObject {\n name: string\n in: string\n required?: boolean\n schema?: JsonSchema\n description?: string\n}\n\ninterface RequestBodyObject {\n required?: boolean\n content?: Record<string, { schema?: JsonSchema }>\n}\n\n/**\n * Converts an OpenAPI 3.0 spec into callable tool definitions.\n *\n * Plain class (no DI) — reusable across MCP, CLI, and custom tooling.\n */\nexport class OpenApiToolsService {\n private static readonly HTTP_METHODS: Set<string> = new Set<HttpMethod>(['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace'])\n\n private tools: ToolDefinition[] = []\n private toolMap = new Map<string, ToolDefinition>()\n private dispatcher?: Dispatcher\n\n private spec: OpenAPIObject\n\n constructor(spec: OpenAPIObject, options?: { dispatcher?: Dispatcher }) {\n this.spec = spec\n this.dispatcher = options?.dispatcher\n this.tools = this.buildTools()\n for (const tool of this.tools) {\n this.toolMap.set(tool.name, tool)\n }\n }\n\n getTools(filter?: ToolFilter): ToolDefinition[] {\n let tools = this.tools\n\n if (filter?.tags?.length) {\n const tagSet = new Set(filter.tags)\n tools = tools.filter((t) => {\n const op = this.getOperation(t.method, t.path)\n return op?.tags?.some((tag) => tagSet.has(tag)) ?? false\n })\n }\n\n if (filter?.pathPrefix) {\n const prefix = filter.pathPrefix\n tools = tools.filter((t) => t.path.startsWith(prefix))\n }\n\n return tools\n }\n\n getTool(name: string): ToolDefinition | undefined {\n return this.toolMap.get(name)\n }\n\n async executeTool(name: string, args: Record<string, unknown>): Promise<ToolExecutionResult> {\n const tool = this.toolMap.get(name)\n if (!tool) {\n throw new Error(`Tool not found: ${name}`)\n }\n if (!this.dispatcher) {\n throw new Error('No dispatcher configured')\n }\n\n // Interpolate path params\n let url = tool.path\n for (const param of tool.pathParams) {\n const value = args[`path_${param}`]\n if (value === undefined) {\n throw new Error(`Missing required path parameter: ${param}`)\n }\n url = url.replace(`{${param}}`, encodeURIComponent(value != null && typeof value === 'object' ? JSON.stringify(value) : String(value as string | number | boolean)))\n }\n\n // Collect query params\n const queryParts: string[] = []\n for (const key of Object.keys(args)) {\n if (key.startsWith('query_')) {\n const paramName = key.slice(6)\n const value = args[key]\n queryParts.push(`${encodeURIComponent(paramName)}=${encodeURIComponent(value != null && typeof value === 'object' ? JSON.stringify(value) : String(value as string | number | boolean))}`)\n }\n }\n if (queryParts.length > 0) {\n url += `?${queryParts.join('&')}`\n }\n\n const body = args.body\n const response = await this.dispatcher(\n tool.method.toUpperCase(),\n url,\n body !== undefined ? { body } : undefined,\n )\n\n const responseHeaders: Record<string, string> = {}\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value\n })\n\n return {\n status: response.status,\n body: await response.text(),\n headers: responseHeaders,\n }\n }\n\n private buildTools(): ToolDefinition[] {\n const tools: ToolDefinition[] = []\n const paths = this.spec.paths\n\n for (const [path, pathItem] of Object.entries(paths)) {\n for (const [method, operation] of Object.entries(pathItem)) {\n if (!operation || typeof operation !== 'object') continue\n if (!OpenApiToolsService.HTTP_METHODS.has(method.toLowerCase())) continue\n\n const op = operation as OperationObject\n const name = op.operationId ?? this.generateName(method, path)\n const description = this.buildDescription(op, method, path)\n const pathItemParams = (pathItem as Record<string, unknown>).parameters as ParameterObject[] | undefined\n const mergedParameters = this.mergeParameters(pathItemParams, op.parameters)\n const { schema, pathParams } = this.buildInputSchema({ ...op, parameters: mergedParameters })\n\n tools.push({ name, description, inputSchema: schema, method: method.toUpperCase(), path, pathParams })\n }\n }\n\n return tools\n }\n\n private generateName(method: string, path: string): string {\n const snake = path\n .replace(/[{}]/g, '')\n .replace(/[^a-zA-Z0-9]+/g, '_')\n .replace(/^_|_$/g, '')\n .toLowerCase()\n return `${method.toLowerCase()}_${snake}`\n }\n\n private buildDescription(op: OperationObject, method: string, path: string): string {\n if (op.summary && op.description) {\n return `${op.summary} — ${op.description}`\n }\n return op.summary ?? op.description ?? `${method.toUpperCase()} ${path}`\n }\n\n private buildInputSchema(op: OperationObject): { schema: JsonSchema; pathParams: string[] } {\n const properties: Record<string, JsonSchema> = {}\n const required: string[] = []\n const pathParams: string[] = []\n\n // Parameters (path + query)\n for (const param of op.parameters ?? []) {\n const resolvedParam = this.resolveRef(param) as ParameterObject\n const paramSchema = resolvedParam.schema ? this.resolveRef(resolvedParam.schema) as JsonSchema : { type: 'string' }\n\n if (resolvedParam.in === 'path') {\n pathParams.push(resolvedParam.name)\n const key = `path_${resolvedParam.name}`\n properties[key] = { ...paramSchema }\n if (resolvedParam.description) properties[key].description = resolvedParam.description\n required.push(key)\n } else if (resolvedParam.in === 'query') {\n const key = `query_${resolvedParam.name}`\n properties[key] = { ...paramSchema }\n if (resolvedParam.description) properties[key].description = resolvedParam.description\n if (resolvedParam.required) required.push(key)\n }\n }\n\n // Request body\n const resolvedBody = op.requestBody ? this.resolveRef(op.requestBody) as RequestBodyObject : undefined\n if (resolvedBody) {\n const jsonContent = resolvedBody.content?.['application/json']\n if (jsonContent?.schema) {\n properties.body = this.resolveRef(jsonContent.schema) as JsonSchema\n } else {\n properties.body = { type: 'object' }\n }\n const needsBody = resolvedBody.required === true\n if (needsBody) {\n required.push('body')\n }\n }\n\n const schema: JsonSchema = { type: 'object', properties }\n if (required.length > 0) {\n schema.required = required\n }\n\n return { schema, pathParams }\n }\n\n private mergeParameters(pathLevel?: ParameterObject[], opLevel?: ParameterObject[]): ParameterObject[] {\n const resolvedPathLevel = pathLevel?.map((p) => this.resolveRef(p) as ParameterObject)\n const resolvedOpLevel = opLevel?.map((p) => this.resolveRef(p) as ParameterObject)\n\n if (!resolvedPathLevel?.length) return resolvedOpLevel ?? []\n if (!resolvedOpLevel?.length) return resolvedPathLevel\n\n const opKeys = new Set(resolvedOpLevel.map((p) => `${p.in}:${p.name}`))\n const inherited = resolvedPathLevel.filter((p) => !opKeys.has(`${p.in}:${p.name}`))\n return [...inherited, ...resolvedOpLevel]\n }\n\n private resolveRef(obj: unknown, seen = new Set<string>()): unknown {\n if (!obj || typeof obj !== 'object') return obj\n const record = obj as Record<string, unknown>\n\n if (typeof record.$ref === 'string') {\n const refPath = record.$ref\n if (seen.has(refPath)) return obj\n seen.add(refPath)\n\n const resolved = this.lookupRef(refPath)\n if (!resolved) return obj\n return this.resolveRef(resolved, seen)\n }\n\n // Recursively walk all properties and resolve nested $refs\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (Array.isArray(value)) {\n result[key] = value.map((item) => this.resolveRef(item, seen))\n } else if (value && typeof value === 'object') {\n result[key] = this.resolveRef(value, seen)\n } else {\n result[key] = value\n }\n }\n return result\n }\n\n private lookupRef(refPath: string): unknown {\n const components = this.spec.components as Record<string, Record<string, unknown>> | undefined\n if (!components) return undefined\n\n const match = /^#\\/components\\/(\\w+)\\/(.+)$/.exec(refPath)\n if (!match) return undefined\n\n const [, section, name] = match\n const sectionObj = components[section]\n if (typeof sectionObj !== 'object') return undefined\n return sectionObj[name]\n }\n\n private getOperation(method: string, path: string): OperationObject | undefined {\n const paths = this.spec.paths as Record<string, Record<string, unknown>> | undefined\n return paths?.[path]?.[method.toLowerCase()] as OperationObject | undefined\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"quarry-registry-D4hIGScf.d.mts","names":[],"sources":["../src/quarry/quarry-registry.ts"],"mappings":";;;;;;AAuBA;;;;;;;;cACa,cAAA,YAA0B,MAAA;EAAA,iBAKqB,SAAA;EAAA,QAJlD,QAAA;EAAA,QACA,UAAA;EAAA,QACA,OAAA;cAEkD,SAAA,EAAW,SAAA;EAuJlC;;;;EAjJ7B,IAAA,CAAK,IAAA,UAAc,KAAA,GAAQ,YAAA,GAAe,OAAA,CAAQ,aAAA;EAXnB;;;EA4ErC,GAAA,CAAI,IAAA;EAzEI;;;EAiFR,GAAA,CAAI,IAAA,WAAe,WAAA,CAAY,OAAA;EAzEzB;;;EAiFN,GAAA,CAAA,GAAO,GAAA,SAAY,WAAA,CAAY,OAAA;EAjFiB;;;EAwFhD,IAAA,CAAA;IAAU,IAAA;IAAc,WAAA;IAAsB,OAAA;EAAA;EAP9C;;;EAiCM,SAAA,CAAU,OAAA;IAAY,UAAA;IAAqB,WAAA;IAAsB,aAAA;EAAA,IAA2B,OAAA;EAA5F;;;EAWA,KAAA,CAAM,IAAA,WAAe,OAAA;EAXX;;;;EA+BhB,QAAA,CAAS,YAAA,EAAc,WAAA,CAAY,OAAA;EAAA,QAkC3B,WAAA;EAAA,QAQA,WAAA;EAAA,QAIA,aAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"quarry-registry-DkraZNwn.mjs","names":[],"sources":["../src/quarry/command-internals.ts","../src/quarry/errors/command-not-found.error.ts","../src/quarry/signature-parser.ts","../src/quarry/quarry-registry.ts"],"sourcesContent":["import type { Command } from './command'\nimport { COMMAND_INTERNALS } from './constants'\nimport type { CommandInput, CommandInternals, CommandResult } from './types'\n\n/** @internal Set the flat input values before calling handle() */\nexport function setCommandInputs(command: Command, values: CommandInput): void {\n command[COMMAND_INTERNALS].inputs = { ...values }\n}\n\n/** @internal Set the Quarry reference for this.call() support */\nexport function setCommandQuarry(\n command: Command,\n quarry: { call(name: string, input?: CommandInput): Promise<CommandResult> },\n): void {\n command[COMMAND_INTERNALS].quarry = quarry\n}\n\n/** @internal Collect the result after handle() completes */\nexport function getCommandResult(command: Command): CommandResult {\n const internals: CommandInternals = command[COMMAND_INTERNALS]\n return {\n exitCode: internals.exitCode,\n output: [...internals.output],\n errors: [...internals.errors],\n }\n}\n\n/** @internal Reset state between invocations */\nexport function resetCommandState(command: Command): void {\n const internals: CommandInternals = command[COMMAND_INTERNALS]\n internals.inputs = {}\n internals.output = []\n internals.errors = []\n internals.exitCode = 0\n}\n","/**\n * Thrown when a command is not found in the Quarry registry.\n */\nexport class CommandNotFoundError extends Error {\n constructor(name: string) {\n super(`Command \"${name}\" is not registered.`)\n this.name = 'CommandNotFoundError'\n }\n}\n","import { CommandError } from './errors/command.error'\nimport type { ParsedArgument, ParsedOption, ParsedSignature } from './types'\n\n/**\n * Parse a Laravel-style command signature string.\n *\n * Signature syntax:\n * command-name {arg} ... — flat command\n * group subcommand {arg} ... — subcommand hierarchy (space-separated)\n * namespace:command {arg} ... — namespaced flat command (colon-separated)\n * {--flag} {--name=} {--name=default} {--name=*} {--A|name} {--name= : desc}\n *\n * Pure function, zero dependencies, edge-compatible.\n */\nexport function parseSignature(signature: string): ParsedSignature {\n const tokens = extractTokens(signature)\n const name = extractCommandName(signature)\n const args: ParsedArgument[] = []\n const options: ParsedOption[] = []\n\n for (const token of tokens) {\n const inner = token.slice(1, -1).trim() // strip { }\n\n if (inner.startsWith('--')) {\n options.push(parseOption(inner))\n } else {\n args.push(parseArgument(inner))\n }\n }\n\n return { name, arguments: args, options }\n}\n\nfunction extractCommandName(signature: string): string {\n const match = /^[\\w:.-]+(?:\\s+[\\w:.-]+)*/.exec(signature)\n if (!match) {\n throw new CommandError(`Invalid signature: cannot extract command name from \"${signature}\"`)\n }\n return match[0]\n}\n\nfunction extractTokens(signature: string): string[] {\n const tokens: string[] = []\n const regex = /\\{[^}]+\\}/g\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(signature)) !== null) {\n tokens.push(match[0])\n }\n\n return tokens\n}\n\nfunction parseArgument(inner: string): ParsedArgument {\n const { value, description } = splitDescription(inner)\n\n // {name*} — array/variadic argument\n if (value.endsWith('*')) {\n return {\n name: value.slice(0, -1).trim(),\n required: true,\n isArray: true,\n description,\n }\n }\n\n // {name=default} — argument with default value\n const eqIdx = value.indexOf('=')\n if (eqIdx !== -1) {\n return {\n name: value.slice(0, eqIdx).trim(),\n required: false,\n default: value.slice(eqIdx + 1).trim(),\n isArray: false,\n description,\n }\n }\n\n // {name?} — optional argument\n if (value.endsWith('?')) {\n return {\n name: value.slice(0, -1).trim(),\n required: false,\n isArray: false,\n description,\n }\n }\n\n // {name} — required argument\n return {\n name: value.trim(),\n required: true,\n isArray: false,\n description,\n }\n}\n\nfunction parseOption(inner: string): ParsedOption {\n // Remove leading --\n const withoutDashes = inner.slice(2)\n const { value, description } = splitDescription(withoutDashes)\n\n // Check for alias: {--A|name...}\n let alias: string | undefined\n let optBody = value\n\n const pipeIdx = optBody.indexOf('|')\n if (pipeIdx !== -1) {\n alias = optBody.slice(0, pipeIdx).trim()\n optBody = optBody.slice(pipeIdx + 1).trim()\n }\n\n // {--name=*} — array option\n if (optBody.endsWith('=*')) {\n return {\n name: optBody.slice(0, -2).trim(),\n alias,\n isFlag: false,\n isArray: true,\n description,\n }\n }\n\n // {--name=default} — option with default value\n const eqIdx = optBody.indexOf('=')\n if (eqIdx !== -1) {\n const name = optBody.slice(0, eqIdx).trim()\n const defaultValue = optBody.slice(eqIdx + 1).trim()\n\n return {\n name,\n alias,\n isFlag: false,\n isArray: false,\n default: defaultValue || undefined,\n description,\n }\n }\n\n // {--flag} — boolean flag\n return {\n name: optBody.trim(),\n alias,\n isFlag: true,\n isArray: false,\n description,\n }\n}\n\nfunction splitDescription(value: string): { value: string; description?: string } {\n const colonIdx = value.indexOf(' : ')\n if (colonIdx === -1) {\n return { value }\n }\n\n return {\n value: value.slice(0, colonIdx).trim(),\n description: value.slice(colonIdx + 3).trim(),\n }\n}\n","import { inject } from '../di'\nimport type { Container } from '../di/container'\nimport { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport { createCliExceptionContext } from '../errors/exception-context'\nimport type { ExceptionHandler } from '../errors/exception-handler'\nimport type { Constructor } from '../types'\nimport { type Command } from './command'\nimport { getCommandResult, setCommandInputs, setCommandQuarry } from './command-internals'\nimport { CommandNotFoundError } from './errors/command-not-found.error'\nimport { CommandError } from './errors/command.error'\nimport { parseSignature } from './signature-parser'\nimport type { CommandInput, CommandResult, ParsedSignature, Quarry } from './types'\n\n/**\n * QuarryRegistry — edge-compatible programmatic API for running commands.\n *\n * Registered as a singleton via DI_TOKENS.Quarry.\n * Commands are auto-discovered from module providers and registered at bootstrap.\n * Command constructors are stored at bootstrap; fresh instances are resolved per `call()`.\n *\n * Users should inject and type as `Quarry` (the interface), which only exposes `call()`.\n */\n@Transient(DI_TOKENS.Quarry)\nexport class QuarryRegistry implements Quarry {\n private commands = new Map<string, Constructor<Command>>()\n private signatures = new Map<string, ParsedSignature>()\n private aliases = new Map<string, string>()\n\n constructor(@inject(DI_TOKENS.Container) private readonly container: Container) { }\n\n /**\n * Execute a command by name with optional flat input.\n * A fresh command instance is resolved from the container per invocation.\n */\n async call(name: string, input?: CommandInput): Promise<CommandResult> {\n const resolvedName = this.resolveName(name)\n const CommandClass = this.commands.get(resolvedName)\n\n if (!CommandClass) {\n throw new CommandNotFoundError(name)\n }\n\n const signature = this.signatures.get(resolvedName)!\n const mergedInput = this.applyDefaults(input ?? {}, signature)\n\n // Validate required arguments\n for (const arg of signature.arguments) {\n if (arg.required && (mergedInput[arg.name] === undefined || mergedInput[arg.name] === null)) {\n throw new CommandError(`Missing required argument: ${arg.name}`)\n }\n }\n\n let command: Command | undefined\n\n try {\n // Resolve a fresh instance per invocation to avoid shared mutable state\n command = this.container.resolve<Command>(CommandClass)\n\n setCommandQuarry(command, this)\n setCommandInputs(command, mergedInput)\n\n const exitCode = await command.handle()\n const result = getCommandResult(command)\n\n if (typeof exitCode === 'number') {\n return { ...result, exitCode }\n }\n\n return result\n } catch (error) {\n if (error instanceof CommandError) {\n if (command) {\n const result = getCommandResult(command)\n return {\n exitCode: result.exitCode === 0 ? 1 : result.exitCode,\n output: result.output,\n errors: [...result.errors, error.message],\n }\n }\n return { exitCode: 1, output: [], errors: [error.message] }\n }\n\n const errorMessage = this.handleError(error, resolvedName)\n\n if (command) {\n const result = getCommandResult(command)\n return {\n exitCode: result.exitCode === 0 ? 1 : result.exitCode,\n output: result.output,\n errors: [...result.errors, errorMessage],\n }\n }\n return { exitCode: 1, output: [], errors: [errorMessage] }\n }\n }\n\n /**\n * Check if a command exists by name or alias.\n */\n has(name: string): boolean {\n const resolved = this.resolveName(name)\n return this.commands.has(resolved)\n }\n\n /**\n * Get a command constructor by name or alias.\n */\n get(name: string): Constructor<Command> | undefined {\n const resolved = this.resolveName(name)\n return this.commands.get(resolved)\n }\n\n /**\n * Get all registered command constructors.\n */\n all(): Map<string, Constructor<Command>> {\n return new Map(this.commands)\n }\n\n /**\n * List all commands with their descriptions and aliases.\n */\n list(): { name: string; description?: string; aliases: string[] }[] {\n const result: { name: string; description?: string; aliases: string[] }[] = []\n\n for (const [name, CommandClass] of this.commands) {\n const staticCommand = CommandClass as unknown as typeof Command\n const commandAliases: string[] = []\n\n for (const [alias, target] of this.aliases) {\n if (target === name) {\n commandAliases.push(alias)\n }\n }\n\n result.push({\n name,\n description: staticCommand.description,\n aliases: commandAliases,\n })\n }\n\n return result.sort((a, b) => a.name.localeCompare(b.name))\n }\n\n /**\n * Generate a compact listing of all commands with visual hierarchy and colors.\n */\n async listUsage(options?: { binaryName?: string; binaryLabel?: string; binaryVersion?: string }): Promise<string> {\n const commands = this.list()\n\n // Dynamic import to keep usage-generator tree-shakeable\n const { generateListing } = await import('./usage-generator')\n return generateListing(commands, this.signatures, options)\n }\n\n /**\n * Get auto-generated usage text for a command.\n */\n async usage(name: string): Promise<string> {\n const resolvedName = this.resolveName(name)\n const CommandClass = this.commands.get(resolvedName)\n\n if (!CommandClass) {\n throw new CommandNotFoundError(name)\n }\n\n const signature = this.signatures.get(resolvedName)!\n const staticCommand = CommandClass as unknown as typeof Command\n\n // Dynamic import to keep usage-generator tree-shakeable\n const { generateUsage } = await import('./usage-generator')\n return generateUsage(signature, staticCommand.description)\n }\n\n /**\n * Register a command constructor with the registry.\n * @internal Called by Application during bootstrap.\n */\n register(commandClass: Constructor<Command>): void {\n const staticCommand = commandClass as unknown as typeof Command\n\n if (!staticCommand.command) {\n throw new CommandError(`Command class ${commandClass.name} is missing static \"command\" signature`)\n }\n\n const signature = parseSignature(staticCommand.command)\n const name = signature.name\n\n if (this.commands.has(name) || this.aliases.has(name)) {\n throw new CommandError(`Duplicate command name: \"${name}\" is already registered`)\n }\n\n // Validate all aliases before any mutation\n if (staticCommand.aliases) {\n for (const alias of staticCommand.aliases) {\n if (this.commands.has(alias) || this.aliases.has(alias)) {\n throw new CommandError(`Duplicate alias: \"${alias}\" conflicts with an existing command or alias`)\n }\n }\n }\n\n // All checks passed — safe to mutate\n this.commands.set(name, commandClass)\n this.signatures.set(name, signature)\n\n if (staticCommand.aliases) {\n for (const alias of staticCommand.aliases) {\n this.aliases.set(alias, name)\n }\n }\n }\n\n private handleError(error: unknown, commandName: string): string {\n const handler = this.container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n const ctx = createCliExceptionContext(commandName)\n // Fire-and-forget — reporting happens via waitUntil internally\n void handler.handle(error, ctx)\n return error instanceof Error ? error.message : String(error)\n }\n\n private resolveName(name: string): string {\n return this.aliases.get(name) ?? name\n }\n\n private applyDefaults(input: CommandInput, signature: ParsedSignature): CommandInput {\n const result = { ...input }\n\n // Apply argument defaults\n for (const arg of signature.arguments) {\n if (result[arg.name] === undefined && arg.default !== undefined) {\n result[arg.name] = arg.default\n }\n }\n\n // Apply option defaults\n for (const opt of signature.options) {\n if (result[opt.name] === undefined) {\n if (opt.default !== undefined) {\n result[opt.name] = opt.default\n } else if (opt.isFlag) {\n result[opt.name] = false\n }\n }\n }\n\n return result\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
import { a as ApplicationError } from "./container-storage-GpNNz79X.mjs";
|
|
2
|
-
import { c as DI_TOKENS, d as Transient, l as Request, m as inject, u as Singleton } from "./di-BO1QIb5H.mjs";
|
|
3
|
-
import { n as __decorateParam, t as __decorate } from "./decorate-HgTKAYK8.mjs";
|
|
4
|
-
import "./errors-BBZTnjdq.mjs";
|
|
5
|
-
import { f as Module } from "./module-xYoHba6B.mjs";
|
|
6
|
-
import { t as I18N_TOKENS } from "./i18n.tokens-hwRpmjRq.mjs";
|
|
7
|
-
//#region src/queue/consumer-registry.ts
|
|
8
|
-
let ConsumerRegistry = class ConsumerRegistry {
|
|
9
|
-
/** Map from message type to consumers handling that type */
|
|
10
|
-
consumersByType = /* @__PURE__ */ new Map();
|
|
11
|
-
/** Set of all registered consumers (for iteration) */
|
|
12
|
-
allConsumers = /* @__PURE__ */ new Set();
|
|
13
|
-
/**
|
|
14
|
-
* Register a queue consumer
|
|
15
|
-
*
|
|
16
|
-
* Indexes the consumer by each of its declared message types.
|
|
17
|
-
*
|
|
18
|
-
* @param consumer - Queue consumer to register
|
|
19
|
-
*/
|
|
20
|
-
register(consumer) {
|
|
21
|
-
if (this.allConsumers.has(consumer)) return;
|
|
22
|
-
this.allConsumers.add(consumer);
|
|
23
|
-
for (const messageType of consumer.messageTypes) {
|
|
24
|
-
const existing = this.consumersByType.get(messageType) ?? [];
|
|
25
|
-
existing.push(consumer);
|
|
26
|
-
this.consumersByType.set(messageType, existing);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Get all consumers that can handle a specific message type
|
|
31
|
-
*
|
|
32
|
-
* Returns consumers that either:
|
|
33
|
-
* - Explicitly declare the message type
|
|
34
|
-
* - Use '*' wildcard to handle all types
|
|
35
|
-
*
|
|
36
|
-
* @param messageType - The message type to find consumers for
|
|
37
|
-
* @returns Array of consumers that can handle this message type
|
|
38
|
-
*/
|
|
39
|
-
getConsumers(messageType) {
|
|
40
|
-
const exactMatch = this.consumersByType.get(messageType) ?? [];
|
|
41
|
-
const wildcardMatch = this.consumersByType.get("*") ?? [];
|
|
42
|
-
const combined = new Set([...exactMatch, ...wildcardMatch]);
|
|
43
|
-
return Array.from(combined);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Check if any consumers can handle a message type
|
|
47
|
-
*
|
|
48
|
-
* @param messageType - The message type to check
|
|
49
|
-
* @returns true if at least one consumer can handle this type
|
|
50
|
-
*/
|
|
51
|
-
hasConsumers(messageType) {
|
|
52
|
-
return this.getConsumers(messageType).length > 0;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Get all registered message types
|
|
56
|
-
*
|
|
57
|
-
* @returns Array of message types with registered consumers
|
|
58
|
-
*/
|
|
59
|
-
getMessageTypes() {
|
|
60
|
-
return Array.from(this.consumersByType.keys());
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Get all registered consumers
|
|
64
|
-
*
|
|
65
|
-
* @returns Array of all registered consumers
|
|
66
|
-
*/
|
|
67
|
-
getAllConsumers() {
|
|
68
|
-
return Array.from(this.allConsumers);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
ConsumerRegistry = __decorate([Singleton(DI_TOKENS.ConsumerRegistry)], ConsumerRegistry);
|
|
72
|
-
//#endregion
|
|
73
|
-
//#region src/queue/queue-sender.ts
|
|
74
|
-
/**
|
|
75
|
-
* Queue Sender
|
|
76
|
-
*
|
|
77
|
-
* Implementation of IQueueSender bound to a specific queue binding.
|
|
78
|
-
* Created by QueueRegistry for each registered binding.
|
|
79
|
-
*
|
|
80
|
-
* Automatically enriches messages with:
|
|
81
|
-
* - `id`: UUID generated via crypto.randomUUID()
|
|
82
|
-
* - `timestamp`: Current time in milliseconds
|
|
83
|
-
* - `metadata.locale`: Current locale from I18n context
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* ```typescript
|
|
87
|
-
* // Created by QueueRegistry, not directly instantiated
|
|
88
|
-
* const sender = registry.getQueue('NOTIFICATIONS_QUEUE')
|
|
89
|
-
*
|
|
90
|
-
* await sender.dispatch({
|
|
91
|
-
* type: 'email.send',
|
|
92
|
-
* payload: { to: 'user@example.com', subject: 'Hello' }
|
|
93
|
-
* })
|
|
94
|
-
* ```
|
|
95
|
-
*/
|
|
96
|
-
var QueueSender = class {
|
|
97
|
-
binding;
|
|
98
|
-
provider;
|
|
99
|
-
i18n;
|
|
100
|
-
constructor(binding, provider, i18n) {
|
|
101
|
-
this.binding = binding;
|
|
102
|
-
this.provider = provider;
|
|
103
|
-
this.i18n = i18n;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Dispatch a message to this queue.
|
|
107
|
-
*
|
|
108
|
-
* @param message - Message to dispatch (without id/timestamp)
|
|
109
|
-
*/
|
|
110
|
-
async dispatch(message) {
|
|
111
|
-
const metadata = { ...message.metadata };
|
|
112
|
-
if (!metadata.locale) {
|
|
113
|
-
const locale = this.i18n.getLocale();
|
|
114
|
-
if (locale) metadata.locale = locale;
|
|
115
|
-
}
|
|
116
|
-
const fullMessage = {
|
|
117
|
-
id: crypto.randomUUID(),
|
|
118
|
-
timestamp: Date.now(),
|
|
119
|
-
...message,
|
|
120
|
-
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
121
|
-
};
|
|
122
|
-
await this.provider.send(this.binding, fullMessage);
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
//#endregion
|
|
126
|
-
//#region src/queue/queue.tokens.ts
|
|
127
|
-
const QUEUE_TOKENS = {
|
|
128
|
-
QueueProviderFactory: Symbol.for("stratal:queue:provider:factory"),
|
|
129
|
-
QueueRegistry: Symbol.for("stratal:queue:registry"),
|
|
130
|
-
QueueModuleOptions: Symbol.for("stratal:queue:options")
|
|
131
|
-
};
|
|
132
|
-
//#endregion
|
|
133
|
-
//#region src/queue/queue-registry.ts
|
|
134
|
-
let QueueRegistry = class QueueRegistry {
|
|
135
|
-
i18n;
|
|
136
|
-
provider;
|
|
137
|
-
senders = /* @__PURE__ */ new Map();
|
|
138
|
-
constructor(providerFactory, i18n) {
|
|
139
|
-
this.i18n = i18n;
|
|
140
|
-
this.provider = providerFactory.create();
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Get or create a QueueSender for the specified binding.
|
|
144
|
-
*
|
|
145
|
-
* Senders are cached per binding within the request scope.
|
|
146
|
-
*
|
|
147
|
-
* @param binding - The queue binding to get a sender for
|
|
148
|
-
* @returns QueueSender bound to the specified binding
|
|
149
|
-
*/
|
|
150
|
-
getQueue(binding) {
|
|
151
|
-
let sender = this.senders.get(binding);
|
|
152
|
-
if (!sender) {
|
|
153
|
-
sender = new QueueSender(binding, this.provider, this.i18n);
|
|
154
|
-
this.senders.set(binding, sender);
|
|
155
|
-
}
|
|
156
|
-
return sender;
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
QueueRegistry = __decorate([
|
|
160
|
-
Request(QUEUE_TOKENS.QueueRegistry),
|
|
161
|
-
__decorateParam(0, inject(QUEUE_TOKENS.QueueProviderFactory)),
|
|
162
|
-
__decorateParam(1, inject(I18N_TOKENS.I18nService))
|
|
163
|
-
], QueueRegistry);
|
|
164
|
-
//#endregion
|
|
165
|
-
//#region src/queue/queue.error.ts
|
|
166
|
-
var QueueError = class extends ApplicationError {};
|
|
167
|
-
//#endregion
|
|
168
|
-
//#region src/queue/providers/cloudflare-queue.provider.ts
|
|
169
|
-
let CloudflareQueueProvider = class CloudflareQueueProvider {
|
|
170
|
-
env;
|
|
171
|
-
constructor(env) {
|
|
172
|
-
this.env = env;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Send a message to a Cloudflare Queue
|
|
176
|
-
*
|
|
177
|
-
* @param binding - Queue binding identifier (e.g., 'NOTIFICATIONS_QUEUE')
|
|
178
|
-
* @param message - Complete message with id, timestamp, and payload
|
|
179
|
-
* @throws {QueueError} If the binding is not configured on env
|
|
180
|
-
*/
|
|
181
|
-
async send(binding, message) {
|
|
182
|
-
const queue = this.env[binding];
|
|
183
|
-
if (!queue) throw new QueueError(`Queue binding "${binding}" was not found in the environment`);
|
|
184
|
-
await queue.send(message);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
CloudflareQueueProvider = __decorate([Transient(), __decorateParam(0, inject(DI_TOKENS.CloudflareEnv))], CloudflareQueueProvider);
|
|
188
|
-
//#endregion
|
|
189
|
-
//#region src/queue/providers/sync-queue.provider.ts
|
|
190
|
-
let SyncQueueProvider = class SyncQueueProvider {
|
|
191
|
-
registry;
|
|
192
|
-
constructor(registry) {
|
|
193
|
-
this.registry = registry;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Process a message synchronously
|
|
197
|
-
*
|
|
198
|
-
* Finds all matching consumers by message type and calls their handle() method.
|
|
199
|
-
* If any consumer throws, onError() is called and the error is re-thrown.
|
|
200
|
-
*
|
|
201
|
-
* @param _binding - Queue binding (not used for routing, consumers match by message type)
|
|
202
|
-
* @param message - Complete message with id, timestamp, and payload
|
|
203
|
-
* @throws Re-throws any error from consumer.handle() after calling onError()
|
|
204
|
-
*/
|
|
205
|
-
async send(_binding, message) {
|
|
206
|
-
const consumers = this.registry.getConsumers(message.type);
|
|
207
|
-
for (const consumer of consumers) try {
|
|
208
|
-
await consumer.handle(message);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
const errorInstance = error instanceof Error ? error : new Error(String(error));
|
|
211
|
-
if (consumer.onError) await consumer.onError(errorInstance, message);
|
|
212
|
-
throw errorInstance;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
SyncQueueProvider = __decorate([Transient(), __decorateParam(0, inject(DI_TOKENS.ConsumerRegistry))], SyncQueueProvider);
|
|
217
|
-
//#endregion
|
|
218
|
-
//#region src/queue/services/queue-provider-factory.ts
|
|
219
|
-
let QueueProviderFactory = class QueueProviderFactory {
|
|
220
|
-
env;
|
|
221
|
-
registry;
|
|
222
|
-
options;
|
|
223
|
-
constructor(env, registry, options) {
|
|
224
|
-
this.env = env;
|
|
225
|
-
this.registry = registry;
|
|
226
|
-
this.options = options;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Create a queue provider based on module configuration
|
|
230
|
-
*
|
|
231
|
-
* @returns Queue provider instance
|
|
232
|
-
* @throws {QueueError} If provider type is not supported
|
|
233
|
-
*/
|
|
234
|
-
create() {
|
|
235
|
-
const providerType = this.options?.provider ?? "cloudflare";
|
|
236
|
-
switch (providerType) {
|
|
237
|
-
case "cloudflare": return new CloudflareQueueProvider(this.env);
|
|
238
|
-
case "sync": return new SyncQueueProvider(this.registry);
|
|
239
|
-
default: throw new QueueError(`Queue provider "${String(providerType)}" is not supported`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
QueueProviderFactory = __decorate([
|
|
244
|
-
Transient(QUEUE_TOKENS.QueueProviderFactory),
|
|
245
|
-
__decorateParam(0, inject(DI_TOKENS.CloudflareEnv)),
|
|
246
|
-
__decorateParam(1, inject(DI_TOKENS.ConsumerRegistry)),
|
|
247
|
-
__decorateParam(2, inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true }))
|
|
248
|
-
], QueueProviderFactory);
|
|
249
|
-
//#endregion
|
|
250
|
-
//#region src/queue/queue.module.ts
|
|
251
|
-
/**
|
|
252
|
-
* Queue Module
|
|
253
|
-
*
|
|
254
|
-
* Provides declarative queue infrastructure with provider abstraction.
|
|
255
|
-
*
|
|
256
|
-
* **Usage:**
|
|
257
|
-
* ```typescript
|
|
258
|
-
* // 1. Configure provider (once, in app root)
|
|
259
|
-
* QueueModule.forRootAsync({
|
|
260
|
-
* inject: [CONFIG_TOKENS.ConfigService],
|
|
261
|
-
* useFactory: (config) => ({ provider: config.get('queue').provider })
|
|
262
|
-
* })
|
|
263
|
-
*
|
|
264
|
-
* // 2. Register queue bindings (the binding IS the injection token)
|
|
265
|
-
* QueueModule.registerQueue('NOTIFICATIONS_QUEUE')
|
|
266
|
-
* QueueModule.registerQueue('BACKGROUND_QUEUE')
|
|
267
|
-
*
|
|
268
|
-
* // 3. Inject and use
|
|
269
|
-
* constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}
|
|
270
|
-
* await this.queue.dispatch({ type: 'email.send', payload: {...} })
|
|
271
|
-
* ```
|
|
272
|
-
*
|
|
273
|
-
* **Providers:**
|
|
274
|
-
* - `cloudflare`: Production provider using Cloudflare Queue bindings
|
|
275
|
-
* - `sync`: Testing provider that processes messages immediately
|
|
276
|
-
*/
|
|
277
|
-
var _QueueModule;
|
|
278
|
-
let QueueModule = _QueueModule = class QueueModule {
|
|
279
|
-
/**
|
|
280
|
-
* Configure queue infrastructure with async factory.
|
|
281
|
-
*
|
|
282
|
-
* Use when provider configuration depends on other services like ConfigService.
|
|
283
|
-
*
|
|
284
|
-
* @param options - Async configuration with factory and inject tokens
|
|
285
|
-
* @returns Dynamic module with queue infrastructure
|
|
286
|
-
*
|
|
287
|
-
* @example
|
|
288
|
-
* ```typescript
|
|
289
|
-
* QueueModule.forRootAsync({
|
|
290
|
-
* inject: [CONFIG_TOKENS.ConfigService],
|
|
291
|
-
* useFactory: (config: IConfigService) => ({
|
|
292
|
-
* provider: config.get('queue').provider
|
|
293
|
-
* })
|
|
294
|
-
* })
|
|
295
|
-
* ```
|
|
296
|
-
*/
|
|
297
|
-
static forRootAsync(options) {
|
|
298
|
-
return {
|
|
299
|
-
module: _QueueModule,
|
|
300
|
-
providers: [{
|
|
301
|
-
provide: QUEUE_TOKENS.QueueModuleOptions,
|
|
302
|
-
useFactory: options.useFactory,
|
|
303
|
-
inject: options.inject
|
|
304
|
-
}]
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Register a queue binding for injection.
|
|
309
|
-
*
|
|
310
|
-
* The binding name doubles as the DI injection token and the
|
|
311
|
-
* `env`-lookup key. Binding names are typed against `StratalEnv`
|
|
312
|
-
* (autocomplete works once an app augments `StratalEnv` with its
|
|
313
|
-
* Cloudflare bindings).
|
|
314
|
-
*
|
|
315
|
-
* @param binding - Queue binding identifier (e.g. `NOTIFICATIONS_QUEUE`).
|
|
316
|
-
* @returns Dynamic module that provides the queue sender
|
|
317
|
-
*
|
|
318
|
-
* @example
|
|
319
|
-
* ```typescript
|
|
320
|
-
* // In AppModule imports
|
|
321
|
-
* QueueModule.registerQueue('NOTIFICATIONS_QUEUE')
|
|
322
|
-
*
|
|
323
|
-
* // Then inject using the binding name
|
|
324
|
-
* constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}
|
|
325
|
-
* ```
|
|
326
|
-
*/
|
|
327
|
-
static registerQueue(binding) {
|
|
328
|
-
return {
|
|
329
|
-
module: _QueueModule,
|
|
330
|
-
providers: [{
|
|
331
|
-
provide: binding,
|
|
332
|
-
useFactory: (registry) => registry.getQueue(binding),
|
|
333
|
-
inject: [QUEUE_TOKENS.QueueRegistry]
|
|
334
|
-
}]
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
QueueModule = _QueueModule = __decorate([Module({ providers: [
|
|
339
|
-
{
|
|
340
|
-
provide: DI_TOKENS.ConsumerRegistry,
|
|
341
|
-
useClass: ConsumerRegistry
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
provide: QUEUE_TOKENS.QueueProviderFactory,
|
|
345
|
-
useClass: QueueProviderFactory
|
|
346
|
-
},
|
|
347
|
-
{
|
|
348
|
-
provide: QUEUE_TOKENS.QueueRegistry,
|
|
349
|
-
useClass: QueueRegistry
|
|
350
|
-
}
|
|
351
|
-
] })], QueueModule);
|
|
352
|
-
//#endregion
|
|
353
|
-
export { QueueError as a, QueueSender as c, CloudflareQueueProvider as i, ConsumerRegistry as l, QueueProviderFactory as n, QueueRegistry as o, SyncQueueProvider as r, QUEUE_TOKENS as s, QueueModule as t };
|
|
354
|
-
|
|
355
|
-
//# sourceMappingURL=queue.module-DeWJ0tQM.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"queue.module-DeWJ0tQM.mjs","names":[],"sources":["../src/queue/consumer-registry.ts","../src/queue/queue-sender.ts","../src/queue/queue.tokens.ts","../src/queue/queue-registry.ts","../src/queue/queue.error.ts","../src/queue/providers/cloudflare-queue.provider.ts","../src/queue/providers/sync-queue.provider.ts","../src/queue/services/queue-provider-factory.ts","../src/queue/queue.module.ts"],"sourcesContent":["import { Singleton } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { IQueueConsumer } from './queue-consumer'\n\n/**\n * Consumer Registry\n *\n * Singleton service that holds all registered queue consumers indexed by message type.\n * Consumers declare the message types they handle, and this registry routes messages\n * to the appropriate consumers based on their types.\n *\n * **Message-Type Routing:**\n * - Consumers declare `messageTypes` array (e.g., `['email.send', 'email.batch.send']`)\n * - When a message arrives, consumers matching the message type are invoked\n * - A consumer can handle messages from ANY queue (routing is by type, not queue)\n * - Use `'*'` as a wildcard to handle all message types\n *\n * @example Consumer registration\n * ```typescript\n * // In consumer.ts\n * @Transient()\n * export class EmailConsumer implements IQueueConsumer {\n * readonly messageTypes = ['email.send', 'email.batch.send']\n * // ...\n * }\n *\n * // In module.ts\n * @Module({\n * consumers: [EmailConsumer]\n * })\n *\n * // Application auto-registers via ConsumerRegistry\n * this.consumerRegistry.register(consumer)\n * ```\n */\n@Singleton(DI_TOKENS.ConsumerRegistry)\nexport class ConsumerRegistry {\n /** Map from message type to consumers handling that type */\n private consumersByType = new Map<string, IQueueConsumer[]>()\n\n /** Set of all registered consumers (for iteration) */\n private allConsumers = new Set<IQueueConsumer>()\n\n /**\n * Register a queue consumer\n *\n * Indexes the consumer by each of its declared message types.\n *\n * @param consumer - Queue consumer to register\n */\n register(consumer: IQueueConsumer): void {\n if (this.allConsumers.has(consumer)) {\n return // Already registered\n }\n\n this.allConsumers.add(consumer)\n\n for (const messageType of consumer.messageTypes) {\n const existing = this.consumersByType.get(messageType) ?? []\n existing.push(consumer)\n this.consumersByType.set(messageType, existing)\n }\n }\n\n /**\n * Get all consumers that can handle a specific message type\n *\n * Returns consumers that either:\n * - Explicitly declare the message type\n * - Use '*' wildcard to handle all types\n *\n * @param messageType - The message type to find consumers for\n * @returns Array of consumers that can handle this message type\n */\n getConsumers(messageType: string): IQueueConsumer[] {\n const exactMatch = this.consumersByType.get(messageType) ?? []\n const wildcardMatch = this.consumersByType.get('*') ?? []\n\n // Combine and dedupe\n const combined = new Set([...exactMatch, ...wildcardMatch])\n return Array.from(combined)\n }\n\n /**\n * Check if any consumers can handle a message type\n *\n * @param messageType - The message type to check\n * @returns true if at least one consumer can handle this type\n */\n hasConsumers(messageType: string): boolean {\n return this.getConsumers(messageType).length > 0\n }\n\n /**\n * Get all registered message types\n *\n * @returns Array of message types with registered consumers\n */\n getMessageTypes(): string[] {\n return Array.from(this.consumersByType.keys())\n }\n\n /**\n * Get all registered consumers\n *\n * @returns Array of all registered consumers\n */\n getAllConsumers(): IQueueConsumer[] {\n return Array.from(this.allConsumers)\n }\n}\n","import type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { QueueMessage } from './queue-consumer'\nimport type { DispatchMessage, IQueueSender } from './queue-sender.interface'\n\n/**\n * Queue Sender\n *\n * Implementation of IQueueSender bound to a specific queue binding.\n * Created by QueueRegistry for each registered binding.\n *\n * Automatically enriches messages with:\n * - `id`: UUID generated via crypto.randomUUID()\n * - `timestamp`: Current time in milliseconds\n * - `metadata.locale`: Current locale from I18n context\n *\n * @example\n * ```typescript\n * // Created by QueueRegistry, not directly instantiated\n * const sender = registry.getQueue('NOTIFICATIONS_QUEUE')\n *\n * await sender.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n */\nexport class QueueSender implements IQueueSender {\n constructor(\n private readonly binding: string,\n private readonly provider: IQueueProvider,\n private readonly i18n: II18nService\n ) {}\n\n /**\n * Dispatch a message to this queue.\n *\n * @param message - Message to dispatch (without id/timestamp)\n */\n async dispatch<T>(message: DispatchMessage<T>): Promise<void> {\n const metadata = { ...message.metadata }\n\n if (!metadata.locale) {\n const locale = this.i18n.getLocale()\n if (locale) {\n metadata.locale = locale\n }\n }\n\n const fullMessage: QueueMessage<T> = {\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n ...message,\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined\n }\n\n await this.provider.send(this.binding, fullMessage)\n }\n}\n","export const QUEUE_TOKENS = {\n QueueProviderFactory: Symbol.for('stratal:queue:provider:factory'),\n QueueRegistry: Symbol.for('stratal:queue:registry'),\n QueueModuleOptions: Symbol.for('stratal:queue:options'),\n} as const\n\nexport type QueueToken = (typeof QUEUE_TOKENS)[keyof typeof QUEUE_TOKENS]\n","import { inject } from '../di'\nimport { Request } from '../di/decorators'\nimport { I18N_TOKENS } from '../i18n/i18n.tokens'\nimport type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QueueSender } from './queue-sender'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { type QueueProviderFactory } from './services'\n\n/**\n * Queue Registry\n *\n * Request-scoped factory service for creating QueueSender instances.\n * Caches senders per binding within the request scope.\n *\n * This service is used internally by QueueModule.registerQueue() to provide\n * IQueueSender instances for each registered binding.\n *\n * **Why request-scoped?**\n * - Needs access to I18nService for locale-aware message metadata\n * - Provider is created once per request for consistency\n * - Queue senders are cached per request to avoid recreating them\n *\n * @example\n * ```typescript\n * // Used internally by QueueModule.registerQueue()\n * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')\n *\n * // The module creates a factory provider:\n * {\n * provide: 'NOTIFICATIONS_QUEUE',\n * useFactory: (registry: QueueRegistry) => registry.getQueue('NOTIFICATIONS_QUEUE'),\n * inject: [QUEUE_TOKENS.QueueRegistry],\n * }\n * ```\n */\n@Request(QUEUE_TOKENS.QueueRegistry)\nexport class QueueRegistry {\n private readonly provider: IQueueProvider\n private readonly senders = new Map<string, IQueueSender>()\n\n constructor(\n @inject(QUEUE_TOKENS.QueueProviderFactory) providerFactory: QueueProviderFactory,\n @inject(I18N_TOKENS.I18nService) private readonly i18n: II18nService\n ) {\n this.provider = providerFactory.create()\n }\n\n /**\n * Get or create a QueueSender for the specified binding.\n *\n * Senders are cached per binding within the request scope.\n *\n * @param binding - The queue binding to get a sender for\n * @returns QueueSender bound to the specified binding\n */\n getQueue(binding: string): IQueueSender {\n let sender = this.senders.get(binding)\n\n if (!sender) {\n sender = new QueueSender(binding, this.provider, this.i18n)\n this.senders.set(binding, sender)\n }\n\n return sender\n }\n}\n","import { ApplicationError } from '../errors'\n\nexport class QueueError extends ApplicationError {}\n","import { inject } from '../../di'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { QueueError } from '../queue.error'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Cloudflare Queue Provider\n *\n * Sends messages to Cloudflare Queues by resolving the binding directly on\n * the worker's `env`. Used in production environments where Cloudflare Workers\n * handle queue processing.\n *\n * @example\n * ```typescript\n * const provider = new CloudflareQueueProvider(env)\n * await provider.send('NOTIFICATIONS_QUEUE', message)\n * ```\n */\n@Transient()\nexport class CloudflareQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv\n ) { }\n\n /**\n * Send a message to a Cloudflare Queue\n *\n * @param binding - Queue binding identifier (e.g., 'NOTIFICATIONS_QUEUE')\n * @param message - Complete message with id, timestamp, and payload\n * @throws {QueueError} If the binding is not configured on env\n */\n async send<T>(binding: string, message: QueueMessage<T>): Promise<void> {\n const queue = (this.env as unknown as Record<string, unknown>)[binding] as Queue | undefined\n\n if (!queue) {\n throw new QueueError(`Queue binding \"${binding}\" was not found in the environment`)\n }\n\n await queue.send(message)\n }\n}\n","import { inject } from '../../di'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type ConsumerRegistry } from '../consumer-registry'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Sync Queue Provider\n *\n * Processes messages immediately by finding matching consumers and calling\n * their handle() method directly. Used for testing and development where\n * real queue infrastructure is not available.\n *\n * **Behavior:**\n * - Messages are processed synchronously when send() is called\n * - Matching consumers are found via ConsumerRegistry by message type\n * - All matching consumers are called sequentially\n * - Errors are re-thrown after onError() is called (fail-fast for testing)\n *\n * **Consumer Matching:**\n * - Consumers are matched by message type, not queue name\n * - Wildcard ('*') matches all message types\n *\n * @example Testing with sync provider\n * ```typescript\n * const provider = new SyncQueueProvider(registry)\n * await provider.send('NOTIFICATIONS_QUEUE', {\n * id: '123',\n * timestamp: Date.now(),\n * type: 'email.send',\n * payload: { to: 'test@example.com' }\n * })\n * // Consumer's handle() is called immediately!\n * ```\n */\n@Transient()\nexport class SyncQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry\n ) {}\n\n /**\n * Process a message synchronously\n *\n * Finds all matching consumers by message type and calls their handle() method.\n * If any consumer throws, onError() is called and the error is re-thrown.\n *\n * @param _binding - Queue binding (not used for routing, consumers match by message type)\n * @param message - Complete message with id, timestamp, and payload\n * @throws Re-throws any error from consumer.handle() after calling onError()\n */\n async send<T>(_binding: string, message: QueueMessage<T>): Promise<void> {\n // Consumers are matched by message type, not queue name\n const consumers = this.registry.getConsumers(message.type)\n\n // Process synchronously - call each matching consumer\n for (const consumer of consumers) {\n try {\n await consumer.handle(message)\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n // Call onError hook if defined\n if (consumer.onError) {\n await consumer.onError(errorInstance, message)\n }\n\n // Re-throw for fail-fast behavior in tests\n throw errorInstance\n }\n }\n }\n}\n","import { inject } from '../../di'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type ConsumerRegistry } from '../consumer-registry'\nimport { QueueError } from '../queue.error'\nimport { CloudflareQueueProvider, SyncQueueProvider, type IQueueProvider } from '../providers'\nimport type { QueueModuleOptions } from '../queue.module'\nimport { QUEUE_TOKENS } from '../queue.tokens'\n\n/**\n * Queue Provider Factory\n *\n * Creates the appropriate queue provider based on configuration provided\n * via QueueModule.forRootAsync().\n *\n * **Provider Selection:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n *\n * @example\n * ```typescript\n * // Configuration via QueueModule.forRootAsync()\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // Factory usage (internal)\n * const factory = container.resolve(QueueProviderFactory)\n * const provider = factory.create()\n * ```\n */\n@Transient(QUEUE_TOKENS.QueueProviderFactory)\nexport class QueueProviderFactory {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry,\n @inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true }) private readonly options?: QueueModuleOptions,\n ) { }\n\n /**\n * Create a queue provider based on module configuration\n *\n * @returns Queue provider instance\n * @throws {QueueError} If provider type is not supported\n */\n create(): IQueueProvider {\n const providerType = this.options?.provider ?? 'cloudflare'\n\n switch (providerType) {\n case 'cloudflare':\n return new CloudflareQueueProvider(this.env)\n\n case 'sync':\n return new SyncQueueProvider(this.registry)\n\n default:\n throw new QueueError(`Queue provider \"${String(providerType)}\" is not supported`)\n }\n }\n}\n","/**\n * Queue Module\n *\n * Provides declarative queue infrastructure with provider abstraction.\n *\n * **Usage:**\n * ```typescript\n * // 1. Configure provider (once, in app root)\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // 2. Register queue bindings (the binding IS the injection token)\n * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')\n * QueueModule.registerQueue('BACKGROUND_QUEUE')\n *\n * // 3. Inject and use\n * constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}\n * await this.queue.dispatch({ type: 'email.send', payload: {...} })\n * ```\n *\n * **Providers:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n */\n\nimport { DI_TOKENS } from '../di/tokens'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule, InjectionToken } from '../module/types'\nimport { ConsumerRegistry } from './consumer-registry'\nimport type { QueueBinding } from './queue-binding'\nimport { QueueRegistry } from './queue-registry'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { QueueProviderFactory } from './services'\n\n/**\n * Queue module configuration options\n */\nexport interface QueueModuleOptions {\n /**\n * Queue provider type\n * - 'cloudflare': Production provider using Cloudflare Queue bindings\n * - 'sync': Testing provider that processes messages immediately\n */\n provider: 'cloudflare' | 'sync'\n}\n\n@Module({\n providers: [\n { provide: DI_TOKENS.ConsumerRegistry, useClass: ConsumerRegistry },\n { provide: QUEUE_TOKENS.QueueProviderFactory, useClass: QueueProviderFactory },\n { provide: QUEUE_TOKENS.QueueRegistry, useClass: QueueRegistry },\n ],\n})\nexport class QueueModule {\n /**\n * Configure queue infrastructure with async factory.\n *\n * Use when provider configuration depends on other services like ConfigService.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with queue infrastructure\n *\n * @example\n * ```typescript\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config: IConfigService) => ({\n * provider: config.get('queue').provider\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<QueueModuleOptions>): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: QUEUE_TOKENS.QueueModuleOptions,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n /**\n * Register a queue binding for injection.\n *\n * The binding name doubles as the DI injection token and the\n * `env`-lookup key. Binding names are typed against `StratalEnv`\n * (autocomplete works once an app augments `StratalEnv` with its\n * Cloudflare bindings).\n *\n * @param binding - Queue binding identifier (e.g. `NOTIFICATIONS_QUEUE`).\n * @returns Dynamic module that provides the queue sender\n *\n * @example\n * ```typescript\n * // In AppModule imports\n * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')\n *\n * // Then inject using the binding name\n * constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}\n * ```\n */\n static registerQueue(binding: QueueBinding): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: binding as InjectionToken<IQueueSender>,\n useFactory: (registry: QueueRegistry) => registry.getQueue(binding),\n inject: [QUEUE_TOKENS.QueueRegistry],\n },\n ],\n }\n }\n}\n"],"mappings":"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"}
|