stratal 0.0.26 → 0.1.0
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/CHANGELOG.md +912 -0
- package/README.md +96 -14
- package/dist/bin/cloudflare-workers-loader.mjs +18 -3
- package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
- package/dist/bin/quarry.mjs +85 -14
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +8 -9
- package/dist/cache/index.d.mts.map +1 -1
- package/dist/cache/index.mjs +56 -17
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{cache.service-uElmBtdS.d.mts → cache.service-Dg0Hj0gp.d.mts} +27 -12
- package/dist/cache.service-Dg0Hj0gp.d.mts.map +1 -0
- package/dist/{command-DoBD2Cwl.d.mts → command-BXMccV_9.d.mts} +6 -3
- package/dist/command-BXMccV_9.d.mts.map +1 -0
- package/dist/{command-BvmUAPPQ.mjs → command-Bb820B6W.mjs} +36 -13
- package/dist/command-Bb820B6W.mjs.map +1 -0
- package/dist/common.schemas-BigQCwYB.mjs +89 -0
- package/dist/common.schemas-BigQCwYB.mjs.map +1 -0
- package/dist/config/index.d.mts +15 -15
- package/dist/config/index.d.mts.map +1 -1
- package/dist/config/index.mjs +4 -6
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-D3iMTSdy.d.mts → consumer-registry-i9oCdarv.d.mts} +2 -3
- package/dist/consumer-registry-i9oCdarv.d.mts.map +1 -0
- package/dist/{container-storage-BmOJ4_Na.mjs → container-storage-DfF7SXdV.mjs} +11 -1
- package/dist/container-storage-DfF7SXdV.mjs.map +1 -0
- package/dist/{controller.decorator-YSTPQntu.mjs → controller.decorator-BB0JcwMg.mjs} +18 -9
- package/dist/controller.decorator-BB0JcwMg.mjs.map +1 -0
- package/dist/cron/index.d.mts +6 -7
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +1 -1
- package/dist/{cron-job-NesZRk8F.d.mts → cron-job-DAgNw4Bn.d.mts} +1 -2
- package/dist/cron-job-DAgNw4Bn.d.mts.map +1 -0
- package/dist/{cron.module-C81HTzR7.mjs → cron.module-Dnyl9sFk.mjs} +6 -7
- package/dist/{cron.module-C81HTzR7.mjs.map → cron.module-Dnyl9sFk.mjs.map} +1 -1
- package/dist/{decorate-CuAoSZvs.mjs → decorate-C79yt3Wt.mjs} +2 -2
- package/dist/di/index.d.mts +1 -1
- package/dist/di/index.mjs +2 -3
- package/dist/{di-D7qmrAir.mjs → di-DQ3xbjgf.mjs} +48 -6
- package/dist/di-DQ3xbjgf.mjs.map +1 -0
- package/dist/email/index.d.mts +158 -133
- package/dist/email/index.d.mts.map +1 -1
- package/dist/email/index.mjs +66 -78
- package/dist/email/index.mjs.map +1 -1
- package/dist/{en-CDZBMcc1.mjs → en-CCL84z4k.mjs} +2 -2
- package/dist/{en-CDZBMcc1.mjs.map → en-CCL84z4k.mjs.map} +1 -1
- package/dist/env-ug22bJj7.d.mts.map +1 -1
- package/dist/errors/index.d.mts +1 -1
- package/dist/errors/index.mjs +3 -3
- package/dist/{errors-C01O2T-n.mjs → errors-CrOXI_y5.mjs} +6 -8
- package/dist/errors-CrOXI_y5.mjs.map +1 -0
- package/dist/events/index.d.mts +8 -9
- package/dist/events/index.d.mts.map +1 -1
- package/dist/events/index.mjs +1 -1
- package/dist/{events-BhEQuT1X.mjs → events-GtwGEHyT.mjs} +5 -6
- package/dist/events-GtwGEHyT.mjs.map +1 -0
- package/dist/{exception-context-D-kvney-.mjs → exception-context-CdnGTQNF.mjs} +13 -7
- package/dist/exception-context-CdnGTQNF.mjs.map +1 -0
- package/dist/{gateway-context-m7kEzRa2.mjs → gateway-context-Cmc1NRS0.mjs} +7 -7
- package/dist/{gateway-context-m7kEzRa2.mjs.map → gateway-context-Cmc1NRS0.mjs.map} +1 -1
- package/dist/gateway-mode-0rUaeW3g.mjs +53 -0
- package/dist/gateway-mode-0rUaeW3g.mjs.map +1 -0
- package/dist/guards/index.d.mts +38 -20
- package/dist/guards/index.d.mts.map +1 -1
- package/dist/guards/index.mjs +2 -2
- package/dist/{guards-DALPXy3_.mjs → guards-DOGly9IF.mjs} +34 -11
- package/dist/guards-DOGly9IF.mjs.map +1 -0
- package/dist/hono-app-IhdqL0Fk.mjs +399 -0
- package/dist/hono-app-IhdqL0Fk.mjs.map +1 -0
- package/dist/{http-method.decorator-BljM8BDj.mjs → http-method.decorator-BDi9LTWj.mjs} +9 -8
- package/dist/http-method.decorator-BDi9LTWj.mjs.map +1 -0
- package/dist/i18n/index.d.mts +26 -137
- package/dist/i18n/index.d.mts.map +1 -1
- package/dist/i18n/index.mjs +4 -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.d.mts +1 -2
- package/dist/i18n/utils/index.d.mts.map +1 -1
- package/dist/i18n/validation/index.d.mts +80 -5
- package/dist/i18n/validation/index.d.mts.map +1 -0
- package/dist/i18n/validation/index.mjs +4 -5
- package/dist/{i18n.module-B2DvWUPa.mjs → i18n.module-Bm3xlViA.mjs} +28 -13
- package/dist/i18n.module-Bm3xlViA.mjs.map +1 -0
- package/dist/index-0ItCjaqw.d.mts.map +1 -1
- package/dist/{index-CNuFQSNj.d.mts → index-3HJfhLlE.d.mts} +9 -11
- package/dist/index-3HJfhLlE.d.mts.map +1 -0
- package/dist/{index-uybm0bhQ.d.mts → index-B2WtEagR.d.mts} +1187 -234
- package/dist/index-B2WtEagR.d.mts.map +1 -0
- package/dist/index-BUt92sAE.d.mts.map +1 -1
- package/dist/index-CAHNCpvr.d.mts +203 -0
- package/dist/index-CAHNCpvr.d.mts.map +1 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/{is-command-CEPO9n8c.mjs → is-command-CSVv14wh.mjs} +2 -2
- package/dist/{is-command-CEPO9n8c.mjs.map → is-command-CSVv14wh.mjs.map} +1 -1
- package/dist/{is-seeder-Gvh_AM71.mjs → is-seeder-gLgXYbqC.mjs} +2 -2
- package/dist/is-seeder-gLgXYbqC.mjs.map +1 -0
- package/dist/{lazy-module-loader-M6YKudNL.d.mts → lazy-module-loader-C3OHoUnR.d.mts} +4 -5
- package/dist/lazy-module-loader-C3OHoUnR.d.mts.map +1 -0
- package/dist/limit-BE0HNAF6.mjs +116 -0
- package/dist/limit-BE0HNAF6.mjs.map +1 -0
- package/dist/locale-path.service-BqzDIrgy.mjs +213 -0
- package/dist/locale-path.service-BqzDIrgy.mjs.map +1 -0
- package/dist/locale-url-nZrZxqJP.mjs.map +1 -1
- package/dist/{locale-url.service-6bgia24_.mjs → locale-url.service-H8PH6GGh.mjs} +15 -5
- package/dist/{locale-url.service-6bgia24_.mjs.map → locale-url.service-H8PH6GGh.mjs.map} +1 -1
- package/dist/logger/index.mjs +3 -5
- package/dist/logger/index.mjs.map +1 -1
- package/dist/macroable-cvDTFZ_A.mjs.map +1 -1
- package/dist/metadata-Ba8Fxs1F.mjs +48 -0
- package/dist/metadata-Ba8Fxs1F.mjs.map +1 -0
- package/dist/module/index.d.mts +8 -9
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +3 -4
- package/dist/module/index.mjs.map +1 -1
- package/dist/{module-registry-NxX5O0Qk.mjs → module-registry-lkQPDuQe.mjs} +17 -15
- package/dist/module-registry-lkQPDuQe.mjs.map +1 -0
- package/dist/{module.decorator-CYHY6pG5.mjs → module.decorator-CYbrOlX9.mjs} +2 -2
- package/dist/{module.decorator-CYHY6pG5.mjs.map → module.decorator-CYbrOlX9.mjs.map} +1 -1
- package/dist/openapi/index.d.mts +19 -16
- package/dist/openapi/index.d.mts.map +1 -1
- package/dist/openapi/index.mjs +2 -2
- package/dist/{openapi-CMwuCp31.mjs → openapi-DBfBa5Qh.mjs} +41 -65
- package/dist/openapi-DBfBa5Qh.mjs.map +1 -0
- package/dist/openapi-generator-CCwiu8AU.mjs +134 -0
- package/dist/openapi-generator-CCwiu8AU.mjs.map +1 -0
- package/dist/{openapi-tools.service-BC5EC3R3.mjs → openapi-tools.service-VKZJlTIX.mjs} +12 -4
- package/dist/openapi-tools.service-VKZJlTIX.mjs.map +1 -0
- package/dist/openapi.service-Ds-R1tih.d.mts +43 -0
- package/dist/openapi.service-Ds-R1tih.d.mts.map +1 -0
- package/dist/quarry/index.d.mts +10 -11
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +4 -4
- package/dist/quarry/runner.d.mts +19 -20
- package/dist/quarry/runner.d.mts.map +1 -1
- package/dist/quarry/runner.mjs +16 -13
- package/dist/quarry/runner.mjs.map +1 -1
- package/dist/{quarry-registry-DRnV-DDa.d.mts → quarry-registry-Ci4k58FZ.d.mts} +3 -4
- package/dist/quarry-registry-Ci4k58FZ.d.mts.map +1 -0
- package/dist/{quarry.module-CcGxU2dJ.mjs → quarry.module-BssRR8f0.mjs} +20 -37
- package/dist/quarry.module-BssRR8f0.mjs.map +1 -0
- package/dist/queue/index.d.mts +2 -2
- package/dist/queue/index.mjs +3 -3
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-CEs4_kEM.mjs → queue.module-DnAdSbR-.mjs} +40 -41
- package/dist/queue.module-DnAdSbR-.mjs.map +1 -0
- package/dist/{r2-storage.provider-BoZmR6Ut.mjs → r2-storage.provider-wU_6api4.mjs} +45 -6
- package/dist/r2-storage.provider-wU_6api4.mjs.map +1 -0
- package/dist/{rate-limit.decorator-Djs4oYDB.mjs → rate-limit.decorator-PYQ9EYqs.mjs} +10 -5
- package/dist/{rate-limit.decorator-Djs4oYDB.mjs.map → rate-limit.decorator-PYQ9EYqs.mjs.map} +1 -1
- package/dist/rate-limiter/index.d.mts +64 -19
- package/dist/rate-limiter/index.d.mts.map +1 -1
- package/dist/rate-limiter/index.mjs +67 -83
- package/dist/rate-limiter/index.mjs.map +1 -1
- package/dist/response-cache/index.d.mts +326 -0
- package/dist/response-cache/index.d.mts.map +1 -0
- package/dist/response-cache/index.mjs +511 -0
- package/dist/response-cache/index.mjs.map +1 -0
- package/dist/response-cache.error-B4D0zy9W.mjs +24 -0
- package/dist/response-cache.error-B4D0zy9W.mjs.map +1 -0
- package/dist/response-headers-BqA57S_X.mjs +158 -0
- package/dist/response-headers-BqA57S_X.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/route-cache-binding-CrLh8tB6.mjs +390 -0
- package/dist/route-cache-binding-CrLh8tB6.mjs.map +1 -0
- package/dist/route-metadata-QQhbed1i.mjs +17 -0
- package/dist/route-metadata-QQhbed1i.mjs.map +1 -0
- package/dist/{route-name-DGoBOfPg.mjs → route-name-BslSbmMP.mjs} +15 -11
- package/dist/route-name-BslSbmMP.mjs.map +1 -0
- package/dist/route-registration.service-COKBZTsn.mjs +1265 -0
- package/dist/route-registration.service-COKBZTsn.mjs.map +1 -0
- package/dist/{route-registry-BvLJisvK.mjs → route-registry-DFz9_-N6.mjs} +6 -6
- package/dist/{route-registry-BvLJisvK.mjs.map → route-registry-DFz9_-N6.mjs.map} +1 -1
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +16 -15
- package/dist/{router-resolver-sUV_jTrU.mjs → router-resolver-B-dMSMIG.mjs} +7 -3
- package/dist/router-resolver-B-dMSMIG.mjs.map +1 -0
- package/dist/{router-DwyqEXgf.mjs → router-wlsVkGkM.mjs} +5 -14
- package/dist/{router-DwyqEXgf.mjs.map → router-wlsVkGkM.mjs.map} +1 -1
- package/dist/seeder/index.d.mts +12 -13
- package/dist/seeder/index.d.mts.map +1 -1
- package/dist/seeder/index.mjs +4 -4
- package/dist/{seeder-BPGY5rUb.mjs → seeder-BrAk7XIj.mjs} +5 -6
- package/dist/{seeder-BPGY5rUb.mjs.map → seeder-BrAk7XIj.mjs.map} +1 -1
- package/dist/{seeder-registry-DEvCycsT.mjs → seeder-registry-Bo5BgLbC.mjs} +5 -6
- package/dist/{seeder-registry-DEvCycsT.mjs.map → seeder-registry-Bo5BgLbC.mjs.map} +1 -1
- package/dist/{seeder.module-CIwQbdN4.mjs → seeder.module-DOoVgTsK.mjs} +5 -5
- package/dist/{seeder.module-CIwQbdN4.mjs.map → seeder.module-DOoVgTsK.mjs.map} +1 -1
- package/dist/signed-url-DIU0sK_6.mjs.map +1 -1
- package/dist/storage/index.d.mts +63 -13
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +2 -2
- package/dist/storage/providers/index.d.mts +18 -16
- package/dist/storage/providers/index.d.mts.map +1 -1
- package/dist/storage/providers/index.mjs +1 -1
- package/dist/{storage-C30X81CS.mjs → storage-DBWVYMnn.mjs} +167 -49
- package/dist/storage-DBWVYMnn.mjs.map +1 -0
- package/dist/storage-provider.interface-CinHpjl1.d.mts +315 -0
- package/dist/storage-provider.interface-CinHpjl1.d.mts.map +1 -0
- package/dist/storage.error-Ao38Qdjx.mjs +7 -0
- package/dist/storage.error-Ao38Qdjx.mjs.map +1 -0
- package/dist/{stratal-D5j_I14G.d.mts → stratal-Bs1_E8GQ.d.mts} +13 -3
- package/dist/stratal-Bs1_E8GQ.d.mts.map +1 -0
- package/dist/{stratal-BL6FKUM_.mjs → stratal-CiwNpWy2.mjs} +56 -23
- package/dist/stratal-CiwNpWy2.mjs.map +1 -0
- package/dist/{tiered-cache.service-Dv3BhxxE.d.mts → tiered-cache.service-Bz8bbvyb.d.mts} +16 -3
- package/dist/tiered-cache.service-Bz8bbvyb.d.mts.map +1 -0
- package/dist/trailing-slash-2SctvePW.mjs.map +1 -1
- package/dist/types-CmV_9xBD.d.mts.map +1 -1
- package/dist/{uri-iwofWJ_T.mjs → uri-BrOq148R.mjs} +13 -9
- package/dist/{uri-iwofWJ_T.mjs.map → uri-BrOq148R.mjs.map} +1 -1
- package/dist/{usage-generator-DAWYasuP.mjs → usage-generator-BW7EcO-C.mjs} +3 -3
- package/dist/{usage-generator-DAWYasuP.mjs.map → usage-generator-BW7EcO-C.mjs.map} +1 -1
- package/dist/{zod-eKqqhZ5_.mjs → validation-CZ_B0aXY.mjs} +38 -17
- package/dist/validation-CZ_B0aXY.mjs.map +1 -0
- package/dist/{validation.context-CRvmrhq7.mjs → validation.context-CdbwQWIr.mjs} +2 -2
- package/dist/{validation.context-CRvmrhq7.mjs.map → validation.context-CdbwQWIr.mjs.map} +1 -1
- package/dist/{versioning.service-CCa2oYMJ.mjs → versioning.service-DnF_4Lnd.mjs} +5 -5
- package/dist/{versioning.service-CCa2oYMJ.mjs.map → versioning.service-DnF_4Lnd.mjs.map} +1 -1
- package/dist/websocket/index.d.mts +12 -13
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +1 -1
- package/dist/workers/index.d.mts +70 -7
- package/dist/workers/index.d.mts.map +1 -1
- package/dist/workers/index.mjs +72 -3
- package/dist/workers/index.mjs.map +1 -1
- package/dist/{zod-wecrEVAs.d.mts → zod-BGisqLs3.d.mts} +9 -16
- package/dist/zod-BGisqLs3.d.mts.map +1 -0
- package/package.json +33 -25
- package/dist/cache.service-uElmBtdS.d.mts.map +0 -1
- package/dist/chunk-BBjsoOtd.mjs +0 -27
- package/dist/command-BvmUAPPQ.mjs.map +0 -1
- package/dist/command-DoBD2Cwl.d.mts.map +0 -1
- package/dist/consumer-registry-D3iMTSdy.d.mts.map +0 -1
- package/dist/container-storage-BmOJ4_Na.mjs.map +0 -1
- package/dist/controller.decorator-YSTPQntu.mjs.map +0 -1
- package/dist/cron-job-NesZRk8F.d.mts.map +0 -1
- package/dist/di-D7qmrAir.mjs.map +0 -1
- package/dist/errors-C01O2T-n.mjs.map +0 -1
- package/dist/events-BhEQuT1X.mjs.map +0 -1
- package/dist/exception-context-D-kvney-.mjs.map +0 -1
- package/dist/guards-DALPXy3_.mjs.map +0 -1
- package/dist/hono-app-COAgmutc.mjs +0 -168
- package/dist/hono-app-COAgmutc.mjs.map +0 -1
- package/dist/http-method.decorator-BljM8BDj.mjs.map +0 -1
- package/dist/i18n.module-B2DvWUPa.mjs.map +0 -1
- package/dist/index-CNuFQSNj.d.mts.map +0 -1
- package/dist/index-DtBNIFuP.d.mts +0 -42
- package/dist/index-DtBNIFuP.d.mts.map +0 -1
- package/dist/index-HgOLNruQ.d.mts +0 -203
- package/dist/index-HgOLNruQ.d.mts.map +0 -1
- package/dist/index-uybm0bhQ.d.mts.map +0 -1
- package/dist/is-seeder-Gvh_AM71.mjs.map +0 -1
- package/dist/lazy-module-loader-M6YKudNL.d.mts.map +0 -1
- package/dist/locale-path.service-CH0CaxwH.mjs +0 -165
- package/dist/locale-path.service-CH0CaxwH.mjs.map +0 -1
- package/dist/metadata-DzzprcID.mjs +0 -39
- package/dist/metadata-DzzprcID.mjs.map +0 -1
- package/dist/module-registry-NxX5O0Qk.mjs.map +0 -1
- package/dist/openapi-CMwuCp31.mjs.map +0 -1
- package/dist/openapi-tools.service-BC5EC3R3.mjs.map +0 -1
- package/dist/openapi.service-2rvJBCEg.d.mts +0 -50
- package/dist/openapi.service-2rvJBCEg.d.mts.map +0 -1
- package/dist/quarry-registry-DRnV-DDa.d.mts.map +0 -1
- package/dist/quarry.module-CcGxU2dJ.mjs.map +0 -1
- package/dist/queue.module-CEs4_kEM.mjs.map +0 -1
- package/dist/r2-storage.provider-BoZmR6Ut.mjs.map +0 -1
- package/dist/route-name-DGoBOfPg.mjs.map +0 -1
- package/dist/route-registration.service-CDPQKpm4.mjs +0 -918
- package/dist/route-registration.service-CDPQKpm4.mjs.map +0 -1
- package/dist/router-resolver-sUV_jTrU.mjs.map +0 -1
- package/dist/storage-C30X81CS.mjs.map +0 -1
- package/dist/storage-provider.interface-ClUwxz4S.d.mts +0 -201
- package/dist/storage-provider.interface-ClUwxz4S.d.mts.map +0 -1
- package/dist/storage.error-BStXPmO4.mjs +0 -8
- package/dist/storage.error-BStXPmO4.mjs.map +0 -1
- package/dist/stratal-BL6FKUM_.mjs.map +0 -1
- package/dist/stratal-D5j_I14G.d.mts.map +0 -1
- package/dist/tiered-cache.service-Dv3BhxxE.d.mts.map +0 -1
- package/dist/validation-CpOjviyT.mjs +0 -49
- package/dist/validation-CpOjviyT.mjs.map +0 -1
- package/dist/zod-eKqqhZ5_.mjs.map +0 -1
- package/dist/zod-wecrEVAs.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,6 @@ A modular framework for building Cloudflare Workers with dependency injection, O
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/stratal)
|
|
6
6
|
[](https://github.com/strataljs/stratal/actions/workflows/ci.yml)
|
|
7
|
-
[](https://github.com/strataljs/stratal/actions/workflows/benchmark.yml)
|
|
8
7
|
[](https://opensource.org/licenses/MIT)
|
|
9
8
|
[](https://securityscorecards.dev/viewer/?uri=github.com/strataljs/stratal)
|
|
10
9
|
[](https://snyk.io/test/github/strataljs/stratal)
|
|
@@ -20,18 +19,27 @@ Full guides and examples are available at **[stratal.dev](https://stratal.dev)**
|
|
|
20
19
|
|
|
21
20
|
## Features
|
|
22
21
|
|
|
23
|
-
- **Dependency Injection** — Two-tier DI container (global + request-scoped) with
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
22
|
+
- **Dependency Injection** — Two-tier DI container (global + request-scoped) with decorator-based injection
|
|
23
|
+
- **Modular Architecture** — NestJS-style modules with lifecycle hooks, dynamic configuration, and lazy loading
|
|
24
|
+
- **Routing** — Convention-based RESTful controllers on Hono, plus explicit method decorators, named routes, versioning, and domain routing
|
|
25
|
+
- **OpenAPI Documentation** — Define Zod schemas once and get an OpenAPI 3.0 spec with Swagger UI
|
|
26
|
+
- **Queues and Cron** — Typed Cloudflare Queue consumers with message-type filtering, and scheduled jobs via cron triggers
|
|
27
|
+
- **Events** — In-process and queue-backed event listeners with `@Listener` / `@On`
|
|
28
|
+
- **WebSockets and SSE** — Gateway classes with `@OnMessage` / `@OnClose` / `@OnError`, and server-sent event streams
|
|
29
|
+
- **Caching** — KV-backed cache with optional isolate-level L1 tiering, plus `@Cacheable` / `@PurgesCache` response caching on the Workers Cache API
|
|
30
|
+
- **Rate Limiting** — Declarative `@RateLimit` with named limiters
|
|
31
|
+
- **Storage** — Cloudflare R2 file storage with presigned URLs and resumable multipart uploads (TUS-compatible)
|
|
32
|
+
- **Email** — SMTP provider with React Email template support and queued delivery
|
|
31
33
|
- **i18n** — Type-safe internationalization with locale detection from request headers
|
|
34
|
+
- **Configuration** — Namespaced, validated config with typed dot-path access
|
|
32
35
|
- **Guards and Middleware** — Route protection and per-module middleware configuration
|
|
36
|
+
- **Seeders** — Ordered database seeders with `db:seed` commands
|
|
37
|
+
- **Quarry CLI** — Built-in commands for routes, queues, events, i18n and schedules — and `mcp:serve`, which exposes your API routes to an AI agent as MCP tools
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
## Requirements
|
|
40
|
+
|
|
41
|
+
- Node.js ≥ 22
|
|
42
|
+
- A Cloudflare Workers project
|
|
35
43
|
|
|
36
44
|
## Installation
|
|
37
45
|
|
|
@@ -58,7 +66,7 @@ Available templates:
|
|
|
58
66
|
| `scheduled-tasks` | Cron job scheduling with the CronJob interface | [Source](https://github.com/strataljs/examples/tree/main/07-scheduled-tasks) |
|
|
59
67
|
| `openapi` | OpenAPI docs with Swagger UI and Zod schema integration | [Source](https://github.com/strataljs/examples/tree/main/08-openapi) |
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
More runnable examples — seeders, events, auth, database, access control, factories, Workers bindings, commands and Inertia — live at [strataljs/examples](https://github.com/strataljs/examples).
|
|
62
70
|
|
|
63
71
|
You can also specify a template directly:
|
|
64
72
|
|
|
@@ -82,7 +90,7 @@ npx skills add strataljs/stratal
|
|
|
82
90
|
|
|
83
91
|
| Skill | Description |
|
|
84
92
|
|---|---|
|
|
85
|
-
| `stratal` | Build Cloudflare Workers apps with the Stratal framework — modules, DI, controllers, routing, OpenAPI, queues, cron, events, seeders, CLI, auth, database,
|
|
93
|
+
| `stratal` | Build Cloudflare Workers apps with the Stratal framework — modules, DI, controllers, routing, OpenAPI, queues, cron, events, seeders, CLI, auth, database, access control, testing, and more |
|
|
86
94
|
|
|
87
95
|
## Quick Start
|
|
88
96
|
|
|
@@ -92,14 +100,14 @@ Define a module with a controller and wire it up as a Cloudflare Worker:
|
|
|
92
100
|
import { Stratal } from 'stratal'
|
|
93
101
|
import { Module } from 'stratal/module'
|
|
94
102
|
import { Controller, Route, type RouterContext } from 'stratal/router'
|
|
95
|
-
import {
|
|
103
|
+
import { object, string } from 'zod/mini'
|
|
96
104
|
|
|
97
105
|
// Define a controller
|
|
98
106
|
@Controller('/api/greetings')
|
|
99
107
|
class GreetingsController {
|
|
100
108
|
@Route({
|
|
101
109
|
summary: 'Say hello',
|
|
102
|
-
response:
|
|
110
|
+
response: object({ message: string() }),
|
|
103
111
|
})
|
|
104
112
|
async index(ctx: RouterContext) {
|
|
105
113
|
return ctx.json({ message: 'Hello from Stratal!' })
|
|
@@ -116,10 +124,84 @@ class AppModule {}
|
|
|
116
124
|
export default new Stratal({ module: AppModule })
|
|
117
125
|
```
|
|
118
126
|
|
|
127
|
+
### Routing conventions
|
|
128
|
+
|
|
129
|
+
`@Route()` derives the HTTP method, path and status code from the method name:
|
|
130
|
+
|
|
131
|
+
| Method | HTTP | Path | Status |
|
|
132
|
+
|---|---|---|---|
|
|
133
|
+
| `index()` | GET | `/base-path` | 200 |
|
|
134
|
+
| `show()` | GET | `/base-path/:id` | 200 |
|
|
135
|
+
| `create()` | POST | `/base-path` | 201 |
|
|
136
|
+
| `update()` | PUT | `/base-path/:id` | 200 |
|
|
137
|
+
| `patch()` | PATCH | `/base-path/:id` | 200 |
|
|
138
|
+
| `destroy()` | DELETE | `/base-path/:id` | 200 |
|
|
139
|
+
|
|
140
|
+
When the convention doesn't fit, use the explicit decorators instead — `@Get()`, `@Post()`, `@Put()`, `@Patch()`, `@Delete()`, `@All()`. A controller uses one style or the other, not both.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { Controller, Get, Post, type RouterContext } from 'stratal/router'
|
|
144
|
+
import { object, string, uuid } from 'zod/mini'
|
|
145
|
+
|
|
146
|
+
@Controller('/notes', { tags: ['Notes'], version: '1' })
|
|
147
|
+
export class NotesController {
|
|
148
|
+
@Get('/:id', { params: object({ id: uuid() }), response: noteSchema })
|
|
149
|
+
async getNote(ctx: RouterContext) {
|
|
150
|
+
return ctx.json(await this.notes.find(ctx.param('id')))
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@Post('/', { body: createNoteSchema, response: noteSchema, statusCode: 201 })
|
|
154
|
+
async createNote(ctx: RouterContext) {
|
|
155
|
+
return ctx.json(await this.notes.create(await ctx.body()), 201)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Versioning
|
|
161
|
+
|
|
162
|
+
Don't hard-code the version into the path. Configure it once and the router builds the prefixed paths for you:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
export default new Stratal({
|
|
166
|
+
module: AppModule,
|
|
167
|
+
versioning: { prefix: 'api/v', defaultVersion: '1' },
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
@Controller('/notes', { version: '1' }) // → /api/v1/notes
|
|
173
|
+
@Controller('/notes', { version: ['1', '2'] }) // → /api/v1/notes and /api/v2/notes
|
|
174
|
+
@Controller('/health', { version: VERSION_NEUTRAL }) // → /health, unprefixed
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
A controller with no `version` picks up `defaultVersion`. `router.version('2')` in a module's `configureRoutes()` applies a version to every controller in that module.
|
|
178
|
+
|
|
179
|
+
### Validation
|
|
180
|
+
|
|
181
|
+
Schemas come from `zod/mini` directly — named imports keep them tree-shakeable. `stratal/validation` adds the framework's own helpers on top (schema metadata, localized error messages, and shared types):
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { describe, named, cuid2 } from 'stratal/validation'
|
|
185
|
+
import { object, string } from 'zod/mini'
|
|
186
|
+
|
|
187
|
+
const noteSchema = named(object({
|
|
188
|
+
id: cuid2(),
|
|
189
|
+
title: describe(string(), 'Note title'),
|
|
190
|
+
}), 'Note')
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Support the project
|
|
194
|
+
|
|
195
|
+
If Stratal is useful to you, **[star the repository](https://github.com/strataljs/stratal)** — it is the simplest way to help others find it.
|
|
196
|
+
|
|
119
197
|
## Contributing
|
|
120
198
|
|
|
121
199
|
Contributions are welcome! Please see [CONTRIBUTING.md](https://github.com/strataljs/stratal/blob/main/CONTRIBUTING.md) for guidelines.
|
|
122
200
|
|
|
201
|
+
## Maintainer
|
|
202
|
+
|
|
203
|
+
Built and maintained by **Temitayo Fadojutimi** — [@adesege_](https://x.com/adesege_).
|
|
204
|
+
|
|
123
205
|
## License
|
|
124
206
|
|
|
125
207
|
MIT
|
|
@@ -31,7 +31,9 @@ async function resolve(specifier, context, nextResolve) {
|
|
|
31
31
|
}
|
|
32
32
|
async function load(url, context, nextLoad) {
|
|
33
33
|
if (url.endsWith(RAW_SUFFIX)) {
|
|
34
|
-
const
|
|
34
|
+
const fileUrl = url.slice(0, -4);
|
|
35
|
+
const filePath = fileURLToPath(fileUrl);
|
|
36
|
+
const content = readFileSync(filePath, "utf-8");
|
|
35
37
|
return {
|
|
36
38
|
format: "module",
|
|
37
39
|
shortCircuit: true,
|
|
@@ -112,7 +114,7 @@ export function connect(address, options) {
|
|
|
112
114
|
|
|
113
115
|
const closedPromise = new Promise((resolve) => currentSocket.on('close', resolve));
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
const socket = {
|
|
116
118
|
readable,
|
|
117
119
|
writable,
|
|
118
120
|
startTls() {
|
|
@@ -124,10 +126,23 @@ export function connect(address, options) {
|
|
|
124
126
|
tlsSocket.on('data', dataHandler);
|
|
125
127
|
tlsSocket.on('end', endHandler);
|
|
126
128
|
tlsSocket.on('error', errorHandler);
|
|
129
|
+
// CF's Socket.startTls() returns a brand-new Socket. The same readable/writable
|
|
130
|
+
// stay valid because they read from / write to the reassigned currentSocket, so
|
|
131
|
+
// returning this object preserves the caller's re-derived reader/writer.
|
|
132
|
+
return socket;
|
|
133
|
+
},
|
|
134
|
+
// CF's Socket.close() returns Promise<void>. Resolve once the socket is closed
|
|
135
|
+
// (or immediately if already destroyed) so callers can await socket.close().
|
|
136
|
+
close() {
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
if (currentSocket.destroyed) { resolve(); return; }
|
|
139
|
+
currentSocket.once('close', () => resolve());
|
|
140
|
+
currentSocket.destroy();
|
|
141
|
+
});
|
|
127
142
|
},
|
|
128
|
-
close() { currentSocket.destroy(); },
|
|
129
143
|
closed: closedPromise,
|
|
130
144
|
};
|
|
145
|
+
return socket;
|
|
131
146
|
}
|
|
132
147
|
`
|
|
133
148
|
};
|
|
@@ -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 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
|
|
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 const socket = {\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 // CF's Socket.startTls() returns a brand-new Socket. The same readable/writable\n // stay valid because they read from / write to the reassigned currentSocket, so\n // returning this object preserves the caller's re-derived reader/writer.\n return socket;\n },\n // CF's Socket.close() returns Promise<void>. Resolve once the socket is closed\n // (or immediately if already destroyed) so callers can await socket.close().\n close() {\n return new Promise((resolve) => {\n if (currentSocket.destroyed) { resolve(); return; }\n currentSocket.once('close', () => resolve());\n currentSocket.destroy();\n });\n },\n closed: closedPromise,\n };\n return socket;\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,EAAA,CACzB,MAAM;EAAY,cAAc;CAAK;CAE9D,OAAO,YAAY,WAAW,OAAO;AACvC;AAeA,eAAsB,KACpB,KACA,SACA,UACqB;CACrB,IAAI,IAAI,SAAS,UAAU,GAAG;EAC5B,MAAM,UAAU,IAAI,MAAM,GAAG,EAAkB;EAC/C,MAAM,WAAW,cAAc,OAAO;EACtC,MAAM,UAAU,aAAa,UAAU,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4EV;CAEF,OAAO,SAAS,KAAK,OAAO;AAC9B"}
|
package/dist/bin/quarry.mjs
CHANGED
|
@@ -60,10 +60,7 @@ function createDynamicCommands(quarry, parseSignature, app) {
|
|
|
60
60
|
const value = this[opt.name];
|
|
61
61
|
if (value !== void 0) input[opt.name] = value;
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
for (const line of result.output) this.context.stdout.write(line + "\n");
|
|
65
|
-
for (const err of result.errors) this.context.stderr.write(err + "\n");
|
|
66
|
-
return result.exitCode;
|
|
63
|
+
return (await app.handleCommand(entry.name, input)).exitCode;
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
66
|
const proto = DynCmd.prototype;
|
|
@@ -82,9 +79,10 @@ function createDynamicCommands(quarry, parseSignature, app) {
|
|
|
82
79
|
if (opt.default !== void 0) optDescParts.push(`(default: ${opt.default})`);
|
|
83
80
|
const optDesc = optDescParts.length > 0 ? optDescParts.join(" ") : void 0;
|
|
84
81
|
if (opt.isFlag) proto[opt.name] = Option.Boolean(optName, { description: optDesc });
|
|
85
|
-
else if (opt.isArray)
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
else if (opt.isArray) {
|
|
83
|
+
if (opt.default !== void 0) proto[opt.name] = Option.Array(optName, [opt.default], { description: optDesc });
|
|
84
|
+
else proto[opt.name] = Option.Array(optName, { description: optDesc });
|
|
85
|
+
} else if (opt.default !== void 0) proto[opt.name] = Option.String(optName, opt.default, { description: optDesc });
|
|
88
86
|
else proto[opt.name] = Option.String(optName, { description: optDesc });
|
|
89
87
|
}
|
|
90
88
|
commands.push(DynCmd);
|
|
@@ -92,6 +90,36 @@ function createDynamicCommands(quarry, parseSignature, app) {
|
|
|
92
90
|
return commands;
|
|
93
91
|
}
|
|
94
92
|
//#endregion
|
|
93
|
+
//#region src/bin/module-rules.ts
|
|
94
|
+
/**
|
|
95
|
+
* Remove `modulesRules` from a sourceless quarry worker's Miniflare options,
|
|
96
|
+
* returning whether anything was removed.
|
|
97
|
+
*
|
|
98
|
+
* `modulesRules` tells Miniflare how to interpret files it pulls off disk when
|
|
99
|
+
* it assembles a worker's module graph (`.sql` as Text, `.wasm` as CompiledWasm,
|
|
100
|
+
* and so on). The quarry CLI host has no module graph: it runs an
|
|
101
|
+
* empty script (`script: ''`) purely to own a worker's bindings for command code
|
|
102
|
+
* executing in Node, so no file is ever matched against a rule and the option
|
|
103
|
+
* cannot change how this host behaves.
|
|
104
|
+
*
|
|
105
|
+
* It has to be dropped rather than passed through. Miniflare 5 replaced the flat
|
|
106
|
+
* v4 worker shape with a `workers` array, and its `convertV4MiniflareOptions()`
|
|
107
|
+
* bridge refuses any input carrying `modulesRules` — it has no v5 equivalent to
|
|
108
|
+
* map onto, so converting would silently lose behaviour and it throws instead.
|
|
109
|
+
* Wrangler's `unstable_getMiniflareWorkerOptions()` always emits the key
|
|
110
|
+
* (`config.rules` concatenated with its own defaults, so never empty), which
|
|
111
|
+
* means every quarry boot on wrangler ≥4.124 hits that guard and dies before the
|
|
112
|
+
* command runs.
|
|
113
|
+
*
|
|
114
|
+
* Dropping it is only sound *because* the host is sourceless — a worker that
|
|
115
|
+
* actually loads modules would need these rules honoured, not discarded.
|
|
116
|
+
*/
|
|
117
|
+
function stripModuleRules(workerOptions) {
|
|
118
|
+
if (workerOptions.modulesRules === void 0) return false;
|
|
119
|
+
delete workerOptions.modulesRules;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
95
123
|
//#region src/bin/registry.ts
|
|
96
124
|
const LOCKFILES = [
|
|
97
125
|
"yarn.lock",
|
|
@@ -145,9 +173,38 @@ function resolveDevRegistryPath(env, cwd) {
|
|
|
145
173
|
return join(findProjectRoot(cwd) ?? cwd, ".wrangler", "registry");
|
|
146
174
|
}
|
|
147
175
|
//#endregion
|
|
176
|
+
//#region src/bin/workflow-bindings.ts
|
|
177
|
+
/**
|
|
178
|
+
* Remove Workflow bindings from a sourceless quarry worker's Miniflare options,
|
|
179
|
+
* returning the names removed so the caller can surface them.
|
|
180
|
+
*
|
|
181
|
+
* The quarry CLI worker runs an empty script (`script: ''`): it exists to host a
|
|
182
|
+
* worker's bindings for command code running in Node, but defines no worker
|
|
183
|
+
* module. A Workflow's engine binds to the `class_name` entrypoint of the worker
|
|
184
|
+
* that declares it (Miniflare wires `USER_WORKFLOW` as a service binding to
|
|
185
|
+
* `core:user:<scriptName>`, validated at boot), and an empty script exposes no
|
|
186
|
+
* such entrypoint — so the runtime refuses to start.
|
|
187
|
+
*
|
|
188
|
+
* Redirecting the binding cross-script to the defining worker does not help in
|
|
189
|
+
* local development: the quarry host boots its own Miniflare instance *before*
|
|
190
|
+
* that worker exists (a worker's `dev` script boots the quarry host, then spawns
|
|
191
|
+
* the worker), so the cross-script service is undefined at boot. Workflows also
|
|
192
|
+
* cannot be remote bindings. A sourceless host therefore cannot run or reach a
|
|
193
|
+
* Workflow; the binding is dropped here. Triggering a Workflow must happen from
|
|
194
|
+
* the worker that defines it (e.g. an HTTP/queue handler), not the CLI host.
|
|
195
|
+
*/
|
|
196
|
+
function stripWorkflowBindings(workerOptions) {
|
|
197
|
+
const workflows = workerOptions.workflows;
|
|
198
|
+
if (!workflows) return [];
|
|
199
|
+
const names = Object.keys(workflows);
|
|
200
|
+
delete workerOptions.workflows;
|
|
201
|
+
return names;
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
148
204
|
//#region src/bin/quarry.ts
|
|
149
205
|
const require = createRequire(import.meta.url);
|
|
150
|
-
|
|
206
|
+
const swcRegisterPath = join(dirname(require.resolve("@swc-node/register")), "esm/esm.mjs");
|
|
207
|
+
register(pathToFileURL(swcRegisterPath), pathToFileURL("./"));
|
|
151
208
|
register(new URL("./cloudflare-workers-loader.mjs", import.meta.url), pathToFileURL("./"));
|
|
152
209
|
const DEFAULT_ENTRY = "./src/quarry.ts";
|
|
153
210
|
let environment;
|
|
@@ -175,8 +232,8 @@ if (!existsSync(entryPath)) {
|
|
|
175
232
|
}
|
|
176
233
|
async function main() {
|
|
177
234
|
const cwdRequire = createRequire(join(process.cwd(), "package.json"));
|
|
178
|
-
const { unstable_readConfig: readConfig, unstable_getMiniflareWorkerOptions: getMiniflareWorkerOptions, unstable_getVarsForDev: getVarsForDev } = await import(cwdRequire.resolve("wrangler"));
|
|
179
|
-
const { Miniflare } = await import(cwdRequire.resolve("miniflare"));
|
|
235
|
+
const { unstable_readConfig: readConfig, unstable_getMiniflareWorkerOptions: getMiniflareWorkerOptions, unstable_getVarsForDev: getVarsForDev, maybeStartOrUpdateRemoteProxySession } = await import(cwdRequire.resolve("wrangler"));
|
|
236
|
+
const { Miniflare, convertV4MiniflareOptions } = await import(cwdRequire.resolve("miniflare"));
|
|
180
237
|
const configName = [
|
|
181
238
|
"wrangler.jsonc",
|
|
182
239
|
"wrangler.json",
|
|
@@ -203,7 +260,17 @@ async function main() {
|
|
|
203
260
|
config: configPath,
|
|
204
261
|
env: environment
|
|
205
262
|
});
|
|
206
|
-
const
|
|
263
|
+
const remoteProxy = configPath ? await maybeStartOrUpdateRemoteProxySession({
|
|
264
|
+
path: configPath,
|
|
265
|
+
environment
|
|
266
|
+
}) : null;
|
|
267
|
+
if (remoteProxy) {
|
|
268
|
+
await remoteProxy.session.ready;
|
|
269
|
+
const remoteNames = Object.keys(remoteProxy.remoteBindings);
|
|
270
|
+
console.log(`Remote bindings: proxying ${remoteNames.length} binding(s) to deployed resources (${remoteNames.join(", ")})`);
|
|
271
|
+
}
|
|
272
|
+
const { workerOptions } = getMiniflareWorkerOptions(config, environment, { remoteProxyConnectionString: remoteProxy?.session.remoteProxyConnectionString });
|
|
273
|
+
process.env.CLOUDFLARE_INCLUDE_PROCESS_ENV = "true";
|
|
207
274
|
const vars = getVarsForDev(configPath, void 0, config.vars, environment);
|
|
208
275
|
const varsRecord = {};
|
|
209
276
|
for (const [key, binding] of Object.entries(vars)) varsRecord[key] = binding.value;
|
|
@@ -212,14 +279,17 @@ async function main() {
|
|
|
212
279
|
...varsRecord,
|
|
213
280
|
QUEUE_PROVIDER: "sync"
|
|
214
281
|
};
|
|
282
|
+
const strippedWorkflows = stripWorkflowBindings(workerOptions);
|
|
283
|
+
if (strippedWorkflows.length > 0) console.log(`Workflow binding(s) unavailable to CLI commands (defined on the worker, not this host): ${strippedWorkflows.join(", ")}`);
|
|
215
284
|
workerOptions.name = config.name ? `quarry-${config.name}-${process.pid}` : `quarry-${process.pid}`;
|
|
216
|
-
|
|
285
|
+
stripModuleRules(workerOptions);
|
|
286
|
+
const mf = new Miniflare(convertV4MiniflareOptions({
|
|
217
287
|
...workerOptions,
|
|
218
288
|
script: "",
|
|
219
289
|
modules: true,
|
|
220
290
|
unsafeDevRegistryPath: registryPath,
|
|
221
|
-
|
|
222
|
-
});
|
|
291
|
+
resourcePersistencePath: join(process.cwd(), ".wrangler/state/v3")
|
|
292
|
+
}));
|
|
223
293
|
await mf.ready;
|
|
224
294
|
const env = await mf.getBindings();
|
|
225
295
|
const pendingPromises = [];
|
|
@@ -252,6 +322,7 @@ async function main() {
|
|
|
252
322
|
await Promise.allSettled(pendingPromises);
|
|
253
323
|
await app?.shutdown();
|
|
254
324
|
await mf.dispose();
|
|
325
|
+
await remoteProxy?.session.dispose();
|
|
255
326
|
}
|
|
256
327
|
}
|
|
257
328
|
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/registry.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 } from 'node:fs'\nimport { dirname, join, parse, resolve } from 'node:path'\n\nconst LOCKFILES = [\n 'yarn.lock',\n 'package-lock.json',\n 'pnpm-lock.yaml',\n 'bun.lock',\n 'bun.lockb',\n]\n\n/**\n * Walk up from `startDir` to the project root.\n *\n * The root is the nearest ancestor containing a package-manager lockfile —\n * lockfiles exist only at install roots, so the marker is immune to nested\n * workspace `package.json`s and to stray `.git` directories tools sometimes\n * leave behind. When no lockfile exists (e.g. a project before its first\n * install), the outermost ancestor containing a `package.json` is used —\n * every project has one, monorepo or not, git or not.\n *\n * Returns `undefined` when neither marker is found.\n */\nexport function findProjectRoot(startDir: string): string | undefined {\n // A relative `startDir` would never equal its parsed root (`''`) — `dirname`\n // converges to `'.'` and the walk never terminates. Resolve up front so the\n // traversal (and the returned path) is always absolute.\n let dir = resolve(startDir)\n const { root } = parse(dir)\n let outermostPackageJson: string | undefined\n while (true) {\n if (LOCKFILES.some(lockfile => existsSync(join(dir, lockfile)))) return dir\n if (existsSync(join(dir, 'package.json'))) outermostPackageJson = dir\n if (dir === root) return outermostPackageJson\n dir = dirname(dir)\n }\n}\n\nexport interface DevRegistryEnv {\n MINIFLARE_REGISTRY_PATH?: string\n WRANGLER_REGISTRY_PATH?: string\n}\n\n/**\n * Resolve the Miniflare dev-registry path for a quarry run.\n *\n * Precedence:\n * 1. an explicit `MINIFLARE_REGISTRY_PATH` (the variable Miniflare actually\n * reads) or `WRANGLER_REGISTRY_PATH` (the variable `wrangler dev` reads —\n * honoured here too so one override redirects every tool the same way)\n * 2. `<projectRoot>/.wrangler/registry` (see `findProjectRoot`) — so every\n * process in a checkout (dev servers, quarry CLI runs) deterministically\n * shares one registry, and parallel checkouts (git worktrees) each get\n * their own.\n *\n * The global `~/.wrangler/registry` is deliberately never used: a registry\n * shared across checkouts lets identically-named workers from parallel dev\n * environments overwrite each other and breaks service-binding resolution.\n */\nexport function resolveDevRegistryPath(env: DevRegistryEnv, cwd: string): string {\n const explicit = env.MINIFLARE_REGISTRY_PATH ?? env.WRANGLER_REGISTRY_PATH\n if (explicit) return explicit\n return join(findProjectRoot(cwd) ?? cwd, '.wrangler', 'registry')\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';\nimport { resolveDevRegistryPath } from './registry';\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}\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 } = 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 // Deterministically resolve the dev service registry from the project root\n // (`<projectRoot>/.wrangler/registry`) so a bare `quarry` invocation shares\n // the SAME project-local registry every other process in this checkout uses —\n // never the global `~/.wrangler/registry`, where parallel checkouts'\n // identically-named workers would overwrite each other and break cross-worker\n // service-binding resolution. An explicit `WRANGLER_REGISTRY_PATH` /\n // `MINIFLARE_REGISTRY_PATH` (from the shell or the `.env` files above) wins.\n //\n // Exported as `MINIFLARE_REGISTRY_PATH` — the variable Miniflare's\n // `getDefaultDevRegistryPath()` reads at call time — so any child process a\n // command spawns (notably the vite dev server launched by `inertia:dev`,\n // whose `@cloudflare/vite-plugin` resolves its registry the same way) lands\n // in the identical registry.\n const registryPath = resolveDevRegistryPath({\n MINIFLARE_REGISTRY_PATH: process.env.MINIFLARE_REGISTRY_PATH,\n WRANGLER_REGISTRY_PATH: process.env.WRANGLER_REGISTRY_PATH,\n }, process.cwd())\n process.env.MINIFLARE_REGISTRY_PATH = registryPath\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 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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"}
|
|
1
|
+
{"version":3,"file":"quarry.mjs","names":[],"sources":["../../src/bin/argv.ts","../../src/bin/commands/dynamic-command.ts","../../src/bin/module-rules.ts","../../src/bin/registry.ts","../../src/bin/workflow-bindings.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 // Output and errors stream live via the command's helpers\n // (Command.emit/emitError) and via quarry.call on failure, so there is\n // nothing to flush here — just return the exit code.\n const result = await app.handleCommand(entry.name, input)\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","/**\n * Remove `modulesRules` from a sourceless quarry worker's Miniflare options,\n * returning whether anything was removed.\n *\n * `modulesRules` tells Miniflare how to interpret files it pulls off disk when\n * it assembles a worker's module graph (`.sql` as Text, `.wasm` as CompiledWasm,\n * and so on). The quarry CLI host has no module graph: it runs an\n * empty script (`script: ''`) purely to own a worker's bindings for command code\n * executing in Node, so no file is ever matched against a rule and the option\n * cannot change how this host behaves.\n *\n * It has to be dropped rather than passed through. Miniflare 5 replaced the flat\n * v4 worker shape with a `workers` array, and its `convertV4MiniflareOptions()`\n * bridge refuses any input carrying `modulesRules` — it has no v5 equivalent to\n * map onto, so converting would silently lose behaviour and it throws instead.\n * Wrangler's `unstable_getMiniflareWorkerOptions()` always emits the key\n * (`config.rules` concatenated with its own defaults, so never empty), which\n * means every quarry boot on wrangler ≥4.124 hits that guard and dies before the\n * command runs.\n *\n * Dropping it is only sound *because* the host is sourceless — a worker that\n * actually loads modules would need these rules honoured, not discarded.\n */\nexport function stripModuleRules(workerOptions: Record<string, unknown>): boolean {\n if (workerOptions.modulesRules === undefined) return false\n delete workerOptions.modulesRules\n return true\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join, parse, resolve } from 'node:path'\n\nconst LOCKFILES = [\n 'yarn.lock',\n 'package-lock.json',\n 'pnpm-lock.yaml',\n 'bun.lock',\n 'bun.lockb',\n]\n\n/**\n * Walk up from `startDir` to the project root.\n *\n * The root is the nearest ancestor containing a package-manager lockfile —\n * lockfiles exist only at install roots, so the marker is immune to nested\n * workspace `package.json`s and to stray `.git` directories tools sometimes\n * leave behind. When no lockfile exists (e.g. a project before its first\n * install), the outermost ancestor containing a `package.json` is used —\n * every project has one, monorepo or not, git or not.\n *\n * Returns `undefined` when neither marker is found.\n */\nexport function findProjectRoot(startDir: string): string | undefined {\n // A relative `startDir` would never equal its parsed root (`''`) — `dirname`\n // converges to `'.'` and the walk never terminates. Resolve up front so the\n // traversal (and the returned path) is always absolute.\n let dir = resolve(startDir)\n const { root } = parse(dir)\n let outermostPackageJson: string | undefined\n while (true) {\n if (LOCKFILES.some(lockfile => existsSync(join(dir, lockfile)))) return dir\n if (existsSync(join(dir, 'package.json'))) outermostPackageJson = dir\n if (dir === root) return outermostPackageJson\n dir = dirname(dir)\n }\n}\n\nexport interface DevRegistryEnv {\n MINIFLARE_REGISTRY_PATH?: string\n WRANGLER_REGISTRY_PATH?: string\n}\n\n/**\n * Resolve the Miniflare dev-registry path for a quarry run.\n *\n * Precedence:\n * 1. an explicit `MINIFLARE_REGISTRY_PATH` (the variable Miniflare actually\n * reads) or `WRANGLER_REGISTRY_PATH` (the variable `wrangler dev` reads —\n * honoured here too so one override redirects every tool the same way)\n * 2. `<projectRoot>/.wrangler/registry` (see `findProjectRoot`) — so every\n * process in a checkout (dev servers, quarry CLI runs) deterministically\n * shares one registry, and parallel checkouts (git worktrees) each get\n * their own.\n *\n * The global `~/.wrangler/registry` is deliberately never used: a registry\n * shared across checkouts lets identically-named workers from parallel dev\n * environments overwrite each other and breaks service-binding resolution.\n */\nexport function resolveDevRegistryPath(env: DevRegistryEnv, cwd: string): string {\n const explicit = env.MINIFLARE_REGISTRY_PATH ?? env.WRANGLER_REGISTRY_PATH\n if (explicit) return explicit\n return join(findProjectRoot(cwd) ?? cwd, '.wrangler', 'registry')\n}\n","/**\n * Remove Workflow bindings from a sourceless quarry worker's Miniflare options,\n * returning the names removed so the caller can surface them.\n *\n * The quarry CLI worker runs an empty script (`script: ''`): it exists to host a\n * worker's bindings for command code running in Node, but defines no worker\n * module. A Workflow's engine binds to the `class_name` entrypoint of the worker\n * that declares it (Miniflare wires `USER_WORKFLOW` as a service binding to\n * `core:user:<scriptName>`, validated at boot), and an empty script exposes no\n * such entrypoint — so the runtime refuses to start.\n *\n * Redirecting the binding cross-script to the defining worker does not help in\n * local development: the quarry host boots its own Miniflare instance *before*\n * that worker exists (a worker's `dev` script boots the quarry host, then spawns\n * the worker), so the cross-script service is undefined at boot. Workflows also\n * cannot be remote bindings. A sourceless host therefore cannot run or reach a\n * Workflow; the binding is dropped here. Triggering a Workflow must happen from\n * the worker that defines it (e.g. an HTTP/queue handler), not the CLI host.\n */\nexport function stripWorkflowBindings(workerOptions: Record<string, unknown>): string[] {\n const workflows = workerOptions.workflows as Record<string, unknown> | undefined\n if (!workflows) return []\n const names = Object.keys(workflows)\n delete workerOptions.workflows\n return names\n}\n","import type { MiniflareOptions, V4MiniflareOptions } 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';\nimport { stripModuleRules } from './module-rules';\nimport { resolveDevRegistryPath } from './registry';\nimport { stripWorkflowBindings } from './workflow-bindings';\n\ninterface WranglerConfig {\n name?: string\n vars?: Record<string, unknown>\n}\n\ninterface MiniflareWorkerResult {\n workerOptions: Record<string, unknown>\n}\n\ninterface RemoteProxySessionData {\n session: {\n ready: Promise<unknown>\n dispose: () => Promise<void>\n remoteProxyConnectionString: unknown\n }\n remoteBindings: Record<string, unknown>\n}\n\ninterface WranglerModule {\n unstable_readConfig: (args: { config?: string; env?: string }) => WranglerConfig\n unstable_getMiniflareWorkerOptions: (config: WranglerConfig, env?: string, options?: { remoteProxyConnectionString?: unknown }) => MiniflareWorkerResult\n unstable_getVarsForDev: (configPath: string | undefined, envFiles: undefined, vars: unknown, env: string | undefined) => Record<string, { value: string }>\n maybeStartOrUpdateRemoteProxySession: (config: { path: string; environment?: string }) => Promise<RemoteProxySessionData | null>\n}\n\ninterface MiniflareModule {\n Miniflare: new (opts: MiniflareOptions) => { ready: Promise<URL>; getBindings: (name?: string) => Promise<Record<string, unknown>>; dispose: () => Promise<void> }\n // Miniflare 5 takes a `workers` array; wrangler's `unstable_getMiniflareWorkerOptions`\n // still returns the flat v4 shape, so this converter is the supported bridge\n // between them — see the `new Miniflare(...)` call below.\n convertV4MiniflareOptions: (opts: V4MiniflareOptions) => MiniflareOptions\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, maybeStartOrUpdateRemoteProxySession } = await import(cwdRequire.resolve('wrangler')) as WranglerModule\n const { Miniflare, convertV4MiniflareOptions } = 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 // Deterministically resolve the dev service registry from the project root\n // (`<projectRoot>/.wrangler/registry`) so a bare `quarry` invocation shares\n // the SAME project-local registry every other process in this checkout uses —\n // never the global `~/.wrangler/registry`, where parallel checkouts'\n // identically-named workers would overwrite each other and break cross-worker\n // service-binding resolution. An explicit `WRANGLER_REGISTRY_PATH` /\n // `MINIFLARE_REGISTRY_PATH` (from the shell or the `.env` files above) wins.\n //\n // Exported as `MINIFLARE_REGISTRY_PATH` — the variable Miniflare's\n // `getDefaultDevRegistryPath()` reads at call time — so any child process a\n // command spawns (notably the vite dev server launched by `inertia:dev`,\n // whose `@cloudflare/vite-plugin` resolves its registry the same way) lands\n // in the identical registry.\n const registryPath = resolveDevRegistryPath({\n MINIFLARE_REGISTRY_PATH: process.env.MINIFLARE_REGISTRY_PATH,\n WRANGLER_REGISTRY_PATH: process.env.WRANGLER_REGISTRY_PATH,\n }, process.cwd())\n process.env.MINIFLARE_REGISTRY_PATH = registryPath\n\n const config = readConfig({ config: configPath, env: environment })\n\n // Honor `remote: true` bindings (wrangler remote bindings): start a proxy\n // session against the deployed Cloudflare resources and thread its\n // connection string into the Miniflare worker options, so e.g. a service\n // binding marked remote calls the deployed worker instead of the local dev\n // registry. Returns null when the resolved config declares no remote\n // bindings — plain local runs are untouched. Requires wrangler credentials\n // (`wrangler login` / CLOUDFLARE_API_TOKEN) when a session does start.\n const remoteProxy = configPath\n ? await maybeStartOrUpdateRemoteProxySession({ path: configPath, environment })\n : null\n if (remoteProxy) {\n await remoteProxy.session.ready\n const remoteNames = Object.keys(remoteProxy.remoteBindings)\n console.log(`Remote bindings: proxying ${remoteNames.length} binding(s) to deployed resources (${remoteNames.join(', ')})`)\n }\n\n const { workerOptions } = getMiniflareWorkerOptions(config, environment, {\n remoteProxyConnectionString: remoteProxy?.session.remoteProxyConnectionString,\n })\n\n // Always source `process.env` into the worker vars/secrets — quarry is a CLI/CI tool, so config passed\n // through the environment must resolve like any other binding. `getVarsForDev` (and the vite build\n // quarry spawns for `inertia:build`, which inherits this process env) only merges `process.env` when\n // `CLOUDFLARE_INCLUDE_PROCESS_ENV` is set, and it defaults OFF — and only when no `.dev.vars` shadows\n // it, so local runs (which have a `.dev.vars`) are unchanged while CI runs (no `.dev.vars`) resolve\n // env-provided config instead of failing validation on a missing binding.\n process.env.CLOUDFLARE_INCLUDE_PROCESS_ENV = 'true'\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 // The quarry worker runs an empty script (see the Miniflare options below), so it can't own\n // a Workflow entrypoint, and a sourceless CLI host can't reach a Workflow\n // defined in another worker either (it boots before that worker exists, and\n // Workflows can't be remote bindings). Drop the Workflow bindings so Miniflare\n // doesn't stand up an engine demanding an entrypoint this host can't provide —\n // see stripWorkflowBindings. Surfaced (not silent) so a command that expects\n // one sees why it's absent; triggering must happen from the defining worker.\n const strippedWorkflows = stripWorkflowBindings(workerOptions)\n if (strippedWorkflows.length > 0) {\n console.log(`Workflow binding(s) unavailable to CLI commands (defined on the worker, not this host): ${strippedWorkflows.join(', ')}`)\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 // Wrangler always emits `modulesRules`, and the v4→v5 converter below rejects\n // any input carrying it. A sourceless host loads no modules, so the rules can\n // never apply here — see stripModuleRules.\n stripModuleRules(workerOptions)\n\n // Miniflare 5 dropped the flat `script`/`modules` options for a `workers`\n // array, but wrangler still hands back v4-shaped worker options, so the\n // options are built in the v4 shape and converted. Miniflare owns that\n // mapping; hand-rolling it here would drift from whatever wrangler emits.\n const mf = new Miniflare(convertV4MiniflareOptions({\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. Layout is\n // `<root>/<plugin>`, unchanged from v4.\n //\n // Deliberately the v5 key in an otherwise v4-shaped object: the converter\n // maps the worker options SHAPE, not option names, and `defaultPersistRoot`\n // no longer exists anywhere in Miniflare 5 — its v4-compatibility input\n // already spells this `resourcePersistencePath`. The v4 name is accepted and\n // then ignored, which would leave no persistence path at all and silently\n // discard local state between runs.\n resourcePersistencePath: 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 await remoteProxy?.session.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":";;;;;;;AAAA,SAAgB,eAAe,MAA6D;CAC1F,IAAI;CACJ,MAAM,OAAiB,CAAC;CACxB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,MAAM;GAChB,KAAK,KAAK,GAAG,KAAK,MAAM,CAAC,CAAC;GAC1B;EACF;EACA,MAAM,UAAU,IAAI,MAAM,qBAAqB;EAC/C,IAAI,SAAS;GACX,IAAI,CAAC,QAAQ,IAAI,MAAM,IAAI,MAAM,6CAA6C;GAC9E,IAAI,QAAQ,KAAA,GAAW,MAAM,IAAI,MAAM,gCAAgC;GACvE,MAAM,QAAQ;GACd;EACF;EACA,IAAI,QAAQ,WAAW,QAAQ,MAAM;GACnC,MAAM,OAAO,KAAK,IAAI;GACtB,IAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,GAAG,MAAM,IAAI,MAAM,6CAA6C;GAChG,IAAI,QAAQ,KAAA,GAAW,MAAM,IAAI,MAAM,gCAAgC;GACvE,MAAM;GACN;GACA;EACF;EACA,KAAK,KAAK,GAAG;CACf;CACA,OAAO;EAAE;EAAK;CAAK;AACrB;;;;ACrBA,SAAgB,sBACd,QACA,gBACA,KACA;CACA,MAAM,WAA2B,CAAC;CAElC,KAAK,MAAM,SAAS,OAAO,KAAK,GAAG;EACjC,MAAM,eAAe,OAAO,IAAI,MAAM,IAAI;EAC1C,MAAM,YAAY,eAAe,aAAa,OAAO;EAErD,MAAM,QAAoB,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC;EAChD,IAAI,aAAa,SACf,KAAK,MAAM,SAAS,aAAa,SAC/B,MAAM,KAAK,MAAM,MAAM,GAAG,CAAC;EAK/B,IAAI,MAAM,SAAS,QACjB,MAAM,KAAK,CAAC,CAAC;EAGf,MAAM,eAAe,QAAQ;GAC3B,OAAgB,QAAQ;GACxB,OAAgB,QAA2B,aAAa,cACpD,QAAQ,MAAM,EAAE,aAAa,aAAa,YAAY,CAAC,IACvD,KAAA;GAEJ,MAAM,UAA2B;IAC/B,MAAM,QAAiC,CAAC;IAExC,KAAK,MAAM,OAAO,UAAU,WAAW;KACrC,MAAM,QAAS,KAAiC,IAAI;KACpD,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,QAAQ;IAC7C;IAEA,KAAK,MAAM,OAAO,UAAU,SAAS;KACnC,MAAM,QAAS,KAAiC,IAAI;KACpD,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,QAAQ;IAC7C;IAMA,QAAO,MADc,IAAI,cAAc,MAAM,MAAM,KAAK,EAAA,CAC1C;GAChB;EACF;EAGA,MAAM,QAAQ,OAAO;EACrB,KAAK,MAAM,OAAO,UAAU,WAC1B,IAAI,IAAI,SACN,MAAM,IAAI,QAAQ,OAAO,KAAK;GAAE,MAAM,IAAI;GAAM,UAAU,IAAI,WAAW,IAAI;EAAE,CAAC;OAEhF,MAAM,IAAI,QAAQ,OAAO,OAAO;GAAE,MAAM,IAAI;GAAM,UAAU,IAAI;EAAS,CAAC;EAI9E,KAAK,MAAM,OAAO,UAAU,SAAS;GACnC,MAAM,UAAU,IAAI,QAAQ,IAAI,IAAI,MAAM,KAAK,IAAI,SAAS,KAAK,IAAI;GACrE,MAAM,eAAyB,CAAC;GAChC,IAAI,IAAI,aAAa,aAAa,KAAK,IAAI,WAAW;GACtD,IAAI,IAAI,YAAY,KAAA,GAAW,aAAa,KAAK,aAAa,IAAI,QAAQ,EAAE;GAC5E,MAAM,UAAU,aAAa,SAAS,IAAI,aAAa,KAAK,GAAG,IAAI,KAAA;GAEnE,IAAI,IAAI,QACN,MAAM,IAAI,QAAQ,OAAO,QAAQ,SAAS,EAAE,aAAa,QAAQ,CAAC;QAC7D,IAAI,IAAI,SAAS;IACtB,IAAI,IAAI,YAAY,KAAA,GAClB,MAAM,IAAI,QAAQ,OAAO,MAAM,SAAS,CAAC,IAAI,OAAO,GAAG,EAAE,aAAa,QAAQ,CAAC;SAE/E,MAAM,IAAI,QAAQ,OAAO,MAAM,SAAS,EAAE,aAAa,QAAQ,CAAC;GAEpE,OACE,IAAI,IAAI,YAAY,KAAA,GAClB,MAAM,IAAI,QAAQ,OAAO,OAAO,SAAS,IAAI,SAAS,EAAE,aAAa,QAAQ,CAAC;QAE9E,MAAM,IAAI,QAAQ,OAAO,OAAO,SAAS,EAAE,aAAa,QAAQ,CAAC;EAGvE;EAEA,SAAS,KAAK,MAAM;CACtB;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;ACvEA,SAAgB,iBAAiB,eAAiD;CAChF,IAAI,cAAc,iBAAiB,KAAA,GAAW,OAAO;CACrD,OAAO,cAAc;CACrB,OAAO;AACT;;;ACxBA,MAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;AAcA,SAAgB,gBAAgB,UAAsC;CAIpE,IAAI,MAAM,QAAQ,QAAQ;CAC1B,MAAM,EAAE,SAAS,MAAM,GAAG;CAC1B,IAAI;CACJ,OAAO,MAAM;EACX,IAAI,UAAU,MAAK,aAAY,WAAW,KAAK,KAAK,QAAQ,CAAC,CAAC,GAAG,OAAO;EACxE,IAAI,WAAW,KAAK,KAAK,cAAc,CAAC,GAAG,uBAAuB;EAClE,IAAI,QAAQ,MAAM,OAAO;EACzB,MAAM,QAAQ,GAAG;CACnB;AACF;;;;;;;;;;;;;;;;;AAuBA,SAAgB,uBAAuB,KAAqB,KAAqB;CAC/E,MAAM,WAAW,IAAI,2BAA2B,IAAI;CACpD,IAAI,UAAU,OAAO;CACrB,OAAO,KAAK,gBAAgB,GAAG,KAAK,KAAK,aAAa,UAAU;AAClE;;;;;;;;;;;;;;;;;;;;;;AC5CA,SAAgB,sBAAsB,eAAkD;CACtF,MAAM,YAAY,cAAc;CAChC,IAAI,CAAC,WAAW,OAAO,CAAC;CACxB,MAAM,QAAQ,OAAO,KAAK,SAAS;CACnC,OAAO,cAAc;CACrB,OAAO;AACT;;;ACqBA,MAAM,UAAU,cAAc,YAAY,GAAG;AAG7C,MAAM,kBAAkB,KAAK,QAAQ,QAAQ,QAAQ,oBAAoB,CAAC,GAAG,aAAa;AAC1F,SAAS,cAAc,eAAe,GAAG,cAAc,IAAI,CAAC;AAG5D,SAAS,IAAI,IAAI,mCAAmC,YAAY,GAAG,GAAG,cAAc,IAAI,CAAC;AAEzF,MAAM,gBAAgB;AAEtB,IAAI;AACJ,IAAI;CACF,MAAM,SAAS,eAAe,QAAQ,KAAK,MAAM,CAAC,CAAC;CACnD,cAAc,OAAO;CACrB,QAAQ,KAAK,OAAO,GAAG,QAAQ,KAAK,SAAS,GAAG,GAAG,OAAO,IAAI;AAChE,SAAS,GAAG;CACV,QAAQ,MAAM,UAAW,EAAY,SAAS;CAC9C,QAAQ,KAAK,CAAC;AAChB;AAGA,MAAM,WAAW,QAAQ,KAAK;AAC9B,IAAI,YAAY;AAEhB,IAAI,aAAa,SAAS,SAAS,GAAG,KAAK,SAAS,SAAS,IAAI,KAAK,qBAAqB,KAAK,QAAQ,IAAI;CAC1G,YAAY;CAEZ,QAAQ,KAAK,OAAO,GAAG,CAAC;AAC1B;AAGA,MAAM,YAAY,QAAQ,QAAQ,IAAI,GAAG,SAAS;AAElD,IAAI,CAAC,WAAW,SAAS,GAAG;CAC1B,QAAQ,MAAM,gCAAgC,WAAW;CACzD,QAAQ,MAAM,EAAE;CAChB,QAAQ,MAAM,sGAAsG;CACpH,QAAQ,MAAM,qDAAqD;CACnE,QAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,OAAsB;CACnC,MAAM,aAAa,cAAc,KAAK,QAAQ,IAAI,GAAG,cAAc,CAAC;CAEpE,MAAM,EAAE,qBAAqB,YAAY,oCAAoC,2BAA2B,wBAAwB,eAAe,yCAAyC,MAAM,OAAO,WAAW,QAAQ,UAAU;CAClO,MAAM,EAAE,WAAW,8BAA8B,MAAM,OAAO,WAAW,QAAQ,WAAW;CAG5F,MAAM,aAAa;EADC;EAAkB;EAAiB;CAC3B,CAAC,CAAC,MAAK,MAAK,WAAW,QAAQ,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC;CAC7E,MAAM,aAAa,aAAa,QAAQ,QAAQ,IAAI,GAAG,UAAU,IAAI,KAAA;CAMrE,MAAM,WAAW;EACf;EACA;EACA,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,YAAY,UAAU;CAC9C;CACA,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,CAAC,SAAS;EACd,MAAM,UAAU,QAAQ,QAAQ,IAAI,GAAG,OAAO;EAC9C,IAAI,WAAW,OAAO,GAAG,QAAQ,YAAY,OAAO;CACtD;CAeA,MAAM,eAAe,uBAAuB;EAC1C,yBAAyB,QAAQ,IAAI;EACrC,wBAAwB,QAAQ,IAAI;CACtC,GAAG,QAAQ,IAAI,CAAC;CAChB,QAAQ,IAAI,0BAA0B;CAEtC,MAAM,SAAS,WAAW;EAAE,QAAQ;EAAY,KAAK;CAAY,CAAC;CASlE,MAAM,cAAc,aAChB,MAAM,qCAAqC;EAAE,MAAM;EAAY;CAAY,CAAC,IAC5E;CACJ,IAAI,aAAa;EACf,MAAM,YAAY,QAAQ;EAC1B,MAAM,cAAc,OAAO,KAAK,YAAY,cAAc;EAC1D,QAAQ,IAAI,6BAA6B,YAAY,OAAO,qCAAqC,YAAY,KAAK,IAAI,EAAE,EAAE;CAC5H;CAEA,MAAM,EAAE,kBAAkB,0BAA0B,QAAQ,aAAa,EACvE,6BAA6B,aAAa,QAAQ,4BACpD,CAAC;CAQD,QAAQ,IAAI,iCAAiC;CAC7C,MAAM,OAAO,cAAc,YAAY,KAAA,GAAW,OAAO,MAAM,WAAW;CAC1E,MAAM,aAAqC,CAAC;CAC5C,KAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ,IAAI,GAC9C,WAAW,OAAO,QAAQ;CAI5B,cAAc,WAAW;EACvB,GAFuB,cAAc,YAAuC,CAAC;EAG7E,GAAG;EACH,gBAAgB;CAClB;CASA,MAAM,oBAAoB,sBAAsB,aAAa;CAC7D,IAAI,kBAAkB,SAAS,GAC7B,QAAQ,IAAI,2FAA2F,kBAAkB,KAAK,IAAI,GAAG;CAOvI,cAAc,OADK,OAAO,OAAO,UAAU,OAAO,KAAK,GAAG,QAAQ,QAAQ,UAAU,QAAQ;CAM5F,iBAAiB,aAAa;CAM9B,MAAM,KAAK,IAAI,UAAU,0BAA0B;EACjD,GAAG;EACH,QAAQ;EACR,SAAS;EACT,uBAAuB;EAYvB,yBAAyB,KAAK,QAAQ,IAAI,GAAG,oBAAoB;CACnE,CAAC,CAAC;CAEF,MAAM,GAAG;CACT,MAAM,MAAM,MAAM,GAAG,YAAY;CAEjC,MAAM,kBAAsC,CAAC;CAC7C,MAAM,oBAAoB,YAA8B;EACtD,gBAAgB,KAAK,OAAO;CAC9B;CAEA,IAAI;CACJ,IAAI;EACF,WAAwC,yBAAyB;GAC/D;GACA,WAAW;EACb;EAEA,MAAM,OAAO,cAAc,SAAS,CAAC,CAAC;EAEtC,MAAM,CACJ,EAAE,WACF,EAAE,aACF,EAAE,oBACA,MAAM,QAAQ,IAAI;GACpB,OAAO;GACP,OAAO;GACP,OAAO;EACT,CAAC;EAED,MAAM,MAAM,QAAQ,mBAAmB;EACvC,MAAM,SAAS,IAAI,UAAU,QAAwB,UAAU,MAAM;EAErE,MAAM,EAAE,QAAQ,MAAM,OAAO;EAG7B,MAAM,MAAM,IAAI,IAAI;GAClB,YAAY;GACZ,aAAa;GACb,eALU,QAAQ,oBAKD,CAAC,CAAC;EACrB,CAAC;EAED,KAAK,MAAM,OAAO,sBAAsB,QAAQ,gBAAgB,GAAG,GACjE,IAAI,SAAS,GAAG;EAGlB,MAAM,IAAI,QAAQ,QAAQ,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,eAAe,CAAC;CACpE,UAAU;EACR,MAAM,QAAQ,WAAW,eAAe;EACxC,MAAM,KAAK,SAAS;EACpB,MAAM,GAAG,QAAQ;EACjB,MAAM,aAAa,QAAQ,QAAQ;CACrC;AACF;AAEA,KAAK,CAAC,CAAC,MAAM,OAAO,UAAmB;CACrC,MAAM,EAAE,0BAA0B,MAAM,OAAO;CAE/C,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;CACrE,QAAQ,MAAM,gBAAgB,OAAO;CACrC,IAAI,iBAAiB,uBACnB,QAAQ,MAAM,MAAM,OAAO,OAAO;CAEpC,QAAQ,KAAK,CAAC;AAChB,CAAC"}
|
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as CacheService } from "../cache.service-
|
|
3
|
-
import { t as TieredCacheService } from "../tiered-cache.service-
|
|
4
|
-
|
|
1
|
+
import { li as ApplicationError } from "../index-B2WtEagR.mjs";
|
|
2
|
+
import { t as CacheService } from "../cache.service-Dg0Hj0gp.mjs";
|
|
3
|
+
import { t as TieredCacheService } from "../tiered-cache.service-Bz8bbvyb.mjs";
|
|
5
4
|
//#region src/cache/cache.error.d.ts
|
|
6
|
-
declare class CacheError extends ApplicationError {}
|
|
5
|
+
export declare class CacheError extends ApplicationError {}
|
|
7
6
|
//#endregion
|
|
8
7
|
//#region src/cache/cache.module.d.ts
|
|
9
8
|
/**
|
|
@@ -17,14 +16,14 @@ declare class CacheError extends ApplicationError {}
|
|
|
17
16
|
* - Automatic error handling with security-focused logging
|
|
18
17
|
* - Singleton service for optimal performance
|
|
19
18
|
*/
|
|
20
|
-
declare class CacheModule {}
|
|
19
|
+
export declare class CacheModule {}
|
|
21
20
|
//#endregion
|
|
22
21
|
//#region src/cache/cache.tokens.d.ts
|
|
23
|
-
declare const CACHE_TOKENS: {
|
|
22
|
+
export declare const CACHE_TOKENS: {
|
|
24
23
|
readonly CacheService: symbol;
|
|
25
24
|
readonly TieredCacheService: symbol;
|
|
26
25
|
};
|
|
27
|
-
type CacheToken = (typeof CACHE_TOKENS)[keyof typeof CACHE_TOKENS];
|
|
26
|
+
export type CacheToken = (typeof CACHE_TOKENS)[keyof typeof CACHE_TOKENS];
|
|
28
27
|
//#endregion
|
|
29
|
-
export {
|
|
28
|
+
export { CacheService, TieredCacheService };
|
|
30
29
|
//# 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":";;;;qBAEa,mBAAmB;;;;;;;;;;;;;;qBCsBnB;;;qBCxBA;WACX;WACA;;YAGU,qBAAqB,2BAA2B"}
|