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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
# stratal
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a753e55: Move routing onto plain Hono with lazy OpenAPI generation, add declarative response caching on Cloudflare Workers Caching, and add per-path locale detection.
|
|
8
|
+
|
|
9
|
+
### Routing and validation
|
|
10
|
+
- Build the router on plain Hono with per-route validation and lazy OpenAPI generation. Validation is attached only to routes that declare `params`, `query` or `body`, so a schema-less route pulls in none of it: a hello-world worker drops from 944 KB to 504 KB raw. `ctx.param()`, `ctx.query()` and `ctx.body()` are unchanged.
|
|
11
|
+
- Validate request and response schemas asynchronously, so a schema may carry a refinement that reaches a database, a cache or a service binding to decide whether a value is acceptable. Such a refinement runs inside the request's DI scope, so it needs no context threaded into the schema, and a failure now surfaces as a 400 carrying the refinement's message rather than a 500 with no field detail. Fully synchronous schemas are unaffected.
|
|
12
|
+
- Accept full schema metadata in `describe()` and `named()`, not just a description string — `example`, `examples`, `title` and `deprecated` all flow through to the generated OpenAPI document.
|
|
13
|
+
- Add route visibility `groups`. `@Controller` and route options take a `groups: string[]` label list, exposed on each route's schema metadata, so the OpenAPI `routeFilter` can scope a document by group instead of by path string.
|
|
14
|
+
|
|
15
|
+
### Response caching
|
|
16
|
+
|
|
17
|
+
Add declarative HTTP response caching through the new `stratal/response-cache` entry. On a cache hit the Worker never runs, so no CPU is billed.
|
|
18
|
+
- `@Cacheable({ ttl, browserTtl, swr, tags, vary })` on `GET` and `HEAD` routes emits `CDN-Cache-Control` and `Cache-Control` alongside `Cache-Tag`. `browserTtl` defaults to `ttl`, which is what makes a repeat visit free rather than a round trip; set `browserTtl: 0` where retraction has to be reliable, since tags and `ctx.cache.purge()` reach the shared cache and nothing else.
|
|
19
|
+
- `@PurgesCache({ tags, pathPrefixes, purgeEverything })` purges after a `2xx` or `3xx`. The purge is awaited and a failure is rethrown as `CachePurgeError`, rather than leaving the cache silently inconsistent with the database.
|
|
20
|
+
- `ResponseCacheModule.forRoot({ defaults })` supplies `ttl`, `swr` and `vary` for every `@Cacheable` route. Defaults never make a route cacheable on their own — `@Cacheable` stays mandatory.
|
|
21
|
+
- Interpolate `{param.*}`, `{query.*}`, `{data.*}` and `{partition.*}` into cache tags, with a `.*` suffix fanning an array out to one tag per element. A rendered tag must be printable ASCII with no space, comma or double quote and at most 1024 bytes, or it throws `InvalidCacheTagError` — slugify any request-derived value before interpolating it.
|
|
22
|
+
- Cache guarded and per-tenant routes with `@Cacheable({ partitionBy: [...] })`. Export `cachedEntrypoint(stratal)` from `stratal/workers` alongside your default export, then configure `gateway: { entrypoint: 'Cached' }` with `partitions` and `primers`. Partitioned reads are forwarded to that entrypoint, which places the resolved partitions in the part of the cache key that cannot be bypassed. `gateway.entrypoint` is type-checked against your Worker's real exports once you have run `wrangler types`. A guarded route is only ever cacheable with a non-empty `partitionBy`, and a partition that fails to resolve runs inline and is stamped `private, no-store` rather than being cached publicly.
|
|
23
|
+
- Put a response's representation in the cache key with `gateway: { keyBy: [...] }`, so one URL answered two ways is two entries rather than one entry with two variants. **An Inertia app behind a gateway should set `keyBy: INERTIA_VARY_HEADERS`, exported from `@stratal/inertia`; without it those pages stop caching.**
|
|
24
|
+
- Strip `X-RateLimit-*` from a response a shared cache may store. They describe one caller's budget, so on a shared response they are replayed to every other caller, and a cache hit never runs the throttle to count them down. The limit is still consumed and enforced; only the reporting is withheld. Adds `isSharedCacheable` and `PER_CALLER_RATE_LIMIT_HEADERS`.
|
|
25
|
+
- Requires `"cache": { "enabled": true }` in `wrangler.jsonc`, Wrangler 4.69.0 or newer, and a `compatibility_date` of `2026-07-06` or later. Without those a `@Cacheable` route is served uncached and stamped `private, no-store`, and the reason is logged once per entrypoint.
|
|
26
|
+
- New errors: `ResponseCacheConfigError`, `CachePurgeError`, `InvalidCacheTagError`.
|
|
27
|
+
|
|
28
|
+
### Storage
|
|
29
|
+
- Add `head()`, `list()` and `deleteMany()` to `StorageService`. `head(path, disk?)` reads an object's size, content type, etag, upload time and custom metadata without transferring its body, or `null` when nothing is stored there. `list(options?, disk?)` pages objects under a disk-relative `prefix` and carries `truncated` and `cursor`, so loop while `truncated` is true to cover a whole prefix. `deleteMany(paths, disk?)` deletes in bulk instead of one call per file.
|
|
30
|
+
- Stop serving arbitrary stored content types inline from downloads. An object stored as `text/html`, or as a scriptable `image/svg+xml`, previously executed against whatever session fetched it, since objects are served from the application's own origin. Only `application/pdf`, `image/png`, `image/jpeg`, `image/gif` and `image/webp` now render inline; everything else returns as an attachment. Every download also carries `X-Content-Type-Options: nosniff` and a sandboxing `Content-Security-Policy`.
|
|
31
|
+
- Fix downloads of keys containing a space, a non-ASCII character, `#` or `?` — most user-supplied filenames — being reported as missing, and stop a key containing a control character producing a malformed header. Non-ASCII filenames are preserved.
|
|
32
|
+
|
|
33
|
+
### Internationalisation
|
|
34
|
+
- Add per-path locale detection: `detection` accepts a `(path) => options` resolver, alongside `I18nModule.forRootAsync` and a strategy-aware `ctx.setLocale`. Different areas can now use different strategies — a path-localized public site with a cookie-localized `/admin` panel, say — which is necessary when an area's session cookie is path-scoped. Only routes whose path resolves to `strategy: 'path'` get a `/:locale` variant; everything else is served at its bare path with no change to URL builders. The resolver must be a pure function of the path, since it is consulted both at boot and per request.
|
|
35
|
+
- The cookie strategy scopes the `locale` cookie by the resolved `cookieOptions`, so a per-path cookie area writes `{ path: '/admin' }`. Plain `strategy: 'cookie'` behaviour is unchanged.
|
|
36
|
+
- Fix localized multi-segment URLs matching the wrong route when two or more locales are path-prefixed, which could produce a redirect loop on a homepage that redirects elsewhere.
|
|
37
|
+
|
|
38
|
+
### Quarry CLI
|
|
39
|
+
- Run the Quarry host on Miniflare 5. **Quarry now requires Wrangler 4.124 or newer.** Local state still lands in `.wrangler/state/v3/<plugin>`, so existing KV, D1, R2, Durable Object and cache state carries over and is still shared with a running `wrangler dev`.
|
|
40
|
+
- Stream command output to the terminal as it is produced rather than only after the command finishes, so long-running commands show progress live.
|
|
41
|
+
- Source `process.env` into worker vars and secrets, so CI and scripted runs that pass config through the environment no longer fail validation on a missing binding. Local runs with a `.dev.vars` are unchanged.
|
|
42
|
+
- Stop failing with `The Workers runtime failed to start` on a worker that declares a Cloudflare Workflow. Workflow bindings are stripped from the host and logged — trigger workflows from the worker that defines them.
|
|
43
|
+
- Fix `mcp:serve` and `mcp:tools` failing to start: both built the OpenAPI document from the root container, but commands run in a request scope and the document needs the request-scoped OpenAPI config service.
|
|
44
|
+
- Match the Workers socket contract in the Node polyfill, so closing a socket resolves once it is closed and a TLS upgrade returns the upgraded socket. Sending mail through the CLI was the common path affected.
|
|
45
|
+
|
|
46
|
+
### Other fixes
|
|
47
|
+
- `Limit.distinctBy(value)` counts distinct values in the window instead of requests, for caps like "ten different courses a day".
|
|
48
|
+
- Honour a `Response` returned by a short-circuiting middleware even when an outer middleware forwards control with `await next()` and discards the result. An early `ctx.redirect(...)` was previously dropped, leaving the request unfinalized and throwing "Context is not finalized". `Next` is widened to `() => Promise<Response | void>` so a forwarding middleware can `return next()` without a cast.
|
|
49
|
+
- Let errors contribute structured fields to their own log entry through an overridable `reportContext()` hook on `ApplicationError`. A failed validation now logs which field failed and why, where it previously logged only a generic line.
|
|
50
|
+
- Stop `/openapi.json` failing when a route schema contains a type with no JSON Schema representation, such as `z.custom`, `z.date` or `z.set`. Those emit an empty schema instead of throwing, so one unrepresentable field no longer takes down the whole document.
|
|
51
|
+
- Fix route registration failing when the router module is evaluated more than once, for example under a bundler or an SSR module runner.
|
|
52
|
+
- Declare `openapi3-ts` as a direct dependency, which a clean install such as CI could not otherwise resolve.
|
|
53
|
+
|
|
54
|
+
### Breaking Changes
|
|
55
|
+
- **The validation API is `zod/mini`.** The `z` re-export from `stratal/validation` is removed. Import schema builders directly from `zod/mini` using named imports and replace classic chaining with the functional API: `z.string().min(1).optional()` becomes `optional(string().check(minLength(1)))`. `stratal/validation` still exports `cuid2` and `withZodI18n`, plus `describe()` and `named()` for descriptions and OpenAPI component ids, since `zod/mini` has no `.describe()` or `.meta()`.
|
|
56
|
+
- **OpenAPI documents are generated lazily**, on the first request to the docs endpoint. `OpenAPIService.getSpec()` becomes `getSpec(container)` and is async — update any direct call. `routeFilter` is now a metadata predicate `(route: RouteSchemaMeta) => boolean` instead of `(path, pathItem)`; filter on `route.groups` or `route.meta` rather than on the path string.
|
|
57
|
+
- **Every response now carries an explicit `Cache-Control` header.** Routes without `@Cacheable` are stamped `private, no-store`. This affects every app, not only those adopting caching: Cloudflare applies heuristic freshness to a response carrying no `Cache-Control` at all, caching a `200` for two hours, so the explicit header is what keeps an uncacheable route uncached. Routes that set their own `Cache-Control` are left alone — if you relied on a response having none, set one explicitly.
|
|
58
|
+
- **`CacheService.put` is now fire-and-forget and can no longer report failure.** It schedules the write, resolves immediately and logs a rejection instead of throwing, so `try { await cache.put(...) } catch { … }` now sees success even when the value was never stored. A cache is best-effort, and a KV write can add hundreds of milliseconds to a request, so this is the right default — but move any write that must not be silently lost to `CacheService.putDurable` / `TieredCacheService.putDurable`, which await the write and throw on failure. `delete` is unchanged and remains durable and awaited.
|
|
59
|
+
- **Storage downloads no longer render arbitrary content types inline.** Only `application/pdf`, `image/png`, `image/jpeg`, `image/gif` and `image/webp` render inline; everything else downloads as an attachment. If you relied on another type rendering in the browser, serve that content from a separate origin, where a compromise cannot reach the application's session.
|
|
60
|
+
- **Guards now deny when `canActivate` returns `false`**, with `GuardRejectedError` (403), instead of the return value being ignored. Audit your `canActivate` implementations before upgrading — requests that previously reached the handler now 403. `GuardRejectedError` is also re-exported from `@stratal/framework/guards`.
|
|
61
|
+
- **Quarry requires Miniflare 5**, which comes in with Wrangler 4.124 or newer. Apps on an older Wrangler must upgrade before `npx quarry` will start.
|
|
62
|
+
|
|
63
|
+
## 0.0.27
|
|
64
|
+
|
|
65
|
+
### Patch Changes
|
|
66
|
+
|
|
67
|
+
- 41a9140: Support wrangler remote bindings in the Quarry dev runner
|
|
68
|
+
|
|
69
|
+
Bindings marked `remote: true` in your `wrangler` config now connect to the deployed Cloudflare resources during local development, instead of resolving against the local dev registry. Plain local runs are unaffected. When a remote binding is in use, Quarry logs which bindings are being proxied and requires valid wrangler credentials (`wrangler login` or `CLOUDFLARE_API_TOKEN`).
|
|
70
|
+
|
|
71
|
+
## 0.0.26
|
|
72
|
+
|
|
73
|
+
### Patch Changes
|
|
74
|
+
|
|
75
|
+
- ab95f52: Fix memory leaks that crashed the dev server (OOM) after repeated hot reloads
|
|
76
|
+
|
|
77
|
+
### Details
|
|
78
|
+
- Hot reloads now fully tear down the previous application before the new one boots — old and new dependency graphs no longer coexist, so memory stays flat across reloads
|
|
79
|
+
- Instances superseded mid-boot by a newer reload now reject with `StratalSupersededError` instead of hanging forever; in-flight requests during a reload are transparently served by the replacing instance
|
|
80
|
+
- `Container.dispose()` is now async and invokes `Symbol.asyncDispose`, `Symbol.dispose`, or `dispose()` on container-created instances, letting services release timers, sockets, and pools on shutdown — `await` it if you call it directly
|
|
81
|
+
- i18n message registrations are deduplicated by content, so module re-evaluation on hot reload no longer grows the message store unboundedly
|
|
82
|
+
|
|
83
|
+
- bb6d3b9: Trailing-slash exclusions: `trailingSlash` accepts `{ mode, exclude }`
|
|
84
|
+
|
|
85
|
+
### Details
|
|
86
|
+
- `trailingSlash` application config now accepts `{ mode, exclude }` alongside a bare mode. Excluded paths are never redirected (308) and never rewritten by URL generation — for routes whose canonical form is owned externally (e.g. OAuth redirect URIs matched byte-for-byte).
|
|
87
|
+
- String patterns are segment-aware prefixes; RegExp patterns match both slash forms of the pathname regardless of anchoring.
|
|
88
|
+
- Exclusions match in route space: with path-based locale detection, a leading locale segment is stripped before matching, so `'/callback'` also exempts `/fr/callback` — in the redirect middleware, `Uri` helpers, and hreflang link generation.
|
|
89
|
+
- `@stratal/inertia` threads the widened config through hreflang URL generation and shares only the resolved mode with the React client (exclusions are server-side; excluded paths are served in both slash forms, so client-built URLs never redirect).
|
|
90
|
+
- New exports from `stratal/router`: `resolveTrailingSlash`, `isTrailingSlashExcluded`, and the `TrailingSlashConfig` / `TrailingSlashOptions` / `TrailingSlashExclude` types.
|
|
91
|
+
|
|
92
|
+
## 0.0.25
|
|
93
|
+
|
|
94
|
+
### Patch Changes
|
|
95
|
+
|
|
96
|
+
- e93db60: Add `hasListeners()` to the event registry for checking whether any handler matches an event
|
|
97
|
+
|
|
98
|
+
Uses the same pattern matching as `emit()` (exact, model wildcard, operation wildcard, phase wildcard), letting emitters skip expensive payload construction when nobody is listening.
|
|
99
|
+
|
|
100
|
+
## 0.0.24
|
|
101
|
+
|
|
102
|
+
## 0.0.23
|
|
103
|
+
|
|
104
|
+
### Patch Changes
|
|
105
|
+
|
|
106
|
+
- 13b0e8d: Reorganize core subsystem registries into modules
|
|
107
|
+
|
|
108
|
+
The event, cron, quarry, and seeder registries — previously registered imperatively in `Application` — are now declared as ordinary `@Module`s (`EventsModule`, `CronModule`, `QuarryModule`, `SeederModule`), consistent with every other subsystem. The `Application` constructor now only sets up the bootstrap kernel (`ExceptionHandler`, `LazyModuleLoader`, logging); all module registration happens during initialization. `application.ts` has no static subsystem imports — every built-in module is loaded via dynamic `import()`.
|
|
109
|
+
|
|
110
|
+
### Breaking Changes
|
|
111
|
+
- **`EventRegistry`, `QuarryRegistry`, `CronManager`, `SeederRegistry` are now `@Singleton`** (they were `@Transient` but always force-registered as singletons). This aligns the class decorator with their actual lifecycle; their canonical DI tokens are declared on the decorator.
|
|
112
|
+
- **`SeederRegistry` now injects the `Application`** (`@inject(DI_TOKENS.Application)`) instead of being constructed manually.
|
|
113
|
+
- **`@stratal/framework` `DatabaseModule.onInitialize` is now `async`** and loads `EventsModule` on demand via `LazyModuleLoader` (the event registry is no longer eagerly registered). No change is required for apps that use `DatabaseModule` normally.
|
|
114
|
+
- New public modules are exported from their sub-paths: `EventsModule` (`stratal/events`), `CronModule` (`stratal/cron`), `QuarryModule` (`stratal/quarry`), `SeederModule` (`stratal/seeder`).
|
|
115
|
+
- The `schedule:list` command now lazy-loads `CronModule` via `LazyModuleLoader` rather than injecting `DI_TOKENS.Cron`; with no jobs registered it prints "No cron jobs found" instead of failing to resolve.
|
|
116
|
+
|
|
117
|
+
- 13b0e8d: Replace the email provider layer with a built-in Cloudflare Workers-compatible SMTP client and defer React Email rendering
|
|
118
|
+
- Email is now sent through a built-in SMTP client and MIME builder, removing the runtime dependency on `nodemailer`.
|
|
119
|
+
- `@react-email/render` is loaded on demand only when sending a React template, reducing cold-start overhead for requests that don't send email.
|
|
120
|
+
|
|
121
|
+
### Breaking Changes
|
|
122
|
+
- **Resend provider removed.** Switch to SMTP. Remove the `provider` and `apiKey` options from your email configuration and remove `resend` from your dependencies.
|
|
123
|
+
- **SMTP configuration uses a connection URL.** Replace individual `host`/`port`/`secure`/`username`/`password` fields with a single `url`:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
// Before
|
|
127
|
+
smtp: { host: 'smtp.example.com', port: 587, username: 'user', password: 'pass' }
|
|
128
|
+
// After
|
|
129
|
+
smtp: { url: 'smtp://user:pass@smtp.example.com:587' } // or smtps:// for TLS
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- **Dependencies changed.** `nodemailer`, `resend`, and `@react-email/components` are no longer peer dependencies. If you render React email templates, install `@react-email/render` directly.
|
|
133
|
+
|
|
134
|
+
- 13b0e8d: Add lazy module loading and reduce cold start by loading built-in subsystems on demand
|
|
135
|
+
|
|
136
|
+
### New: `LazyModuleLoader`
|
|
137
|
+
|
|
138
|
+
Inject `LazyModuleLoader` (or resolve `DI_TOKENS.LazyModuleLoader`) to load a module at runtime, NestJS-style:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const ref = await loader.load(() =>
|
|
142
|
+
import("./reports.module").then((m) => m.ReportsModule),
|
|
143
|
+
);
|
|
144
|
+
ref.get(ReportService);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The loaded module's nested `imports` and `providers` are registered into the global container and its `onInitialize` hook runs once. Repeat loads return the cached `ModuleRef`. Controllers, queue consumers, and cron jobs declared by a lazily loaded module are skipped (with a warning) — that wiring is finalized at bootstrap.
|
|
148
|
+
|
|
149
|
+
If a lazy module provides a token that another module has already bound on the global container, the existing binding is kept and the colliding lazy provider is ignored (with a warning) — a lazy module cannot silently clobber an already-registered token.
|
|
150
|
+
|
|
151
|
+
### Breaking Changes
|
|
152
|
+
- **Built-in subsystems are no longer registered eagerly at boot.** `I18nModule`, `QueueModule`, `CacheModule`, `OpenAPIModule`, the cron manager, and router services are now loaded via dynamic `import()` at their trigger points (i18n/routing on the first HTTP request, queue on the first batch, cron on the first scheduled invocation or when the app declares jobs). HTTP-only apps no longer evaluate queue/cron code at cold start.
|
|
153
|
+
- **`CacheService` is no longer globally available unless `CacheModule` is loaded.** `RateLimiterModule` now imports `CacheModule` itself; apps that relied on the implicit global `CacheService` must import `CacheModule` (or use `LazyModuleLoader`).
|
|
154
|
+
- **`Application.initializeHandlers()` is removed.** Non-HTTP entrypoints (Durable Objects, Workflows, WorkerEntrypoints) now use `Application.ensureScopedHandlers()` via the internal `runInScope` helper — no action required for typical apps.
|
|
155
|
+
|
|
156
|
+
- 13b0e8d: Add locale-aware URL generation for path-prefixed and querystring localized routing
|
|
157
|
+
- Route URL generation now applies the active locale automatically — e.g. `uri.route('posts.show', { locale: 'es' })` produces `/es/posts/...` when locale prefixing is enabled.
|
|
158
|
+
- New `LocaleUrlConfig` and a locale-aware URL service for producing locale variants of any URL (used for hreflang alternates, canonical URLs, sitemaps, and redirects).
|
|
159
|
+
- Configurable trailing-slash handling for consistent URL formatting.
|
|
160
|
+
|
|
161
|
+
- 13b0e8d: Fix correctness and security issues found in review.
|
|
162
|
+
|
|
163
|
+
Queue:
|
|
164
|
+
- Retry the correct binding: dispatch stamps the producer binding into message metadata and failed jobs record it, so `queue:retry` re-enqueues through the Cloudflare binding instead of the queue name (which is not a valid binding key and broke retry whenever the two differed). A message with no binding metadata is logged and acked rather than stored as an unretryable job.
|
|
165
|
+
- Honor the documented retry budget: `maxRetries` now counts retries correctly against Cloudflare's 1-based `message.attempts` (previously gave one fewer retry than configured).
|
|
166
|
+
- Derive idempotency keys from an order-stable serialization of `type` + `payload`, so payloads that differ only in key order dedupe correctly.
|
|
167
|
+
- `queue:retry --all` / `queue:purge --all --queue` collect matching keys before deleting, so cursor pagination no longer skips jobs; `queue:failed --queue --limit` now counts matching jobs rather than scanned keys.
|
|
168
|
+
- Documented that delivery is at-least-once with best-effort de-duplication (not exactly-once), since the processed marker is written only after a handler succeeds and KV is eventually consistent — handlers must be idempotent.
|
|
169
|
+
|
|
170
|
+
Email (SMTP):
|
|
171
|
+
- Upgrade STARTTLS onto the socket `startTls()` returns: the original socket is closed by the runtime, so the post-upgrade reader/writer are re-derived from the new secure socket and any pre-handshake bytes are discarded (fixes a broken `smtp://` STARTTLS path on real Workers and closes the STARTTLS plaintext-injection vector).
|
|
172
|
+
- Refuse to send credentials over an unencrypted connection: an `smtp://` server that doesn't offer STARTTLS now fails loudly instead of leaking the password (blocks STARTTLS-stripping downgrades). Credential-free connections (e.g. local Mailpit) are unaffected.
|
|
173
|
+
- AUTH is gated on the server's advertised mechanisms and supports both `PLAIN` and `LOGIN`; usernames are percent-decoded like passwords.
|
|
174
|
+
- Add a response timeout so a hung SMTP server can't wedge the worker; QUIT/socket close are now best-effort and never mask a successful send.
|
|
175
|
+
- MIME builder strips CR/LF from headers, escapes/RFC 2231-encodes attachment filenames (prevents header injection), base64-encodes message bodies (fixes long-line corruption), and rejects envelope addresses containing whitespace or angle brackets (prevents `MAIL FROM`/`RCPT TO` desync).
|
|
176
|
+
|
|
177
|
+
Inertia SEO:
|
|
178
|
+
- `titleTemplate` substitutes every `%s` and treats `$`-sequences in the title literally.
|
|
179
|
+
- Inject head/body content via function replacements, so SEO/page content containing `$`-sequences (`$$`, `$&`, `` $` ``, `$'`) is no longer corrupted or able to splice a template placeholder back into the output.
|
|
180
|
+
- Drop unsafe attribute names — including inline event handlers (`on*`) — from custom `meta`/`link` entries (prevents tag breakout server-side, `setAttribute` errors during client head-sync, and developer-supplied event-handler attributes).
|
|
181
|
+
|
|
182
|
+
Feature flags:
|
|
183
|
+
- `FeatureFlagService.use()` binds the target app exactly once.
|
|
184
|
+
|
|
185
|
+
Database (framework):
|
|
186
|
+
- The reentrant `$transaction` proxy forwards the receiver for non-transaction property access.
|
|
187
|
+
|
|
188
|
+
Testing:
|
|
189
|
+
- `TestingModule.close()` drops the isolated per-file database even if shutdown throws; the stale-database sweep escapes LIKE metacharacters so a prefix containing `_` can't over-match.
|
|
190
|
+
|
|
191
|
+
DI:
|
|
192
|
+
- Construct singletons against the root container so they can never capture a request-scoped dependency (which would leak one request's state across every later request); an illegal singleton→request dependency now throws loudly.
|
|
193
|
+
- Detect circular dependencies and throw a clear error naming the cycle instead of overflowing the stack.
|
|
194
|
+
- `tryResolve` only swallows "no provider"; a registered provider that throws while constructing now surfaces the real error instead of injecting `undefined`.
|
|
195
|
+
- Request-cache invalidation tracks transitive constructor dependencies, so re-registering a value rebuilds cached services that depend on it through a transient intermediary.
|
|
196
|
+
|
|
197
|
+
Quarry dev runtime:
|
|
198
|
+
- Persist every durable plugin (KV, D1, R2, Durable Objects, cache) under `.wrangler/state/v3`, matching `wrangler dev` (previously only R2 was persisted); load `.env.local` / `.env.<env>.local` into `process.env` for full parity.
|
|
199
|
+
- The `cloudflare:sockets` STARTTLS shim re-attaches the stream error handler to the upgraded socket, so post-upgrade connection errors still surface.
|
|
200
|
+
|
|
201
|
+
- 13b0e8d: Align the Quarry CLI dev runtime with `wrangler dev`
|
|
202
|
+
|
|
203
|
+
The Quarry CLI now builds its local environment directly from your Wrangler config via Miniflare, so bindings, `vars`, and `.dev.vars` / `.env` files resolve exactly as they do under `wrangler dev` (including environment-specific `.env.<environment>` files loaded by `--env`).
|
|
204
|
+
- **Shared R2 state** — R2 buckets now persist to `.wrangler/state/v3/r2`, so data written by Quarry commands and `wrangler dev` is shared.
|
|
205
|
+
- **Parallel dev environments** — set `WRANGLER_REGISTRY_PATH` to isolate the dev service registry, allowing multiple dev environments to run side by side without service-binding collisions. Quarry also discovers a running `wrangler dev` session so service bindings resolve against it.
|
|
206
|
+
- **SMTP/socket support** — outbound TCP/TLS (e.g. sending email over SMTP) now works when running under Quarry.
|
|
207
|
+
- **Queues and events in commands** — CLI commands can now dispatch to queues and emit events, with listeners wired automatically.
|
|
208
|
+
|
|
209
|
+
- 13b0e8d: Add failed-job storage, idempotent dispatch, and queue management CLI commands
|
|
210
|
+
- Messages that exhaust their retry attempts are persisted to a KV-backed store so they can be inspected and replayed.
|
|
211
|
+
- New Quarry commands to manage failed jobs:
|
|
212
|
+
- `queue:failed` — list failed jobs (filter with `--queue`, cap with `--limit`).
|
|
213
|
+
- `queue:retry` — re-dispatch a job by id, a whole queue (`--queue`), or everything (`--all`).
|
|
214
|
+
- `queue:purge` — delete a failed job by id, a whole queue (`--queue`), or everything (`--all`).
|
|
215
|
+
- Messages stay auto-idempotent: every dispatch carries an idempotency key (an explicit `metadata.idempotencyKey`, otherwise a deterministic SHA-256 hash of `type` + `payload`), and an already-processed message is skipped. `idempotency.ttl` bounds how long processed keys are remembered (default 24h).
|
|
216
|
+
- Failed jobs persist indefinitely until retried or purged. Register the opt-in `FailedJobCleanupJob` cron (in a module's `jobs` array) to delete failed jobs older than `failedJobs.retention` (default 7 days); use `failedJobCleanupJob(schedule)` for a custom schedule.
|
|
217
|
+
- The KV store binding is validated at app boot: a missing binding throws a clear, actionable `QueueError` during module initialization instead of failing on every queue invocation.
|
|
218
|
+
- Queue state (idempotency claims and failed jobs) is stored in a KV namespace that defaults to the `CACHE` binding; override it with `store: { binding: 'YOUR_KV' }` in the queue module options.
|
|
219
|
+
|
|
220
|
+
- 13b0e8d: Add precognition request validation, a safe WebSocket send, and cron misconfiguration warnings
|
|
221
|
+
- **Precognition** — send a `Precognition: true` header to run a route's validators (across all parameters, including localized/prefixed routes) and get a `204` without executing the handler, enabling live form validation.
|
|
222
|
+
- **`trySend()`** — gateways can now send a WebSocket message only when the socket is open, returning `false` instead of throwing for closed connections.
|
|
223
|
+
- A warning is now logged when a cron job is registered without a `schedule`, instead of silently skipping it.
|
|
224
|
+
|
|
225
|
+
- 13b0e8d: Add an opt-in isolate-local L1 cache tier and back queue idempotency with it.
|
|
226
|
+
- New `TieredCacheService` (`CACHE_TOKENS.TieredCacheService`) layers an isolate-local in-memory L1 over `CacheService` (KV). It gives read-after-write coherence within an isolate, closing KV's eventual-consistency gap (a `get` can otherwise return an edge-cached value for up to ~60s after a `put`). Same API as `CacheService` plus `binding(name)`, which memoizes a tiered instance per binding so each KV namespace keeps a stable, isolate-lifetime L1.
|
|
227
|
+
- L1 semantics: caches string-backed values only (`text`/`json`); `put`/`delete` are write-through; `text` reads back-populate; `arrayBuffer`/`stream` reads and non-string writes bypass and invalidate L1; `getWithMetadata`/`list` always read KV. FIFO-bounded.
|
|
228
|
+
- Queue idempotency claims and failed-job storage (`QueueStore`) now run through `TieredCacheService`, so a message redelivered to the same warm isolate is de-duplicated even inside KV's consistency window. Delivery remains at-least-once with best-effort de-duplication, not exactly-once. `QueueModule` now imports `CacheModule`.
|
|
229
|
+
- `CacheService` stays a thin KV wrapper (eventually consistent) and gains a `binding(name)` helper plus a `namespace` getter. Use it — not the tiered cache — for read-modify-write counters that need cross-edge freshness (e.g. rate limiting), where an isolate-local L1 would read its own stale value and miss other isolates' writes.
|
|
230
|
+
|
|
231
|
+
- be813bc: Update bundled runtime dependencies to their latest patch releases (Hono, `@swc/core`, `@swc/helpers`)
|
|
232
|
+
|
|
233
|
+
## 0.0.22
|
|
234
|
+
|
|
235
|
+
### Patch Changes
|
|
236
|
+
|
|
237
|
+
- 1658945: Overhaul error handling, rename queue "name" to "binding", add i18n CLI commands, and introduce QuarryRunner
|
|
238
|
+
|
|
239
|
+
### Breaking Changes
|
|
240
|
+
- **`ApplicationError`** — Constructor changed from `(i18nKey, code, metadata?)` to `(message?, cause?)`. Remove error code and i18n key arguments from any subclass `super()` calls. The `code`, `metadata`, `toErrorResponse()`, `toJSON()`, `report()`, and `render()` members are removed.
|
|
241
|
+
- **Error codes removed** — `ERROR_CODES` registry and `ErrorCode` type are deleted. Use plain error messages or custom properties on `HttpException` subclasses instead.
|
|
242
|
+
- **Per-module error consolidation** — Individual error classes (e.g. `QueueBindingNotFoundError`, `CacheGetError`, `ConfigModuleNotInitializedError`) are replaced by single per-module error classes (`QueueError`, `CacheError`, `ConfigError`, etc.). Update any `catch` blocks or `instanceof` checks.
|
|
243
|
+
- **Queue "name" → "binding"** — `@InjectQueue('queue-name')` now takes the exact Cloudflare binding key (e.g. `BACKGROUND_QUEUE`) instead of a kebab-case name. The automatic `kebab-case → UPPER_SNAKE_CASE` conversion is removed. Rename all queue references to match your `wrangler.jsonc` binding names.
|
|
244
|
+
- **`withI18n` renamed to `withZodI18n`** — Update imports from `stratal/i18n` accordingly.
|
|
245
|
+
- **Logger transport system removed** — `ConsoleTransport`, `BaseTransport`, and the transport plugin interface are deleted. The logger now writes directly to console.
|
|
246
|
+
- **`ExceptionHandler` simplified** — The handler no longer translates i18n message keys or builds `ErrorResponse` objects. It renders errors using `HttpException.status` and plain messages.
|
|
247
|
+
|
|
248
|
+
- 4b273ea: Replace tsyringe and reflect-metadata with a built-in dependency injection container and switch i18n engine from @intlify/core-base to intl-messageformat
|
|
249
|
+
|
|
250
|
+
### Breaking Changes
|
|
251
|
+
- **`tsyringe` and `reflect-metadata` removed** — All imports from `tsyringe` (`inject`, `injectable`, `container`, `delay`, `Lifecycle`) must be replaced with equivalents from `stratal/di`. Remove `reflect-metadata` from your dependencies and imports.
|
|
252
|
+
- **`@Transient` decorator renamed to `@Request`** — Update all `@Transient(TOKEN)` usages to `@Request(TOKEN)` for request-scoped services.
|
|
253
|
+
- **`delay()` replaced by `lazy()`** — Replace `delay(() => MyClass)` with `lazy(() => MyClass)` from `stratal/di`.
|
|
254
|
+
- **`scope` removed from module providers** — The `scope` option on `ClassProvider` is removed. Scope is now determined by the class decorator (`@Singleton`, `@Request`). Remove `scope: Scope.Singleton` or `scope: Scope.Request` from provider definitions.
|
|
255
|
+
- **`Scope` enum simplified** — `Scope.Singleton`, `Scope.Request`, and `Scope.Transient` are still available as types, but are no longer passed to module providers. Use `@Singleton()` or `@Request()` decorators on the class instead.
|
|
256
|
+
- **`@intlify/core-base` replaced by `intl-messageformat`** — If you extended `MessageLoaderService` or used `getCoreContext()`, switch to the new `translate(locale, key, params?)` method. The public `I18nService.t()` API is unchanged.
|
|
257
|
+
- **`setupI18nCompiler()` removed** — No manual compiler setup is needed. Remove any calls to this function.
|
|
258
|
+
- **OpenAPI Swagger UI is now dynamically imported** — No action required; reduces initial bundle size.
|
|
259
|
+
|
|
260
|
+
## 0.0.21
|
|
261
|
+
|
|
262
|
+
### Patch Changes
|
|
263
|
+
|
|
264
|
+
- 3489cfd: Warn when a scheduled cron trigger doesn't match any registered job
|
|
265
|
+
|
|
266
|
+
`CronManager` now logs a warning (with the incoming cron expression and the list of registered schedules) when Cloudflare invokes a `scheduled()` trigger that no `@Cron` job is registered for. Previously the call returned silently, making misconfigured cron triggers in `wrangler.toml` invisible.
|
|
267
|
+
|
|
268
|
+
- 3489cfd: Match locale-prefixed routes ahead of their primary so catch-alls don't swallow the locale segment
|
|
269
|
+
|
|
270
|
+
Routes registered with `Router.locales(...)` previously sorted **after** their primary, so a request like `/sw/applications/123` against a primary catch-all (`/:slug{.+}`) was matched as `slug='sw/applications/123'` instead of `locale='sw' + slug='applications/123'`. Locale variants now sort just ahead of their primary using the path-with-locale-stripped score plus their extra segment count as the tie-breaker, restoring the expected priority for both static and catch-all routes.
|
|
271
|
+
|
|
272
|
+
## 0.0.20
|
|
273
|
+
|
|
274
|
+
### Patch Changes
|
|
275
|
+
|
|
276
|
+
- f8c61e1: Preserve forward slashes when encoding catch-all path parameters
|
|
277
|
+
|
|
278
|
+
URL generation previously percent-encoded `/` inside path-param values, so a value like `'auth/login'` for a catch-all route (`:slug{.+}`) became `'auth%2Flogin'`. Each segment is now encoded individually, so slash-containing values round-trip cleanly while single segments still behave like `encodeURIComponent`.
|
|
279
|
+
|
|
280
|
+
- f8c61e1: Add stricter `cuid2()` validator as a drop-in for `z.cuid2()`
|
|
281
|
+
|
|
282
|
+
Zod's built-in `z.cuid2()` accepts any non-empty lowercase-alphanumeric string, which makes it ineffective as a tenant-id or external-id validator. The new `cuid2()` helper from `stratal/validation` enforces the actual cuid2 shape (24-32 chars, leading letter) while preserving the OpenAPI `format: 'cuid2'` metadata. Custom regex and i18n-aware error messages are supported.
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
import { cuid2 } from "stratal/validation";
|
|
286
|
+
|
|
287
|
+
z.object({ tenantId: cuid2() });
|
|
288
|
+
z.object({ tenantId: cuid2({ pattern: /^[a-z][0-9a-z]{23}$/ }) });
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Also exports `CUID2_REGEX` for callers composing the pattern into custom schemas.
|
|
292
|
+
|
|
293
|
+
- f8c61e1: Add `RateLimiterModule` for request throttling with KV and in-memory stores
|
|
294
|
+
- New opt-in `RateLimiterModule` configurable with `forRoot({ store: 'kv', binding })` or `forRoot({ store: 'memory' })` (or a custom `IRateLimiterStore`).
|
|
295
|
+
- `Limit` builder API with `perSecond`, `perSeconds`, `perMinute`, `perMinutes`, `perHour`, `perDay`, and `none()` helpers; `.by(key)` scopes per-actor and `.response(handler)` overrides the default 429.
|
|
296
|
+
- `RateLimiterRegistry.for(name, resolver)` defines named limiters; apply them with `router.throttle(name)` or the `@RateLimit(name)` decorator on controllers and route methods.
|
|
297
|
+
- `TooManyRequestsError` returns HTTP 429 with `Retry-After` and `X-RateLimit-*` headers automatically; the body honors content negotiation (JSON, HTML, Inertia).
|
|
298
|
+
- Misconfiguration surfaces at boot (missing `forRoot`) rather than on the first throttled request.
|
|
299
|
+
|
|
300
|
+
- f8c61e1: Add Cloudflare request properties and full-record access on `RouterContext`
|
|
301
|
+
- New `ctx.cf` getter exposes Cloudflare-provided request properties (geo, TLS, bot management, etc.) as `CfProperties`.
|
|
302
|
+
- `ctx.param()` (no args) now returns the full validated param record as `Record<string, string>`. The single-key overload (`ctx.param('id')`) is unchanged. The same overload is available on `GatewayContext` for WebSocket gateways.
|
|
303
|
+
|
|
304
|
+
- f8c61e1: Add `trailingSlash` application option for canonical URL handling
|
|
305
|
+
|
|
306
|
+
A new `trailingSlash` field on `ApplicationConfig` controls how incoming paths and generated URLs handle a trailing `/`:
|
|
307
|
+
- `'ignore'` (default) — both `/foo` and `/foo/` resolve to the same route; URL helpers leave paths unchanged.
|
|
308
|
+
- `'always'` — non-trailing requests are 308-redirected to the trailing-slash form; URL helpers append `/`. Paths whose last segment looks file-like (e.g. `/api/openapi.json`) are skipped.
|
|
309
|
+
- `'never'` — trailing requests are 308-redirected to the non-trailing form; URL helpers strip a single trailing `/`.
|
|
310
|
+
|
|
311
|
+
`Uri.to()`, `Uri.url()`, `Uri.current()`, `Uri.full()`, and the global `route()` helper all apply the configured mode. 308 preserves request method and body, and `Location` headers are emitted as path-relative URIs to avoid mixed-content issues behind HTTPS-terminating proxies.
|
|
312
|
+
|
|
313
|
+
## 0.0.19
|
|
314
|
+
|
|
315
|
+
### Patch Changes
|
|
316
|
+
|
|
317
|
+
- 3b16f5b: Resolve cron jobs from request-scoped DI container at execution time
|
|
318
|
+
|
|
319
|
+
### Breaking Changes
|
|
320
|
+
- `CronManager.registerJob()` now accepts `(schedule, jobClass)` instead of a `CronJob` instance. Jobs are resolved from the container at execution time, ensuring request-scoped dependencies (e.g. database connections) are properly scoped.
|
|
321
|
+
- `CronManager.executeScheduled()` now requires a `Container` as its second argument.
|
|
322
|
+
- `CronManager.getJobsForSchedule()` returns `RegisteredJob[]` instead of `CronJob[]`.
|
|
323
|
+
|
|
324
|
+
- 5d26c24: Rearchitect i18n module augmentation to a per-module keyed registry (breaking change)
|
|
325
|
+
|
|
326
|
+
**Why:** Multiple modules augmenting `AppMessages` with a shared top-level parent (e.g., `errors.auth`, `errors.uploads`, `errors.branding`) collided with TypeScript error **TS2717** ("Subsequent property declarations must have the same type"). Interface merging adds new properties across declarations but requires same-named properties to have structurally identical types — it does not deep-merge nested shapes.
|
|
327
|
+
|
|
328
|
+
**What changed:**
|
|
329
|
+
- Replaced the single augmentable `AppMessages` interface with an `AppMessageNamespaces` keyed registry. Each module declares its own distinct top-level key (Laravel-style package namespacing). Because each declaration adds a different property, interface merging accepts them all.
|
|
330
|
+
- `AppMessages` is now derived: `{ [K in keyof AppMessageNamespaces]: AppMessageNamespaces[K] }`.
|
|
331
|
+
- Access keys are unchanged dot-notation — `i18n.t('auth.errors.invalidCredentials')` — so no custom resolver is needed.
|
|
332
|
+
|
|
333
|
+
**Migration:**
|
|
334
|
+
|
|
335
|
+
Before:
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
declare module "stratal/i18n" {
|
|
339
|
+
interface AppMessages {
|
|
340
|
+
errors: { uploads: { notFound: string } };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
After:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
declare module "stratal/i18n" {
|
|
349
|
+
interface AppMessageNamespaces {
|
|
350
|
+
uploads: { errors: { notFound: string } };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Framework package moves:**
|
|
356
|
+
- All `errors.auth.*` keys (previously split between `stratal` core and `@stratal/framework`) now live in the auth module as `auth.errors.*`. `errors.auth.org.*` → `auth.org.*`. The `errors.auth.*` namespace has been removed from `stratal`'s core messages.
|
|
357
|
+
- `@stratal/framework`'s `DatabaseModule` now registers its `database.*` validation messages via `I18nModule.registerMessages` (previously the messages file existed but was never wired up).
|
|
358
|
+
- `@stratal/inertia-modal`'s `errors.modal.*` key moved to `modal.errors.*`.
|
|
359
|
+
|
|
360
|
+
**Callsite updates required in downstream apps:**
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
// Before
|
|
364
|
+
new ApplicationError('errors.auth.invalidCredentials', ...)
|
|
365
|
+
i18n.t('errors.auth.org.organizationNotFound')
|
|
366
|
+
|
|
367
|
+
// After
|
|
368
|
+
new ApplicationError('auth.errors.invalidCredentials', ...)
|
|
369
|
+
i18n.t('auth.org.organizationNotFound')
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
No runtime API change: `I18nModule.registerMessages(messages)` keeps its existing signature, and deep-merge behavior is unchanged. Locale-only contributions that override core's built-in `errors.*` / `common.*` / etc. continue to work.
|
|
373
|
+
|
|
374
|
+
- 3b16f5b: Add `Macroable` base class for dynamic method registration and introduce `ConfigStore` for request-scoped configuration
|
|
375
|
+
- Add `Macroable` class (inspired by Laravel/AdonisJS) that supports `macro()`, `instanceProperty()`, and `getter()` for runtime method registration with full inheritance support.
|
|
376
|
+
- Introduce `ConfigStore` as a singleton source of truth for validated config, making `ConfigService` request-scoped with per-request overrides via `set()` and `reset()`.
|
|
377
|
+
- `ConfigService` now extends `Macroable`, allowing apps to add domain-specific getters and methods.
|
|
378
|
+
|
|
379
|
+
- 3b16f5b: Improve middleware error handling and defer routing initialization for better performance
|
|
380
|
+
- Add `MiddlewareNextCalledMultipleTimesError` to detect and report when `next()` is called more than once in a middleware.
|
|
381
|
+
- Defer routing and handler initialization until first request for improved cold-start performance.
|
|
382
|
+
- Improve `isApplicationError` type guard with structural fallback for cross-module boundary cases.
|
|
383
|
+
|
|
384
|
+
- 5d26c24: Prevent `quarry` from breaking a concurrent `wrangler dev` session
|
|
385
|
+
|
|
386
|
+
Running a Quarry command while `wrangler dev` was active could overwrite the worker's entry in the local dev registry, causing peer workers to fail service-binding RPC calls (e.g. `couldn't find a local dev session for the X entrypoint`). Quarry now registers its ephemeral miniflare under a unique per-process worker name, leaving the running dev session's registry entry untouched.
|
|
387
|
+
|
|
388
|
+
- 5d26c24: Fix route path joining to avoid double slashes and handle empty route paths
|
|
389
|
+
|
|
390
|
+
Composing a controller base path with an empty `@Route({ path: '' })` or a base path ending in `/` could previously yield URLs with double slashes or a missing trailing route. Empty route paths now resolve to the controller's base path, and trailing slashes on the base path are stripped consistently.
|
|
391
|
+
|
|
392
|
+
- 3b16f5b: Migrate storage from AWS S3 to Cloudflare R2 for all storage operations
|
|
393
|
+
|
|
394
|
+
### Breaking Changes
|
|
395
|
+
- The `S3StorageProvider` has been removed. All storage operations now use the native Cloudflare R2 API via `R2StorageProvider`.
|
|
396
|
+
- Storage configuration no longer requires AWS credentials or S3 endpoint settings. Instead, configure an R2 bucket binding in your `wrangler.toml` and reference it in your storage config.
|
|
397
|
+
- Presigned URLs now require the `APP_SECRET` environment variable instead of AWS credentials.
|
|
398
|
+
- The `StorageProviderNotSupportedError` has been replaced with `R2BindingNotFoundError` and `R2PresignedUrlSecretMissingError`.
|
|
399
|
+
|
|
400
|
+
### Migration
|
|
401
|
+
1. Replace any `S3StorageProvider` references with `R2StorageProvider`.
|
|
402
|
+
2. Update your `wrangler.toml` to bind your R2 bucket.
|
|
403
|
+
3. Set `APP_SECRET` in your environment for presigned URL support.
|
|
404
|
+
4. Remove AWS SDK credentials from your environment.
|
|
405
|
+
|
|
406
|
+
## 0.0.18
|
|
407
|
+
|
|
408
|
+
### Patch Changes
|
|
409
|
+
|
|
410
|
+
- fcb71c4: Add stub exports for Cloudflare Workers APIs in the virtual ESM loader used by Quarry CLI
|
|
411
|
+
- 17f8675: Add `ExceptionHandler` with customizable error reporting, rendering, and throttling support
|
|
412
|
+
|
|
413
|
+
### Details
|
|
414
|
+
- Introduce `ExceptionHandler` base class with `report()`, `render()`, `shouldReport()`, and `throttle()` hooks
|
|
415
|
+
- Add `HttpException` class for structured HTTP error responses with fluent API
|
|
416
|
+
- Add `ExceptionContext` for collecting contextual metadata during error handling
|
|
417
|
+
- Replace `GlobalErrorHandler` with the new `ExceptionHandler` pipeline
|
|
418
|
+
- Add `stratal` as a CLI bin alias for `quarry`
|
|
419
|
+
- Streamline OpenAPI service and routing metadata handling
|
|
420
|
+
|
|
421
|
+
### Breaking Changes
|
|
422
|
+
- `GlobalErrorHandler` has been removed. Migrate to `ExceptionHandler` by extending the base class and implementing the `render()` hook for custom error responses.
|
|
423
|
+
|
|
424
|
+
- c9176ea: Enhance i18n locale detection with configurable strategies and message loader service
|
|
425
|
+
|
|
426
|
+
### Details
|
|
427
|
+
- Support multiple locale detection strategies: cookie, header, querystring, and path-based
|
|
428
|
+
- Add `MessageLoaderService` for dynamic message loading and registration
|
|
429
|
+
- Add `stratal/i18n/utils` subpath export for i18n setup utilities
|
|
430
|
+
|
|
431
|
+
- c9176ea: Add Laravel-style routing with named routes, URI generation, signed URLs, domain routing, and response validation
|
|
432
|
+
|
|
433
|
+
### Details
|
|
434
|
+
- Add `Uri` service for generating URLs from named routes with parameter binding
|
|
435
|
+
- Add signed URL support with HMAC-based signature generation and verification
|
|
436
|
+
- Add domain-based routing with `@Route({ domain })` and domain middleware
|
|
437
|
+
- Add response validation to verify route handler responses match OpenAPI schemas
|
|
438
|
+
- Add `RouteRegistry` for route name lookups and `RouteMap` for serialized route definitions
|
|
439
|
+
- Add `RouterResolver` for programmatic route resolution and middleware chain composition
|
|
440
|
+
- Add `LocalePathService` for locale-aware URL path handling
|
|
441
|
+
- Add `route:types` Quarry command for generating typed route helpers
|
|
442
|
+
- Replace module-level middleware system with router-scoped middleware via `RouteConfigurable`
|
|
443
|
+
|
|
444
|
+
### Breaking Changes
|
|
445
|
+
- The `stratal/middleware` subpath export has been removed. Middleware is now configured through the router using `RouteConfigurable` instead of `MiddlewareConfigurable`. Implement `configureRoutes(router: Router)` on your module and use `router.use(...)` to apply middleware.
|
|
446
|
+
|
|
447
|
+
## 0.0.17
|
|
448
|
+
|
|
449
|
+
### Patch Changes
|
|
450
|
+
|
|
451
|
+
- [#147](https://github.com/strataljs/stratal/pull/147) [`7f2772b`](https://github.com/strataljs/stratal/commit/7f2772ba90a9b6a91603f79293d384e972864125) Thanks [@adesege](https://github.com/adesege)! - Add MCP server support and API CLI commands
|
|
452
|
+
|
|
453
|
+
### Details
|
|
454
|
+
- Add `mcp:serve` command to start a stdio MCP server that exposes OpenAPI routes as tools
|
|
455
|
+
- Add `mcp:tools` command to list available MCP tools derived from the OpenAPI spec
|
|
456
|
+
- Add `api` command to invoke API endpoints directly from the CLI
|
|
457
|
+
- Add `OpenApiToolsService` for converting OpenAPI specs into tool definitions, reusable across MCP, CLI, and custom tooling
|
|
458
|
+
|
|
459
|
+
- [`6cccfef`](https://github.com/strataljs/stratal/commit/6cccfefdde703c5c6eaba199d05307ab9fe36085) Thanks [@adesege](https://github.com/adesege)! - Add `stratal/storage/providers` sub-path export for direct access to storage provider implementations
|
|
460
|
+
|
|
461
|
+
- [#145](https://github.com/strataljs/stratal/pull/145) [`79e05de`](https://github.com/strataljs/stratal/commit/79e05de7482c925323a2f37a00e47929133a979f) Thanks [@adesege](https://github.com/adesege)! - Enhance Quarry CLI with dynamic command generation, improved help output, and usage generator
|
|
462
|
+
|
|
463
|
+
### Details
|
|
464
|
+
- Replace static `ListCommand` with dynamic command generation via `createDynamicCommands` that auto-registers user-defined commands with Clipanion
|
|
465
|
+
- Improve `HelpCommand` to display detailed usage for specific commands including arguments, options, and aliases
|
|
466
|
+
- Add `UsageGenerator` for rendering formatted command usage with ANSI colors (name, description, arguments, options sections)
|
|
467
|
+
- Add `colors` utility module for ANSI terminal color output
|
|
468
|
+
- Add `QuarryRegistry.list()` method to retrieve all registered command entries
|
|
469
|
+
- Add comprehensive tests for dynamic commands, help command, and usage generator
|
|
470
|
+
|
|
471
|
+
- [`3c89c14`](https://github.com/strataljs/stratal/commit/3c89c147fca366382c0771bb442f29a6fc73601e) Thanks [@adesege](https://github.com/adesege)! - Fix module registry to prevent duplicate command registration and exclude internal module property from dynamic module providers
|
|
472
|
+
|
|
473
|
+
- [`916fd90`](https://github.com/strataljs/stratal/commit/916fd90727a06b5ce7c0397467fe9dc1f859f841) Thanks [@adesege](https://github.com/adesege)! - Add `I18nModule.registerMessages()` for decentralized i18n message registration
|
|
474
|
+
|
|
475
|
+
### Details
|
|
476
|
+
- Any module can now call `I18nModule.registerMessages()` to contribute translations, enabling package-level message ownership
|
|
477
|
+
- Messages are deep-merged across all registrations in order — later calls override earlier ones at leaf level
|
|
478
|
+
- `RouterContext.json()` now accepts `null` and automatically returns 204 No Content
|
|
479
|
+
|
|
480
|
+
### Breaking Changes
|
|
481
|
+
- Remove `messages` option from `I18nModule.forRoot()` — use `I18nModule.registerMessages()` instead
|
|
482
|
+
|
|
483
|
+
**Before:**
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
I18nModule.forRoot({
|
|
487
|
+
defaultLocale: 'en',
|
|
488
|
+
messages: { en: { ... }, fr: { ... } },
|
|
489
|
+
})
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**After:**
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
I18nModule.forRoot({ defaultLocale: 'en' }),
|
|
496
|
+
I18nModule.registerMessages({ en: { ... }, fr: { ... } }),
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
- [`cbfce8b`](https://github.com/strataljs/stratal/commit/cbfce8b3a3517b60d94f500c5dc1ef68d8ee76f4) Thanks [@adesege](https://github.com/adesege)! - Support multiple seeder names in `db:seed` command via variadic `{names*}` argument
|
|
500
|
+
|
|
501
|
+
### Details
|
|
502
|
+
- Change `db:seed {name?}` to `db:seed {names*}` to accept multiple seeder class names in a single invocation
|
|
503
|
+
- When `--all` is used with named seeders, warn and ignore the names
|
|
504
|
+
- Iterate over all provided names, running each seeder sequentially
|
|
505
|
+
|
|
506
|
+
## 0.0.16
|
|
507
|
+
|
|
508
|
+
### Patch Changes
|
|
509
|
+
|
|
510
|
+
- [#144](https://github.com/strataljs/stratal/pull/144) [`3dd0bc8`](https://github.com/strataljs/stratal/commit/3dd0bc84c8638db30db7b70f3532a44aa187ace8) Thanks [@adesege](https://github.com/adesege)! - Enhance Quarry CLI with dynamic command generation, improved help output, and usage generator
|
|
511
|
+
|
|
512
|
+
### Details
|
|
513
|
+
- Replace static `ListCommand` with dynamic command generation via `createDynamicCommands` that auto-registers user-defined commands with Clipanion
|
|
514
|
+
- Improve `HelpCommand` to display detailed usage for specific commands including arguments, options, and aliases
|
|
515
|
+
- Add `UsageGenerator` for rendering formatted command usage with ANSI colors (name, description, arguments, options sections)
|
|
516
|
+
- Add `colors` utility module for ANSI terminal color output
|
|
517
|
+
- Add `QuarryRegistry.list()` method to retrieve all registered command entries
|
|
518
|
+
- Add comprehensive tests for dynamic commands, help command, and usage generator
|
|
519
|
+
|
|
520
|
+
- [#142](https://github.com/strataljs/stratal/pull/142) [`4b958e2`](https://github.com/strataljs/stratal/commit/4b958e250c99681a99a34a398fbf706546f556cc) Thanks [@adesege](https://github.com/adesege)! - Lazy-load S3 storage provider and enhance StorageManagerService with promise deduplication
|
|
521
|
+
|
|
522
|
+
### Details
|
|
523
|
+
- `StorageManager.getProvider()` is now async and dynamically imports `S3StorageProvider` to avoid loading AWS SDK at module evaluation time
|
|
524
|
+
- Add promise deduplication to prevent concurrent `getProvider` calls from creating multiple provider instances
|
|
525
|
+
- Register `StorageManager` as a singleton to share cached providers across requests
|
|
526
|
+
- Move `reflect-metadata` from hard dependency to optional peer dependency
|
|
527
|
+
- Remove `@tus/server` peer dependency
|
|
528
|
+
- Remove direct exports of `S3StorageProvider` and S3 multipart types from the storage barrel — use dynamic import instead
|
|
529
|
+
|
|
530
|
+
## 0.0.15
|
|
531
|
+
|
|
532
|
+
### Patch Changes
|
|
533
|
+
|
|
534
|
+
- [#125](https://github.com/strataljs/stratal/pull/125) [`0731e99`](https://github.com/strataljs/stratal/commit/0731e99c3e0c96f988387611f0ef8559b63d7bd8) Thanks [@adesege](https://github.com/adesege)! - Introduce Quarry command framework with auto-discovery and Clipanion-based CLI
|
|
535
|
+
|
|
536
|
+
### Details
|
|
537
|
+
- Add `Command` base class with declarative signature parsing (arguments, options, flags)
|
|
538
|
+
- Add `QuarryRegistry` for command registration, discovery from modules, and execution
|
|
539
|
+
- Add `quarry` CLI bin (`npx quarry`) with Clipanion-based command routing
|
|
540
|
+
- Add virtual `cloudflare:workers` ESM loader hook for Node compatibility
|
|
541
|
+
- Built-in commands: `list`, `help <command>`, and dynamic command dispatch
|
|
542
|
+
- Auto-discover commands from module `providers` via `isCommand()` utility
|
|
543
|
+
- Support usage/help generation with `UsageGenerator`
|
|
544
|
+
- Custom error types: `CommandError`, `CommandNotFoundError`
|
|
545
|
+
- New sub-path export `stratal/quarry`
|
|
546
|
+
- New dependencies: `clipanion`, `@swc-node/register`
|
|
547
|
+
|
|
548
|
+
- [#134](https://github.com/strataljs/stratal/pull/134) [`52f1daa`](https://github.com/strataljs/stratal/commit/52f1daa981f5a38b983bb3c14abfefb663eb6941) Thanks [@adesege](https://github.com/adesege)! - Move seeders from standalone `@stratal/seeders` package into core as `stratal/seeder`
|
|
549
|
+
|
|
550
|
+
### Details
|
|
551
|
+
- Add `Seeder` abstract base class with `run()` and `call(OtherSeeder)` methods
|
|
552
|
+
- Add `SeederRegistry` for seeder registration and execution
|
|
553
|
+
- Auto-discover seeders from module `providers` (any class extending `Seeder`)
|
|
554
|
+
- Add built-in Quarry commands: `db:seed {name?} {--all}`, `db:seed:list`
|
|
555
|
+
- Seeders execute within request-scoped DI containers with full access to injected services
|
|
556
|
+
- Use DI-resolved seeders instead of manual instantiation
|
|
557
|
+
- Route unexpected command errors through `GlobalErrorHandler`
|
|
558
|
+
- Remove standalone `packages/seeders` package — all seeder functionality now lives in core
|
|
559
|
+
- New sub-path export: `stratal/seeder`
|
|
560
|
+
|
|
561
|
+
## 0.0.14
|
|
562
|
+
|
|
563
|
+
### Patch Changes
|
|
564
|
+
|
|
565
|
+
- [#124](https://github.com/strataljs/stratal/pull/124) [`59251d3`](https://github.com/strataljs/stratal/commit/59251d32743cbd461f952985f192a68cb7ccdb91) Thanks [@adesege](https://github.com/adesege)! - Export `instancePerContainerCachingFactory` from tsyringe via `stratal/di`
|
|
566
|
+
|
|
567
|
+
- [#122](https://github.com/strataljs/stratal/pull/122) [`47530bd`](https://github.com/strataljs/stratal/commit/47530bd31bc91329788b4ba7b03a389f0e722f46) Thanks [@adesege](https://github.com/adesege)! - Migrate build system from tsc to tsdown for faster builds and code-splitting support
|
|
568
|
+
|
|
569
|
+
## 0.0.13
|
|
570
|
+
|
|
571
|
+
### Patch Changes
|
|
572
|
+
|
|
573
|
+
- [#120](https://github.com/strataljs/stratal/pull/120) [`8d0df50`](https://github.com/strataljs/stratal/commit/8d0df506411bc725ef4e4eaf4efdb314b3384d98) Thanks [@adesege](https://github.com/adesege)! - Add WebSocket gateway support with `@Gateway`, `@OnMessage`, `@OnClose`, and `@OnError` decorators
|
|
574
|
+
|
|
575
|
+
### Details
|
|
576
|
+
- `@Gateway(path, options?)` decorator marks a class as a WebSocket gateway, reusing controller route metadata for middleware compatibility. Accepts optional `GatewayOptions` with `version` support (single, array, or `VERSION_NEUTRAL`)
|
|
577
|
+
- `@OnMessage()`, `@OnClose()`, `@OnError()` method decorators wire handler methods to WebSocket events
|
|
578
|
+
- `GatewayContext` extends `RouterContext` with WebSocket-specific methods (`send()`, `close()`, `readyState`)
|
|
579
|
+
- `GatewayContext` overrides `param()` and `query()` to use raw Hono request methods (no OpenAPI validation for WebSocket upgrade requests)
|
|
580
|
+
- `GatewayContext.body()` throws `WebSocketBodyNotAvailableError` — WebSocket upgrade requests have no body
|
|
581
|
+
- Gateways support versioning and class-level guards
|
|
582
|
+
- New `stratal/websocket` sub-path export with `GatewayOptions` type
|
|
583
|
+
|
|
584
|
+
- [#117](https://github.com/strataljs/stratal/pull/117) [`527f675`](https://github.com/strataljs/stratal/commit/527f675ea3b4cdb98165cbe1f81e820fa9e79490) Thanks [@adesege](https://github.com/adesege)! - Add configurable content type support for request and response bodies in route definitions
|
|
585
|
+
|
|
586
|
+
### Details
|
|
587
|
+
- Add `RouteBodyObject` and `RouteResponseObject` types with optional `contentType` field
|
|
588
|
+
- Support `{ schema, contentType }` object form for `body` and `response` in `@Route()` config
|
|
589
|
+
- Bare `ZodType` values default to `application/json` (backward-compatible)
|
|
590
|
+
- Export new types: `RouteBody`, `RouteBodyObject`, `RouteResponseObject`
|
|
591
|
+
- Add `DEFAULT_CONTENT_TYPE` constant
|
|
592
|
+
- Error response schemas always use `application/json` regardless of route content type
|
|
593
|
+
|
|
594
|
+
- [#115](https://github.com/strataljs/stratal/pull/115) [`bb99119`](https://github.com/strataljs/stratal/commit/bb991196dbcc55963d16ee1a6f5db580c18c796a) Thanks [@adesege](https://github.com/adesege)! - Replace Scalar with Swagger UI as the default OpenAPI docs renderer and add pluggable UI support
|
|
595
|
+
|
|
596
|
+
### Details
|
|
597
|
+
- Replace `@scalar/hono-api-reference` dependency with `@hono/swagger-ui`
|
|
598
|
+
- Add `OpenAPIUIRenderer` type for custom docs UI renderers
|
|
599
|
+
- Add `ui` option to `OpenAPIModuleOptions` with `path` and `renderer` fields
|
|
600
|
+
- Support disabling docs UI entirely by setting `ui: false`
|
|
601
|
+
- Remove `docsPath` option in favor of `ui.path` (default remains `/api/docs`)
|
|
602
|
+
|
|
603
|
+
### Breaking Changes
|
|
604
|
+
- The `docsPath` option in `OpenAPIModuleOptions` has been removed. Use `ui.path` instead:
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
// Before
|
|
608
|
+
OpenAPIModule.forRoot({ docsPath: "/docs" });
|
|
609
|
+
|
|
610
|
+
// After
|
|
611
|
+
OpenAPIModule.forRoot({ ui: { path: "/docs" } });
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
- The default docs UI is now Swagger UI instead of Scalar. To use a custom renderer (e.g., Scalar), provide a `ui.renderer` function.
|
|
615
|
+
|
|
616
|
+
- [#119](https://github.com/strataljs/stratal/pull/119) [`957de6e`](https://github.com/strataljs/stratal/commit/957de6e88684344bf26e95d03187345bf77f4f52) Thanks [@adesege](https://github.com/adesege)! - Remove redundant `i18nKey` property from `ApplicationError` and use `Error.message` instead
|
|
617
|
+
|
|
618
|
+
### Details
|
|
619
|
+
- Remove `i18nKey` property — the i18n key is already stored in `Error.message` via `super(i18nKey)`
|
|
620
|
+
- `toErrorResponse()` now uses `this.message` for fallback and stack trace rewriting
|
|
621
|
+
- `GlobalErrorHandler.translateError()` casts `error.message as MessageKeys` for i18n lookup
|
|
622
|
+
- Stack traces in development mode now rewrite the first line with the translated message for readable debugging
|
|
623
|
+
|
|
624
|
+
- [#118](https://github.com/strataljs/stratal/pull/118) [`0ade941`](https://github.com/strataljs/stratal/commit/0ade94162f9058e9230039fa72efbbf3e57cf572) Thanks [@adesege](https://github.com/adesege)! - Add streaming response methods (`stream`, `streamText`, `streamSSE`) to RouterContext
|
|
625
|
+
|
|
626
|
+
### Details
|
|
627
|
+
- `stream()` — generic/binary streaming via Hono's `stream` helper
|
|
628
|
+
- `streamText()` — text streaming with automatic `Content-Encoding: Identity` for Cloudflare Workers compatibility
|
|
629
|
+
- `streamSSE()` — Server-Sent Events streaming with automatic `Content-Encoding: Identity` for Cloudflare Workers compatibility
|
|
630
|
+
- Re-export `StreamingApi`, `SSEStreamingApi`, and `SSEMessage` types from `stratal/router`
|
|
631
|
+
|
|
632
|
+
## 0.0.12
|
|
633
|
+
|
|
634
|
+
### Patch Changes
|
|
635
|
+
|
|
636
|
+
- [#113](https://github.com/strataljs/stratal/pull/113) [`11b0da9`](https://github.com/strataljs/stratal/commit/11b0da97ef436bffef592fbc34685bbcc85d7ef7) Thanks [@adesege](https://github.com/adesege)! - Add HTTP method decorators (`@Get`, `@Post`, `@Put`, `@Patch`, `@Delete`, `@All`) for explicit route handling as an alternative to convention-based `@Route()` routing
|
|
637
|
+
|
|
638
|
+
### Details
|
|
639
|
+
- **stratal**
|
|
640
|
+
- Add `@Get`, `@Post`, `@Put`, `@Patch`, `@Delete`, and `@All` decorators that accept an explicit path and optional `RouteConfig`
|
|
641
|
+
- Routes decorated with `@All` are automatically hidden from OpenAPI documentation
|
|
642
|
+
- Enforce mutual exclusivity: a controller cannot mix `@Route()` with HTTP method decorators
|
|
643
|
+
- Add `statusCode` option to `RouteConfig` for explicit status code control in HTTP method decorators
|
|
644
|
+
- Add `HttpRouteMetadata` type and `'all'` to the `HttpMethod` union
|
|
645
|
+
- Remove `statusCode` from the `@Route()` decorator signature (status codes are auto-derived from method names in convention-based routing)
|
|
646
|
+
- Export new decorators and helpers (`getHttpRouteMetadata`, `getHttpDecoratedMethods`) from `stratal/router`
|
|
647
|
+
|
|
648
|
+
- [#114](https://github.com/strataljs/stratal/pull/114) [`e1a2ba2`](https://github.com/strataljs/stratal/commit/e1a2ba2da883481d192a15b8015456705982d683) Thanks [@adesege](https://github.com/adesege)! - Add URI-based API versioning support with configurable version prefix and default version
|
|
649
|
+
|
|
650
|
+
### Details
|
|
651
|
+
- **stratal**
|
|
652
|
+
- Add `versioning` option to `ApplicationConfig` to enable URI-based versioning (e.g., `/v1/users`, `/v2/users`)
|
|
653
|
+
- Add `version` option to `ControllerOptions` for per-controller version assignment (single, array, or `VERSION_NEUTRAL`)
|
|
654
|
+
- Add `VERSION_NEUTRAL` sentinel symbol to opt controllers out of versioning even when a `defaultVersion` is set
|
|
655
|
+
- Add `VersioningOptions` type with `prefix` (default `'v'`) and `defaultVersion` fields
|
|
656
|
+
- Add `getControllerVersion()` helper exported from `stratal/router`
|
|
657
|
+
- Extend `RouteRegistrationService` to resolve versioned paths for all route registration patterns (wildcard, OpenAPI, HTTP method, RESTful)
|
|
658
|
+
- Extend `MiddlewareConfigurationService` to resolve versioned `RouteInfo` targets with `version` field
|
|
659
|
+
- Add `version` field to `RouteInfo` middleware type for targeting versioned middleware routes
|
|
660
|
+
- Export `VersioningOptions` and `VERSION_NEUTRAL` from `stratal/router`
|
|
661
|
+
|
|
662
|
+
- [#97](https://github.com/strataljs/stratal/pull/97) [`d58b878`](https://github.com/strataljs/stratal/commit/d58b8782848562a50b79cd558eaf01978aa77f26) Thanks [@adesege](https://github.com/adesege)! - Add `stratalTest()` vitest plugin and migrate fetch mocking from Cloudflare's undici-based `fetchMock` to MSW
|
|
663
|
+
|
|
664
|
+
### Details
|
|
665
|
+
- **@stratal/testing**
|
|
666
|
+
- Add `@stratal/testing/vitest-plugin` sub-export with `stratalTest()` — wraps `cloudflareTest` with Stratal defaults (tslib alias, ZenStack mocks, SSR externals)
|
|
667
|
+
- Replace `FetchMock`/`createFetchMock` with `MockFetch`/`createMockFetch` backed by MSW (`setupServer`)
|
|
668
|
+
- Re-export `http` and `HttpResponse` from `msw` for convenience
|
|
669
|
+
- Update `cloudflare:test` imports to `cloudflare:workers`
|
|
670
|
+
- Bump vitest peer dependency from `^3.2.0` to `^4.1.0`
|
|
671
|
+
|
|
672
|
+
- **stratal**
|
|
673
|
+
- Update test mocks to use class syntax for Vitest 4 compatibility
|
|
674
|
+
- Bump dependencies: `@intlify/*`, `@scalar/hono-api-reference`, `hono`, `@aws-sdk/*`, `vitest`
|
|
675
|
+
|
|
676
|
+
- **@stratal/framework**
|
|
677
|
+
- Refactor vitest config to use `stratalTest()` plugin, removing manual pool/alias config
|
|
678
|
+
- Bump dependencies: `better-auth`, `@zenstackhq/*`, `wrangler`, `vitest`
|
|
679
|
+
|
|
680
|
+
### Breaking Changes
|
|
681
|
+
- **@stratal/testing**: `FetchMock` and `createFetchMock` are removed. Use `MockFetch`/`createMockFetch` instead. The new API uses MSW lifecycle methods (`listen`/`reset`/`close`) instead of `activate`/`disableNetConnect`/`deactivate`.
|
|
682
|
+
- **@stratal/testing**: Vitest peer dependency is now `^4.1.0` (was `^3.2.0`).
|
|
683
|
+
|
|
684
|
+
## 0.0.11
|
|
685
|
+
|
|
686
|
+
### Patch Changes
|
|
687
|
+
|
|
688
|
+
- [#92](https://github.com/strataljs/stratal/pull/92) [`bae01ef`](https://github.com/strataljs/stratal/commit/bae01eff7cb7f520ad00206377d9f5f4968076b6) Thanks [@adesege](https://github.com/adesege)! - Update symbol tokens to use 'stratal' namespace for consistency across modules
|
|
689
|
+
|
|
690
|
+
## 0.0.10
|
|
691
|
+
|
|
692
|
+
### Patch Changes
|
|
693
|
+
|
|
694
|
+
- [#88](https://github.com/strataljs/stratal/pull/88) [`3329d20`](https://github.com/strataljs/stratal/commit/3329d20658ea6a6f7cadbbb3efb7630b1cca9ad2) Thanks [@adesege](https://github.com/adesege)! - Add worker base classes (`StratalDurableObject`, `StratalWorkerEntrypoint`, `StratalWorkflow`) with DI support and request-scoped containers
|
|
695
|
+
|
|
696
|
+
### Details
|
|
697
|
+
- Introduce `stratal/workers` sub-path export with `StratalDurableObject`, `StratalWorkerEntrypoint`, `StratalWorkflow`, and `runInScope` helper
|
|
698
|
+
- Add `Stratal.resolveApplication()` static method for worker classes to access the DI container
|
|
699
|
+
- Add `StratalNotInitializedError` for when `resolveApplication()` is called before Stratal is instantiated
|
|
700
|
+
- Add `DurableObjectState` and `DurableObjectId` DI tokens for Durable Object context injection
|
|
701
|
+
|
|
702
|
+
## 0.0.9
|
|
703
|
+
|
|
704
|
+
### Patch Changes
|
|
705
|
+
|
|
706
|
+
- [#86](https://github.com/strataljs/stratal/pull/86) [`c0d9313`](https://github.com/strataljs/stratal/commit/c0d9313b30272eece8a4596718b7d4c1b442c221) Thanks [@adesege](https://github.com/adesege)! - Remove default CORS middleware from HonoApp
|
|
707
|
+
|
|
708
|
+
### Breaking Changes
|
|
709
|
+
- **stratal**: `HonoApp` no longer applies `cors()` middleware by default. If your application relies on the built-in CORS handling, add it explicitly via a custom middleware in your module's `configure()` method or by registering it globally.
|
|
710
|
+
|
|
711
|
+
## 0.0.8
|
|
712
|
+
|
|
713
|
+
## 0.0.7
|
|
714
|
+
|
|
715
|
+
### Patch Changes
|
|
716
|
+
|
|
717
|
+
- [#81](https://github.com/strataljs/stratal/pull/81) [`89e06b5`](https://github.com/strataljs/stratal/commit/89e06b57f8aca553f60cbefab8f931cf5554f1b3) Thanks [@adesege](https://github.com/adesege)! - Rearchitect core internals: replace AsyncLocalStorage-based request context with explicit container passing, and replace RouterService with HonoApp
|
|
718
|
+
|
|
719
|
+
### Breaking Changes
|
|
720
|
+
|
|
721
|
+
**`stratal` (core)**
|
|
722
|
+
- **Removed `RequestContextStore`** — The `AsyncLocalStorage`-based request context propagation is eliminated. This removes the dependency on the `nodejs_als` compatibility flag in Cloudflare Workers.
|
|
723
|
+
- **Removed `RouterService` and `RequestScopeService`** — Replaced by `HonoApp`, a subclass of `OpenAPIHono` that directly integrates request scoping, middleware class support, and global error handling.
|
|
724
|
+
- **Removed `RouterAlreadyConfiguredError` and `RouterNotConfiguredError`** — Replaced by `HonoAppAlreadyConfiguredError`.
|
|
725
|
+
- **`Container` no longer accepts `env` or `ctx` in options** — These are now registered as values in the container by `Application` directly.
|
|
726
|
+
- **`runInRequestScope()` callback signature changed** — The callback now receives `(requestContainer: Container) => T | Promise<T>` instead of `() => T | Promise<T>`. Callers must use the passed container for resolution.
|
|
727
|
+
- **`Stratal` initialization changed from lazy to eager** — `Stratal` now eagerly bootstraps by dynamically importing `cloudflare:workers` for `env` and `waitUntil`, instead of lazily initializing on first request.
|
|
728
|
+
- **`queue()` and `scheduled()` no longer accept `env` and `ctx` parameters** — These are obtained from `cloudflare:workers` during eager init.
|
|
729
|
+
- **New `StratalExecutionContext` interface** — A minimal abstraction over Cloudflare's `ExecutionContext` with only `waitUntil()`.
|
|
730
|
+
- **New `HonoApp` class** — Extends `OpenAPIHono` with Stratal concerns; supports `Constructor<Middleware>` in `use()` via module augmentation.
|
|
731
|
+
|
|
732
|
+
**`@stratal/framework`**
|
|
733
|
+
- **`DatabaseConnectionConfig.dialect` changed from `Dialect` to `() => Dialect`** — Database connections now take a factory function for lazy dialect/pool creation.
|
|
734
|
+
- **Caching strategy changed from `instancePerContainerCachingFactory` to `instanceCachingFactory`**.
|
|
735
|
+
|
|
736
|
+
**`@stratal/testing`**
|
|
737
|
+
- **`TestingModule.runInRequestScope()` callback now receives a `container` parameter** — Update all callbacks to use the passed container for service resolution.
|
|
738
|
+
- **`TestingModule.fetch()` now routes through `HonoApp`** instead of `RouterService`.
|
|
739
|
+
- **`TestingModuleBuilder.compile()` now applies overrides before `initialize()`** — Fixes issue where overrides were applied after initialization.
|
|
740
|
+
|
|
741
|
+
### Minor Changes
|
|
742
|
+
|
|
743
|
+
**`@stratal/seeders`**
|
|
744
|
+
- Updated `executeSeeder()` to use the explicit `requestContainer` parameter from `runInRequestScope()`.
|
|
745
|
+
|
|
746
|
+
- [#83](https://github.com/strataljs/stratal/pull/83) [`bcb3556`](https://github.com/strataljs/stratal/commit/bcb3556a6e1f185e088286f202c605c73799e63f) Thanks [@adesege](https://github.com/adesege)! - Introduce @stratal/zenstack-plugin and rearchitect database module to use shared schema with per-connection slicing
|
|
747
|
+
|
|
748
|
+
### New Package
|
|
749
|
+
|
|
750
|
+
**`@stratal/zenstack-plugin`**
|
|
751
|
+
- ZenStack plugin for multi-connection database support with schema slicing
|
|
752
|
+
- Generates connection-specific schema types and `StratalDatabase` augmentation
|
|
753
|
+
- CLI commands: `stratal-db migrate` and `stratal-db push` for per-connection database management
|
|
754
|
+
- Plugin model (`plugin.zmodel`) for ZenStack integration
|
|
755
|
+
|
|
756
|
+
### Breaking Changes
|
|
757
|
+
|
|
758
|
+
**`stratal` (core)**
|
|
759
|
+
- Re-exports `delay` from tsyringe via `stratal/di`
|
|
760
|
+
|
|
761
|
+
**`@stratal/framework`**
|
|
762
|
+
- **Replaced `DatabaseSchemaRegistry` and `DefaultDatabaseConnection` with unified `StratalDatabase` interface** — Consumers must update their type augmentations to use the new single interface with `schema`, `defaultConnection`, and `slicing` properties.
|
|
763
|
+
- **`schema` moved from `DatabaseConnectionConfig` to `DatabaseModuleConfig`** — All connections now share a single schema; per-connection schema is no longer supported.
|
|
764
|
+
- **Added `slicing` option to `DatabaseConnectionConfig`** — Connections can narrow available models via ZenStack slicing options.
|
|
765
|
+
- **Database services are now lazily created** — Dialect factories are not called during module initialization; services are instantiated on first resolve within a request scope.
|
|
766
|
+
- **Database services are now request-scoped** — Each request gets its own database client instance via tsyringe's `delay()` + `Scope.Request`.
|
|
767
|
+
- **`DatabaseEvents` type no longer parameterized by connection** — Event types (`ModelName`, `DatabaseEventName`, `GetData`, `GetResult`, etc.) derive from the shared schema instead of per-connection schemas.
|
|
768
|
+
- **Removed `InferConnectionSchema` type** — Replaced by `InferDatabaseSchema` (shared) and `InferConnectionSlicing` (per-connection slicing).
|
|
769
|
+
|
|
770
|
+
**`@stratal/testing`**
|
|
771
|
+
- **`TestingModule.getDb()` is now synchronous** — Returns `DatabaseService` directly instead of `Promise<DatabaseService>`.
|
|
772
|
+
- **`TestingModule` creates a single request-scoped container at construction** — `container` property now returns the request-scoped container. The `runInRequestScope` pattern is removed.
|
|
773
|
+
- **`TestingModule.close()` now disposes the request container** before shutting down the application.
|
|
774
|
+
|
|
775
|
+
## 0.0.6
|
|
776
|
+
|
|
777
|
+
### Patch Changes
|
|
778
|
+
|
|
779
|
+
- [#79](https://github.com/strataljs/stratal/pull/79) [`6542f78`](https://github.com/strataljs/stratal/commit/6542f78fda2bf851df7ee5d88d6f7c7d04ea6388) Thanks [@adesege](https://github.com/adesege)! - Replace `StratalWorker` class with `Stratal` entry point. The new `Stratal` class is a plain object (not a `WorkerEntrypoint` subclass) that lazily initializes the application and exposes `fetch`, `queue`, and `scheduled` handlers directly. This removes the dependency on `cloudflare:workers` and simplifies the worker setup from a class with an abstract `configure()` method to a single `new Stratal({ module: AppModule })` call.
|
|
780
|
+
|
|
781
|
+
**Breaking change:** `StratalWorker` and the `stratal/worker` export have been removed. Migrate by replacing:
|
|
782
|
+
|
|
783
|
+
```ts
|
|
784
|
+
// Before
|
|
785
|
+
import { StratalWorker } from "stratal/worker";
|
|
786
|
+
|
|
787
|
+
export default class Backend extends StratalWorker<Env> {
|
|
788
|
+
protected configure() {
|
|
789
|
+
return { module: AppModule };
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// After
|
|
794
|
+
import { Stratal } from "stratal";
|
|
795
|
+
|
|
796
|
+
export default new Stratal({ module: AppModule });
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
## 0.0.5
|
|
800
|
+
|
|
801
|
+
### Patch Changes
|
|
802
|
+
|
|
803
|
+
- [#66](https://github.com/strataljs/stratal/pull/66) [`c8ea964`](https://github.com/strataljs/stratal/commit/c8ea964e272b09ebc6619843e77d2b51178f9423) Thanks [@adesege](https://github.com/adesege)! - Add type-safe events system with `@Listener` and `@On` decorators, `EventRegistry` for handler registration and emission, and automatic listener discovery from module providers during application bootstrap. Enhanced DI container with service decoration support.
|
|
804
|
+
|
|
805
|
+
## 0.0.4
|
|
806
|
+
|
|
807
|
+
### Patch Changes
|
|
808
|
+
|
|
809
|
+
- **Lazy i18n context building** — `MessageLoaderService` now lazily builds and caches `CoreContext` per locale on first access instead of eagerly at startup, reducing initialization overhead.
|
|
810
|
+
- **Pre-build i18n contexts at startup** — Moved `CoreContext` creation and message flattening from per-request `I18nService` to the singleton `MessageLoaderService`, eliminating repeated work on every `t()` call.
|
|
811
|
+
- **Skip stack traces in production** — Added a static flag on `ApplicationError` to disable stack trace capture in production, where traces are stripped from responses anyway.
|
|
812
|
+
|
|
813
|
+
- **Cross-realm metadata keys** — Migrated all internal metadata symbols from `Symbol()` to `Symbol.for()` with a `stratal:` prefix for reliable cross-realm identity in Cloudflare Workers.
|
|
814
|
+
- **Benchmark configuration** — Added a benchmark section to the Vitest config and updated Wrangler compatibility flags for Node.js modules.
|
|
815
|
+
- **i18n integration tests** — Added localization integratnning in the workerd pool.
|
|
816
|
+
|
|
817
|
+
- **Consistent zod imports** — Updated zod imports across benchmark files to use the i18n validation module.
|
|
818
|
+
- **Cleaner error handling** — `GlobalErrorHandler` now passes the translated message directly to the logger, avoiding double-translation.
|
|
819
|
+
|
|
820
|
+
## 0.0.3
|
|
821
|
+
|
|
822
|
+
### Patch Changes
|
|
823
|
+
|
|
824
|
+
- - **Lazy i18n context building** — `MessageLoaderService` now lazily builds and caches `CoreContext` per locale on first access instead of eagerly at startup, reducing initialization overhead.
|
|
825
|
+
- **Pre-build i18n contexts at startup** — Moved `CoreContext` creation and message flattening from per-request `I18nService` to the singleton `MessageLoaderService`, eliminating repeated work on every `t()` call.
|
|
826
|
+
- **Skip stack traces in production** — Added a static flag on `ApplicationError` to disable stack trace capture in production, where traces are stripped from responses anyway.
|
|
827
|
+
|
|
828
|
+
- **Cross-realm metadata keys** — Migrated all internal metadata symbols from `Symbol()` to `Symbol.for()` with a `stratal:` prefix for reliable cross-realm identity in Cloudflare Workers.
|
|
829
|
+
- **Benchmark configuration** — Added a benchmark section to the Vitest config and updated Wrangler compatibility flags for Node.js modules.
|
|
830
|
+
- **i18n integration tests** — Added localization integratnning in the workerd pool.
|
|
831
|
+
|
|
832
|
+
- **Consistent zod imports** — Updated zod imports across benchmark files to use the i18n validation module.
|
|
833
|
+
- **Cleaner error handling** — `GlobalErrorHandler` now passes the translated message directly to the logger, avoiding double-translation.
|
|
834
|
+
|
|
835
|
+
## 0.0.2
|
|
836
|
+
|
|
837
|
+
### Patch Changes
|
|
838
|
+
|
|
839
|
+
#### `stratal` (core)
|
|
840
|
+
|
|
841
|
+
##### Breaking Changes
|
|
842
|
+
|
|
843
|
+
- **`withRoot`/`withRootAsync` renamed to `forRoot`/`forRootAsync`** — All dynamic module configuration methods have been renamed for consistency. Update every `SomeModule.withRoot(...)` call to `SomeModule.forRoot(...)` and `SomeModule.withRootAsync(...)` to `SomeModule.forRootAsync(...)`. ([`152913a`](https://github.com/strataljs/stratal/commit/152913a))
|
|
844
|
+
|
|
845
|
+
- **Barrel export removed — use sub-path imports** — The top-level `stratal` barrel export has been removed. Consumers must now import from sub-paths (`stratal/di`, `stratal/router`, `stratal/cache`, `stratal/validation`, etc.). ([`af073d8`](https://github.com/strataljs/stratal/commit/af073d8))
|
|
846
|
+
|
|
847
|
+
##### Features
|
|
848
|
+
|
|
849
|
+
- **DOM polyfill for Cloudflare Workers** — Added a DOM polyfill to support AWS SDK v3 XML parsing in Cloudflare Workers environments. ([`f3b2cb9`](https://github.com/strataljs/stratal/commit/f3b2cb9))
|
|
850
|
+
|
|
851
|
+
- **Benchmark suite** — Added benchmark functionality for measuring framework performa30438`](https://github.com/strataljs/stratal/commit/7230438))
|
|
852
|
+
|
|
853
|
+
##### Security
|
|
854
|
+
|
|
855
|
+
- **ConfigService blocks prototype pollution** — `ConfigService` now rejects dangerous keys (`__proto__`, `constructor`, `prototype`) to prevent prototype pollution attacks. ([`567139c`](https://github.com/strataljs/stratal/commit/567139c), [`e64b4e7`](https://github.com/strataljs/stratal/commit/e64b4e7))
|
|
856
|
+
|
|
857
|
+
##### Bug Fixes
|
|
858
|
+
|
|
859
|
+
- **`reflect-metadata` import moved to vitest setup** — Removed unused `reflect-metadata` imports from example files and centralized the import in `vitest.setup.ts`. ([`f3b2cb9`](https://github.com/strataljs/stratal/commit/f3b2cb9))
|
|
860
|
+
|
|
861
|
+
---
|
|
862
|
+
|
|
863
|
+
#### `@stratal/testing`
|
|
864
|
+
|
|
865
|
+
##### Breaking Changes
|
|
866
|
+
|
|
867
|
+
- **Import paths updated** — Import paths updated to match the new core sub-path exports (e.g. `stratal/di` instead of the barrel `stratal`). ([`af073d8`](https://github.com/strataljs/stratal/commit/af073d8))
|
|
868
|
+
|
|
869
|
+
- Updated dependencies []:
|
|
870
|
+
- stratal@0.0.2
|
|
871
|
+
|
|
872
|
+
## 0.0.1
|
|
873
|
+
|
|
874
|
+
### Patch Changes
|
|
875
|
+
|
|
876
|
+
- Initial release of the Stratal framework — a modular Cloudflare Workers framework built on Hono and tsyringe.
|
|
877
|
+
|
|
878
|
+
**Core Infrastructure**
|
|
879
|
+
- NestJS-style module system with `@Module()` decorator, dynamic modules (`withRoot`, `withRootAsync`), and lifecycle hooks (`OnInitialize`, `OnShutdown`)
|
|
880
|
+
- Two-tier dependency injection container (global singletons + request-scoped) powered by tsyringe with conditional registration and service decoration
|
|
881
|
+
- `StratalWorker` entry point extending Cloudflare's `WorkerEntrypoint` for HTTP fetch, queue batches, and scheduled cron triggers
|
|
882
|
+
|
|
883
|
+
**Routing & API**
|
|
884
|
+
- Hono-based routing with `@Controller()` and `@Route()` decorators, automatic controller discovery, and route guards via `@UseGuards()`
|
|
885
|
+
- OpenAPI schema generation with `@hono/zod-openapi` and Scalar API reference integration
|
|
886
|
+
- NestJS-like middleware configuration with route-specific application and exclusion
|
|
887
|
+
|
|
888
|
+
**Background Processing**
|
|
889
|
+
- Queue consumerfor Cloudflare Queues with `@Consumer()` and `@QueueJob()` decorators and batch processing
|
|
890
|
+
- Cron job scheduling via `CronManager` integrated with Cloudflare's scheduled events
|
|
891
|
+
|
|
892
|
+
**Services & Integrations**
|
|
893
|
+
- Email module with pluggable providers (Nodemailer, Resend) and queue-based sending
|
|
894
|
+
- Storage module with AWS S3 / Cloudflare R2 support, multipart uploads, presigned URLs, and TUS resumable uploads
|
|
895
|
+
- Internationalization (i18n) module with locale detection, message compilation, and request-scoped translations
|
|
896
|
+
- Cache module with pluggable providers and Cloudflare KV integration
|
|
897
|
+
- Configuration module with `registerAs()` namespaces and Zod-based validation
|
|
898
|
+
- Structured logging with JSON and pretty formatters
|
|
899
|
+
|
|
900
|
+
**Developer Experience**
|
|
901
|
+
- Zod-powered request/response validation with type inference
|
|
902
|
+
- Custom `ApplicationError` class with HTTP status mapping
|
|
903
|
+
- ESM-only with full TypeScript decorator support (`emitDecoratorMetadata`)
|
|
904
|
+
- Sub-path exports for tree-shakeable imports (`stratal/di`, `stratal/router`, `stratal/cache`, etc.)
|
|
905
|
+
|
|
906
|
+
- `TestingModule` and `TestingModuleBuilder` for bootstrapping isolated module environments in tests
|
|
907
|
+
- `TestHttpClient` with request builder and response wrapper for integration testing
|
|
908
|
+
- `FetchMock` for mocking HTTP fetch calls
|
|
909
|
+
- `FakeStorageService` for in-memory storage testing without S3/R2
|
|
910
|
+
- `ProviderOverrideBuilder` for replacing providers with test doubles
|
|
911
|
+
- Nodemailer mock for email testing
|
|
912
|
+
- Test environment utilities via `getTestEnv()`
|