stratal 0.0.22 → 0.0.24
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
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-BBjsoOtd.mjs";
|
|
2
|
+
import { c as Transient, d as inject, o as Request, r as DI_TOKENS, s as Singleton } from "./di-DseMn-z9.mjs";
|
|
3
|
+
import { a as ApplicationError, n as getContainer, t as containerStorage } from "./container-storage-BmOJ4_Na.mjs";
|
|
4
|
+
import { n as __decorateParam, t as __decorate } from "./decorate-CuAoSZvs.mjs";
|
|
5
|
+
import { LOGGER_TOKENS } from "./logger/index.mjs";
|
|
6
|
+
import "./errors-mXYxG0XB.mjs";
|
|
7
|
+
import { n as Module } from "./module.decorator-CYHY6pG5.mjs";
|
|
8
|
+
import "./module/index.mjs";
|
|
9
|
+
import { CACHE_TOKENS, CacheModule } from "./cache/index.mjs";
|
|
10
|
+
import { t as QUEUE_TOKENS } from "./queue.tokens-DjHnFmre.mjs";
|
|
11
|
+
import { t as I18N_TOKENS } from "./i18n.tokens-CZ_v8oyS.mjs";
|
|
12
|
+
//#region src/queue/queue.error.ts
|
|
13
|
+
var QueueError = class extends ApplicationError {};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/queue/queue-manager.ts
|
|
16
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
17
|
+
let QueueManager = class QueueManager {
|
|
18
|
+
registry;
|
|
19
|
+
logger;
|
|
20
|
+
store;
|
|
21
|
+
maxRetries;
|
|
22
|
+
constructor(registry, logger, store, options) {
|
|
23
|
+
this.registry = registry;
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
this.store = store;
|
|
26
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
27
|
+
}
|
|
28
|
+
async processBatch(queueName, batch) {
|
|
29
|
+
for (const message of batch.messages) {
|
|
30
|
+
const queueMessage = message.body;
|
|
31
|
+
const idempotencyKey = queueMessage.metadata?.idempotencyKey ?? queueMessage.id;
|
|
32
|
+
if (await this.store.isProcessed(idempotencyKey)) {
|
|
33
|
+
message.ack();
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const container = getContainer();
|
|
37
|
+
const consumers = this.registry.getConsumerClasses(queueMessage.type).map((ConsumerClass) => container.resolve(ConsumerClass));
|
|
38
|
+
const results = await Promise.allSettled(consumers.map((consumer) => consumer.handle(queueMessage)));
|
|
39
|
+
let lastError;
|
|
40
|
+
let failedConsumer;
|
|
41
|
+
for (let i = 0; i < results.length; i++) {
|
|
42
|
+
const result = results[i];
|
|
43
|
+
if (result.status === "rejected") {
|
|
44
|
+
const consumer = consumers[i];
|
|
45
|
+
const errorInstance = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
|
|
46
|
+
this.logger.error("Queue message processing failed", errorInstance, {
|
|
47
|
+
type: queueMessage.type,
|
|
48
|
+
queue: queueName,
|
|
49
|
+
messageId: queueMessage.id,
|
|
50
|
+
idempotencyKey
|
|
51
|
+
});
|
|
52
|
+
if (consumer.onError) await consumer.onError(errorInstance, queueMessage);
|
|
53
|
+
lastError = errorInstance;
|
|
54
|
+
failedConsumer = consumer.constructor.name;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (lastError !== void 0) if (message.attempts > this.maxRetries) {
|
|
58
|
+
try {
|
|
59
|
+
const binding = queueMessage.metadata?.binding;
|
|
60
|
+
if (!binding) throw new QueueError(`Queue message ${queueMessage.id} has no binding metadata and cannot be recorded for retry. Messages must be dispatched through a Stratal queue sender (@InjectQueue).`);
|
|
61
|
+
const failedJob = {
|
|
62
|
+
id: queueMessage.id,
|
|
63
|
+
queue: queueName,
|
|
64
|
+
binding,
|
|
65
|
+
type: queueMessage.type,
|
|
66
|
+
message: queueMessage,
|
|
67
|
+
error: {
|
|
68
|
+
name: lastError.name,
|
|
69
|
+
message: lastError.message,
|
|
70
|
+
stack: lastError.stack
|
|
71
|
+
},
|
|
72
|
+
consumer: failedConsumer,
|
|
73
|
+
attempts: message.attempts,
|
|
74
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
75
|
+
};
|
|
76
|
+
await this.store.storeFailedJob(failedJob);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
this.logger.error("Failed to persist failed queue job", error instanceof Error ? error : new Error(String(error)), {
|
|
79
|
+
queue: queueName,
|
|
80
|
+
messageId: queueMessage.id,
|
|
81
|
+
type: queueMessage.type
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
message.ack();
|
|
85
|
+
} else message.retry();
|
|
86
|
+
else {
|
|
87
|
+
await this.store.markProcessed(idempotencyKey);
|
|
88
|
+
message.ack();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
QueueManager = __decorate([
|
|
94
|
+
Transient(DI_TOKENS.Queue),
|
|
95
|
+
__decorateParam(0, inject(DI_TOKENS.ConsumerRegistry)),
|
|
96
|
+
__decorateParam(1, inject(LOGGER_TOKENS.LoggerService)),
|
|
97
|
+
__decorateParam(2, inject(QUEUE_TOKENS.QueueStore)),
|
|
98
|
+
__decorateParam(3, inject(QUEUE_TOKENS.QueueModuleOptions))
|
|
99
|
+
], QueueManager);
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/queue/consumer-registry.ts
|
|
102
|
+
let ConsumerRegistry = class ConsumerRegistry {
|
|
103
|
+
/** Map from message type to consumer classes handling that type */
|
|
104
|
+
classesByType = /* @__PURE__ */ new Map();
|
|
105
|
+
/** All registrations (for iteration / listing) */
|
|
106
|
+
registrations = [];
|
|
107
|
+
/** Registered consumer classes (dedupe) */
|
|
108
|
+
registeredClasses = /* @__PURE__ */ new Set();
|
|
109
|
+
/**
|
|
110
|
+
* Register a queue consumer class.
|
|
111
|
+
*
|
|
112
|
+
* Indexes the class by each of its declared message types. A fresh instance
|
|
113
|
+
* is resolved per message from the request-scoped container at dispatch time —
|
|
114
|
+
* consumers are never held as long-lived singletons, so they may safely inject
|
|
115
|
+
* request-scoped providers (`@InjectQueue`, i18n, auth context, …).
|
|
116
|
+
*
|
|
117
|
+
* @param consumerClass - Queue consumer class to register
|
|
118
|
+
* @param messageTypes - Message types the consumer handles
|
|
119
|
+
*/
|
|
120
|
+
register(consumerClass, messageTypes) {
|
|
121
|
+
if (this.registeredClasses.has(consumerClass)) return;
|
|
122
|
+
this.registeredClasses.add(consumerClass);
|
|
123
|
+
this.registrations.push({
|
|
124
|
+
consumerClass,
|
|
125
|
+
messageTypes
|
|
126
|
+
});
|
|
127
|
+
for (const messageType of messageTypes) {
|
|
128
|
+
const existing = this.classesByType.get(messageType) ?? [];
|
|
129
|
+
existing.push(consumerClass);
|
|
130
|
+
this.classesByType.set(messageType, existing);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get all consumer classes that can handle a specific message type.
|
|
135
|
+
*
|
|
136
|
+
* Returns classes that either declare the message type explicitly or use the
|
|
137
|
+
* `'*'` wildcard. Callers resolve a fresh instance per message from the
|
|
138
|
+
* request-scoped container.
|
|
139
|
+
*
|
|
140
|
+
* @param messageType - The message type to find consumers for
|
|
141
|
+
* @returns Array of consumer classes that can handle this message type
|
|
142
|
+
*/
|
|
143
|
+
getConsumerClasses(messageType) {
|
|
144
|
+
const exactMatch = this.classesByType.get(messageType) ?? [];
|
|
145
|
+
const wildcardMatch = this.classesByType.get("*") ?? [];
|
|
146
|
+
return Array.from(new Set([...exactMatch, ...wildcardMatch]));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Check if any consumers can handle a message type
|
|
150
|
+
*
|
|
151
|
+
* @param messageType - The message type to check
|
|
152
|
+
* @returns true if at least one consumer can handle this type
|
|
153
|
+
*/
|
|
154
|
+
hasConsumers(messageType) {
|
|
155
|
+
return this.getConsumerClasses(messageType).length > 0;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get all registered message types
|
|
159
|
+
*
|
|
160
|
+
* @returns Array of message types with registered consumers
|
|
161
|
+
*/
|
|
162
|
+
getMessageTypes() {
|
|
163
|
+
return Array.from(this.classesByType.keys());
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get all consumer registrations (class + message types) for listing.
|
|
167
|
+
*/
|
|
168
|
+
getRegistrations() {
|
|
169
|
+
return this.registrations;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
ConsumerRegistry = __decorate([Singleton(DI_TOKENS.ConsumerRegistry)], ConsumerRegistry);
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/queue/queue-sender.ts
|
|
175
|
+
/**
|
|
176
|
+
* Queue Sender
|
|
177
|
+
*
|
|
178
|
+
* Implementation of IQueueSender bound to a specific queue binding.
|
|
179
|
+
* Created by QueueRegistry for each registered binding.
|
|
180
|
+
*
|
|
181
|
+
* Automatically enriches messages with:
|
|
182
|
+
* - `id`: UUID generated via crypto.randomUUID()
|
|
183
|
+
* - `metadata.locale`: Current locale from I18n context
|
|
184
|
+
* - `metadata.idempotencyKey`: Deterministic SHA-256 hash of type + payload (if not provided)
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* // Created by QueueRegistry, not directly instantiated
|
|
189
|
+
* const sender = registry.getQueue('NOTIFICATIONS_QUEUE')
|
|
190
|
+
*
|
|
191
|
+
* await sender.dispatch({
|
|
192
|
+
* type: 'email.send',
|
|
193
|
+
* payload: { to: 'user@example.com', subject: 'Hello' }
|
|
194
|
+
* })
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
var QueueSender = class {
|
|
198
|
+
binding;
|
|
199
|
+
provider;
|
|
200
|
+
i18n;
|
|
201
|
+
constructor(binding, provider, i18n) {
|
|
202
|
+
this.binding = binding;
|
|
203
|
+
this.provider = provider;
|
|
204
|
+
this.i18n = i18n;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Dispatch a message to this queue.
|
|
208
|
+
*
|
|
209
|
+
* @param message - Message to dispatch (without id)
|
|
210
|
+
*/
|
|
211
|
+
async dispatch(message) {
|
|
212
|
+
const metadata = { ...message.metadata };
|
|
213
|
+
if (!metadata.locale) {
|
|
214
|
+
const locale = this.i18n.getLocale();
|
|
215
|
+
if (locale) metadata.locale = locale;
|
|
216
|
+
}
|
|
217
|
+
metadata.idempotencyKey ??= await this.generateIdempotencyKey(message.type, message.payload);
|
|
218
|
+
metadata.binding = this.binding;
|
|
219
|
+
const fullMessage = {
|
|
220
|
+
id: crypto.randomUUID(),
|
|
221
|
+
...message,
|
|
222
|
+
metadata
|
|
223
|
+
};
|
|
224
|
+
await this.provider.send(this.binding, fullMessage);
|
|
225
|
+
}
|
|
226
|
+
async generateIdempotencyKey(type, payload) {
|
|
227
|
+
const data = new TextEncoder().encode(stableStringify({
|
|
228
|
+
type,
|
|
229
|
+
payload
|
|
230
|
+
}));
|
|
231
|
+
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
232
|
+
return `queue:${Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Deterministic JSON serialization: object keys are emitted in sorted order at
|
|
237
|
+
* every level so the output depends only on the data, not on key insertion
|
|
238
|
+
* order. Arrays keep their order (order is significant). Used to derive a stable
|
|
239
|
+
* idempotency hash from a message's `type` + `payload`.
|
|
240
|
+
*/
|
|
241
|
+
function stableStringify(value) {
|
|
242
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
|
|
243
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
244
|
+
return `{${Object.keys(value).sort().map((key) => {
|
|
245
|
+
const v = value[key];
|
|
246
|
+
if (v === void 0) return void 0;
|
|
247
|
+
return `${JSON.stringify(key)}:${stableStringify(v)}`;
|
|
248
|
+
}).filter((entry) => entry !== void 0).join(",")}}`;
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/queue/queue-registry.ts
|
|
252
|
+
let QueueRegistry = class QueueRegistry {
|
|
253
|
+
i18n;
|
|
254
|
+
provider;
|
|
255
|
+
senders = /* @__PURE__ */ new Map();
|
|
256
|
+
constructor(providerFactory, i18n) {
|
|
257
|
+
this.i18n = i18n;
|
|
258
|
+
this.provider = providerFactory.create();
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get or create a QueueSender for the specified binding.
|
|
262
|
+
*
|
|
263
|
+
* Senders are cached per binding within the request scope.
|
|
264
|
+
*
|
|
265
|
+
* @param binding - The queue binding to get a sender for
|
|
266
|
+
* @returns QueueSender bound to the specified binding
|
|
267
|
+
*/
|
|
268
|
+
getQueue(binding) {
|
|
269
|
+
let sender = this.senders.get(binding);
|
|
270
|
+
if (!sender) {
|
|
271
|
+
sender = new QueueSender(binding, this.provider, this.i18n);
|
|
272
|
+
this.senders.set(binding, sender);
|
|
273
|
+
}
|
|
274
|
+
return sender;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
QueueRegistry = __decorate([
|
|
278
|
+
Request(QUEUE_TOKENS.QueueRegistry),
|
|
279
|
+
__decorateParam(0, inject(QUEUE_TOKENS.QueueProviderFactory)),
|
|
280
|
+
__decorateParam(1, inject(I18N_TOKENS.I18nService))
|
|
281
|
+
], QueueRegistry);
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/queue/queue-store.ts
|
|
284
|
+
const IDEM_PREFIX = "queue:idem:";
|
|
285
|
+
const FAILED_PREFIX = "queue:failed:";
|
|
286
|
+
const DEFAULT_IDEMPOTENCY_TTL = 86400;
|
|
287
|
+
/** Default KV binding name used for queue state when none is configured. */
|
|
288
|
+
const DEFAULT_STORE_BINDING = "CACHE";
|
|
289
|
+
let QueueStore = class QueueStore {
|
|
290
|
+
cache;
|
|
291
|
+
idempotencyTtl;
|
|
292
|
+
constructor(cache, options) {
|
|
293
|
+
this.cache = cache.binding(options.store?.binding ?? "CACHE");
|
|
294
|
+
this.idempotencyTtl = options.idempotency?.ttl ?? DEFAULT_IDEMPOTENCY_TTL;
|
|
295
|
+
}
|
|
296
|
+
async isProcessed(key) {
|
|
297
|
+
return await this.cache.get(`${IDEM_PREFIX}${key}`) !== null;
|
|
298
|
+
}
|
|
299
|
+
async markProcessed(key) {
|
|
300
|
+
await this.cache.put(`${IDEM_PREFIX}${key}`, "1", { expirationTtl: this.idempotencyTtl });
|
|
301
|
+
}
|
|
302
|
+
async storeFailedJob(job) {
|
|
303
|
+
const metadata = {
|
|
304
|
+
queue: job.queue,
|
|
305
|
+
binding: job.binding,
|
|
306
|
+
type: job.type,
|
|
307
|
+
consumer: job.consumer,
|
|
308
|
+
attempts: job.attempts,
|
|
309
|
+
failedAt: job.failedAt
|
|
310
|
+
};
|
|
311
|
+
await this.cache.put(`${FAILED_PREFIX}${job.id}`, JSON.stringify(job), { metadata });
|
|
312
|
+
}
|
|
313
|
+
async getFailedJob(messageId) {
|
|
314
|
+
return this.cache.get(`${FAILED_PREFIX}${messageId}`, "json");
|
|
315
|
+
}
|
|
316
|
+
async removeFailedJob(messageId) {
|
|
317
|
+
await this.cache.delete(`${FAILED_PREFIX}${messageId}`);
|
|
318
|
+
}
|
|
319
|
+
async listFailedJobs(options) {
|
|
320
|
+
const result = await this.cache.list({
|
|
321
|
+
prefix: FAILED_PREFIX,
|
|
322
|
+
limit: options?.limit ?? 50,
|
|
323
|
+
cursor: options?.cursor
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
keys: result.keys.filter((key) => key.metadata != null).map((key) => ({
|
|
327
|
+
id: key.name.slice(13),
|
|
328
|
+
metadata: key.metadata
|
|
329
|
+
})),
|
|
330
|
+
cursor: result.list_complete ? void 0 : result.cursor
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
async purgeFailedJobs() {
|
|
334
|
+
let cursor;
|
|
335
|
+
do {
|
|
336
|
+
const result = await this.cache.list({
|
|
337
|
+
prefix: FAILED_PREFIX,
|
|
338
|
+
cursor
|
|
339
|
+
});
|
|
340
|
+
await Promise.all(result.keys.map((key) => this.cache.delete(key.name)));
|
|
341
|
+
cursor = result.list_complete ? void 0 : result.cursor;
|
|
342
|
+
} while (cursor);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Delete failed jobs older than `retentionSeconds` (by their `failedAt`
|
|
346
|
+
* timestamp). Returns the number removed. Backs the opt-in
|
|
347
|
+
* {@link FailedJobCleanupJob} cron — failed jobs otherwise persist
|
|
348
|
+
* indefinitely until retried or purged.
|
|
349
|
+
*/
|
|
350
|
+
async purgeFailedJobsOlderThan(retentionSeconds) {
|
|
351
|
+
const cutoff = Date.now() - retentionSeconds * 1e3;
|
|
352
|
+
let cursor;
|
|
353
|
+
let removed = 0;
|
|
354
|
+
do {
|
|
355
|
+
const result = await this.cache.list({
|
|
356
|
+
prefix: FAILED_PREFIX,
|
|
357
|
+
cursor
|
|
358
|
+
});
|
|
359
|
+
const expired = result.keys.filter((key) => {
|
|
360
|
+
const failedAt = key.metadata?.failedAt;
|
|
361
|
+
return failedAt !== void 0 && Date.parse(failedAt) < cutoff;
|
|
362
|
+
});
|
|
363
|
+
await Promise.all(expired.map((key) => this.cache.delete(key.name)));
|
|
364
|
+
removed += expired.length;
|
|
365
|
+
cursor = result.list_complete ? void 0 : result.cursor;
|
|
366
|
+
} while (cursor);
|
|
367
|
+
return removed;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
QueueStore = __decorate([
|
|
371
|
+
Transient(QUEUE_TOKENS.QueueStore),
|
|
372
|
+
__decorateParam(0, inject(CACHE_TOKENS.TieredCacheService)),
|
|
373
|
+
__decorateParam(1, inject(QUEUE_TOKENS.QueueModuleOptions))
|
|
374
|
+
], QueueStore);
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/queue/providers/cloudflare-queue.provider.ts
|
|
377
|
+
let CloudflareQueueProvider = class CloudflareQueueProvider {
|
|
378
|
+
env;
|
|
379
|
+
constructor(env) {
|
|
380
|
+
this.env = env;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Send a message to a Cloudflare Queue
|
|
384
|
+
*
|
|
385
|
+
* @param binding - Queue binding identifier (e.g., 'NOTIFICATIONS_QUEUE')
|
|
386
|
+
* @param message - Complete message with id and payload
|
|
387
|
+
* @throws {QueueError} If the binding is not configured on env
|
|
388
|
+
*/
|
|
389
|
+
async send(binding, message) {
|
|
390
|
+
const queue = this.env[binding];
|
|
391
|
+
if (!queue) throw new QueueError(`Queue binding "${binding}" was not found in the environment`);
|
|
392
|
+
await queue.send(message);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
CloudflareQueueProvider = __decorate([Transient(), __decorateParam(0, inject(DI_TOKENS.CloudflareEnv))], CloudflareQueueProvider);
|
|
396
|
+
//#endregion
|
|
397
|
+
//#region src/queue/providers/sync-queue.provider.ts
|
|
398
|
+
let SyncQueueProvider = class SyncQueueProvider {
|
|
399
|
+
registry;
|
|
400
|
+
root;
|
|
401
|
+
constructor(registry, root) {
|
|
402
|
+
this.registry = registry;
|
|
403
|
+
this.root = root;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Process a message synchronously.
|
|
407
|
+
*
|
|
408
|
+
* Runs inside the active request scope when dispatch happens within one (an
|
|
409
|
+
* HTTP request, or `runInScope` for queues/cron/commands). When dispatched
|
|
410
|
+
* with no ambient scope — e.g. a service invoked directly in a test — it
|
|
411
|
+
* establishes its own request scope (mirroring the production queue handler)
|
|
412
|
+
* so consumers and their request-scoped dependencies resolve correctly.
|
|
413
|
+
*
|
|
414
|
+
* @param _binding - Queue binding (not used for routing, consumers match by message type)
|
|
415
|
+
* @param message - Complete message with id and payload
|
|
416
|
+
* @throws Re-throws any error from consumer.handle() after calling onError()
|
|
417
|
+
*/
|
|
418
|
+
async send(_binding, message) {
|
|
419
|
+
const ambient = containerStorage.getStore();
|
|
420
|
+
if (ambient) {
|
|
421
|
+
await this.process(ambient, message);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const locale = message.metadata?.locale ?? "en";
|
|
425
|
+
await this.root.runInRequestScope({
|
|
426
|
+
getLocale: () => locale,
|
|
427
|
+
setLocale: () => {},
|
|
428
|
+
getContainer: () => containerStorage.getStore() ?? this.root
|
|
429
|
+
}, (container) => this.process(container, message));
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Resolve a fresh consumer per message from `container` (matched by type) and
|
|
433
|
+
* invoke each sequentially, fail-fast on the first error after `onError`.
|
|
434
|
+
*/
|
|
435
|
+
async process(container, message) {
|
|
436
|
+
const consumers = this.registry.getConsumerClasses(message.type).map((ConsumerClass) => container.resolve(ConsumerClass));
|
|
437
|
+
for (const consumer of consumers) try {
|
|
438
|
+
await consumer.handle(message);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
const errorInstance = error instanceof Error ? error : new Error(String(error));
|
|
441
|
+
if (consumer.onError) await consumer.onError(errorInstance, message);
|
|
442
|
+
throw errorInstance;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
SyncQueueProvider = __decorate([
|
|
447
|
+
Transient(),
|
|
448
|
+
__decorateParam(0, inject(DI_TOKENS.ConsumerRegistry)),
|
|
449
|
+
__decorateParam(1, inject(DI_TOKENS.Container))
|
|
450
|
+
], SyncQueueProvider);
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region src/queue/services/queue-provider-factory.ts
|
|
453
|
+
let QueueProviderFactory = class QueueProviderFactory {
|
|
454
|
+
env;
|
|
455
|
+
registry;
|
|
456
|
+
container;
|
|
457
|
+
options;
|
|
458
|
+
constructor(env, registry, container, options) {
|
|
459
|
+
this.env = env;
|
|
460
|
+
this.registry = registry;
|
|
461
|
+
this.container = container;
|
|
462
|
+
this.options = options;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Create a queue provider based on module configuration
|
|
466
|
+
*
|
|
467
|
+
* @returns Queue provider instance
|
|
468
|
+
* @throws {QueueError} If provider type is not supported
|
|
469
|
+
*/
|
|
470
|
+
create() {
|
|
471
|
+
const providerType = this.options?.provider ?? "cloudflare";
|
|
472
|
+
switch (providerType) {
|
|
473
|
+
case "cloudflare": return new CloudflareQueueProvider(this.env);
|
|
474
|
+
case "sync": return new SyncQueueProvider(this.registry, this.container);
|
|
475
|
+
default: throw new QueueError(`Queue provider "${String(providerType)}" is not supported`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
QueueProviderFactory = __decorate([
|
|
480
|
+
Transient(QUEUE_TOKENS.QueueProviderFactory),
|
|
481
|
+
__decorateParam(0, inject(DI_TOKENS.CloudflareEnv)),
|
|
482
|
+
__decorateParam(1, inject(DI_TOKENS.ConsumerRegistry)),
|
|
483
|
+
__decorateParam(2, inject(DI_TOKENS.Container)),
|
|
484
|
+
__decorateParam(3, inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true }))
|
|
485
|
+
], QueueProviderFactory);
|
|
486
|
+
//#endregion
|
|
487
|
+
//#region src/queue/queue.module.ts
|
|
488
|
+
/**
|
|
489
|
+
* Queue Module
|
|
490
|
+
*
|
|
491
|
+
* Provides declarative queue infrastructure with provider abstraction.
|
|
492
|
+
*
|
|
493
|
+
* **Usage:**
|
|
494
|
+
* ```typescript
|
|
495
|
+
* // 1. Configure provider (once, in app root)
|
|
496
|
+
* QueueModule.forRootAsync({
|
|
497
|
+
* inject: [CONFIG_TOKENS.ConfigService],
|
|
498
|
+
* useFactory: (config) => ({ provider: config.get('queue').provider })
|
|
499
|
+
* })
|
|
500
|
+
*
|
|
501
|
+
* // 2. Register queue bindings (the binding IS the injection token)
|
|
502
|
+
* QueueModule.registerQueue('NOTIFICATIONS_QUEUE')
|
|
503
|
+
* QueueModule.registerQueue('BACKGROUND_QUEUE')
|
|
504
|
+
*
|
|
505
|
+
* // 3. Inject and use
|
|
506
|
+
* constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}
|
|
507
|
+
* await this.queue.dispatch({ type: 'email.send', payload: {...} })
|
|
508
|
+
* ```
|
|
509
|
+
*
|
|
510
|
+
* **Providers:**
|
|
511
|
+
* - `cloudflare`: Production provider using Cloudflare Queue bindings
|
|
512
|
+
* - `sync`: Testing provider that processes messages immediately
|
|
513
|
+
*/
|
|
514
|
+
var queue_module_exports = /* @__PURE__ */ __exportAll({ QueueModule: () => QueueModule });
|
|
515
|
+
var _QueueModule;
|
|
516
|
+
let QueueModule = _QueueModule = class QueueModule {
|
|
517
|
+
/**
|
|
518
|
+
* Fail fast at boot if the configured KV store binding is missing, rather
|
|
519
|
+
* than letting every queue invocation hard-fail lazily. The binding backs
|
|
520
|
+
* idempotency claims and failed-job storage, so without it the queue
|
|
521
|
+
* subsystem cannot function.
|
|
522
|
+
*/
|
|
523
|
+
onInitialize({ container }) {
|
|
524
|
+
const options = container.resolve(QUEUE_TOKENS.QueueModuleOptions);
|
|
525
|
+
if (options.provider !== "cloudflare") return;
|
|
526
|
+
const binding = options.store?.binding ?? "CACHE";
|
|
527
|
+
if (!container.resolve(DI_TOKENS.CloudflareEnv)[binding]) throw new QueueError(`Queue KV store binding "${binding}" was not found in the environment. The queue subsystem persists idempotency claims and failed jobs to KV. Add a kv_namespaces entry for "${binding}" in wrangler.jsonc, or set QueueModule.forRootAsync({ ..., store: { binding: 'YOUR_KV' } }) to point at an existing namespace.`);
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Configure queue infrastructure with async factory.
|
|
531
|
+
*
|
|
532
|
+
* Use when provider configuration depends on other services like ConfigService.
|
|
533
|
+
*
|
|
534
|
+
* @param options - Async configuration with factory and inject tokens
|
|
535
|
+
* @returns Dynamic module with queue infrastructure
|
|
536
|
+
*
|
|
537
|
+
* @example
|
|
538
|
+
* ```typescript
|
|
539
|
+
* QueueModule.forRootAsync({
|
|
540
|
+
* inject: [CONFIG_TOKENS.ConfigService],
|
|
541
|
+
* useFactory: (config: IConfigService) => ({
|
|
542
|
+
* provider: config.get('queue').provider
|
|
543
|
+
* })
|
|
544
|
+
* })
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
static forRootAsync(options) {
|
|
548
|
+
return {
|
|
549
|
+
module: _QueueModule,
|
|
550
|
+
providers: [{
|
|
551
|
+
provide: QUEUE_TOKENS.QueueModuleOptions,
|
|
552
|
+
useFactory: options.useFactory,
|
|
553
|
+
inject: options.inject
|
|
554
|
+
}]
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Register a queue binding for injection.
|
|
559
|
+
*
|
|
560
|
+
* The binding name doubles as the DI injection token and the
|
|
561
|
+
* `env`-lookup key. Binding names are typed against `StratalEnv`
|
|
562
|
+
* (autocomplete works once an app augments `StratalEnv` with its
|
|
563
|
+
* Cloudflare bindings).
|
|
564
|
+
*
|
|
565
|
+
* @param binding - Queue binding identifier (e.g. `NOTIFICATIONS_QUEUE`).
|
|
566
|
+
* @returns Dynamic module that provides the queue sender
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* ```typescript
|
|
570
|
+
* // In AppModule imports
|
|
571
|
+
* QueueModule.registerQueue('NOTIFICATIONS_QUEUE')
|
|
572
|
+
*
|
|
573
|
+
* // Then inject using the binding name
|
|
574
|
+
* constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
577
|
+
static registerQueue(binding) {
|
|
578
|
+
return {
|
|
579
|
+
module: _QueueModule,
|
|
580
|
+
providers: [{
|
|
581
|
+
provide: binding,
|
|
582
|
+
useFactory: (registry) => registry.getQueue(binding),
|
|
583
|
+
inject: [QUEUE_TOKENS.QueueRegistry]
|
|
584
|
+
}]
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
QueueModule = _QueueModule = __decorate([Module({
|
|
589
|
+
imports: [CacheModule],
|
|
590
|
+
providers: [
|
|
591
|
+
{
|
|
592
|
+
provide: DI_TOKENS.ConsumerRegistry,
|
|
593
|
+
useClass: ConsumerRegistry
|
|
594
|
+
},
|
|
595
|
+
QueueManager,
|
|
596
|
+
{
|
|
597
|
+
provide: QUEUE_TOKENS.QueueProviderFactory,
|
|
598
|
+
useClass: QueueProviderFactory
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
provide: QUEUE_TOKENS.QueueRegistry,
|
|
602
|
+
useClass: QueueRegistry
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
provide: QUEUE_TOKENS.QueueStore,
|
|
606
|
+
useClass: QueueStore
|
|
607
|
+
}
|
|
608
|
+
]
|
|
609
|
+
})], QueueModule);
|
|
610
|
+
//#endregion
|
|
611
|
+
export { CloudflareQueueProvider as a, QueueRegistry as c, QueueManager as d, QueueError as f, SyncQueueProvider as i, QueueSender as l, queue_module_exports as n, DEFAULT_STORE_BINDING as o, QueueProviderFactory as r, QueueStore as s, QueueModule as t, ConsumerRegistry as u };
|
|
612
|
+
|
|
613
|
+
//# sourceMappingURL=queue.module-nddvxzCB.mjs.map
|