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
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Full guides and examples are available at **[stratal.dev](https://stratal.dev)**
|
|
|
27
27
|
- **Queue Consumers** — Typed Cloudflare Queue consumers with message-type filtering
|
|
28
28
|
- **Cron Jobs** — Scheduled tasks via Cloudflare Workers cron triggers
|
|
29
29
|
- **Storage** — S3-compatible file storage with presigned URLs and TUS upload support
|
|
30
|
-
- **Email** —
|
|
30
|
+
- **Email** — SMTP provider with React Email template support
|
|
31
31
|
- **i18n** — Type-safe internationalization with locale detection from request headers
|
|
32
32
|
- **Guards and Middleware** — Route protection and per-module middleware configuration
|
|
33
33
|
|
|
@@ -3,18 +3,24 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
//#region src/bin/cloudflare-workers-loader.ts
|
|
5
5
|
/**
|
|
6
|
-
* ESM loader hook that provides
|
|
6
|
+
* ESM loader hook that provides virtual modules for Cloudflare-specific imports
|
|
7
7
|
* and handles Vite-style `?raw` imports (returning file contents as a string).
|
|
8
8
|
*
|
|
9
|
-
* When registered via `node --import` or `register()`, this intercepts
|
|
10
|
-
* `
|
|
11
|
-
*
|
|
9
|
+
* When registered via `node --import` or `register()`, this intercepts:
|
|
10
|
+
* - `cloudflare:workers` — virtual env/waitUntil from `globalThis.__stratalPlatformProxy`
|
|
11
|
+
* - `cloudflare:sockets` — Node.js TCP/TLS implementation of the CF connect() API
|
|
12
|
+
* - `?raw` imports — returns file contents as a default string export
|
|
12
13
|
*/
|
|
13
|
-
const
|
|
14
|
+
const VIRTUAL_WORKERS_URL = "cloudflare-workers:virtual";
|
|
15
|
+
const VIRTUAL_SOCKETS_URL = "cloudflare-sockets:virtual";
|
|
14
16
|
const RAW_SUFFIX = "?raw";
|
|
15
17
|
async function resolve(specifier, context, nextResolve) {
|
|
16
18
|
if (specifier === "cloudflare:workers") return {
|
|
17
|
-
url:
|
|
19
|
+
url: VIRTUAL_WORKERS_URL,
|
|
20
|
+
shortCircuit: true
|
|
21
|
+
};
|
|
22
|
+
if (specifier === "cloudflare:sockets") return {
|
|
23
|
+
url: VIRTUAL_SOCKETS_URL,
|
|
18
24
|
shortCircuit: true
|
|
19
25
|
};
|
|
20
26
|
if (specifier.endsWith(RAW_SUFFIX)) return {
|
|
@@ -32,7 +38,7 @@ async function load(url, context, nextLoad) {
|
|
|
32
38
|
source: `export default ${JSON.stringify(content)}`
|
|
33
39
|
};
|
|
34
40
|
}
|
|
35
|
-
if (url ===
|
|
41
|
+
if (url === VIRTUAL_WORKERS_URL) return {
|
|
36
42
|
format: "module",
|
|
37
43
|
shortCircuit: true,
|
|
38
44
|
source: `
|
|
@@ -56,6 +62,73 @@ export const RpcStub = function(value) { return value; };
|
|
|
56
62
|
export function withEnv(newEnv, fn) { return fn(); }
|
|
57
63
|
export function withExports(newExports, fn) { return fn(); }
|
|
58
64
|
export function withEnvAndExports(newEnv, newExports, fn) { return fn(); }
|
|
65
|
+
`
|
|
66
|
+
};
|
|
67
|
+
if (url === VIRTUAL_SOCKETS_URL) return {
|
|
68
|
+
format: "module",
|
|
69
|
+
shortCircuit: true,
|
|
70
|
+
source: `
|
|
71
|
+
import { connect as netConnect } from 'node:net';
|
|
72
|
+
import { connect as tlsConnect } from 'node:tls';
|
|
73
|
+
|
|
74
|
+
export function connect(address, options) {
|
|
75
|
+
const hostname = typeof address === 'string' ? address : address.hostname;
|
|
76
|
+
const port = typeof address === 'string' ? 443 : address.port;
|
|
77
|
+
const secureTransport = options?.secureTransport ?? 'off';
|
|
78
|
+
|
|
79
|
+
let currentSocket;
|
|
80
|
+
if (secureTransport === 'on') {
|
|
81
|
+
currentSocket = tlsConnect({ host: hostname, port, servername: hostname });
|
|
82
|
+
} else {
|
|
83
|
+
currentSocket = netConnect({ host: hostname, port });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let dataHandler, endHandler, errorHandler;
|
|
87
|
+
|
|
88
|
+
const readable = new ReadableStream({
|
|
89
|
+
start(controller) {
|
|
90
|
+
dataHandler = (chunk) => {
|
|
91
|
+
try { controller.enqueue(new Uint8Array(chunk)); } catch {}
|
|
92
|
+
};
|
|
93
|
+
endHandler = () => {
|
|
94
|
+
try { controller.close(); } catch {}
|
|
95
|
+
};
|
|
96
|
+
errorHandler = (err) => {
|
|
97
|
+
try { controller.error(err); } catch {}
|
|
98
|
+
};
|
|
99
|
+
currentSocket.on('data', dataHandler);
|
|
100
|
+
currentSocket.on('end', endHandler);
|
|
101
|
+
currentSocket.on('error', errorHandler);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const writable = new WritableStream({
|
|
106
|
+
write(chunk) {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
currentSocket.write(Buffer.from(chunk), (err) => err ? reject(err) : resolve());
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const closedPromise = new Promise((resolve) => currentSocket.on('close', resolve));
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
readable,
|
|
117
|
+
writable,
|
|
118
|
+
startTls() {
|
|
119
|
+
currentSocket.removeListener('data', dataHandler);
|
|
120
|
+
currentSocket.removeListener('end', endHandler);
|
|
121
|
+
currentSocket.removeListener('error', errorHandler);
|
|
122
|
+
const tlsSocket = tlsConnect({ socket: currentSocket, servername: hostname });
|
|
123
|
+
currentSocket = tlsSocket;
|
|
124
|
+
tlsSocket.on('data', dataHandler);
|
|
125
|
+
tlsSocket.on('end', endHandler);
|
|
126
|
+
tlsSocket.on('error', errorHandler);
|
|
127
|
+
},
|
|
128
|
+
close() { currentSocket.destroy(); },
|
|
129
|
+
closed: closedPromise,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
59
132
|
`
|
|
60
133
|
};
|
|
61
134
|
return nextLoad(url, context);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-workers-loader.mjs","names":[],"sources":["../../src/bin/cloudflare-workers-loader.ts"],"sourcesContent":["/**\n * ESM loader hook that provides
|
|
1
|
+
{"version":3,"file":"cloudflare-workers-loader.mjs","names":[],"sources":["../../src/bin/cloudflare-workers-loader.ts"],"sourcesContent":["/**\n * ESM loader hook that provides virtual modules for Cloudflare-specific imports\n * and handles Vite-style `?raw` imports (returning file contents as a string).\n *\n * When registered via `node --import` or `register()`, this intercepts:\n * - `cloudflare:workers` — virtual env/waitUntil from `globalThis.__stratalPlatformProxy`\n * - `cloudflare:sockets` — Node.js TCP/TLS implementation of the CF connect() API\n * - `?raw` imports — returns file contents as a default string export\n */\n\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\n\nconst VIRTUAL_WORKERS_URL = 'cloudflare-workers:virtual'\nconst VIRTUAL_SOCKETS_URL = 'cloudflare-sockets:virtual'\nconst RAW_SUFFIX = '?raw'\n\ninterface ResolveContext {\n parentURL?: string\n conditions: string[]\n}\n\ninterface ResolveResult {\n url: string\n shortCircuit?: boolean\n}\n\ntype NextResolve = (specifier: string, context: ResolveContext) => Promise<ResolveResult>\n\nexport async function resolve(\n specifier: string,\n context: ResolveContext,\n nextResolve: NextResolve,\n): Promise<ResolveResult> {\n if (specifier === 'cloudflare:workers') {\n return { url: VIRTUAL_WORKERS_URL, shortCircuit: true }\n }\n if (specifier === 'cloudflare:sockets') {\n return { url: VIRTUAL_SOCKETS_URL, shortCircuit: true }\n }\n if (specifier.endsWith(RAW_SUFFIX)) {\n const base = specifier.slice(0, -RAW_SUFFIX.length)\n const resolved = await nextResolve(base, context)\n return { url: resolved.url + RAW_SUFFIX, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n}\n\ninterface LoadContext {\n format?: string\n conditions: string[]\n}\n\ninterface LoadResult {\n format: string\n source: string\n shortCircuit?: boolean\n}\n\ntype NextLoad = (url: string, context: LoadContext) => Promise<LoadResult>\n\nexport async function load(\n url: string,\n context: LoadContext,\n nextLoad: NextLoad,\n): Promise<LoadResult> {\n if (url.endsWith(RAW_SUFFIX)) {\n const fileUrl = url.slice(0, -RAW_SUFFIX.length)\n const filePath = fileURLToPath(fileUrl)\n const content = readFileSync(filePath, 'utf-8')\n return {\n format: 'module',\n shortCircuit: true,\n source: `export default ${JSON.stringify(content)}`,\n }\n }\n if (url === VIRTUAL_WORKERS_URL) {\n return {\n format: 'module',\n shortCircuit: true,\n source: `\nconst proxy = globalThis.__stratalPlatformProxy;\nif (!proxy) throw new Error('globalThis.__stratalPlatformProxy not set — Quarry CLI must initialize it before importing the app entry.');\nexport const env = proxy.env;\nexport const waitUntil = proxy.waitUntil;\nexport const exports = {}\nexport class DurableObject {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkerEntrypoint {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkflowEntrypoint {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkflowStep {}\nexport class RpcTarget {}\nexport const RpcStub = function(value) { return value; };\nexport function withEnv(newEnv, fn) { return fn(); }\nexport function withExports(newExports, fn) { return fn(); }\nexport function withEnvAndExports(newEnv, newExports, fn) { return fn(); }\n`,\n }\n }\n if (url === VIRTUAL_SOCKETS_URL) {\n return {\n format: 'module',\n shortCircuit: true,\n source: `\nimport { connect as netConnect } from 'node:net';\nimport { connect as tlsConnect } from 'node:tls';\n\nexport function connect(address, options) {\n const hostname = typeof address === 'string' ? address : address.hostname;\n const port = typeof address === 'string' ? 443 : address.port;\n const secureTransport = options?.secureTransport ?? 'off';\n\n let currentSocket;\n if (secureTransport === 'on') {\n currentSocket = tlsConnect({ host: hostname, port, servername: hostname });\n } else {\n currentSocket = netConnect({ host: hostname, port });\n }\n\n let dataHandler, endHandler, errorHandler;\n\n const readable = new ReadableStream({\n start(controller) {\n dataHandler = (chunk) => {\n try { controller.enqueue(new Uint8Array(chunk)); } catch {}\n };\n endHandler = () => {\n try { controller.close(); } catch {}\n };\n errorHandler = (err) => {\n try { controller.error(err); } catch {}\n };\n currentSocket.on('data', dataHandler);\n currentSocket.on('end', endHandler);\n currentSocket.on('error', errorHandler);\n }\n });\n\n const writable = new WritableStream({\n write(chunk) {\n return new Promise((resolve, reject) => {\n currentSocket.write(Buffer.from(chunk), (err) => err ? reject(err) : resolve());\n });\n }\n });\n\n const closedPromise = new Promise((resolve) => currentSocket.on('close', resolve));\n\n return {\n readable,\n writable,\n startTls() {\n currentSocket.removeListener('data', dataHandler);\n currentSocket.removeListener('end', endHandler);\n currentSocket.removeListener('error', errorHandler);\n const tlsSocket = tlsConnect({ socket: currentSocket, servername: hostname });\n currentSocket = tlsSocket;\n tlsSocket.on('data', dataHandler);\n tlsSocket.on('end', endHandler);\n tlsSocket.on('error', errorHandler);\n },\n close() { currentSocket.destroy(); },\n closed: closedPromise,\n };\n}\n`,\n }\n }\n return nextLoad(url, context)\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;AAC5B,MAAM,aAAa;AAcnB,eAAsB,QACpB,WACA,SACA,aACwB;CACxB,IAAI,cAAc,sBAChB,OAAO;EAAE,KAAK;EAAqB,cAAc;CAAK;CAExD,IAAI,cAAc,sBAChB,OAAO;EAAE,KAAK;EAAqB,cAAc;CAAK;CAExD,IAAI,UAAU,SAAS,UAAU,GAG/B,OAAO;EAAE,MAAK,MADS,YADV,UAAU,MAAM,GAAG,EACM,GAAG,OAAO,GACzB,MAAM;EAAY,cAAc;CAAK;CAE9D,OAAO,YAAY,WAAW,OAAO;AACvC;AAeA,eAAsB,KACpB,KACA,SACA,UACqB;CACrB,IAAI,IAAI,SAAS,UAAU,GAAG;EAG5B,MAAM,UAAU,aADC,cADD,IAAI,MAAM,GAAG,EACQ,CACD,GAAG,OAAO;EAC9C,OAAO;GACL,QAAQ;GACR,cAAc;GACd,QAAQ,kBAAkB,KAAK,UAAU,OAAO;EAClD;CACF;CACA,IAAI,QAAQ,qBACV,OAAO;EACL,QAAQ;EACR,cAAc;EACd,QAAQ;;;;;;;;;;;;;;;;;;;;;;CAsBV;CAEF,IAAI,QAAQ,qBACV,OAAO;EACL,QAAQ;EACR,cAAc;EACd,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+DV;CAEF,OAAO,SAAS,KAAK,OAAO;AAC9B"}
|
package/dist/bin/quarry.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --no-warnings
|
|
2
2
|
import { createRequire, register } from "node:module";
|
|
3
|
-
import { existsSync
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
5
4
|
import { dirname, join, resolve } from "node:path";
|
|
6
5
|
import { URL, pathToFileURL } from "node:url";
|
|
7
6
|
import { Command, Option } from "clipanion";
|
|
@@ -121,67 +120,58 @@ if (!existsSync(entryPath)) {
|
|
|
121
120
|
console.error(" npx quarry ./path/to/entry.ts <command> [options]");
|
|
122
121
|
process.exit(1);
|
|
123
122
|
}
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
delete envConfig.durable_objects;
|
|
129
|
-
delete envConfig.migrations;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
async function createStrippedConfig(cwdRequire) {
|
|
123
|
+
async function main() {
|
|
124
|
+
const cwdRequire = createRequire(join(process.cwd(), "package.json"));
|
|
125
|
+
const { unstable_readConfig: readConfig, unstable_getMiniflareWorkerOptions: getMiniflareWorkerOptions, unstable_getVarsForDev: getVarsForDev } = await import(cwdRequire.resolve("wrangler"));
|
|
126
|
+
const { Miniflare, getDefaultDevRegistryPath } = await import(cwdRequire.resolve("miniflare"));
|
|
133
127
|
const configName = [
|
|
134
128
|
"wrangler.jsonc",
|
|
135
129
|
"wrangler.json",
|
|
136
130
|
"wrangler.toml"
|
|
137
131
|
].find((c) => existsSync(resolve(process.cwd(), c)));
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (config.env && typeof config.env === "object") {
|
|
150
|
-
for (const envConfig of Object.values(config.env)) if (typeof envConfig.name === "string") envConfig.name = `quarry-${envConfig.name}-${process.pid}`;
|
|
132
|
+
const configPath = configName ? resolve(process.cwd(), configName) : void 0;
|
|
133
|
+
const envFiles = [
|
|
134
|
+
".env",
|
|
135
|
+
".env.local",
|
|
136
|
+
environment ? `.env.${environment}` : null,
|
|
137
|
+
environment ? `.env.${environment}.local` : null
|
|
138
|
+
];
|
|
139
|
+
for (const envFile of envFiles) {
|
|
140
|
+
if (!envFile) continue;
|
|
141
|
+
const envPath = resolve(process.cwd(), envFile);
|
|
142
|
+
if (existsSync(envPath)) process.loadEnvFile(envPath);
|
|
151
143
|
}
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
envFiles,
|
|
175
|
-
configPath: strippedConfigPath
|
|
144
|
+
if (process.env.WRANGLER_REGISTRY_PATH && !process.env.MINIFLARE_REGISTRY_PATH) process.env.MINIFLARE_REGISTRY_PATH = process.env.WRANGLER_REGISTRY_PATH;
|
|
145
|
+
const config = readConfig({
|
|
146
|
+
config: configPath,
|
|
147
|
+
env: environment
|
|
148
|
+
});
|
|
149
|
+
const { workerOptions } = getMiniflareWorkerOptions(config, environment);
|
|
150
|
+
const vars = getVarsForDev(configPath, void 0, config.vars, environment);
|
|
151
|
+
const varsRecord = {};
|
|
152
|
+
for (const [key, binding] of Object.entries(vars)) varsRecord[key] = binding.value;
|
|
153
|
+
workerOptions.bindings = {
|
|
154
|
+
...workerOptions.bindings ?? {},
|
|
155
|
+
...varsRecord,
|
|
156
|
+
QUEUE_PROVIDER: "sync"
|
|
157
|
+
};
|
|
158
|
+
workerOptions.name = config.name ? `quarry-${config.name}-${process.pid}` : `quarry-${process.pid}`;
|
|
159
|
+
const registryPath = getDefaultDevRegistryPath();
|
|
160
|
+
const mf = new Miniflare({
|
|
161
|
+
...workerOptions,
|
|
162
|
+
script: "",
|
|
163
|
+
modules: true,
|
|
164
|
+
unsafeDevRegistryPath: registryPath,
|
|
165
|
+
defaultPersistRoot: join(process.cwd(), ".wrangler/state/v3")
|
|
176
166
|
});
|
|
167
|
+
await mf.ready;
|
|
168
|
+
const env = await mf.getBindings();
|
|
177
169
|
const pendingPromises = [];
|
|
178
170
|
const trackedWaitUntil = (promise) => {
|
|
179
171
|
pendingPromises.push(promise);
|
|
180
|
-
ctx.waitUntil(promise);
|
|
181
172
|
};
|
|
182
173
|
let app;
|
|
183
174
|
try {
|
|
184
|
-
env.QUEUE_PROVIDER = "sync";
|
|
185
175
|
globalThis.__stratalPlatformProxy = {
|
|
186
176
|
env,
|
|
187
177
|
waitUntil: trackedWaitUntil
|
|
@@ -205,10 +195,7 @@ async function main() {
|
|
|
205
195
|
} finally {
|
|
206
196
|
await Promise.allSettled(pendingPromises);
|
|
207
197
|
await app?.shutdown();
|
|
208
|
-
await dispose();
|
|
209
|
-
if (strippedConfigPath) try {
|
|
210
|
-
unlinkSync(strippedConfigPath);
|
|
211
|
-
} catch {}
|
|
198
|
+
await mf.dispose();
|
|
212
199
|
}
|
|
213
200
|
}
|
|
214
201
|
main().catch(async (error) => {
|
package/dist/bin/quarry.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quarry.mjs","names":[],"sources":["../../src/bin/argv.ts","../../src/bin/commands/dynamic-command.ts","../../src/bin/quarry.ts"],"sourcesContent":["export function extractEnvFlag(argv: string[]): { env: string | undefined; rest: string[] } {\n let env: string | undefined\n const rest: string[] = []\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]\n if (tok === '--') {\n rest.push(...argv.slice(i))\n break\n }\n const eqMatch = tok.match(/^(?:--env|-e)=(.*)$/)\n if (eqMatch) {\n if (!eqMatch[1]) throw new Error('--env requires a value (e.g. --env staging)')\n if (env !== undefined) throw new Error('--env specified more than once')\n env = eqMatch[1]\n continue\n }\n if (tok === '--env' || tok === '-e') {\n const next = argv[i + 1]\n if (!next || next.startsWith('-')) throw new Error('--env requires a value (e.g. --env staging)')\n if (env !== undefined) throw new Error('--env specified more than once')\n env = next\n i++\n continue\n }\n rest.push(tok)\n }\n return { env, rest }\n}\n","import { Command, type CommandClass, Option, type Usage } from 'clipanion'\n\nimport type { Application } from 'stratal'\nimport type { ParsedSignature, QuarryRegistry } from 'stratal/quarry'\n\n/** Create Clipanion command classes from Quarry-registered commands. */\nexport function createDynamicCommands(\n quarry: QuarryRegistry,\n parseSignature: (command: string) => ParsedSignature,\n app: Application,\n) {\n const commands: CommandClass[] = []\n\n for (const entry of quarry.list()) {\n const commandClass = quarry.get(entry.name)! as unknown as { command: string; description?: string; aliases?: string[] }\n const signature = parseSignature(commandClass.command)\n\n const paths: string[][] = [entry.name.split(' ')]\n if (commandClass.aliases) {\n for (const alias of commandClass.aliases) {\n paths.push(alias.split(' '))\n }\n }\n\n // Allow bare `npx quarry` (no arguments) to invoke the help command\n if (entry.name === 'help') {\n paths.push([])\n }\n\n class DynCmd extends Command {\n static override paths = paths\n static override usage: Usage | undefined = commandClass.description\n ? Command.Usage({ description: commandClass.description })\n : undefined\n\n async execute(): Promise<number> {\n const input: Record<string, unknown> = {}\n\n for (const arg of signature.arguments) {\n const value = (this as Record<string, unknown>)[arg.name]\n if (value !== undefined) input[arg.name] = value\n }\n\n for (const opt of signature.options) {\n const value = (this as Record<string, unknown>)[opt.name]\n if (value !== undefined) input[opt.name] = value\n }\n\n const result = await app.handleCommand(entry.name, input)\n\n for (const line of result.output) {\n this.context.stdout.write(line + '\\n')\n }\n\n for (const err of result.errors) {\n this.context.stderr.write(err + '\\n')\n }\n\n return result.exitCode\n }\n }\n\n // Define Clipanion options/arguments as class property defaults\n const proto = DynCmd.prototype as unknown as Record<string, unknown>\n for (const arg of signature.arguments) {\n if (arg.isArray) {\n proto[arg.name] = Option.Rest({ name: arg.name, required: arg.required ? 1 : 0 })\n } else {\n proto[arg.name] = Option.String({ name: arg.name, required: arg.required })\n }\n }\n\n for (const opt of signature.options) {\n const optName = opt.alias ? `-${opt.alias},--${opt.name}` : `--${opt.name}`\n const optDescParts: string[] = []\n if (opt.description) optDescParts.push(opt.description)\n if (opt.default !== undefined) optDescParts.push(`(default: ${opt.default})`)\n const optDesc = optDescParts.length > 0 ? optDescParts.join(' ') : undefined\n\n if (opt.isFlag) {\n proto[opt.name] = Option.Boolean(optName, { description: optDesc })\n } else if (opt.isArray) {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.Array(optName, [opt.default], { description: optDesc })\n } else {\n proto[opt.name] = Option.Array(optName, { description: optDesc })\n }\n } else {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.String(optName, opt.default, { description: optDesc })\n } else {\n proto[opt.name] = Option.String(optName, { description: optDesc })\n }\n }\n }\n\n commands.push(DynCmd)\n }\n\n return commands\n}\n","import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { createRequire, register } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { URL, pathToFileURL } from 'node:url'\nimport type { QuarryRegistry } from 'stratal/quarry'\nimport { type Application } from '../application'\nimport { extractEnvFlag } from './argv'\nimport { createDynamicCommands } from './commands/dynamic-command'\n\nconst require = createRequire(import.meta.url)\n\n// Register @swc-node/register for TypeScript + decorator support\nconst swcRegisterPath = join(dirname(require.resolve('@swc-node/register')), 'esm/esm.mjs')\nregister(pathToFileURL(swcRegisterPath), pathToFileURL('./'))\n\n// Register cloudflare:workers virtual module loader\nregister(new URL('./cloudflare-workers-loader.mjs', import.meta.url), pathToFileURL('./'))\n\nconst DEFAULT_ENTRY = './src/quarry.ts'\n\nlet environment: string | undefined\ntry {\n const parsed = extractEnvFlag(process.argv.slice(2))\n environment = parsed.env\n process.argv.splice(2, process.argv.length - 2, ...parsed.rest)\n} catch (e) {\n console.error(`Error: ${(e as Error).message}`)\n process.exit(1)\n}\n\n// Determine entry file: if first arg looks like a file path, use it; otherwise use default\nconst firstArg = process.argv[2]\nlet entryFile = DEFAULT_ENTRY\n\nif (firstArg && (firstArg.includes('/') || firstArg.includes('\\\\') || /\\.(ts|js|mts|mjs)$/.test(firstArg))) {\n entryFile = firstArg\n // Remove the entry file from argv so Clipanion sees: [node, script, command, ...options]\n process.argv.splice(2, 1)\n}\n\n// Resolve and validate the entry file\nconst entryPath = resolve(process.cwd(), entryFile)\n\nif (!existsSync(entryPath)) {\n console.error(`Error: Entry file not found: ${entryFile}`)\n console.error('')\n console.error('Create src/quarry.ts that exports `QuarryRunner.run({ module, seeders })`, or specify a custom path:')\n console.error(' npx quarry ./path/to/entry.ts <command> [options]')\n process.exit(1)\n}\n\nfunction stripDurableObjects(config: Record<string, unknown>): void {\n delete config.durable_objects\n delete config.migrations\n if (config.env && typeof config.env === 'object') {\n for (const envConfig of Object.values(config.env as Record<string, Record<string, unknown>>)) {\n delete envConfig.durable_objects\n delete envConfig.migrations\n }\n }\n}\n\nasync function createStrippedConfig(cwdRequire: NodeRequire): Promise<string | undefined> {\n const candidates = ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']\n const configName = candidates.find(c => existsSync(resolve(process.cwd(), c)))\n if (!configName) return undefined\n\n const configPath = resolve(process.cwd(), configName)\n const raw = readFileSync(configPath, 'utf-8')\n\n let config: Record<string, unknown>\n if (configName.endsWith('.toml')) {\n const { parse } = await import(cwdRequire.resolve('smol-toml')) as { parse: (input: string) => Record<string, unknown> }\n config = parse(raw)\n } else {\n const { parse: parseJsonc } = await import(cwdRequire.resolve('jsonc-parser')) as { parse: (input: string) => Record<string, unknown> }\n config = parseJsonc(raw)\n }\n\n // Rename so quarry's ephemeral miniflare doesn't claim the running\n // `wrangler dev` session's dev-registry slot. getPlatformProxy registers\n // its worker with empty entrypointAddresses (no direct sockets), and that\n // overwrite makes peer workers route TenantsRpc/BrandingRpc/etc. calls to\n // their fallback service (\"couldn't find a local dev session for the X\n // entrypoint\"). A unique name keeps the running session's entry untouched.\n if (typeof config.name === 'string') {\n config.name = `quarry-${config.name}-${process.pid}`\n }\n if (config.env && typeof config.env === 'object') {\n for (const envConfig of Object.values(config.env as Record<string, Record<string, unknown>>)) {\n if (typeof envConfig.name === 'string') {\n envConfig.name = `quarry-${envConfig.name}-${process.pid}`\n }\n }\n }\n\n stripDurableObjects(config)\n\n const tmpPath = resolve(tmpdir(), `quarry-wrangler-${Date.now()}.json`)\n writeFileSync(tmpPath, JSON.stringify(config, null, 2))\n return tmpPath\n}\n\nfunction discoverEnvFiles(): string[] {\n const cwd = process.cwd()\n const files = readdirSync(cwd)\n return files\n .filter(file => (/^\\.dev\\.vars($|\\.)/.test(file) || /^\\.env($|\\.)/.test(file)) && !file.endsWith('.example') && !file.endsWith('.sample'))\n .sort((a, b) => {\n // Load .env files before .dev.vars so .dev.vars takes precedence\n const aIsDevVars = a.startsWith('.dev.vars')\n const bIsDevVars = b.startsWith('.dev.vars')\n if (aIsDevVars !== bIsDevVars) return aIsDevVars ? 1 : -1\n // Within each group, .local files load last (highest precedence)\n const aIsLocal = a.endsWith('.local')\n const bIsLocal = b.endsWith('.local')\n if (aIsLocal !== bIsLocal) return aIsLocal ? 1 : -1\n return a.localeCompare(b)\n })\n .map(file => join(cwd, file))\n}\n\nasync function main(): Promise<void> {\n const cwdRequire = createRequire(join(process.cwd(), 'package.json'))\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n const { getPlatformProxy } = await import(cwdRequire.resolve('wrangler')) as typeof import('wrangler')\n\n const strippedConfigPath = await createStrippedConfig(cwdRequire)\n\n const envFiles = discoverEnvFiles()\n const { env, ctx, dispose } = await getPlatformProxy({\n environment, envFiles, configPath: strippedConfigPath,\n })\n\n // Track waitUntil promises so we can drain them before shutdown.\n // In Workers runtime, waitUntil keeps the isolate alive. In Quarry (miniflare),\n // dispose() tears down without awaiting pending promises — so we track and drain them.\n const pendingPromises: Promise<unknown>[] = []\n const trackedWaitUntil = (promise: Promise<unknown>) => {\n pendingPromises.push(promise)\n ctx.waitUntil(promise)\n }\n\n let app: Application | undefined\n try {\n env.QUEUE_PROVIDER = 'sync';\n\n // Store platform proxy on globalThis so the cloudflare:workers virtual module can read it\n (globalThis as Record<string, unknown>).__stratalPlatformProxy = {\n env,\n waitUntil: trackedWaitUntil,\n }\n\n // Import user's entry file — triggers `new Stratal(...)` + full Application init\n await import(pathToFileURL(entryPath).href)\n\n // Parallel import of stratal modules\n const [\n { Stratal },\n { DI_TOKENS },\n { parseSignature },\n ] = await Promise.all([\n import('stratal'),\n import('stratal/di'),\n import('stratal/quarry'),\n ])\n\n app = await Stratal.resolveApplication()\n const quarry = app.container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n\n // Build Clipanion CLI\n const { Cli } = await import('clipanion')\n const pkg = require('../../package.json') as { version: string }\n\n const cli = new Cli({\n binaryName: 'quarry',\n binaryLabel: 'Quarry CLI',\n binaryVersion: pkg.version,\n })\n\n for (const cmd of createDynamicCommands(quarry, parseSignature, app)) {\n cli.register(cmd)\n }\n\n await cli.runExit(process.argv.slice(2), { ...Cli.defaultContext })\n } finally {\n await Promise.allSettled(pendingPromises);\n\n await app?.shutdown()\n await dispose()\n\n if (strippedConfigPath) {\n try { unlinkSync(strippedConfigPath) } catch {\n //\n }\n }\n }\n}\n\nmain().catch(async (error: unknown) => {\n const { ConfigValidationError } = await import('stratal/config')\n\n const message = error instanceof Error ? error.message : String(error)\n console.error('Fatal error:', message)\n if (error instanceof ConfigValidationError) {\n console.error(error.errors.message)\n }\n process.exit(1)\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"quarry.mjs","names":[],"sources":["../../src/bin/argv.ts","../../src/bin/commands/dynamic-command.ts","../../src/bin/quarry.ts"],"sourcesContent":["export function extractEnvFlag(argv: string[]): { env: string | undefined; rest: string[] } {\n let env: string | undefined\n const rest: string[] = []\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]\n if (tok === '--') {\n rest.push(...argv.slice(i))\n break\n }\n const eqMatch = tok.match(/^(?:--env|-e)=(.*)$/)\n if (eqMatch) {\n if (!eqMatch[1]) throw new Error('--env requires a value (e.g. --env staging)')\n if (env !== undefined) throw new Error('--env specified more than once')\n env = eqMatch[1]\n continue\n }\n if (tok === '--env' || tok === '-e') {\n const next = argv[i + 1]\n if (!next || next.startsWith('-')) throw new Error('--env requires a value (e.g. --env staging)')\n if (env !== undefined) throw new Error('--env specified more than once')\n env = next\n i++\n continue\n }\n rest.push(tok)\n }\n return { env, rest }\n}\n","import { Command, type CommandClass, Option, type Usage } from 'clipanion'\n\nimport type { Application } from 'stratal'\nimport type { ParsedSignature, QuarryRegistry } from 'stratal/quarry'\n\n/** Create Clipanion command classes from Quarry-registered commands. */\nexport function createDynamicCommands(\n quarry: QuarryRegistry,\n parseSignature: (command: string) => ParsedSignature,\n app: Application,\n) {\n const commands: CommandClass[] = []\n\n for (const entry of quarry.list()) {\n const commandClass = quarry.get(entry.name)! as unknown as { command: string; description?: string; aliases?: string[] }\n const signature = parseSignature(commandClass.command)\n\n const paths: string[][] = [entry.name.split(' ')]\n if (commandClass.aliases) {\n for (const alias of commandClass.aliases) {\n paths.push(alias.split(' '))\n }\n }\n\n // Allow bare `npx quarry` (no arguments) to invoke the help command\n if (entry.name === 'help') {\n paths.push([])\n }\n\n class DynCmd extends Command {\n static override paths = paths\n static override usage: Usage | undefined = commandClass.description\n ? Command.Usage({ description: commandClass.description })\n : undefined\n\n async execute(): Promise<number> {\n const input: Record<string, unknown> = {}\n\n for (const arg of signature.arguments) {\n const value = (this as Record<string, unknown>)[arg.name]\n if (value !== undefined) input[arg.name] = value\n }\n\n for (const opt of signature.options) {\n const value = (this as Record<string, unknown>)[opt.name]\n if (value !== undefined) input[opt.name] = value\n }\n\n const result = await app.handleCommand(entry.name, input)\n\n for (const line of result.output) {\n this.context.stdout.write(line + '\\n')\n }\n\n for (const err of result.errors) {\n this.context.stderr.write(err + '\\n')\n }\n\n return result.exitCode\n }\n }\n\n // Define Clipanion options/arguments as class property defaults\n const proto = DynCmd.prototype as unknown as Record<string, unknown>\n for (const arg of signature.arguments) {\n if (arg.isArray) {\n proto[arg.name] = Option.Rest({ name: arg.name, required: arg.required ? 1 : 0 })\n } else {\n proto[arg.name] = Option.String({ name: arg.name, required: arg.required })\n }\n }\n\n for (const opt of signature.options) {\n const optName = opt.alias ? `-${opt.alias},--${opt.name}` : `--${opt.name}`\n const optDescParts: string[] = []\n if (opt.description) optDescParts.push(opt.description)\n if (opt.default !== undefined) optDescParts.push(`(default: ${opt.default})`)\n const optDesc = optDescParts.length > 0 ? optDescParts.join(' ') : undefined\n\n if (opt.isFlag) {\n proto[opt.name] = Option.Boolean(optName, { description: optDesc })\n } else if (opt.isArray) {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.Array(optName, [opt.default], { description: optDesc })\n } else {\n proto[opt.name] = Option.Array(optName, { description: optDesc })\n }\n } else {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.String(optName, opt.default, { description: optDesc })\n } else {\n proto[opt.name] = Option.String(optName, { description: optDesc })\n }\n }\n }\n\n commands.push(DynCmd)\n }\n\n return commands\n}\n","import type { MiniflareOptions } from 'miniflare';\nimport { existsSync } from 'node:fs';\nimport { createRequire, register } from 'node:module';\nimport { dirname, join, resolve } from 'node:path';\nimport { URL, pathToFileURL } from 'node:url';\nimport type { QuarryRegistry } from 'stratal/quarry';\nimport { type Application } from '../application';\nimport { extractEnvFlag } from './argv';\nimport { createDynamicCommands } from './commands/dynamic-command';\n\ninterface WranglerConfig {\n name?: string\n vars?: Record<string, unknown>\n}\n\ninterface MiniflareWorkerResult {\n workerOptions: Record<string, unknown>\n}\n\ninterface WranglerModule {\n unstable_readConfig: (args: { config?: string; env?: string }) => WranglerConfig\n unstable_getMiniflareWorkerOptions: (config: WranglerConfig, env?: string) => MiniflareWorkerResult\n unstable_getVarsForDev: (configPath: string | undefined, envFiles: undefined, vars: unknown, env: string | undefined) => Record<string, { value: string }>\n}\n\ninterface MiniflareModule {\n Miniflare: new (opts: MiniflareOptions) => { ready: Promise<URL>; getBindings: (name?: string) => Promise<Record<string, unknown>>; dispose: () => Promise<void> }\n getDefaultDevRegistryPath: () => string\n}\n\nconst require = createRequire(import.meta.url)\n\n// Register @swc-node/register for TypeScript + decorator support\nconst swcRegisterPath = join(dirname(require.resolve('@swc-node/register')), 'esm/esm.mjs')\nregister(pathToFileURL(swcRegisterPath), pathToFileURL('./'))\n\n// Register cloudflare:workers virtual module loader\nregister(new URL('./cloudflare-workers-loader.mjs', import.meta.url), pathToFileURL('./'))\n\nconst DEFAULT_ENTRY = './src/quarry.ts'\n\nlet environment: string | undefined\ntry {\n const parsed = extractEnvFlag(process.argv.slice(2))\n environment = parsed.env\n process.argv.splice(2, process.argv.length - 2, ...parsed.rest)\n} catch (e) {\n console.error(`Error: ${(e as Error).message}`)\n process.exit(1)\n}\n\n// Determine entry file: if first arg looks like a file path, use it; otherwise use default\nconst firstArg = process.argv[2]\nlet entryFile = DEFAULT_ENTRY\n\nif (firstArg && (firstArg.includes('/') || firstArg.includes('\\\\') || /\\.(ts|js|mts|mjs)$/.test(firstArg))) {\n entryFile = firstArg\n // Remove the entry file from argv so Clipanion sees: [node, script, command, ...options]\n process.argv.splice(2, 1)\n}\n\n// Resolve and validate the entry file\nconst entryPath = resolve(process.cwd(), entryFile)\n\nif (!existsSync(entryPath)) {\n console.error(`Error: Entry file not found: ${entryFile}`)\n console.error('')\n console.error('Create src/quarry.ts that exports `QuarryRunner.run({ module, seeders })`, or specify a custom path:')\n console.error(' npx quarry ./path/to/entry.ts <command> [options]')\n process.exit(1)\n}\n\nasync function main(): Promise<void> {\n const cwdRequire = createRequire(join(process.cwd(), 'package.json'))\n\n const { unstable_readConfig: readConfig, unstable_getMiniflareWorkerOptions: getMiniflareWorkerOptions, unstable_getVarsForDev: getVarsForDev } = await import(cwdRequire.resolve('wrangler')) as WranglerModule\n const { Miniflare, getDefaultDevRegistryPath } = await import(cwdRequire.resolve('miniflare')) as MiniflareModule\n\n const candidates = ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']\n const configName = candidates.find(c => existsSync(resolve(process.cwd(), c)))\n const configPath = configName ? resolve(process.cwd(), configName) : undefined\n\n // Load .env into process.env before building Miniflare options, mirroring\n // `wrangler dev`'s precedence: base `.env`, then `.env.local`, then the\n // env-specific `.env.<environment>` and `.env.<environment>.local` (later\n // load wins).\n const envFiles = [\n '.env',\n '.env.local',\n environment ? `.env.${environment}` : null,\n environment ? `.env.${environment}.local` : null,\n ]\n for (const envFile of envFiles) {\n if (!envFile) continue\n const envPath = resolve(process.cwd(), envFile)\n if (existsSync(envPath)) process.loadEnvFile(envPath)\n }\n\n // Bridge the documented `WRANGLER_REGISTRY_PATH` to `MINIFLARE_REGISTRY_PATH`,\n // the variable Miniflare's `getDefaultDevRegistryPath()` actually reads.\n // `wrangler dev` performs the same translation internally; mirroring it here\n // lets a single documented env var redirect the dev service registry for BOTH\n // this CLI's Miniflare (below) AND any vite dev server a command launches\n // (`@cloudflare/vite-plugin` also resolves its registry via\n // `getDefaultDevRegistryPath()`). That allows several isolated dev environments\n // to run in parallel without sharing the global `~/.wrangler/registry`, where\n // their identically-named workers would otherwise overwrite each other and\n // break cross-worker service-binding resolution. Only set when unset so an\n // explicit override still wins.\n if (process.env.WRANGLER_REGISTRY_PATH && !process.env.MINIFLARE_REGISTRY_PATH) {\n process.env.MINIFLARE_REGISTRY_PATH = process.env.WRANGLER_REGISTRY_PATH\n }\n\n const config = readConfig({ config: configPath, env: environment })\n const { workerOptions } = getMiniflareWorkerOptions(config, environment)\n\n const vars = getVarsForDev(configPath, undefined, config.vars, environment)\n const varsRecord: Record<string, string> = {}\n for (const [key, binding] of Object.entries(vars)) {\n varsRecord[key] = binding.value\n }\n\n const existingBindings = workerOptions.bindings as Record<string, unknown> ?? {}\n workerOptions.bindings = {\n ...existingBindings,\n ...varsRecord,\n QUEUE_PROVIDER: 'sync',\n }\n\n // Rename so quarry doesn't overwrite a running `wrangler dev` session's\n // dev-registry entry. The registry is how Miniflare discovers peer workers\n // for service binding resolution — a collision would break the running session.\n const workerName = config.name ? `quarry-${config.name}-${process.pid}` : `quarry-${process.pid}`\n workerOptions.name = workerName\n\n // Resolve the dev-registry path so Miniflare can discover running\n // `wrangler dev` sessions for service binding resolution.\n const registryPath = getDefaultDevRegistryPath()\n\n const mf = new Miniflare({\n ...workerOptions,\n script: '',\n modules: true,\n unsafeDevRegistryPath: registryPath,\n // Persist every durable plugin (KV, D1, R2, Durable Objects, cache) under\n // the same root `wrangler dev` uses, so state survives across `quarry`\n // invocations and is shared with a running `wrangler dev` session.\n defaultPersistRoot: join(process.cwd(), '.wrangler/state/v3'),\n })\n\n await mf.ready\n const env = await mf.getBindings()\n\n const pendingPromises: Promise<unknown>[] = []\n const trackedWaitUntil = (promise: Promise<unknown>) => {\n pendingPromises.push(promise)\n }\n\n let app: Application | undefined\n try {\n (globalThis as Record<string, unknown>).__stratalPlatformProxy = {\n env,\n waitUntil: trackedWaitUntil,\n }\n\n await import(pathToFileURL(entryPath).href)\n\n const [\n { Stratal },\n { DI_TOKENS },\n { parseSignature },\n ] = await Promise.all([\n import('stratal'),\n import('stratal/di'),\n import('stratal/quarry'),\n ])\n\n app = await Stratal.resolveApplication()\n const quarry = app.container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n\n const { Cli } = await import('clipanion')\n const pkg = require('../../package.json') as { version: string }\n\n const cli = new Cli({\n binaryName: 'quarry',\n binaryLabel: 'Quarry CLI',\n binaryVersion: pkg.version,\n })\n\n for (const cmd of createDynamicCommands(quarry, parseSignature, app)) {\n cli.register(cmd)\n }\n\n await cli.runExit(process.argv.slice(2), { ...Cli.defaultContext })\n } finally {\n await Promise.allSettled(pendingPromises)\n await app?.shutdown()\n await mf.dispose()\n }\n}\n\nmain().catch(async (error: unknown) => {\n const { ConfigValidationError } = await import('stratal/config')\n\n const message = error instanceof Error ? error.message : String(error)\n console.error('Fatal error:', message)\n if (error instanceof ConfigValidationError) {\n console.error(error.errors.message)\n }\n process.exit(1)\n})\n"],"mappings":"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"}
|
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as CacheService } from "../cache.service-
|
|
1
|
+
import { Dr as ApplicationError } from "../index-B_JoEl3V.mjs";
|
|
2
|
+
import { t as CacheService } from "../cache.service-uElmBtdS.mjs";
|
|
3
|
+
import { t as TieredCacheService } from "../tiered-cache.service-Dv3BhxxE.mjs";
|
|
3
4
|
|
|
4
5
|
//#region src/cache/cache.error.d.ts
|
|
5
6
|
declare class CacheError extends ApplicationError {}
|
|
@@ -21,8 +22,9 @@ declare class CacheModule {}
|
|
|
21
22
|
//#region src/cache/cache.tokens.d.ts
|
|
22
23
|
declare const CACHE_TOKENS: {
|
|
23
24
|
readonly CacheService: symbol;
|
|
25
|
+
readonly TieredCacheService: symbol;
|
|
24
26
|
};
|
|
25
27
|
type CacheToken = (typeof CACHE_TOKENS)[keyof typeof CACHE_TOKENS];
|
|
26
28
|
//#endregion
|
|
27
|
-
export { CACHE_TOKENS, CacheError, CacheModule, CacheService, CacheToken };
|
|
29
|
+
export { CACHE_TOKENS, CacheError, CacheModule, CacheService, CacheToken, TieredCacheService };
|
|
28
30
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cache/cache.error.ts","../../src/cache/cache.module.ts","../../src/cache/cache.tokens.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cache/cache.error.ts","../../src/cache/cache.module.ts","../../src/cache/cache.tokens.ts"],"mappings":";;;;;cAEa,UAAA,SAAmB,gBAAgB;;;;;;;;AAAhD;;;;AAAgD;;cCsBnC,WAAA;;;cCxBA,YAAA;EAAA,SAGH,YAAA;EAAA,SAAA,kBAAA;AAAA;AAAA,KAEE,UAAA,WAAqB,YAAA,eAA2B,YAAY"}
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as __decorateParam, t as __decorate } from "../decorate-
|
|
1
|
+
import { d as inject, r as DI_TOKENS, s as Singleton } from "../di-DseMn-z9.mjs";
|
|
2
|
+
import { a as ApplicationError } from "../container-storage-BmOJ4_Na.mjs";
|
|
3
|
+
import { n as __decorateParam, t as __decorate } from "../decorate-CuAoSZvs.mjs";
|
|
4
4
|
import { LOGGER_TOKENS } from "../logger/index.mjs";
|
|
5
|
-
import "../errors-
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
5
|
+
import "../errors-mXYxG0XB.mjs";
|
|
6
|
+
import { n as Module } from "../module.decorator-CYHY6pG5.mjs";
|
|
7
|
+
import "../module/index.mjs";
|
|
8
8
|
//#region src/cache/cache.error.ts
|
|
9
9
|
var CacheError = class extends ApplicationError {};
|
|
10
10
|
//#endregion
|
|
11
|
+
//#region src/cache/cache.tokens.ts
|
|
12
|
+
const CACHE_TOKENS = {
|
|
13
|
+
CacheService: Symbol.for("stratal:cache:service"),
|
|
14
|
+
TieredCacheService: Symbol.for("stratal:cache:tiered-service")
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
11
17
|
//#region src/cache/services/cache.service.ts
|
|
12
18
|
var _CacheService;
|
|
13
19
|
let CacheService = _CacheService = class CacheService {
|
|
@@ -19,47 +25,34 @@ let CacheService = _CacheService = class CacheService {
|
|
|
19
25
|
this.logger = logger;
|
|
20
26
|
this.kv = env.CACHE;
|
|
21
27
|
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* Used internally by `withBinding()` to configure different KV instances.
|
|
26
|
-
*
|
|
27
|
-
* @param kv - KV namespace to use
|
|
28
|
-
*/
|
|
29
|
-
setKV(kv) {
|
|
30
|
-
this.kv = kv;
|
|
28
|
+
/** The KV namespace this instance is bound to. */
|
|
29
|
+
get namespace() {
|
|
30
|
+
return this.kv;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Create a new CacheService instance
|
|
34
|
-
*
|
|
35
|
-
* **Pattern:** Returns a new instance (immutable)
|
|
36
|
-
*
|
|
37
|
-
* **Best Practice:** Initialize specialized caches as class properties in constructor
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```typescript
|
|
41
|
-
* class MyService {
|
|
42
|
-
* private readonly uploadsCache: CacheService
|
|
43
|
-
* private readonly systemCache: CacheService
|
|
44
|
-
*
|
|
45
|
-
* constructor(
|
|
46
|
-
* @inject(CACHE_TOKENS.CacheService) private readonly cache: CacheService,
|
|
47
|
-
* @inject(DI_TOKENS.CloudflareEnv) private readonly env: Env
|
|
48
|
-
* ) {
|
|
49
|
-
* this.uploadsCache = this.cache.withBinding(this.env.UPLOADS_CACHE)
|
|
50
|
-
* this.systemCache = this.cache.withBinding(this.env.SYSTEM_CONFIG_KV)
|
|
51
|
-
* }
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
33
|
+
* Create a new CacheService instance bound to a different KV namespace.
|
|
54
34
|
*
|
|
55
35
|
* @param kv - KV namespace to use
|
|
56
|
-
* @returns
|
|
36
|
+
* @returns A new CacheService for the given binding
|
|
57
37
|
*/
|
|
58
38
|
withBinding(kv) {
|
|
59
39
|
const instance = new _CacheService(this.env, this.logger);
|
|
60
|
-
instance.
|
|
40
|
+
instance.kv = kv;
|
|
61
41
|
return instance;
|
|
62
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a new CacheService instance bound to a KV namespace by its binding
|
|
45
|
+
* name, resolved from the environment.
|
|
46
|
+
*
|
|
47
|
+
* @param name - KV namespace binding name (e.g. `'UPLOADS_CACHE'`)
|
|
48
|
+
* @returns A new CacheService for the given binding
|
|
49
|
+
* @throws {CacheError} If no binding with that name exists in the environment
|
|
50
|
+
*/
|
|
51
|
+
binding(name) {
|
|
52
|
+
const kv = this.env[name];
|
|
53
|
+
if (!kv) throw new CacheError(`KV binding "${name}" was not found in the environment`);
|
|
54
|
+
return this.withBinding(kv);
|
|
55
|
+
}
|
|
63
56
|
async get(key, typeOrOptions) {
|
|
64
57
|
try {
|
|
65
58
|
if (typeof typeOrOptions === "string") return await this.kv.get(key, typeOrOptions);
|
|
@@ -174,6 +167,94 @@ CacheService = _CacheService = __decorate([
|
|
|
174
167
|
__decorateParam(1, inject(LOGGER_TOKENS.LoggerService))
|
|
175
168
|
], CacheService);
|
|
176
169
|
//#endregion
|
|
170
|
+
//#region src/cache/services/tiered-cache.service.ts
|
|
171
|
+
var _TieredCacheService;
|
|
172
|
+
/**
|
|
173
|
+
* Max entries kept in the isolate-local L1 before the oldest is evicted (FIFO).
|
|
174
|
+
* Bounds memory per isolate; KV remains the unbounded source of truth.
|
|
175
|
+
*/
|
|
176
|
+
const L1_MAX_ENTRIES = 1e3;
|
|
177
|
+
let TieredCacheService = _TieredCacheService = class TieredCacheService {
|
|
178
|
+
cache;
|
|
179
|
+
/** Isolate-local L1 tier. Per-instance, so each binding has its own. */
|
|
180
|
+
l1 = /* @__PURE__ */ new Map();
|
|
181
|
+
/** Memoized tiered instances per binding name (each with its own L1). */
|
|
182
|
+
children = /* @__PURE__ */ new Map();
|
|
183
|
+
constructor(cache) {
|
|
184
|
+
this.cache = cache;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the tiered cache bound to a KV namespace by its binding name. Memoized:
|
|
188
|
+
* repeated calls with the same name return the same instance, preserving its
|
|
189
|
+
* isolate-local L1 across requests/messages.
|
|
190
|
+
*
|
|
191
|
+
* @param name - KV namespace binding name (e.g. `'UPLOADS_CACHE'`)
|
|
192
|
+
* @throws {CacheError} If no binding with that name exists in the environment
|
|
193
|
+
*/
|
|
194
|
+
binding(name) {
|
|
195
|
+
let child = this.children.get(name);
|
|
196
|
+
if (child === void 0) {
|
|
197
|
+
child = new _TieredCacheService(this.cache.binding(name));
|
|
198
|
+
this.children.set(name, child);
|
|
199
|
+
}
|
|
200
|
+
return child;
|
|
201
|
+
}
|
|
202
|
+
async get(key, typeOrOptions) {
|
|
203
|
+
const type = typeof typeOrOptions === "string" ? typeOrOptions : typeOrOptions?.type ?? "text";
|
|
204
|
+
if (type === "text" || type === "json") {
|
|
205
|
+
const cached = this.l1Read(key);
|
|
206
|
+
if (cached !== null) return type === "json" ? JSON.parse(cached) : cached;
|
|
207
|
+
}
|
|
208
|
+
const value = await this.cache.get(key, typeOrOptions);
|
|
209
|
+
if (type === "text" && typeof value === "string") this.l1Write(key, value, null);
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
212
|
+
async getWithMetadata(key, typeOrOptions) {
|
|
213
|
+
return this.cache.getWithMetadata(key, typeOrOptions);
|
|
214
|
+
}
|
|
215
|
+
async put(key, value, options) {
|
|
216
|
+
await this.cache.put(key, value, options);
|
|
217
|
+
if (typeof value === "string") this.l1Write(key, value, this.l1ExpiresAt(options));
|
|
218
|
+
else this.l1.delete(key);
|
|
219
|
+
}
|
|
220
|
+
async delete(key) {
|
|
221
|
+
await this.cache.delete(key);
|
|
222
|
+
this.l1.delete(key);
|
|
223
|
+
}
|
|
224
|
+
async list(options) {
|
|
225
|
+
return this.cache.list(options);
|
|
226
|
+
}
|
|
227
|
+
/** Read a fresh L1 entry, evicting it if expired. Returns `null` on miss. */
|
|
228
|
+
l1Read(key) {
|
|
229
|
+
const entry = this.l1.get(key);
|
|
230
|
+
if (entry === void 0) return null;
|
|
231
|
+
if (entry.expiresAt !== null && Date.now() >= entry.expiresAt) {
|
|
232
|
+
this.l1.delete(key);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return entry.value;
|
|
236
|
+
}
|
|
237
|
+
/** Write an L1 entry, refreshing its recency and evicting the oldest at cap. */
|
|
238
|
+
l1Write(key, value, expiresAt) {
|
|
239
|
+
this.l1.delete(key);
|
|
240
|
+
if (this.l1.size >= L1_MAX_ENTRIES) {
|
|
241
|
+
const oldest = this.l1.keys().next().value;
|
|
242
|
+
if (oldest !== void 0) this.l1.delete(oldest);
|
|
243
|
+
}
|
|
244
|
+
this.l1.set(key, {
|
|
245
|
+
value,
|
|
246
|
+
expiresAt
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
/** Compute an absolute L1 expiry (epoch ms) from KV put options. */
|
|
250
|
+
l1ExpiresAt(options) {
|
|
251
|
+
if (options?.expiration !== void 0) return options.expiration * 1e3;
|
|
252
|
+
if (options?.expirationTtl !== void 0) return Date.now() + options.expirationTtl * 1e3;
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
TieredCacheService = _TieredCacheService = __decorate([Singleton(CACHE_TOKENS.TieredCacheService), __decorateParam(0, inject(CACHE_TOKENS.CacheService))], TieredCacheService);
|
|
257
|
+
//#endregion
|
|
177
258
|
//#region src/cache/cache.module.ts
|
|
178
259
|
/**
|
|
179
260
|
* Cache Module
|
|
@@ -190,8 +271,11 @@ let CacheModule = class CacheModule {};
|
|
|
190
271
|
CacheModule = __decorate([Module({ providers: [{
|
|
191
272
|
provide: CACHE_TOKENS.CacheService,
|
|
192
273
|
useClass: CacheService
|
|
274
|
+
}, {
|
|
275
|
+
provide: CACHE_TOKENS.TieredCacheService,
|
|
276
|
+
useClass: TieredCacheService
|
|
193
277
|
}] })], CacheModule);
|
|
194
278
|
//#endregion
|
|
195
|
-
export { CACHE_TOKENS, CacheError, CacheModule, CacheService };
|
|
279
|
+
export { CACHE_TOKENS, CacheError, CacheModule, CacheService, TieredCacheService };
|
|
196
280
|
|
|
197
281
|
//# sourceMappingURL=index.mjs.map
|