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
|
@@ -1,102 +1,11 @@
|
|
|
1
|
-
import { r as
|
|
2
|
-
import {
|
|
1
|
+
import { r as DI_TOKENS, t as Container, v as ROUTER_TOKENS } from "./di-DseMn-z9.mjs";
|
|
2
|
+
import { n as getContainer, r as runWithContainer, t as containerStorage } from "./container-storage-BmOJ4_Na.mjs";
|
|
3
3
|
import { JsonFormatter, LOGGER_TOKENS, LoggerService, PrettyFormatter } from "./logger/index.mjs";
|
|
4
|
-
import { a as DefaultExceptionHandler, r as StratalNotInitializedError } from "./errors-
|
|
5
|
-
import { i as createQueueExceptionContext, n as createCronExceptionContext, t as createCliExceptionContext } from "./exception-context-
|
|
6
|
-
import { a as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { t as CronManager } from "./cron-manager-9bpN9bu4.mjs";
|
|
10
|
-
import { n as OpenAPIModule } from "./openapi-C6lm0RmV.mjs";
|
|
11
|
-
import { a as RouteRegistry, d as RouteRegistrationService, n as Uri, o as VersioningService, s as LocalePathService, u as HonoApp } from "./router-Cy6DjkvP.mjs";
|
|
12
|
-
import { t as I18nModule } from "./i18n.module-BlXrtAlV.mjs";
|
|
13
|
-
import { t as QuarryRegistry } from "./quarry-registry-DkraZNwn.mjs";
|
|
14
|
-
import { t as QueueModule } from "./queue.module-DeWJ0tQM.mjs";
|
|
15
|
-
import { i as SeederRegistry, r as SEEDER_TOKENS } from "./seeder-BADTig4n.mjs";
|
|
16
|
-
//#region src/router/router-resolver.ts
|
|
17
|
-
/**
|
|
18
|
-
* Internal resolver that computes the effective Router config for each controller.
|
|
19
|
-
*
|
|
20
|
-
* Inheritance rules:
|
|
21
|
-
* - `middleware`: parent middleware runs first, then child (concatenated)
|
|
22
|
-
* - `prefix`: concatenated (parent + child)
|
|
23
|
-
* - `name`: concatenated (parent + child)
|
|
24
|
-
* - `domain`: child overrides parent
|
|
25
|
-
* - `version`: child overrides parent
|
|
26
|
-
* - `hideFromDocs`: child overrides parent
|
|
27
|
-
*
|
|
28
|
-
* @internal — not exported from stratal/router
|
|
29
|
-
*/
|
|
30
|
-
var RouterResolver = class {
|
|
31
|
-
routers;
|
|
32
|
-
constructor(routers) {
|
|
33
|
-
this.routers = routers;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Resolve the effective config for a given controller class.
|
|
37
|
-
* Searches through all module routers to find the one owning this controller.
|
|
38
|
-
*/
|
|
39
|
-
resolveForController(controller) {
|
|
40
|
-
for (const { router, controllers: moduleControllers } of this.routers) {
|
|
41
|
-
if (!moduleControllers.includes(controller)) continue;
|
|
42
|
-
for (const group of router[getGroups]()) if (group.controllers?.includes(controller)) return this.mergeEntries(router[getDefaultEntry](), group);
|
|
43
|
-
if (!new Set(router[getGroups]().flatMap((g) => g.controllers ?? [])).has(controller)) return this.entryToConfig(router[getDefaultEntry]());
|
|
44
|
-
}
|
|
45
|
-
return { middleware: [] };
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Collect all global middleware registered via `router.use()` across all modules.
|
|
49
|
-
*/
|
|
50
|
-
getGlobalMiddleware() {
|
|
51
|
-
const global = [];
|
|
52
|
-
for (const { router } of this.routers) global.push(...router[getGlobalMiddleware]());
|
|
53
|
-
return global;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Merge parent default entry with child group entry following inheritance rules.
|
|
57
|
-
*/
|
|
58
|
-
mergeEntries(parent, child) {
|
|
59
|
-
return {
|
|
60
|
-
prefix: this.concatPrefixes(parent.prefix, child.prefix),
|
|
61
|
-
domain: child.domain ?? parent.domain,
|
|
62
|
-
name: this.concatNames(parent.name, child.name),
|
|
63
|
-
middleware: [...parent.middleware, ...child.middleware],
|
|
64
|
-
version: child.version ?? parent.version,
|
|
65
|
-
hideFromDocs: child.hideFromDocs ?? parent.hideFromDocs,
|
|
66
|
-
params: this.mergeParams(parent.params, child.params)
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
entryToConfig(entry) {
|
|
70
|
-
return {
|
|
71
|
-
prefix: entry.prefix,
|
|
72
|
-
domain: entry.domain,
|
|
73
|
-
name: entry.name,
|
|
74
|
-
middleware: [...entry.middleware],
|
|
75
|
-
version: entry.version,
|
|
76
|
-
hideFromDocs: entry.hideFromDocs,
|
|
77
|
-
params: entry.params
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
mergeParams(parent, child) {
|
|
81
|
-
if (!parent && !child) return void 0;
|
|
82
|
-
if (!parent) return child;
|
|
83
|
-
if (!child) return parent;
|
|
84
|
-
return parent.extend(child.shape);
|
|
85
|
-
}
|
|
86
|
-
concatPrefixes(parent, child) {
|
|
87
|
-
if (!parent && !child) return void 0;
|
|
88
|
-
if (!parent) return child;
|
|
89
|
-
if (!child) return parent;
|
|
90
|
-
return `${parent.endsWith("/") ? parent.slice(0, -1) : parent}${child.startsWith("/") ? child : `/${child}`}`;
|
|
91
|
-
}
|
|
92
|
-
concatNames(parent, child) {
|
|
93
|
-
if (!parent && !child) return void 0;
|
|
94
|
-
if (!parent) return child;
|
|
95
|
-
if (!child) return parent;
|
|
96
|
-
return `${parent}${child}`;
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
//#endregion
|
|
4
|
+
import { a as DefaultExceptionHandler, r as StratalNotInitializedError } from "./errors-mXYxG0XB.mjs";
|
|
5
|
+
import { i as createQueueExceptionContext, n as createCronExceptionContext, t as createCliExceptionContext } from "./exception-context-kEoMFwze.mjs";
|
|
6
|
+
import { a as getListenerHandlers } from "./events-BXJGZjpG.mjs";
|
|
7
|
+
import { d as LazyModuleLoader, t as ModuleRegistry } from "./module-registry-Dm-pqHd3.mjs";
|
|
8
|
+
import { t as SEEDER_TOKENS } from "./seeder-registry-CyUmKsJq.mjs";
|
|
100
9
|
//#region src/application.ts
|
|
101
10
|
var Application = class {
|
|
102
11
|
_container;
|
|
@@ -107,7 +16,10 @@ var Application = class {
|
|
|
107
16
|
quarry;
|
|
108
17
|
initialized = false;
|
|
109
18
|
routingInitPromise = null;
|
|
110
|
-
|
|
19
|
+
eventsInitPromise = null;
|
|
20
|
+
queueInitPromise = null;
|
|
21
|
+
i18nInitPromise = null;
|
|
22
|
+
cronInitPromise = null;
|
|
111
23
|
env;
|
|
112
24
|
appConfig;
|
|
113
25
|
constructor({ env, ctx, ...config }) {
|
|
@@ -139,48 +51,109 @@ var Application = class {
|
|
|
139
51
|
await runWithContainer(this._container, () => this.initializeInternal());
|
|
140
52
|
}
|
|
141
53
|
async initializeInternal() {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
QueueModule,
|
|
145
|
-
CacheModule
|
|
146
|
-
]);
|
|
54
|
+
const [{ QuarryModule }, { SeederModule }] = await Promise.all([import("./quarry.module-BuRPGMDm.mjs").then((n) => n.n), import("./seeder.module-CYYwk3Qk.mjs").then((n) => n.n)]);
|
|
55
|
+
this.moduleRegistry.registerAll([QuarryModule, SeederModule]);
|
|
147
56
|
this.moduleRegistry.register(this.appConfig.module);
|
|
148
57
|
await this.moduleRegistry.initialize();
|
|
149
58
|
this.initializeExceptionHandler();
|
|
150
|
-
this.cronManager = this._container.resolve(DI_TOKENS.Cron);
|
|
151
|
-
this.registerCronJobs();
|
|
152
|
-
this.registerSeeders();
|
|
153
59
|
this.registerCommands();
|
|
60
|
+
this.registerSeeders();
|
|
61
|
+
if (this.moduleRegistry.getAllListeners().length > 0) await this.initializeEventListeners();
|
|
62
|
+
if (this.moduleRegistry.getAllJobs().length > 0) await this.ensureCron();
|
|
154
63
|
this.initialized = true;
|
|
155
64
|
}
|
|
156
|
-
registerRoutingServices() {
|
|
65
|
+
async registerRoutingServices() {
|
|
66
|
+
const [{ HonoApp }, { RouteRegistry }, { RouterResolver }, { VersioningService }, { LocalePathService }, { LocaleUrlService }, { RouteRegistrationService }, { Uri }] = await Promise.all([
|
|
67
|
+
import("./hono-app-CvV3hOfT.mjs").then((n) => n.n),
|
|
68
|
+
import("./route-registry-CYqLp2Nj.mjs").then((n) => n.n),
|
|
69
|
+
import("./router-resolver-D4YlPNlm.mjs"),
|
|
70
|
+
import("./versioning.service-C6aHky8-.mjs").then((n) => n.n),
|
|
71
|
+
import("./locale-path.service-D-dHiIPc.mjs").then((n) => n.n),
|
|
72
|
+
import("./locale-url.service-C2EWmGdq.mjs").then((n) => n.n),
|
|
73
|
+
import("./route-registration.service-D6vSwiKP.mjs").then((n) => n.n),
|
|
74
|
+
import("./uri-h7Q8Jug9.mjs").then((n) => n.r)
|
|
75
|
+
]);
|
|
157
76
|
this._container.register(ROUTER_TOKENS.VersioningService, VersioningService);
|
|
158
77
|
this._container.register(ROUTER_TOKENS.HonoApp, HonoApp);
|
|
159
78
|
this._container.register(ROUTER_TOKENS.LocalePathService, LocalePathService);
|
|
79
|
+
this._container.register(ROUTER_TOKENS.LocaleUrlService, LocaleUrlService);
|
|
160
80
|
this._container.register(ROUTER_TOKENS.RouteRegistry, RouteRegistry);
|
|
161
81
|
this._container.register(ROUTER_TOKENS.Uri, Uri);
|
|
162
82
|
const routerConfigs = this.moduleRegistry.getAllRouterConfigs();
|
|
163
83
|
if (routerConfigs.length > 0) this._container.registerValue(ROUTER_TOKENS.RouterResolver, new RouterResolver(routerConfigs));
|
|
164
84
|
this._container.register(RouteRegistrationService, RouteRegistrationService);
|
|
165
85
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Load the events subsystem and wire listeners. Needed by the HTTP, queue,
|
|
88
|
+
* and scheduled paths (handlers emit events). Independent of the queue
|
|
89
|
+
* subsystem so HTTP-only apps never load queue code. EventsModule is
|
|
90
|
+
* registered before the (possibly empty) listener wiring so `emit()` works
|
|
91
|
+
* even with zero listeners; dedups against the framework's own load.
|
|
92
|
+
*/
|
|
93
|
+
initializeEventListeners() {
|
|
94
|
+
this.eventsInitPromise ??= runWithContainer(this._container, async () => {
|
|
95
|
+
const { EventsModule } = await import("./events-BXJGZjpG.mjs").then((n) => n.n);
|
|
96
|
+
await this.moduleRegistry.registerLazy(EventsModule);
|
|
170
97
|
this.registerEventListeners();
|
|
171
|
-
return Promise.resolve();
|
|
172
98
|
});
|
|
173
|
-
return this.
|
|
99
|
+
return this.eventsInitPromise;
|
|
174
100
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Wire event and queue handlers for any non-HTTP request scope — queue
|
|
103
|
+
* batches, scheduled/cron runs, CLI commands, Durable Objects, Workflows,
|
|
104
|
+
* WorkerEntrypoints — all of which may emit events or dispatch to queues from
|
|
105
|
+
* arbitrary user code. This is the single source of truth for that wiring:
|
|
106
|
+
* every non-HTTP entry point routes through it rather than open-coding which
|
|
107
|
+
* subsystems to init, so a subsystem can't be silently dropped by a future
|
|
108
|
+
* refactor (the queue half was once lost from the command path that way).
|
|
109
|
+
*
|
|
110
|
+
* HTTP (`fetch`) deliberately does NOT use this: a fetch worker only enqueues
|
|
111
|
+
* to the async Cloudflare queue and never processes consumers inline, so it
|
|
112
|
+
* skips queue init via `initializeRouting`.
|
|
113
|
+
*/
|
|
114
|
+
async ensureScopedHandlers() {
|
|
115
|
+
await this.initializeEventListeners();
|
|
116
|
+
await this.initializeQueue();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Load the queue subsystem on demand (first queue/scheduled trigger). i18n is
|
|
120
|
+
* ensured first because the queue registry depends on it.
|
|
121
|
+
*/
|
|
122
|
+
initializeQueue() {
|
|
123
|
+
this.queueInitPromise ??= runWithContainer(this._container, async () => {
|
|
124
|
+
await this.ensureI18n();
|
|
125
|
+
const { QueueModule } = await import("./queue.module-nddvxzCB.mjs").then((n) => n.n);
|
|
126
|
+
this.moduleRegistry.register(QueueModule);
|
|
127
|
+
this.consumerRegistry = this._container.resolve(DI_TOKENS.ConsumerRegistry);
|
|
128
|
+
await this.registerQueueConsumers();
|
|
182
129
|
});
|
|
183
|
-
return this.
|
|
130
|
+
return this.queueInitPromise;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Load i18n on demand. Coupled to the request path (Zod validation error maps,
|
|
134
|
+
* OpenAPI descriptions, queue registry), so it loads before routing/queue
|
|
135
|
+
* handling. Uses `registerLazy` because I18nModule has an `onInitialize` hook
|
|
136
|
+
* (configures the Zod error map). Dedups if the app already imported i18n.
|
|
137
|
+
*/
|
|
138
|
+
ensureI18n() {
|
|
139
|
+
this.i18nInitPromise ??= runWithContainer(this._container, async () => {
|
|
140
|
+
const { I18nModule } = await import("./i18n.module-DRQAZoSZ.mjs").then((n) => n.n);
|
|
141
|
+
await this.moduleRegistry.registerLazy(I18nModule);
|
|
142
|
+
});
|
|
143
|
+
return this.i18nInitPromise;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Load the cron subsystem on demand (first scheduled trigger, or at bootstrap
|
|
147
|
+
* when the app declares jobs).
|
|
148
|
+
*/
|
|
149
|
+
ensureCron() {
|
|
150
|
+
this.cronInitPromise ??= runWithContainer(this._container, async () => {
|
|
151
|
+
const { CronModule } = await import("./cron.module-Bgzq5hiT.mjs").then((n) => n.n);
|
|
152
|
+
this.moduleRegistry.register(CronModule);
|
|
153
|
+
this.cronManager = this._container.resolve(DI_TOKENS.Cron);
|
|
154
|
+
this.registerCronJobs(this.cronManager);
|
|
155
|
+
});
|
|
156
|
+
return this.cronInitPromise.then(() => this.cronManager);
|
|
184
157
|
}
|
|
185
158
|
resolve(token) {
|
|
186
159
|
try {
|
|
@@ -193,7 +166,7 @@ var Application = class {
|
|
|
193
166
|
}
|
|
194
167
|
}
|
|
195
168
|
async handleQueue(batch, queueName) {
|
|
196
|
-
await this.
|
|
169
|
+
await this.ensureScopedHandlers();
|
|
197
170
|
const locale = (batch.messages[0]?.body)?.metadata?.locale ?? "en";
|
|
198
171
|
const mockRouterContext = this.createMockRouterContext(locale);
|
|
199
172
|
await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {
|
|
@@ -206,11 +179,13 @@ var Application = class {
|
|
|
206
179
|
});
|
|
207
180
|
}
|
|
208
181
|
async handleScheduled(controller) {
|
|
209
|
-
await this.
|
|
182
|
+
const cronManager = await this.ensureCron();
|
|
183
|
+
await this.ensureScopedHandlers();
|
|
184
|
+
await this.ensureI18n();
|
|
210
185
|
const mockRouterContext = this.createMockRouterContext("en");
|
|
211
186
|
await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {
|
|
212
187
|
try {
|
|
213
|
-
await
|
|
188
|
+
await cronManager.executeScheduled(controller, requestContainer);
|
|
214
189
|
} catch (error) {
|
|
215
190
|
await requestContainer.resolve(DI_TOKENS.ExceptionHandler).handle(error, createCronExceptionContext());
|
|
216
191
|
throw error;
|
|
@@ -221,7 +196,7 @@ var Application = class {
|
|
|
221
196
|
return {
|
|
222
197
|
getLocale: () => locale,
|
|
223
198
|
setLocale: () => {},
|
|
224
|
-
getContainer: () => this._container
|
|
199
|
+
getContainer: () => containerStorage.getStore() ?? this._container
|
|
225
200
|
};
|
|
226
201
|
}
|
|
227
202
|
async shutdown() {
|
|
@@ -233,12 +208,25 @@ var Application = class {
|
|
|
233
208
|
}
|
|
234
209
|
async handleCommand(name, input) {
|
|
235
210
|
await this.initializeRouting();
|
|
211
|
+
await this.ensureScopedHandlers();
|
|
236
212
|
this.quarry ??= this._container.resolve(DI_TOKENS.Quarry);
|
|
237
213
|
const mockContext = this.createMockRouterContext("en");
|
|
238
214
|
return this._container.runInRequestScope(mockContext, async () => {
|
|
239
215
|
return this.quarry.call(name, input);
|
|
240
216
|
});
|
|
241
217
|
}
|
|
218
|
+
initializeRouting() {
|
|
219
|
+
this.routingInitPromise ??= runWithContainer(this._container, async () => {
|
|
220
|
+
await this.initializeEventListeners();
|
|
221
|
+
await this.ensureI18n();
|
|
222
|
+
const { OpenAPIModule } = await import("./openapi/index.mjs");
|
|
223
|
+
this.moduleRegistry.register(OpenAPIModule);
|
|
224
|
+
await this.registerRoutingServices();
|
|
225
|
+
this.honoApp = this._container.resolve(ROUTER_TOKENS.HonoApp);
|
|
226
|
+
await this.honoApp.configure();
|
|
227
|
+
});
|
|
228
|
+
return this.routingInitPromise;
|
|
229
|
+
}
|
|
242
230
|
registerCommands() {
|
|
243
231
|
this.quarry ??= this._container.resolve(DI_TOKENS.Quarry);
|
|
244
232
|
const commands = this.moduleRegistry.getAllCommands();
|
|
@@ -250,16 +238,22 @@ var Application = class {
|
|
|
250
238
|
const registry = this._container.resolve(SEEDER_TOKENS.SeederRegistry);
|
|
251
239
|
for (const SeederClass of seeders) registry.register(SeederClass);
|
|
252
240
|
}
|
|
253
|
-
registerQueueConsumers() {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
241
|
+
async registerQueueConsumers() {
|
|
242
|
+
const consumerClasses = this.moduleRegistry.getAllConsumers();
|
|
243
|
+
if (consumerClasses.length === 0) return;
|
|
244
|
+
const mockContext = this.createMockRouterContext("en");
|
|
245
|
+
await this._container.runInRequestScope(mockContext, (requestContainer) => {
|
|
246
|
+
for (const ConsumerClass of consumerClasses) {
|
|
247
|
+
const consumer = requestContainer.resolve(ConsumerClass);
|
|
248
|
+
this.consumerRegistry.register(ConsumerClass, consumer.messageTypes);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
258
251
|
}
|
|
259
|
-
registerCronJobs() {
|
|
252
|
+
registerCronJobs(cronManager) {
|
|
260
253
|
for (const JobClass of this.moduleRegistry.getAllJobs()) {
|
|
261
254
|
const schedule = JobClass.schedule;
|
|
262
|
-
if (schedule)
|
|
255
|
+
if (schedule) cronManager.registerJob(schedule, JobClass);
|
|
256
|
+
else this._container.resolve(LOGGER_TOKENS.LoggerService).warn(`Cron job "${JobClass.name}" has no static schedule property — skipped`);
|
|
263
257
|
}
|
|
264
258
|
}
|
|
265
259
|
registerEventListeners() {
|
|
@@ -267,9 +261,13 @@ var Application = class {
|
|
|
267
261
|
if (listeners.length === 0) return;
|
|
268
262
|
const eventRegistry = this._container.resolve(DI_TOKENS.EventRegistry);
|
|
269
263
|
for (const ListenerClass of listeners) {
|
|
270
|
-
const instance = this._container.resolve(ListenerClass);
|
|
271
264
|
const handlers = getListenerHandlers(ListenerClass);
|
|
272
|
-
for (const { methodName, event, options } of handlers)
|
|
265
|
+
for (const { methodName, event, options } of handlers) {
|
|
266
|
+
const handler = ((...args) => {
|
|
267
|
+
return getContainer().resolve(ListenerClass)[methodName](...args);
|
|
268
|
+
});
|
|
269
|
+
eventRegistry.on(event, handler, options);
|
|
270
|
+
}
|
|
273
271
|
}
|
|
274
272
|
}
|
|
275
273
|
registerLoggerService() {
|
|
@@ -279,12 +277,16 @@ var Application = class {
|
|
|
279
277
|
this._container.when(() => formatter === "pretty").use(LOGGER_TOKENS.Formatter).give(PrettyFormatter).otherwise(JsonFormatter);
|
|
280
278
|
this._container.registerSingleton(LOGGER_TOKENS.LoggerService, LoggerService);
|
|
281
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Bootstrap kernel — registered imperatively because they cannot be expressed
|
|
282
|
+
* as ordinary module providers: ExceptionHandler is a user-overridable forced
|
|
283
|
+
* singleton (a module ClassProvider derives scope from the class decorator,
|
|
284
|
+
* which a user handler may not carry), and LazyModuleLoader is the loader
|
|
285
|
+
* itself. Subsystem registries (events/cron/quarry/seeder) are modules.
|
|
286
|
+
*/
|
|
282
287
|
registerCoreServices() {
|
|
283
|
-
this._container.registerSingleton(DI_TOKENS.Cron, CronManager);
|
|
284
288
|
this._container.registerSingleton(DI_TOKENS.ExceptionHandler, this.appConfig.exceptionHandler ?? DefaultExceptionHandler);
|
|
285
|
-
this._container.registerSingleton(DI_TOKENS.
|
|
286
|
-
this._container.registerSingleton(DI_TOKENS.Quarry, QuarryRegistry);
|
|
287
|
-
this._container.registerValue(SEEDER_TOKENS.SeederRegistry, new SeederRegistry(this));
|
|
289
|
+
this._container.registerSingleton(DI_TOKENS.LazyModuleLoader, LazyModuleLoader);
|
|
288
290
|
}
|
|
289
291
|
initializeExceptionHandler() {
|
|
290
292
|
const handler = this._container.resolve(DI_TOKENS.ExceptionHandler);
|
|
@@ -378,4 +380,4 @@ var Stratal = class Stratal {
|
|
|
378
380
|
//#endregion
|
|
379
381
|
export { Application as n, Stratal as t };
|
|
380
382
|
|
|
381
|
-
//# sourceMappingURL=stratal-
|
|
383
|
+
//# sourceMappingURL=stratal-DL9M38_s.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stratal-DL9M38_s.mjs","names":[],"sources":["../src/application.ts","../src/stratal.ts"],"sourcesContent":["import type { CronJob } from './cron/cron-job'\nimport type { CronManager } from './cron/cron-manager'\nimport { Container } from './di/container'\nimport { containerStorage, getContainer, runWithContainer } from './di/container-storage'\nimport { DI_TOKENS } from './di/tokens'\nimport { type StratalEnv } from './env'\nimport { DefaultExceptionHandler } from './errors/default-exception-handler'\nimport { createCliExceptionContext, createCronExceptionContext, createQueueExceptionContext } from './errors/exception-context'\nimport type { ExceptionHandler } from './errors/exception-handler'\nimport type { EventHandler, EventRegistry } from './events'\nimport { getListenerHandlers } from './events'\nimport type { StratalExecutionContext } from './execution-context'\nimport { JsonFormatter, LOGGER_TOKENS, LoggerService, LogLevel, PrettyFormatter } from './logger'\nimport { LazyModuleLoader } from './module/lazy-module-loader'\nimport { ModuleRegistry } from './module/module-registry'\nimport type { DynamicModule, ModuleClass } from './module/types'\nimport type { Command } from './quarry/command'\nimport type { QuarryRegistry } from './quarry/quarry-registry'\nimport type { CommandInput, CommandResult } from './quarry/types'\nimport type { ConsumerRegistry } from './queue/consumer-registry'\nimport type { IQueueConsumer, QueueMessage } from './queue/queue-consumer'\nimport type { QueueManager } from './queue/queue-manager'\nimport type { RouterContext } from './router'\nimport type { HonoApp } from './router/hono-app'\nimport { ROUTER_TOKENS } from './router/router.tokens'\nimport type { TrailingSlashMode, VersioningOptions } from './router/types'\nimport type { Seeder } from './seeder/seeder'\nimport { SEEDER_TOKENS } from './seeder/seeder-registry'\nimport type { SeederRegistry } from './seeder/seeder-registry'\nimport type { Constructor } from './types'\n\nexport interface ApplicationConfig {\n module: ModuleClass | DynamicModule\n logging?: {\n level?: LogLevel\n formatter?: 'json' | 'pretty'\n }\n versioning?: VersioningOptions\n trailingSlash?: TrailingSlashMode\n exceptionHandler?: Constructor<ExceptionHandler>\n}\n\nexport interface ApplicationOptions extends ApplicationConfig {\n env: StratalEnv\n ctx: StratalExecutionContext\n}\n\nexport class Application {\n private _container: Container\n private honoApp!: HonoApp\n private moduleRegistry: ModuleRegistry\n private consumerRegistry!: ConsumerRegistry\n private cronManager?: CronManager\n private quarry!: QuarryRegistry\n private initialized = false\n\n // Independently memoized lazy-init promises — each built-in subsystem is\n // loaded on demand at its trigger point to keep cold start lean.\n private routingInitPromise: Promise<void> | null = null\n private eventsInitPromise: Promise<void> | null = null\n private queueInitPromise: Promise<void> | null = null\n private i18nInitPromise: Promise<void> | null = null\n private cronInitPromise: Promise<void> | null = null\n\n readonly env: StratalEnv\n private readonly appConfig: ApplicationConfig\n\n constructor({ env, ctx, ...config }: ApplicationOptions) {\n this.env = env\n this.appConfig = config\n\n this._container = new Container()\n\n this._container.registerValue(DI_TOKENS.Application, this)\n this._container.registerValue(DI_TOKENS.CloudflareEnv, env)\n this._container.registerValue(DI_TOKENS.ExecutionContext, ctx)\n this._container.registerValue(ROUTER_TOKENS.RouterContext, null)\n\n this.registerLoggerService()\n this.registerCoreServices()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n this.moduleRegistry = new ModuleRegistry(this._container, logger)\n\n this._container.registerValue(DI_TOKENS.ModuleRegistry, this.moduleRegistry)\n }\n\n get container(): Container {\n return this._container\n }\n\n async ensureHono(): Promise<HonoApp> {\n await this.initializeRouting()\n return this.honoApp\n }\n\n get config(): ApplicationConfig {\n return this.appConfig\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return\n }\n\n await runWithContainer(this._container, () => this.initializeInternal())\n }\n\n private async initializeInternal(): Promise<void> {\n // Eager subsystem modules — Quarry/Seeder are resolved synchronously\n // post-init (CLI runner, test harness), so they must be registered before\n // initialize() completes. Dynamic import keeps application.ts free of static\n // subsystem imports (uniform with the other built-ins). They load at boot\n // regardless, so this is for consistency, not cold-start deferral.\n const [{ QuarryModule }, { SeederModule }] = await Promise.all([\n import('./quarry/quarry.module'),\n import('./seeder/seeder.module'),\n ])\n this.moduleRegistry.registerAll([QuarryModule, SeederModule])\n\n // Register the user's root module (traverses imports). Other built-in\n // subsystems (i18n, queue, cache, openapi, cron, events, router) load on\n // demand at their trigger points.\n this.moduleRegistry.register(this.appConfig.module)\n\n // Initialize all modules (only those with lifecycle hooks)\n await this.moduleRegistry.initialize()\n\n // Initialize ExceptionHandler\n this.initializeExceptionHandler()\n\n // Register CLI commands + seeders (class refs only; no instantiation)\n this.registerCommands()\n this.registerSeeders()\n\n // Wire event listeners as part of initialization whenever the application\n // declares any, so emitted events reach their handlers regardless of which\n // entry point drives the app. Listener wiring otherwise happens lazily only\n // on the HTTP router (`initializeRouting`) and queue/scheduled/command\n // (`ensureScopedHandlers`) paths; any code that emits events without going\n // through one of those — a direct service or repository call, an RPC\n // entrypoint, or a Durable Object — would dispatch into a registry with no\n // handlers attached, silently dropping the event. Guarded on there being\n // listeners so applications with none still skip loading the events\n // subsystem; `initializeEventListeners` dedups, so any later lazy trigger\n // becomes a no-op.\n if (this.moduleRegistry.getAllListeners().length > 0) {\n await this.initializeEventListeners()\n }\n\n // Cron only loads when the app actually declares scheduled jobs\n if (this.moduleRegistry.getAllJobs().length > 0) {\n await this.ensureCron()\n }\n\n this.initialized = true\n }\n\n private async registerRoutingServices(): Promise<void> {\n const [\n { HonoApp },\n { RouteRegistry },\n { RouterResolver },\n { VersioningService },\n { LocalePathService },\n { LocaleUrlService },\n { RouteRegistrationService },\n { Uri },\n ] = await Promise.all([\n import('./router/hono-app'),\n import('./router/route-registry'),\n import('./router/router-resolver'),\n import('./router/services/versioning.service'),\n import('./router/services/locale-path.service'),\n import('./router/services/locale-url.service'),\n import('./router/services/route-registration.service'),\n import('./router/uri'),\n ])\n\n this._container.register(ROUTER_TOKENS.VersioningService, VersioningService)\n this._container.register(ROUTER_TOKENS.HonoApp, HonoApp)\n this._container.register(ROUTER_TOKENS.LocalePathService, LocalePathService)\n this._container.register(ROUTER_TOKENS.LocaleUrlService, LocaleUrlService)\n this._container.register(ROUTER_TOKENS.RouteRegistry, RouteRegistry)\n this._container.register(ROUTER_TOKENS.Uri, Uri)\n\n const routerConfigs = this.moduleRegistry.getAllRouterConfigs()\n if (routerConfigs.length > 0) {\n this._container.registerValue(ROUTER_TOKENS.RouterResolver, new RouterResolver(routerConfigs))\n }\n\n this._container.register(RouteRegistrationService, RouteRegistrationService)\n }\n\n /**\n * Load the events subsystem and wire listeners. Needed by the HTTP, queue,\n * and scheduled paths (handlers emit events). Independent of the queue\n * subsystem so HTTP-only apps never load queue code. EventsModule is\n * registered before the (possibly empty) listener wiring so `emit()` works\n * even with zero listeners; dedups against the framework's own load.\n */\n private initializeEventListeners(): Promise<void> {\n this.eventsInitPromise ??= runWithContainer(this._container, async () => {\n const { EventsModule } = await import('./events/events.module')\n await this.moduleRegistry.registerLazy(EventsModule)\n this.registerEventListeners()\n })\n return this.eventsInitPromise\n }\n\n /**\n * Wire event and queue handlers for any non-HTTP request scope — queue\n * batches, scheduled/cron runs, CLI commands, Durable Objects, Workflows,\n * WorkerEntrypoints — all of which may emit events or dispatch to queues from\n * arbitrary user code. This is the single source of truth for that wiring:\n * every non-HTTP entry point routes through it rather than open-coding which\n * subsystems to init, so a subsystem can't be silently dropped by a future\n * refactor (the queue half was once lost from the command path that way).\n *\n * HTTP (`fetch`) deliberately does NOT use this: a fetch worker only enqueues\n * to the async Cloudflare queue and never processes consumers inline, so it\n * skips queue init via `initializeRouting`.\n */\n async ensureScopedHandlers(): Promise<void> {\n await this.initializeEventListeners()\n await this.initializeQueue()\n }\n\n /**\n * Load the queue subsystem on demand (first queue/scheduled trigger). i18n is\n * ensured first because the queue registry depends on it.\n */\n private initializeQueue(): Promise<void> {\n this.queueInitPromise ??= runWithContainer(this._container, async () => {\n await this.ensureI18n()\n const { QueueModule } = await import('./queue/queue.module')\n this.moduleRegistry.register(QueueModule as unknown as ModuleClass)\n this.consumerRegistry = this._container.resolve<ConsumerRegistry>(DI_TOKENS.ConsumerRegistry)\n await this.registerQueueConsumers()\n })\n return this.queueInitPromise\n }\n\n /**\n * Load i18n on demand. Coupled to the request path (Zod validation error maps,\n * OpenAPI descriptions, queue registry), so it loads before routing/queue\n * handling. Uses `registerLazy` because I18nModule has an `onInitialize` hook\n * (configures the Zod error map). Dedups if the app already imported i18n.\n */\n private ensureI18n(): Promise<void> {\n this.i18nInitPromise ??= runWithContainer(this._container, async () => {\n const { I18nModule } = await import('./i18n/i18n.module')\n await this.moduleRegistry.registerLazy(I18nModule as unknown as ModuleClass)\n })\n return this.i18nInitPromise\n }\n\n /**\n * Load the cron subsystem on demand (first scheduled trigger, or at bootstrap\n * when the app declares jobs).\n */\n private ensureCron(): Promise<CronManager> {\n this.cronInitPromise ??= runWithContainer(this._container, async () => {\n const { CronModule } = await import('./cron/cron.module')\n this.moduleRegistry.register(CronModule)\n this.cronManager = this._container.resolve<CronManager>(DI_TOKENS.Cron)\n this.registerCronJobs(this.cronManager)\n })\n return this.cronInitPromise.then(() => this.cronManager!)\n }\n\n resolve<T>(token: symbol): T {\n try {\n return this._container.resolve(token)\n } catch (error) {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n const ctx = createCliExceptionContext('resolve')\n void handler.handle(error, ctx)\n throw error\n }\n }\n\n async handleQueue(batch: MessageBatch, queueName: string): Promise<void> {\n await this.ensureScopedHandlers()\n\n const firstMessage = batch.messages[0]?.body as QueueMessage | undefined\n const locale = firstMessage?.metadata?.locale ?? 'en'\n const mockRouterContext = this.createMockRouterContext(locale)\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n const queueManager = requestContainer.resolve<QueueManager>(DI_TOKENS.Queue)\n await queueManager.processBatch(queueName, batch)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createQueueExceptionContext(queueName))\n throw error\n }\n })\n }\n\n async handleScheduled(controller: ScheduledController): Promise<void> {\n const cronManager = await this.ensureCron()\n // A scheduled job is a non-HTTP scope that can emit events and dispatch to\n // queues (timeout/reminder/digest emails) — wire both so dispatches aren't\n // silently dropped by the sync provider. i18n is ensured independently for\n // the job execution context. All inits are memoized, so this is idempotent.\n await this.ensureScopedHandlers()\n await this.ensureI18n()\n\n const mockRouterContext = this.createMockRouterContext('en')\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n await cronManager.executeScheduled(controller, requestContainer)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createCronExceptionContext())\n throw error\n }\n })\n }\n\n createMockRouterContext(locale = 'en'): RouterContext {\n return {\n getLocale: () => locale,\n setLocale: () => { /* no-op */ },\n // Inside runInRequestScope the active container is the request-scoped child\n // (set in AsyncLocalStorage); return it so request-scoped providers resolve\n // against the scope, not the global container.\n getContainer: () => containerStorage.getStore() ?? this._container,\n } as unknown as RouterContext\n }\n\n async shutdown(): Promise<void> {\n if (!this.initialized) return\n this.initialized = false\n\n await this.moduleRegistry.shutdown()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.info('Disposing container...')\n\n this._container.dispose()\n }\n\n async handleCommand(name: string, input?: CommandInput): Promise<CommandResult> {\n // Routing first: commands generate URLs via route() (e.g. tenant:bootstrap\n // prints tenant URLs / builds email links).\n await this.initializeRouting()\n // A CLI command is a non-HTTP scope that can dispatch to queues and emit\n // events from arbitrary user code (e.g. tenant:bootstrap → email.send /\n // tenant.geo.seed). Without this the dev/CLI sync provider finds zero\n // consumers and silently drops every dispatched message. All inits are\n // memoized, so the events half (already wired by routing) is a no-op here.\n await this.ensureScopedHandlers()\n // Resolve QuarryRegistry lazily (deferred from bootstrap)\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const mockContext = this.createMockRouterContext('en')\n return this._container.runInRequestScope(mockContext, async () => {\n return this.quarry.call(name, input)\n })\n }\n\n private initializeRouting(): Promise<void> {\n this.routingInitPromise ??= runWithContainer(this._container, async () => {\n await this.initializeEventListeners()\n await this.ensureI18n()\n const { OpenAPIModule } = await import('./openapi')\n this.moduleRegistry.register(OpenAPIModule as unknown as ModuleClass)\n await this.registerRoutingServices()\n this.honoApp = this._container.resolve<HonoApp>(ROUTER_TOKENS.HonoApp)\n await this.honoApp.configure()\n })\n return this.routingInitPromise\n }\n\n private registerCommands(): void {\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const commands = this.moduleRegistry.getAllCommands()\n for (const CommandClass of commands) {\n this.quarry.register(CommandClass as Constructor<Command>)\n }\n }\n\n private registerSeeders(): void {\n const seeders = this.moduleRegistry.getAllSeeders()\n if (seeders.length === 0) return\n const registry = this._container.resolve<SeederRegistry>(SEEDER_TOKENS.SeederRegistry)\n for (const SeederClass of seeders) {\n registry.register(SeederClass as Constructor<Seeder>)\n }\n }\n\n private async registerQueueConsumers(): Promise<void> {\n const consumerClasses = this.moduleRegistry.getAllConsumers()\n if (consumerClasses.length === 0) return\n\n // Consumers are resolved fresh per message at dispatch time (see\n // QueueManager / SyncQueueProvider). Here we only need each consumer's\n // declared message types; read them from a throwaway instance built inside a\n // request scope so a consumer that injects request-scoped providers\n // (@InjectQueue, i18n, auth context, …) doesn't fail to instantiate at boot.\n const mockContext = this.createMockRouterContext('en')\n await this._container.runInRequestScope(mockContext, (requestContainer) => {\n for (const ConsumerClass of consumerClasses) {\n const consumer = requestContainer.resolve(ConsumerClass) as IQueueConsumer\n this.consumerRegistry.register(\n ConsumerClass as Constructor<IQueueConsumer>,\n consumer.messageTypes,\n )\n }\n })\n }\n\n private registerCronJobs(cronManager: CronManager): void {\n for (const JobClass of this.moduleRegistry.getAllJobs()) {\n const schedule = (JobClass as unknown as { schedule: string }).schedule\n if (schedule) {\n cronManager.registerJob(schedule, JobClass as Constructor<CronJob>)\n } else {\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.warn(`Cron job \"${JobClass.name}\" has no static schedule property — skipped`)\n }\n }\n }\n\n private registerEventListeners(): void {\n const listeners = this.moduleRegistry.getAllListeners()\n if (listeners.length === 0) {\n return\n }\n\n const eventRegistry = this._container.resolve<EventRegistry>(DI_TOKENS.EventRegistry)\n\n for (const ListenerClass of listeners) {\n const handlers = getListenerHandlers(ListenerClass)\n\n for (const { methodName, event, options } of handlers) {\n // Resolve the listener fresh per event from the active scope (events are\n // emitted inside an HTTP request or runInScope), so request-scoped\n // listener dependencies bind to the emitting request rather than being\n // frozen at boot. Listener metadata is read from the class statically —\n // no instance is created until an event actually fires.\n const handler: EventHandler = ((...args: unknown[]) => {\n const instance = getContainer().resolve(ListenerClass) as Record<\n string,\n (...a: unknown[]) => unknown\n >\n return instance[methodName](...args)\n }) as EventHandler\n\n eventRegistry.on(event, handler, options)\n }\n }\n }\n\n private registerLoggerService(): void {\n const logLevel = this.appConfig.logging?.level ?? LogLevel.INFO\n const formatter = this.appConfig.logging?.formatter ?? 'json'\n\n this._container.registerValue(LOGGER_TOKENS.LogLevelOptions, logLevel)\n\n this._container\n .when(() => formatter === 'pretty')\n .use(LOGGER_TOKENS.Formatter)\n .give(PrettyFormatter)\n .otherwise(JsonFormatter)\n\n this._container.registerSingleton(LOGGER_TOKENS.LoggerService, LoggerService)\n }\n\n /**\n * Bootstrap kernel — registered imperatively because they cannot be expressed\n * as ordinary module providers: ExceptionHandler is a user-overridable forced\n * singleton (a module ClassProvider derives scope from the class decorator,\n * which a user handler may not carry), and LazyModuleLoader is the loader\n * itself. Subsystem registries (events/cron/quarry/seeder) are modules.\n */\n private registerCoreServices(): void {\n this._container.registerSingleton(\n DI_TOKENS.ExceptionHandler,\n (this.appConfig.exceptionHandler ?? DefaultExceptionHandler) as Constructor,\n )\n this._container.registerSingleton(DI_TOKENS.LazyModuleLoader, LazyModuleLoader)\n }\n\n private initializeExceptionHandler(): void {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n handler.register()\n this.moduleRegistry.configureExceptionHandlers(handler)\n }\n}\n","import { Application, type ApplicationConfig } from './application'\nimport type { StratalEnv } from './env'\nimport { StratalNotInitializedError } from './errors'\nimport type { HonoApp } from './router/hono-app'\n\n/**\n * Stratal — Hono-style entry point for Cloudflare Workers.\n *\n * Eagerly bootstraps the Application at construction time, dynamically\n * importing `cloudflare:workers` for env and waitUntil.\n *\n * @example\n * ```typescript\n * import { Stratal } from 'stratal'\n * import { AppModule } from './app.module'\n *\n * export default new Stratal({ module: AppModule })\n * ```\n */\nexport class Stratal<Env extends StratalEnv = StratalEnv> {\n private app: Application | null = null\n private initPromise: Promise<Application>\n\n private static _application: Promise<Application> | null = null\n private static _generation = 0\n private static _previousInstance: Stratal | null = null\n\n constructor(config: ApplicationConfig) {\n this.fetch = this.fetch.bind(this)\n this.queue = this.queue.bind(this)\n this.scheduled = this.scheduled.bind(this)\n\n // Invalidate any in-flight initialization from a previous instance (Vite HMR reload)\n const generation = ++Stratal._generation\n\n if (Stratal._previousInstance) {\n void Stratal._previousInstance.shutdown()\n }\n Stratal._previousInstance = this\n\n this.initPromise = this.prepareApp(config, generation)\n Stratal._application = this.initPromise\n }\n\n async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {\n const app = await this.ensureReady()\n const hono = await app.ensureHono()\n return hono.fetch(request, env, ctx)\n }\n\n async queue(batch: MessageBatch): Promise<void> {\n const app = await this.ensureReady()\n return app.handleQueue(batch, batch.queue)\n }\n\n async scheduled(controller: ScheduledController): Promise<void> {\n const app = await this.ensureReady()\n return app.handleScheduled(controller)\n }\n\n get hono(): Promise<HonoApp> {\n return this.initPromise.then(app => app.ensureHono())\n }\n\n async shutdown(): Promise<void> {\n try { this.app = await this.initPromise } catch { /* ignore */ }\n if (this.app) {\n await this.app.shutdown()\n this.app = null\n }\n }\n\n /**\n * @internal\n * Resolves the Application instance from the static singleton.\n * Used by worker base classes (DurableObject, Workflow, WorkerEntrypoint)\n * to access the DI container without going through Cloudflare RPC.\n */\n static resolveApplication(): Promise<Application> {\n if (!Stratal._application) {\n throw new StratalNotInitializedError()\n }\n return Stratal._application\n }\n\n private async ensureReady(): Promise<Application> {\n this.app ??= await this.initPromise;\n return this.app\n }\n\n private async prepareApp(config: ApplicationConfig, generation: number): Promise<Application> {\n const { env, waitUntil } = await import('cloudflare:workers')\n\n // After async import, check if a newer instance has replaced us (Vite HMR reload)\n if (generation !== Stratal._generation) {\n return new Promise<Application>(() => {\n //\n }) // Never resolves — avoids cross-request promise warning\n }\n\n const app = new Application({ ...config, env: env as Env, ctx: { waitUntil } })\n await app.initialize()\n\n // Check again after initialization completes\n if (generation !== Stratal._generation) {\n await app.shutdown()\n return new Promise<Application>(() => {\n //\n })\n }\n\n return app\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ct as Application,
|
|
2
|
-
import { t as StratalEnv } from "./env-
|
|
1
|
+
import { ct as Application, gn as HonoApp, lt as ApplicationConfig } from "./index-B_JoEl3V.mjs";
|
|
2
|
+
import { t as StratalEnv } from "./env-ug22bJj7.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/stratal.d.ts
|
|
5
5
|
/**
|
|
@@ -40,4 +40,4 @@ declare class Stratal<Env extends StratalEnv = StratalEnv> {
|
|
|
40
40
|
}
|
|
41
41
|
//#endregion
|
|
42
42
|
export { Stratal as t };
|
|
43
|
-
//# sourceMappingURL=stratal-
|
|
43
|
+
//# sourceMappingURL=stratal-DwDJPY9N.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stratal-
|
|
1
|
+
{"version":3,"file":"stratal-DwDJPY9N.d.mts","names":[],"sources":["../src/stratal.ts"],"mappings":";;;;;AAmBA;;;;;;;;;;;;;cAAa,OAAA,aAAoB,UAAA,GAAa,UAAA;EAAA,QACpC,GAAA;EAAA,QACA,WAAA;EAAA,eAEO,YAAA;EAAA,eACA,WAAA;EAAA,eACA,iBAAA;cAEH,MAAA,EAAQ,iBAAA;EAiBd,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,gBAAA,GAAmB,OAAA,CAAQ,QAAA;EAMlE,KAAA,CAAM,KAAA,EAAO,YAAA,GAAe,OAAA;EAK5B,SAAA,CAAU,UAAA,EAAY,mBAAA,GAAsB,OAAA;EAAA,IAK9C,IAAA,IAAQ,OAAA,CAAQ,OAAA;EAId,QAAA,IAAY,OAAA;EA5CV;;;;;;EAAA,OA0DD,kBAAA,IAAsB,OAAA,CAAQ,WAAA;EAAA,QAOvB,WAAA;EAAA,QAKA,UAAA;AAAA"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { t as CacheService } from "./cache.service-uElmBtdS.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/cache/services/tiered-cache.service.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Tiered Cache Service
|
|
6
|
+
*
|
|
7
|
+
* Layers an **isolate-local in-memory L1** over {@link CacheService} (KV, the
|
|
8
|
+
* L2). Opt-in — inject `CACHE_TOKENS.TieredCacheService` instead of
|
|
9
|
+
* `CACHE_TOKENS.CacheService` when you want it.
|
|
10
|
+
*
|
|
11
|
+
* **Why:** KV reads are eventually consistent (a `get` can return an
|
|
12
|
+
* edge-cached value for up to ~60s after a `put`). The L1 makes writes made
|
|
13
|
+
* through *this isolate* immediately and consistently visible to subsequent
|
|
14
|
+
* reads on the same isolate — closing the read-after-write gap KV alone cannot.
|
|
15
|
+
* This is what makes set-once patterns (e.g. queue idempotency markers) reliable
|
|
16
|
+
* within an isolate, even inside KV's consistency window.
|
|
17
|
+
*
|
|
18
|
+
* **Use it for:** set-once / read-mostly values (idempotency claims, immutable
|
|
19
|
+
* lookups). **Do not use it for** read-modify-write counters that need
|
|
20
|
+
* cross-edge freshness (e.g. rate limiting): an isolate that wrote a key reads
|
|
21
|
+
* its own value until the L1 entry expires, so concurrent increments from other
|
|
22
|
+
* isolates are missed and overwritten. Use plain {@link CacheService} (KV) or a
|
|
23
|
+
* Durable-Object store for those.
|
|
24
|
+
*
|
|
25
|
+
* **Semantics:**
|
|
26
|
+
* - L1 caches string-backed values only (`text`/`json`). `arrayBuffer`/`stream`
|
|
27
|
+
* reads and non-string writes bypass and invalidate L1.
|
|
28
|
+
* - `put`/`delete` are write-through: KV and L1 update in lock-step, honoring
|
|
29
|
+
* `expirationTtl`/`expiration` for the L1 entry's own expiry.
|
|
30
|
+
* - `get` populates L1 from `text` reads (the only path that yields the raw
|
|
31
|
+
* string). `json` reads are served from L1 when the string was cached by a
|
|
32
|
+
* prior `put`/`text` read, otherwise they go straight to KV.
|
|
33
|
+
* - `getWithMetadata` and `list` always read KV directly (metadata is not held
|
|
34
|
+
* in L1).
|
|
35
|
+
*
|
|
36
|
+
* **Cross-isolate caveat:** the L1 only observes writes made through its own
|
|
37
|
+
* isolate. A value mutated by another isolate is not seen until the local entry
|
|
38
|
+
* expires or is invalidated by a local write/delete.
|
|
39
|
+
*
|
|
40
|
+
* **Bindings:** `binding(name)` returns a memoized tiered instance per binding,
|
|
41
|
+
* so each KV namespace has its own stable, isolate-lifetime L1.
|
|
42
|
+
*/
|
|
43
|
+
declare class TieredCacheService {
|
|
44
|
+
private readonly cache;
|
|
45
|
+
/** Isolate-local L1 tier. Per-instance, so each binding has its own. */
|
|
46
|
+
private readonly l1;
|
|
47
|
+
/** Memoized tiered instances per binding name (each with its own L1). */
|
|
48
|
+
private readonly children;
|
|
49
|
+
constructor(cache: CacheService);
|
|
50
|
+
/**
|
|
51
|
+
* Get the tiered cache bound to a KV namespace by its binding name. Memoized:
|
|
52
|
+
* repeated calls with the same name return the same instance, preserving its
|
|
53
|
+
* isolate-local L1 across requests/messages.
|
|
54
|
+
*
|
|
55
|
+
* @param name - KV namespace binding name (e.g. `'UPLOADS_CACHE'`)
|
|
56
|
+
* @throws {CacheError} If no binding with that name exists in the environment
|
|
57
|
+
*/
|
|
58
|
+
binding(name: string): TieredCacheService;
|
|
59
|
+
get(key: string, typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>): Promise<string | null>;
|
|
60
|
+
get<ExpectedValue = unknown>(key: string, typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>): Promise<ExpectedValue | null>;
|
|
61
|
+
get(key: string, typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>): Promise<ArrayBuffer | null>;
|
|
62
|
+
get(key: string, typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>): Promise<ReadableStream | null>;
|
|
63
|
+
getWithMetadata<Metadata = unknown>(key: string, typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>): Promise<KVNamespaceGetWithMetadataResult<string, Metadata>>;
|
|
64
|
+
getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(key: string, typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>): Promise<KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>;
|
|
65
|
+
getWithMetadata<Metadata = unknown>(key: string, typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>): Promise<KVNamespaceGetWithMetadataResult<ArrayBuffer, Metadata>>;
|
|
66
|
+
getWithMetadata<Metadata = unknown>(key: string, typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>): Promise<KVNamespaceGetWithMetadataResult<ReadableStream, Metadata>>;
|
|
67
|
+
put(key: string, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise<void>;
|
|
68
|
+
delete(key: string): Promise<void>;
|
|
69
|
+
list<Metadata = unknown>(options?: KVNamespaceListOptions): Promise<KVNamespaceListResult<Metadata>>;
|
|
70
|
+
/** Read a fresh L1 entry, evicting it if expired. Returns `null` on miss. */
|
|
71
|
+
private l1Read;
|
|
72
|
+
/** Write an L1 entry, refreshing its recency and evicting the oldest at cap. */
|
|
73
|
+
private l1Write;
|
|
74
|
+
/** Compute an absolute L1 expiry (epoch ms) from KV put options. */
|
|
75
|
+
private l1ExpiresAt;
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { TieredCacheService as t };
|
|
79
|
+
//# sourceMappingURL=tiered-cache.service-Dv3BhxxE.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiered-cache.service-Dv3BhxxE.d.mts","names":[],"sources":["../src/cache/services/tiered-cache.service.ts"],"mappings":";;;;;AA0DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cACa,kBAAA;EAAA,iBAO2C,KAAA;EA4FnD;EAAA,iBAjGc,EAAA;EAsHL;EAAA,iBApHK,QAAA;cAGqC,KAAA,EAAO,YAAA;EAkH1D;;;;;;;;EAvGH,OAAA,CAAQ,IAAA,WAAe,kBAAA;EAWjB,GAAA,CAAI,GAAA,UAAa,aAAA,YAAyB,qBAAA,WAAgC,OAAA;EAC1E,GAAA,0BAA6B,GAAA,UAAa,aAAA,WAAwB,qBAAA,WAAgC,OAAA,CAAQ,aAAA;EAC1G,GAAA,CAAI,GAAA,UAAa,aAAA,kBAA+B,qBAAA,kBAAuC,OAAA,CAAQ,WAAA;EAC/F,GAAA,CAAI,GAAA,UAAa,aAAA,aAA0B,qBAAA,aAAkC,OAAA,CAAQ,cAAA;EA+BrF,eAAA,qBACJ,GAAA,UACA,aAAA,YAAyB,qBAAA,WACxB,OAAA,CAAQ,gCAAA,SAAyC,QAAA;EAC9C,eAAA,8CACJ,GAAA,UACA,aAAA,WAAwB,qBAAA,WACvB,OAAA,CAAQ,gCAAA,CAAiC,aAAA,EAAe,QAAA;EACrD,eAAA,qBACJ,GAAA,UACA,aAAA,kBAA+B,qBAAA,kBAC9B,OAAA,CAAQ,gCAAA,CAAiC,WAAA,EAAa,QAAA;EACnD,eAAA,qBACJ,GAAA,UACA,aAAA,aAA0B,qBAAA,aACzB,OAAA,CAAQ,gCAAA,CAAiC,cAAA,EAAgB,QAAA;EAiBtD,GAAA,CACJ,GAAA,UACA,KAAA,WAAgB,WAAA,GAAc,eAAA,GAAkB,cAAA,EAChD,OAAA,GAAU,qBAAA,GACT,OAAA;EAaG,MAAA,CAAO,GAAA,WAAc,OAAA;EAOrB,IAAA,qBACJ,OAAA,GAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA,CAAsB,QAAA;EA3FuC;EAAA,QAkGhE,MAAA;EAlGgG;EAAA,QA6GhG,OAAA;EA5GF;EAAA,QAuHE,WAAA;AAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/router/trailing-slash.ts
|
|
2
|
+
/**
|
|
3
|
+
* Apply a trailing-slash mode to a URL or path.
|
|
4
|
+
*
|
|
5
|
+
* - `'ignore'` — return as-is.
|
|
6
|
+
* - `'always'` — append `/` to the pathname unless it already has one.
|
|
7
|
+
* Skipped when the last segment contains `.` (file-like paths) and for the
|
|
8
|
+
* root `/` path.
|
|
9
|
+
* - `'never'` — strip a trailing `/` from the pathname. Skipped for root.
|
|
10
|
+
*
|
|
11
|
+
* Preserves query string and hash. Handles both relative paths
|
|
12
|
+
* (`/foo?x=1`) and absolute URLs (`https://host/foo?x=1`).
|
|
13
|
+
*
|
|
14
|
+
* Used by URL-generation helpers and the redirect middleware so canonical
|
|
15
|
+
* form is computed in one place.
|
|
16
|
+
*/
|
|
17
|
+
function applyTrailingSlash(url, mode) {
|
|
18
|
+
if (mode === "ignore") return url;
|
|
19
|
+
const isAbsolute = /^https?:\/\//i.test(url);
|
|
20
|
+
const parsed = isAbsolute ? new URL(url) : new URL(url, "http://placeholder.local");
|
|
21
|
+
const path = parsed.pathname;
|
|
22
|
+
if (path === "/") return url;
|
|
23
|
+
const hasTrailing = path.endsWith("/");
|
|
24
|
+
if (mode === "always" && !hasTrailing) {
|
|
25
|
+
if (path.slice(path.lastIndexOf("/") + 1).includes(".")) return url;
|
|
26
|
+
parsed.pathname = `${path}/`;
|
|
27
|
+
} else if (mode === "never" && hasTrailing) parsed.pathname = path.slice(0, -1);
|
|
28
|
+
else return url;
|
|
29
|
+
return isAbsolute ? parsed.toString() : `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { applyTrailingSlash as t };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=trailing-slash-CFyw8nYu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trailing-slash-CFyw8nYu.mjs","names":[],"sources":["../src/router/trailing-slash.ts"],"sourcesContent":["import type { TrailingSlashMode } from './types'\n\n/**\n * Apply a trailing-slash mode to a URL or path.\n *\n * - `'ignore'` — return as-is.\n * - `'always'` — append `/` to the pathname unless it already has one.\n * Skipped when the last segment contains `.` (file-like paths) and for the\n * root `/` path.\n * - `'never'` — strip a trailing `/` from the pathname. Skipped for root.\n *\n * Preserves query string and hash. Handles both relative paths\n * (`/foo?x=1`) and absolute URLs (`https://host/foo?x=1`).\n *\n * Used by URL-generation helpers and the redirect middleware so canonical\n * form is computed in one place.\n */\nexport function applyTrailingSlash(url: string, mode: TrailingSlashMode): string {\n if (mode === 'ignore') return url\n\n const isAbsolute = /^https?:\\/\\//i.test(url)\n const parsed = isAbsolute\n ? new URL(url)\n : new URL(url, 'http://placeholder.local')\n\n const path = parsed.pathname\n if (path === '/') return url\n\n const hasTrailing = path.endsWith('/')\n\n if (mode === 'always' && !hasTrailing) {\n const lastSegment = path.slice(path.lastIndexOf('/') + 1)\n if (lastSegment.includes('.')) return url\n parsed.pathname = `${path}/`\n } else if (mode === 'never' && hasTrailing) {\n parsed.pathname = path.slice(0, -1)\n } else {\n return url\n }\n\n return isAbsolute\n ? parsed.toString()\n : `${parsed.pathname}${parsed.search}${parsed.hash}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,SAAgB,mBAAmB,KAAa,MAAiC;CAC/E,IAAI,SAAS,UAAU,OAAO;CAE9B,MAAM,aAAa,gBAAgB,KAAK,GAAG;CAC3C,MAAM,SAAS,aACX,IAAI,IAAI,GAAG,IACX,IAAI,IAAI,KAAK,0BAA0B;CAE3C,MAAM,OAAO,OAAO;CACpB,IAAI,SAAS,KAAK,OAAO;CAEzB,MAAM,cAAc,KAAK,SAAS,GAAG;CAErC,IAAI,SAAS,YAAY,CAAC,aAAa;EAErC,IADoB,KAAK,MAAM,KAAK,YAAY,GAAG,IAAI,CACzC,EAAE,SAAS,GAAG,GAAG,OAAO;EACtC,OAAO,WAAW,GAAG,KAAK;CAC5B,OAAO,IAAI,SAAS,WAAW,aAC7B,OAAO,WAAW,KAAK,MAAM,GAAG,EAAE;MAElC,OAAO;CAGT,OAAO,aACH,OAAO,SAAS,IAChB,GAAG,OAAO,WAAW,OAAO,SAAS,OAAO;AAClD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-CmV_9xBD.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;AAeA;;;;;;;;AAA+D;;;;;KAAnD,WAAA,uBAAkC,IAAA,YAAgB,CAAC"}
|