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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"route-registration.service-CDPQKpm4.mjs","names":[],"sources":["../src/router/errors/route-not-found.error.ts","../src/router/errors/schema-validation.error.ts","../src/router/errors/index.ts","../src/router/middleware/domain.middleware.ts","../src/router/middleware/middleware-chain.ts","../src/router/decorators/route.decorator.ts","../src/router/schemas/common.schemas.ts","../src/router/services/route-registration.service.ts"],"sourcesContent":["import { HttpException } from '../../errors'\n\nexport class RouteNotFoundError extends HttpException {\n public readonly path: string\n public readonly method: string\n\n constructor(path: string, method: string) {\n super(404, `Route not found: ${method} ${path}`)\n this.path = path\n this.method = method\n }\n}\n","import { HttpException } from '../../errors'\nimport type { ZodError, z } from '../../i18n/validation/zod'\n\nexport class SchemaValidationError extends HttpException {\n public readonly issues: { path: string; message: string; code: string }[]\n\n constructor(zodError: ZodError) {\n super(400, 'Schema validation failed')\n this.issues = zodError.issues.map((err: z.core.$ZodIssue) => ({\n path: err.path.join('.'),\n message: err.message,\n code: err.code,\n }))\n }\n}\n","import { HttpException } from '../../errors';\nimport type { z, ZodError } from '../../i18n/validation/zod';\n\n/**\n * Error thrown when a signed URL has an invalid or expired signature.\n *\n * HTTP Status: 403 Forbidden\n */\nexport class InvalidSignatureError extends HttpException {\n constructor() {\n super(403, 'Invalid or expired signature')\n }\n}\n\n/**\n * ResponseValidationError\n *\n * Thrown when a controller's response body does not match the declared Zod response schema.\n * Indicates a server-side schema mismatch — the controller is returning data that\n * violates its own API contract.\n */\nexport class ResponseValidationError extends HttpException {\n public readonly issues: { path: string; message: string; code: string }[]\n\n constructor(zodError: ZodError) {\n super(500, 'Response validation failed')\n this.issues = zodError.issues.map((err: z.core.$ZodIssue) => ({\n path: err.path.join('.'),\n message: err.message,\n code: err.code,\n }))\n }\n}\n\nexport { RouteNotFoundError } from './route-not-found.error';\n\nexport { SchemaValidationError } from './schema-validation.error';\n","import type { Context, MiddlewareHandler } from 'hono';\nimport { abort } from '../../errors';\nimport type { RouterEnv } from '../types';\n\n/**\n * Parse a domain pattern into a regex and extract parameter names.\n *\n * @example\n * parseDomainPattern('{tenant}.example.com')\n * // => { regex: /^([^.]+)\\.example\\.com$/, paramNames: ['tenant'] }\n *\n * parseDomainPattern('{region}.{tenant}.example.com')\n * // => { regex: /^([^.]+)\\.([^.]+)\\.example\\.com$/, paramNames: ['region', 'tenant'] }\n */\nexport function parseDomainPattern(pattern: string): { regex: RegExp; paramNames: string[] } {\n const paramNames: string[] = []\n\n const regexStr = pattern.replace(\n /\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g,\n (_match, paramName: string) => {\n paramNames.push(paramName)\n return '([^.]+)'\n }\n )\n\n // Escape dots in the remaining static parts\n const escaped = regexStr.replace(/\\./g, '\\\\.')\n return { regex: new RegExp(`^${escaped}$`), paramNames }\n}\n\n/**\n * Strip port number from a host header value.\n * 'example.com:8787' => 'example.com'\n */\nfunction stripPort(host: string): string {\n const colonIdx = host.lastIndexOf(':')\n if (colonIdx === -1) return host\n // Check if it's actually a port (digits after the colon)\n const afterColon = host.slice(colonIdx + 1)\n return /^\\d+$/.test(afterColon) ? host.slice(0, colonIdx) : host\n}\n\n/**\n * Create a Hono middleware that matches the request host against a domain pattern.\n *\n * When the host matches, domain parameters are extracted and stored in context\n * variables accessible via `ctx.domain(key)`.\n *\n * When the host does NOT match, aborts with 404.\n *\n * @param pattern - Domain pattern with `{param}` placeholders (e.g., '{tenant}.myapp.com')\n *\n * @example\n * ```typescript\n * // Applied automatically by RouteRegistrationService for controllers with domain config\n * @Controller('/dashboard', { domain: '{tenant}.myapp.com' })\n * export class DashboardController {\n * async index(ctx: RouterContext) {\n * const tenant = ctx.domain('tenant')\n * }\n * }\n * ```\n */\nexport function createDomainMiddleware(pattern: string): MiddlewareHandler<RouterEnv> {\n const { regex, paramNames } = parseDomainPattern(pattern)\n\n return async (c: Context<RouterEnv>, next: () => Promise<void>) => {\n const host = stripPort(c.req.header('host') ?? '')\n const match = regex.exec(host)\n\n if (!match) {\n abort(404, 'Domain mismatch')\n }\n\n // Store domain params as context variables\n for (let i = 0; i < paramNames.length; i++) {\n c.set(`domain:${paramNames[i]}`, match[i + 1])\n }\n\n await next()\n }\n}\n","import type { Context, MiddlewareHandler } from 'hono'\nimport type { Constructor } from '../../types'\nimport { ROUTER_CONTEXT_KEYS } from '../constants'\nimport { RouterError } from '../router.error'\nimport type { Middleware, Next } from '../middleware.interface'\nimport { RouterContext } from '../router-context'\nimport type { RouterEnv } from '../types'\n\n/**\n * Create a Hono middleware handler that executes a chain of Stratal middleware classes.\n *\n * Each middleware is resolved from the request-scoped container per request,\n * then executed in order (first registered = outermost in the chain).\n *\n * @param classes - Middleware classes to chain\n * @returns Hono middleware handler\n */\nexport function createMiddlewareChain(\n classes: Constructor<Middleware>[]\n): MiddlewareHandler<RouterEnv> {\n return async (c: Context<RouterEnv>, next: () => Promise<Response | void>) => {\n const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n const ctx = new RouterContext(c)\n\n // Build chain from end to start\n let current = next\n for (let i = classes.length - 1; i >= 0; i--) {\n const prevNext = current\n const middlewareClass = classes[i]\n current = () => {\n const middleware = requestContainer.resolve<Middleware>(middlewareClass)\n let called = false\n const guardedNext: Next = () => {\n if (called) {\n return Promise.reject(\n new RouterError(`Middleware \"${middlewareClass.name ?? 'anonymous'}\" called next() multiple times`)\n )\n }\n called = true\n return prevNext() as Promise<void>\n }\n return middleware.handle(ctx, guardedNext) as Promise<void>\n }\n }\n\n const result = await current()\n\n if (result instanceof Response) {\n return result // return to Hono\n }\n }\n}\n","import { defineMetadata, getMetadata } from '../../di/metadata'\nimport { ROUTE_METADATA_KEYS } from '../constants'\nimport type { ConventionRouteMetadata, RouteConfig, RouteMetadata } from '../types'\n\n/**\n * Decorator to add OpenAPI metadata to a controller method using convention-based routing.\n *\n * **Cannot be mixed with HTTP method decorators** (`@Get`, `@Post`, `@Put`, `@Patch`,\n * `@Delete`, `@All`) in the same controller. Use one pattern or the other.\n *\n * Stores route configuration (schemas, response, tags, security) in metadata.\n * HTTP method, path, and success status code are auto-derived from the method name:\n * - index() → GET /base-path → 200\n * - show() → GET /base-path/:id → 200\n * - create() → POST /base-path → 201\n * - update() → PUT /base-path/:id → 200\n * - patch() → PATCH /base-path/:id → 200\n * - destroy() → DELETE /base-path/:id → 200\n *\n * @param config - Route configuration (schemas, response, tags, security)\n *\n * @example\n * ```typescript\n * @Controller('/api/v1/notes', {\n * tags: ['Notes'],\n * security: ['bearerAuth']\n * })\n * export class NotesController implements Controller {\n * @Route({\n * body: createNoteSchema,\n * response: noteSchema, // 201 auto-derived from 'create' method\n * tags: ['Mutations'],\n * description: 'Create a new note'\n * })\n * async create(ctx: RouterContext): Promise<Response> {\n * // POST /api/v1/notes (auto-derived from method name)\n * // Body schema: createNoteSchema (auto-validated)\n * // Response: 201 → noteSchema (status auto-derived)\n * // Tags: ['Notes', 'Mutations'] (merged with controller)\n * // Security: ['bearerAuth'] (inherited from controller)\n * const body = ctx.body()\n * const note = await this.notesService.create(body)\n * return ctx.json(note, 201)\n * }\n *\n * @Route({\n * query: paginationSchema,\n * response: z.array(noteSchema) // 200 auto-derived from 'index' method\n * })\n * async index(ctx: RouterContext): Promise<Response> {\n * // GET /api/v1/notes (auto-derived)\n * // Query params auto-validated\n * const notes = await this.notesService.list()\n * return ctx.json(notes)\n * }\n *\n * @Route({\n * params: z.object({ id: z.string().uuid() }),\n * response: {\n * schema: noteSchema,\n * description: 'Note details'\n * },\n * security: [] // Override to make public\n * })\n * async show(ctx: RouterContext): Promise<Response> {\n * // GET /api/v1/notes/:id (auto-derived)\n * // URL params auto-validated\n * // Response: 200 → noteSchema (status auto-derived)\n * // Security: [] (public route, override controller security)\n * const id = ctx.param('id')\n * const note = await this.notesService.findById(id)\n * return ctx.json(note)\n * }\n * }\n * ```\n */\nexport function Route(config: Omit<RouteConfig, 'statusCode'>) {\n return function (\n target: object,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n const metadata: ConventionRouteMetadata = {\n type: 'convention',\n config,\n }\n\n defineMetadata(\n ROUTE_METADATA_KEYS.ROUTE_CONFIG,\n metadata,\n target,\n propertyKey\n )\n\n // Track this method as decorated on the prototype\n const existing: string[] =\n getMetadata<string[]>(ROUTE_METADATA_KEYS.DECORATED_METHODS, target) ?? []\n existing.push(propertyKey)\n defineMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, existing, target)\n\n return descriptor\n }\n}\n\n/**\n * Get the route metadata from a controller method\n *\n * @param target - Controller instance or prototype\n * @param methodName - Name of the method\n * @returns Route metadata or undefined if not decorated\n */\nexport function getRouteMetadata(target: object, methodName: string): RouteMetadata | undefined {\n return getMetadata<RouteMetadata>(ROUTE_METADATA_KEYS.ROUTE_CONFIG, target, methodName)\n}\n\n/**\n * Get all methods with route decorators (@Route, @Get, @Post, etc.) from a controller\n *\n * @param ControllerClass - Controller class\n * @returns Array of method names that have route metadata\n */\nexport function getRouteDecoratedMethods(ControllerClass: new (...args: unknown[]) => object): string[] {\n const methods = new Set<string>()\n let proto: object | null = ControllerClass.prototype as object\n\n while (proto && proto !== Object.prototype) {\n const own = getMetadata<string[]>(ROUTE_METADATA_KEYS.DECORATED_METHODS, proto)\n if (own) {\n for (const m of own) methods.add(m)\n }\n proto = Object.getPrototypeOf(proto) as object | null\n }\n\n return [...methods]\n}\n","import { z } from '../../i18n/validation/zod'\n\n/**\n * Common OpenAPI Schemas\n *\n * Reusable schema definitions for common API patterns:\n * - Error responses\n * - Pagination\n * - Common parameters\n */\n\n/**\n * Generic error response schema\n * Used for all error responses (4xx, 5xx)\n * Matches the ErrorResponse shape produced by ExceptionHandler\n */\nexport const errorResponseSchema = z.object({\n message: z.string().describe('Human-readable error message'),\n timestamp: z.string().datetime().describe('ISO timestamp when error occurred'),\n stack: z.string().optional().describe('Stack trace (development only)'),\n}).openapi('ErrorResponse')\n\n/**\n * Validation error response schema\n * Used for 400 Bad Request with validation failures\n * Matches the ErrorResponse shape produced by ExceptionHandler\n */\nexport const validationErrorResponseSchema = errorResponseSchema.openapi('ValidationErrorResponse')\n\n/**\n * Pagination query parameters schema\n * Used for list endpoints\n */\nexport const paginationQuerySchema = z.object({\n page: z.coerce.number().int().positive().default(1).describe('Page number (1-indexed)'),\n limit: z.coerce.number().int().positive().max(100).default(20).describe('Items per page (max 100)')\n}).openapi('PaginationQuery')\n\n/**\n * Paginated response wrapper schema\n * Generic wrapper for paginated list responses\n */\nexport const paginatedResponseSchema = <T extends z.ZodType>(itemSchema: T) =>\n z.object({\n data: z.array(itemSchema).describe('Array of items for current page'),\n pagination: z.object({\n page: z.number().int().positive().describe('Current page number'),\n limit: z.number().int().positive().describe('Items per page'),\n total: z.number().int().nonnegative().describe('Total number of items'),\n totalPages: z.number().int().nonnegative().describe('Total number of pages')\n })\n })\n\n/**\n * UUID parameter schema\n * Used for :id parameters in RESTful routes\n */\nexport const uuidParamSchema = z.object({\n id: z.string().uuid().describe('Resource UUID')\n}).openapi('UUIDParam')\n\n/**\n * Success message response schema\n * Used for operations that don't return data (e.g., DELETE)\n */\nexport const successMessageSchema = z.object({\n message: z.string().describe('Success message'),\n data: z.record(z.string(), z.unknown()).optional().describe('Optional additional data')\n}).openapi('SuccessMessage')\n\n/**\n * Common HTTP status error schemas\n * Pre-configured for standard error responses\n */\nexport const commonErrorSchemas = {\n 400: { schema: validationErrorResponseSchema, description: 'Validation error' },\n 401: { schema: errorResponseSchema, description: 'Unauthorized' },\n 403: { schema: errorResponseSchema, description: 'Forbidden' },\n 404: { schema: errorResponseSchema, description: 'Not found' },\n 409: { schema: errorResponseSchema, description: 'Conflict' },\n 500: { schema: errorResponseSchema, description: 'Internal server error' }\n} as const\n","import type { Context, MiddlewareHandler } from 'hono';\nimport type { UpgradeWebSocket, WSContext, WSEvents } from 'hono/ws';\nimport { type Container, getMethodInjections, inject } from '../../di';\nimport { Singleton } from '../../di/decorators';\nimport { DI_TOKENS } from '../../di/tokens';\nimport {\n type Guard,\n GuardExecutionService,\n getControllerGuards,\n getMethodGuards,\n} from '../../guards';\nimport type { ZodType } from '../../i18n/validation/zod';\nimport { createRoute, z } from '../../i18n/validation/zod';\nimport { LOGGER_TOKENS, type LoggerService } from '../../logger';\nimport type { ModuleRegistry } from '../../module/module-registry';\nimport { getRateLimits } from '../../rate-limiter/decorators/rate-limit.decorator';\nimport { createThrottleMiddleware } from '../../rate-limiter/throttle.middleware';\nimport type { Constructor } from '../../types';\nimport { getWsOnCloseMethod, getWsOnErrorMethod, getWsOnMessageMethod, isGateway } from '../../websocket/decorators';\nimport { GatewayContext } from '../../websocket/gateway-context';\nimport { DEFAULT_CONTENT_TYPE, HTTP_METHODS, METHOD_STATUS_CODES, SECURITY_SCHEMES } from '../constants';\nimport type { IController } from '../controller';\nimport {\n getControllerOptions,\n getControllerRoute,\n getRouteDecoratedMethods,\n getRouteMetadata,\n} from '../decorators';\nimport {\n ResponseValidationError,\n} from '../errors';\nimport type { HonoApp } from '../hono-app';\nimport type { Middleware } from '../middleware.interface';\nimport { createDomainMiddleware } from '../middleware/domain.middleware';\nimport { createMiddlewareChain } from '../middleware/middleware-chain';\nimport { type RegisteredRoute, type RouteRegistry } from '../route-registry';\nimport { RouterContext } from '../router-context';\nimport type { RouterResolver } from '../router-resolver';\nimport { RouterError } from '../router.error';\nimport { ROUTER_TOKENS } from '../router.tokens';\nimport { commonErrorSchemas } from '../schemas/common.schemas';\nimport type {\n ControllerOptions,\n HttpMethod,\n OpenAPIRouteConfig,\n RouteBodyObject,\n RouteConfig,\n RouteMetadata,\n RouteResponseObject,\n RouterEnv,\n SecuritySchemeRecord,\n} from '../types';\nimport { toOpenAPIPath, toRoutingOpenAPIPath } from '../utils/path';\nimport { generateConventionRouteName } from '../utils/route-name';\nimport type { LocalePathService } from './locale-path.service';\n\nconst invokeHandler = (instance: Record<string, (...args: unknown[]) => unknown>, method: string, ...args: unknown[]): Promise<unknown> => {\n try {\n return Promise.resolve(instance[method](...args))\n } catch (err: unknown) {\n return Promise.reject(err as Error)\n }\n}\n\n/**\n * Route registration service\n * Manages controller and route registration with OpenAPI support\n *\n * Responsibilities:\n * - Register RESTful controllers with OpenAPI metadata\n * - Auto-derive HTTP methods/paths from controller method names\n * - Build OpenAPI route configurations with guard execution\n * - Validate all controllers have access decorators (strict mode)\n * - Create controller handlers with DI resolution\n *\n * Two-pass strategy:\n * 1. Collect: iterate controllers, register in RouteRegistry, store Hono actions\n * 2. Register: iterate registry.all() (sorted), execute stored actions in Hono\n */\n@Singleton()\nexport class RouteRegistrationService {\n private controllerClasses = new Map<string, Constructor>()\n private upgradeWebSocketFn: UpgradeWebSocket | null = null\n\n constructor(\n @inject(LOGGER_TOKENS.LoggerService) private logger: LoggerService,\n @inject(ROUTER_TOKENS.RouteRegistry) private registry: RouteRegistry,\n @inject(ROUTER_TOKENS.RouterResolver, { isOptional: true }) private routerResolver: RouterResolver | null,\n @inject(ROUTER_TOKENS.LocalePathService) private localePathService: LocalePathService,\n @inject(ROUTER_TOKENS.HonoApp) private app: HonoApp,\n @inject(DI_TOKENS.ModuleRegistry) private moduleRegistry: ModuleRegistry,\n ) { }\n\n /**\n * Configure router with controllers and global middleware.\n * Resolves controllers from ModuleRegistry and global middleware from RouterResolver.\n */\n async configure(): Promise<void> {\n const controllers = this.moduleRegistry.getAllControllers()\n const globalMiddleware = this.routerResolver?.getGlobalMiddleware() ?? []\n\n this.logger.info('Registering controllers', {\n controllerCount: controllers.length,\n })\n\n // Global middleware from Router.use() (applies to ALL routes)\n if (globalMiddleware.length > 0) {\n this.app.use('*', createMiddlewareChain(globalMiddleware))\n }\n\n // Eagerly load upgradeWebSocket once if any gateway exists\n if (controllers.some(isGateway)) {\n const { upgradeWebSocket } = await import('hono/cloudflare-workers')\n this.upgradeWebSocketFn = upgradeWebSocket\n }\n\n // Pass 1: Collect routes into registry + store Hono registration actions\n const actions = new WeakMap<RegisteredRoute, () => void>()\n for (const ControllerClass of controllers) {\n this.collectRoutes(ControllerClass, actions)\n }\n\n // Pass 2: Register in Hono in specificity order from registry\n for (const route of this.registry.all()) {\n actions.get(route)?.()\n }\n\n this.logger.info('Controller registration complete')\n }\n\n /**\n * Pass 1: Collect routes from a controller into RouteRegistry and store Hono actions.\n * Versioning and locale expansion are handled by RouteRegistry.register().\n */\n private collectRoutes(\n ControllerClass: Constructor,\n actions: WeakMap<RegisteredRoute, () => void>,\n ): void {\n const isWsGateway = isGateway(ControllerClass)\n const controllerRoute = getControllerRoute(ControllerClass)\n\n if (!controllerRoute) {\n throw new RouterError(\n `Controller \"${ControllerClass.name}\" registration failed: ${isWsGateway\n ? 'Missing @Gateway decorator or route metadata'\n : 'Missing @Controller decorator or route metadata'}`\n )\n }\n\n const controllerOpts = getControllerOptions(ControllerClass)\n const controllerGuards = getControllerGuards(ControllerClass)?.guards ?? []\n\n // Resolve Router config for this controller (prefix, domain, name, middleware, version, hideFromDocs)\n const routerConfig = this.routerResolver?.resolveForController(ControllerClass) ?? { middleware: [] }\n\n // Class-level @RateLimit decorators — same for every method on this controller.\n // Throttle middleware classes are memoized by name in createThrottleMiddleware,\n // so two `@RateLimit('a')` decorators yield the same class — Set dedupes them.\n const classThrottleMiddleware = Array.from(\n new Set(getRateLimits(ControllerClass).map(createThrottleMiddleware)),\n )\n\n // Apply Router prefix to controller base path\n const basePath = routerConfig.prefix\n ? this.joinPaths(routerConfig.prefix, controllerRoute)\n : controllerRoute\n\n // Version resolution: controller version > Router version\n const effectiveVersion = controllerOpts?.version ?? routerConfig.version\n\n // Apply domain middleware if controller or router has a domain pattern\n const effectiveDomain = controllerOpts?.domain ?? routerConfig.domain\n\n // WebSocket gateway\n if (isWsGateway) {\n // Class-level @RateLimit applies; methods on a gateway aren't decorated routes.\n const wsMiddleware = [...routerConfig.middleware, ...classThrottleMiddleware]\n const expandedRoutes = this.registry.register({\n method: 'ws',\n basePath,\n version: effectiveVersion,\n domain: effectiveDomain,\n controller: ControllerClass.name,\n action: 'ws',\n hidden: routerConfig.hideFromDocs ?? false,\n middleware: wsMiddleware.map(m => m.name),\n })\n\n for (const route of expandedRoutes) {\n actions.set(route, () => {\n // Apply scoped middleware at the exact route path so it runs\n // for this specific route (including the root of the group) —\n // not via a `/*` sub-path wildcard, which would miss the exact\n // path match.\n if (wsMiddleware.length > 0) {\n this.app.use(route.path, createMiddlewareChain(wsMiddleware))\n }\n // Apply domain middleware\n if (effectiveDomain) {\n const domainHandler = createDomainMiddleware(effectiveDomain)\n this.app.use(route.path, domainHandler)\n this.app.use(`${route.path}/*`, domainHandler)\n }\n this.registerGatewayForPath(ControllerClass, route.path, controllerGuards)\n })\n }\n return\n }\n\n const className = ControllerClass.name\n this.controllerClasses.set(className, ControllerClass)\n\n const prototype = ControllerClass.prototype as IController\n\n // Wildcard routes (non-RESTful controllers with handle())\n if (prototype.handle) {\n // No method-level @RateLimit on wildcard handle() — only class-level applies.\n const wildcardMiddleware = [...routerConfig.middleware, ...classThrottleMiddleware]\n const expandedRoutes = this.registry.register({\n method: 'all',\n basePath,\n version: effectiveVersion,\n domain: effectiveDomain,\n controller: className,\n action: 'handle',\n hidden: routerConfig.hideFromDocs ?? false,\n middleware: wildcardMiddleware.map(m => m.name),\n })\n\n for (const route of expandedRoutes) {\n actions.set(route, () => {\n if (wildcardMiddleware.length > 0) {\n this.app.use(route.path, createMiddlewareChain(wildcardMiddleware))\n }\n this.registerWildcardRoute(ControllerClass, route.path)\n })\n }\n return\n }\n\n // Standard HTTP routes — validate decorated methods\n const decoratedMethods = getRouteDecoratedMethods(ControllerClass)\n\n if (decoratedMethods.length === 0) {\n throw new RouterError(\n `Controller \"${ControllerClass.name}\" registration failed: No route decorators found. Use @Route() or HTTP method decorators (@Get, @Post, etc.) on controller methods.`\n )\n }\n\n // Pre-cache metadata for all decorated methods (avoids double getRouteMetadata lookup)\n const methodMetadata: { method: string; meta: RouteMetadata }[] = []\n let hasConvention = false\n let hasExplicit = false\n for (const m of decoratedMethods) {\n const meta = getRouteMetadata(prototype, m)\n if (!meta) continue\n methodMetadata.push({ method: m, meta })\n if (meta.type === 'convention') hasConvention = true\n else if (meta.type === 'explicit') hasExplicit = true\n }\n\n // Enforce mutual exclusivity: no mixing @Route() with @Get/@Post/etc.\n if (hasConvention && hasExplicit) {\n throw new RouterError(\n `Controller \"${ControllerClass.name}\" registration failed: Cannot mix @Route() with HTTP method decorators (@Get, @Post, etc.) in the same controller. Use one pattern or the other.`\n )\n }\n\n const routerHidden = routerConfig.hideFromDocs\n const controllerHidden = controllerOpts?.hideFromDocs ?? false\n\n // Resolve effective name prefix: router-level name (module + group merged by\n // RouterResolver) concatenates with the controller-level name, mirroring how\n // prefixes compose. A controller's `{ name: 'dashboard.' }` inside a module\n // that calls `router.name('admin.')` becomes `admin.dashboard.*` — not\n // `dashboard.*`.\n const routerName = routerConfig.name\n const controllerName = controllerOpts?.name\n const effectiveNamePrefix =\n routerName && controllerName\n ? `${routerName}${controllerName}`\n : (routerName ?? controllerName)\n\n for (const { method: methodName, meta } of methodMetadata) {\n const resolved = this.resolveMethodAndPath(meta, methodName, basePath, className)\n if (!resolved) continue\n\n // Compose per-method middleware: scope (router.throttle/.middleware)\n // → class-level @RateLimit → method-level @RateLimit. Throttle classes\n // are memoized by name, so duplicates across class + method (e.g.\n // `@RateLimit('api')` on both) collapse to a single middleware.\n const methodThrottleMiddleware = getRateLimits(prototype, methodName).map(createThrottleMiddleware)\n const effectiveMiddleware = Array.from(\n new Set([...routerConfig.middleware, ...classThrottleMiddleware, ...methodThrottleMiddleware]),\n )\n const middlewareNames = effectiveMiddleware.map(m => m.name)\n\n const { httpMethod, fullPath, routeConfig: rawRouteConfig, statusCodeOverride } = resolved\n\n // Compose prefix params with route-level params WITHOUT mutating the\n // route's metadata — `meta.config` lives on the controller prototype\n // and is shared across every Application/RouteRegistry instance that\n // resolves this controller. Mutating it leaks state across test runs\n // (and any other multi-app setup), causing later registrations to\n // re-extend an already-injected prefix from a previous run.\n let mergedParams = rawRouteConfig.params\n if (routerConfig.params) {\n const prefixShape = (routerConfig.params as z.ZodObject).shape\n mergedParams = mergedParams\n ? (mergedParams as z.ZodObject).extend(prefixShape)\n : (routerConfig.params as z.ZodObject).extend({})\n }\n const routeConfig: RouteConfig = mergedParams === rawRouteConfig.params\n ? rawRouteConfig\n : { ...rawRouteConfig, params: mergedParams }\n\n const hideFromDocs = routeConfig.hideFromDocs ?? (routerHidden ?? controllerHidden)\n\n // Compute route name\n let routeName: string | undefined\n if (routeConfig.name) {\n routeName = effectiveNamePrefix ? `${effectiveNamePrefix}${routeConfig.name}` : routeConfig.name\n } else if (meta.type === 'convention') {\n const autoName = generateConventionRouteName(basePath, methodName)\n routeName = effectiveNamePrefix ? `${effectiveNamePrefix}${autoName}` : autoName\n }\n\n // Register in RouteRegistry (handles versioning + locale expansion)\n const expandedRoutes = this.registry.register({\n name: routeName,\n method: httpMethod,\n basePath: fullPath,\n version: effectiveVersion,\n domain: effectiveDomain,\n controller: className,\n action: methodName,\n hidden: hideFromDocs,\n middleware: middlewareNames,\n })\n\n // Collect guards — avoid spread when no method-level guards (common case)\n const methodGuards = getMethodGuards(prototype, methodName)?.guards ?? []\n const allGuards: Guard[] = methodGuards.length > 0\n ? [...controllerGuards, ...methodGuards]\n : controllerGuards\n\n const responseSchema = httpMethod !== 'all'\n ? this.extractResponseSchema(routeConfig)\n : null\n\n const handler = this.createControllerHandler(ControllerClass, methodName, responseSchema)\n\n for (const route of expandedRoutes) {\n actions.set(route, () => {\n // Apply domain middleware\n if (effectiveDomain) {\n const domainHandler = createDomainMiddleware(effectiveDomain)\n this.app.use(route.path, domainHandler)\n this.app.use(`${route.path}/*`, domainHandler)\n }\n\n if (allGuards.length > 0) {\n this.logger.info(`Route guards`, {\n controller: className,\n method: httpMethod.toUpperCase(),\n path: route.path,\n methodName,\n guardCount: allGuards.length,\n })\n }\n\n // @All routes can't use OpenAPI — register directly with\n // scoped middleware (if any) + guard middleware + handler.\n if (httpMethod === 'all') {\n this.logger.info(`Registering @All route`, {\n controller: className,\n path: route.path,\n methodName,\n })\n\n if (effectiveMiddleware.length > 0) {\n this.app.use(route.path, createMiddlewareChain(effectiveMiddleware))\n }\n if (allGuards.length > 0) {\n this.app.use(route.path, this.createGuardMiddleware(allGuards))\n }\n this.app.all(route.path, handler)\n return\n }\n\n // Build and register OpenAPI route\n const metadata = this.mergeMetadata(controllerOpts, routeConfig, ControllerClass, methodName)\n const openApiRoute = this.buildOpenAPIRoute(\n httpMethod,\n route.path,\n routeConfig,\n metadata,\n meta.type === 'convention' ? methodName : undefined,\n statusCodeOverride,\n route.isLocaleVariant ?? false,\n )\n\n this.logger.info(`Registering route`, {\n controller: className,\n method: httpMethod.toUpperCase(),\n path: route.path,\n methodName,\n tags: metadata.tags,\n hidden: route.hidden,\n })\n\n // Wrap the controller handler so scoped middleware and guards\n // run AFTER Hono's request validators. @hono/zod-openapi\n // composes a route as `...routeMiddleware, ...validators, handler`\n // (see node_modules/@hono/zod-openapi/dist/index.js), which means\n // anything attached via `route.middleware` runs *before*\n // validation — and therefore can't read `c.req.valid('param')`.\n // Wrapping the handler is the only place we can run middleware\n // after validators in this Hono pipeline.\n //\n // Final order: global app.use → request validators → scoped\n // middleware → guards → controller handler.\n const wrappedHandler = this.wrapHandlerWithChain(handler, effectiveMiddleware, allGuards)\n this.app.openapi(openApiRoute, wrappedHandler)\n\n // Register clean path in OpenAPI spec (strips regex constraints from params)\n if (!route.hidden) {\n const { hide: _, ...specRoute } = openApiRoute\n this.app.openAPIRegistry.registerPath({\n ...specRoute,\n path: toOpenAPIPath(route.path),\n })\n }\n })\n }\n }\n }\n\n\n /**\n * Register a single WebSocket gateway route\n */\n private registerGatewayForPath(\n GatewayClass: Constructor,\n fullPath: string,\n guards: Guard[],\n ): void {\n // Route already registered in RouteRegistry during collectRoutes()\n // Cache WS metadata once at registration time (not per-connection)\n const onMsgMethod = getWsOnMessageMethod(GatewayClass)\n const onCloseMethod = getWsOnCloseMethod(GatewayClass)\n const onErrMethod = getWsOnErrorMethod(GatewayClass)\n\n const wsHandler: MiddlewareHandler<RouterEnv> = this.upgradeWebSocketFn!((c) => {\n const routerCtx = new RouterContext(c as Context<RouterEnv>)\n const container = routerCtx.getContainer()\n const gateway = container.resolve(GatewayClass)\n\n // Cloudflare Workers doesn't support the `onOpen` WebSocket event;\n // the upgrade callback itself serves as the open context.\n const events: Omit<WSEvents, 'onOpen'> = {}\n\n const bindWsHandler = (\n method: string,\n onCatch?: (err: unknown, ws: WSContext) => void\n ) => {\n return (evt: MessageEvent | CloseEvent | Event, ws: WSContext) => {\n const ctx = new GatewayContext(c as Context<RouterEnv>, ws)\n invokeHandler(gateway as Record<string, (...args: unknown[]) => unknown>, method, evt, ctx).catch((err: unknown) => {\n this.logger.error(`WebSocket ${method} handler error`, err as Error, {\n gateway: GatewayClass.name,\n })\n onCatch?.(err, ws)\n })\n }\n }\n\n if (onMsgMethod) {\n events.onMessage = bindWsHandler(onMsgMethod, (_err, ws) => ws.close(1011, 'Internal Error'))\n }\n if (onCloseMethod) {\n events.onClose = bindWsHandler(onCloseMethod)\n } else {\n // Cloudflare Workers (pre-2026-04-07 compat date) requires the server\n // to complete the WebSocket close handshake explicitly. Without a close\n // listener, Hono never calls server.addEventListener('close', ...),\n // leaving the Worker alive until the runtime kills it with\n // \"script will never generate a response\".\n events.onClose = (_evt: CloseEvent, ws: WSContext) => {\n ws.close()\n }\n }\n if (onErrMethod) {\n events.onError = bindWsHandler(onErrMethod)\n }\n\n return events\n }) as MiddlewareHandler<RouterEnv>\n\n this.nameHandler(wsHandler, GatewayClass.name, onMsgMethod ?? '[anonymous]', 'ws')\n\n this.logger.info('Registering WebSocket gateway', {\n gateway: GatewayClass.name,\n path: fullPath,\n })\n\n const handlers: MiddlewareHandler<RouterEnv>[] = []\n\n if (guards.length > 0) {\n this.logger.info('Gateway guards', {\n gateway: GatewayClass.name,\n path: fullPath,\n guardCount: guards.length,\n })\n handlers.push(this.createGuardMiddleware(guards))\n }\n\n handlers.push(wsHandler)\n\n // Type assertion needed because Hono's overloaded .get() signatures\n // don't accept a spread of MiddlewareHandler[] alongside upgradeWebSocket's output type\n this.app.get(fullPath, ...(handlers as [MiddlewareHandler<RouterEnv>]))\n }\n\n\n /**\n * Create a guard execution middleware\n *\n * This middleware executes all guards for a route before the handler.\n * Guards are executed in order; all must pass for the request to proceed.\n *\n * @param guards - Array of guards to execute\n * @returns Hono middleware function\n */\n private createGuardMiddleware(guards: Guard[]) {\n const guardService = new GuardExecutionService(this.logger)\n\n return async (c: Context<RouterEnv>, next: () => Promise<void>) => {\n const ctx = new RouterContext(c)\n const container = ctx.getContainer()\n\n // Execute all guards - throws on failure\n await guardService.executeGuards(guards, ctx, container)\n\n // All guards passed, continue to handler\n await next()\n }\n }\n\n /**\n * Wrap a controller handler with a `scopedMiddleware → guards → handler`\n * chain that runs *inside* the Hono route handler — after request\n * validators have populated `c.req.valid(...)`. This is the only place\n * we can run user middleware after `@hono/zod-openapi`'s validators in\n * the same pipeline.\n *\n * Returns a Hono handler with the same signature as the original so\n * `app.openapi(route, wrapped)` works transparently.\n */\n private wrapHandlerWithChain(\n handler: (c: Context<RouterEnv>) => Promise<Response>,\n scopedMiddleware: Constructor<Middleware>[],\n guards: Guard[],\n ) {\n if (scopedMiddleware.length === 0 && guards.length === 0) {\n return handler\n }\n\n const scopedChain = scopedMiddleware.length > 0\n ? createMiddlewareChain(scopedMiddleware)\n : null\n const guardChain = guards.length > 0\n ? this.createGuardMiddleware(guards)\n : null\n\n return async (c: Context<RouterEnv, string>): Promise<Response> => {\n let captured: Response | undefined\n\n const runHandler = async () => {\n captured = await handler(c)\n }\n const runGuards = guardChain\n ? () => guardChain(c, runHandler)\n : runHandler\n const runScoped = scopedChain\n ? () => scopedChain(c, runGuards)\n : runGuards\n\n const result = await runScoped()\n // A middleware (scoped or guard) may short-circuit by returning a\n // Response from its createMiddlewareChain — surface that. Otherwise\n // the handler always sets `captured`.\n if (result instanceof Response) return result\n return captured!\n }\n }\n\n /**\n * Register wildcard route for non-RESTful controllers\n */\n private registerWildcardRoute(\n ControllerClass: Constructor,\n route: string\n ): void {\n this.logger.info(`Registering wildcard route`, {\n controller: ControllerClass.name,\n route: `${route}/:path{.+}`,\n method: 'ALL',\n })\n\n const handler = this.createControllerHandler(ControllerClass, 'handle')\n // Match base route exactly\n this.app.all(route, handler)\n // Match all sub-paths using named regex wildcard\n this.app.all(`${route}/:path{.+}`, handler)\n }\n\n\n /**\n * Resolve HTTP method, path, route config, and status code from route metadata.\n */\n private resolveMethodAndPath(\n meta: RouteMetadata,\n methodName: string,\n basePath: string,\n className: string\n ): { httpMethod: HttpMethod; fullPath: string; routeConfig: RouteConfig; statusCodeOverride?: number } | null {\n if (meta.type === 'convention') {\n const derived = this.deriveHttpMethodAndPath(methodName, basePath)\n if (!derived) {\n throw new RouterError(\n `Cannot derive HTTP method/path for convention-based route \"${className}.${methodName}\". ` +\n `Ensure the method name follows the naming convention (e.g., index, create, show).`\n )\n }\n return { httpMethod: derived.method, fullPath: derived.path, routeConfig: meta.config, statusCodeOverride: meta.config.statusCode }\n }\n\n return {\n httpMethod: meta.method,\n fullPath: this.joinPaths(basePath, meta.path),\n routeConfig: meta.config,\n statusCodeOverride: meta.config.statusCode,\n }\n }\n\n /**\n * Join a base path and a route path, normalizing slashes\n */\n private joinPaths(basePath: string, routePath: string): string {\n if (basePath.endsWith('/')) basePath = basePath.slice(0, -1)\n if (routePath === '/' || routePath === '') return basePath || '/'\n if (!routePath.startsWith('/')) routePath = '/' + routePath\n return basePath + routePath\n }\n\n\n /**\n * Auto-derive HTTP method and path from controller method name\n * Uses HTTP_METHODS constant for RESTful convention mapping\n */\n private deriveHttpMethodAndPath(methodName: string, basePath: string): { method: Exclude<HttpMethod, 'all'>; path: string } | null {\n if (!(methodName in HTTP_METHODS)) return null\n const mapping = HTTP_METHODS[methodName as keyof typeof HTTP_METHODS]\n\n return {\n method: mapping.method as Exclude<HttpMethod, 'all'>,\n path: basePath + mapping.path,\n }\n }\n\n /**\n * Merge controller-level and route-level metadata\n * Tags are merged (appended), security is merged (union)\n * Guards automatically add sessionCookie security if present\n */\n private mergeMetadata(\n controllerOpts: ControllerOptions | undefined,\n routeConfig: RouteConfig,\n ControllerClass: Constructor,\n methodName: string\n ): { tags: string[]; security: SecuritySchemeRecord[] } {\n const tags = [...(controllerOpts?.tags ?? []), ...(routeConfig.tags ?? [])]\n\n // Check if guards are present (indicates authentication is required)\n const prototype = ControllerClass.prototype as IController\n const hasMethodGuards = (getMethodGuards(prototype, methodName)?.guards.length ?? 0) > 0\n const hasControllerGuards = (getControllerGuards(ControllerClass)?.guards.length ?? 0) > 0\n const requiresAuth = hasMethodGuards || hasControllerGuards\n\n // Merge security: if route explicitly sets security (even empty array), use it\n // Otherwise inherit from controller\n let security: string[] = []\n if (routeConfig.security !== undefined) {\n // Route has explicit security (could be empty for public routes)\n security = [...(controllerOpts?.security ?? []), ...routeConfig.security]\n } else if (controllerOpts?.security) {\n // Inherit controller security\n security = controllerOpts.security\n }\n\n // Auto-add sessionCookie security if guards are present\n if (requiresAuth && !security.includes(SECURITY_SCHEMES.SESSION_COOKIE)) {\n security.push(SECURITY_SCHEMES.SESSION_COOKIE)\n }\n\n // Convert security array to OpenAPI security format\n const securityArray: SecuritySchemeRecord[] =\n security.length > 0\n ? (security.map<SecuritySchemeRecord>((scheme) => ({ [scheme]: [] }) as unknown as SecuritySchemeRecord))\n : ([] as SecuritySchemeRecord[])\n\n return { tags, security: securityArray }\n }\n\n /**\n * Build OpenAPI route configuration from metadata\n * Creates a route definition compatible with @hono/zod-openapi.\n *\n * Scoped middleware and guards are NOT attached to `route.middleware`\n * here — they're composed into a wrapped handler in `collectRoutes` so\n * they run after Hono's request validators. See `wrapHandlerWithChain`.\n */\n private buildOpenAPIRoute(\n method: Exclude<HttpMethod, 'all'>,\n path: string,\n routeConfig: RouteConfig,\n metadata: { tags: string[]; security: Record<string, string[]>[] },\n methodName?: string,\n statusCodeOverride?: number,\n hasLocaleParam = false,\n ): OpenAPIRouteConfig {\n try {\n const route: Partial<OpenAPIRouteConfig> & { hide?: boolean } = {\n method,\n path: toRoutingOpenAPIPath(path),\n request: {},\n responses: {},\n // Always hide from OpenAPI registry — clean paths are registered separately via registerPath()\n hide: true,\n }\n\n // Add request body if defined\n if (routeConfig.body) {\n const bodySchema = this.isRouteBodyObject(routeConfig.body) ? routeConfig.body.schema : routeConfig.body\n const bodyContentType = this.isRouteBodyObject(routeConfig.body) ? routeConfig.body.contentType ?? DEFAULT_CONTENT_TYPE : DEFAULT_CONTENT_TYPE\n\n route.request = {\n ...route.request,\n body: {\n content: {\n [bodyContentType]: {\n schema: bodySchema,\n },\n },\n },\n }\n }\n\n // Add query parameters if defined\n if (routeConfig.query) {\n route.request = {\n ...route.request,\n query: routeConfig.query,\n }\n }\n\n // Add URL parameters if defined\n if (routeConfig.params) {\n route.request = {\n ...route.request,\n params: routeConfig.params,\n }\n }\n\n // Auto-inject locale path parameter for locale-prefixed routes\n const localeConfig = this.localePathService.localePathConfig\n if (hasLocaleParam && localeConfig) {\n const localeParam = z.object({\n locale: z.enum(localeConfig.prefixedLocales as [string, ...string[]]).openapi({\n param: {\n name: 'locale',\n in: 'path',\n },\n }).optional(),\n })\n\n route.request = {\n ...route.request,\n params: route.request!.params\n ? (route.request!.params as z.ZodObject).extend(localeParam.shape)\n : localeParam,\n }\n }\n\n // Derive success status code from method name or use override\n const successStatus = statusCodeOverride\n ?? (methodName && METHOD_STATUS_CODES[methodName as keyof typeof METHOD_STATUS_CODES])\n ?? 200\n\n // Build responses object with auto-derived status\n const responses: NonNullable<OpenAPIRouteConfig['responses']> = {}\n\n // Add success response with derived status code\n const responseDef = routeConfig.response\n if (responseDef) {\n if (typeof responseDef === 'object' && 'schema' in responseDef) {\n const responseContentType = responseDef.contentType ?? DEFAULT_CONTENT_TYPE\n responses[successStatus] = {\n content: {\n [responseContentType]: { schema: responseDef.schema },\n },\n description: responseDef.description ?? `Response ${successStatus}`,\n }\n } else {\n responses[successStatus] = {\n content: {\n [DEFAULT_CONTENT_TYPE]: { schema: responseDef },\n },\n description: `Response ${successStatus}`,\n }\n }\n }\n\n // Auto-merge common error schemas (400, 401, 403, 404, 409, 500)\n // Controllers only need to define success response; error responses are added automatically\n for (const [statusStr, schema] of Object.entries(commonErrorSchemas)) {\n const status = parseInt(statusStr)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive: avoid overwriting success response status\n responses[status] ??= schema\n }\n\n route.responses = responses\n\n // Add tags if provided\n if (metadata.tags.length > 0) {\n route.tags = metadata.tags\n }\n\n // Add security if provided\n if (metadata.security.length > 0) {\n route.security = metadata.security\n }\n\n // Add description and summary\n if (routeConfig.description) {\n route.description = routeConfig.description\n }\n if (routeConfig.summary) {\n route.summary = routeConfig.summary\n }\n\n return createRoute(route as OpenAPIRouteConfig)\n } catch (error) {\n throw new RouterError(`OpenAPI route registration failed for \"${path}\": ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n\n /**\n * Check if a body definition is a RouteBodyObject (has schema key) vs bare ZodType\n */\n private isRouteBodyObject(body: RouteConfig['body']): body is RouteBodyObject {\n return typeof body === 'object' && 'schema' in body\n }\n\n /**\n * Resolve method parameter injections from the container\n *\n * @param prototype - Controller prototype\n * @param methodName - Method name to get injections for\n * @param container - Request-scoped container\n * @returns Array of resolved dependencies in parameter order\n */\n private resolveMethodInjections(\n prototype: object,\n methodName: string,\n container: Container\n ): unknown[] {\n const injections = getMethodInjections(prototype, methodName)\n if (!injections.length) return []\n\n return injections.map((inj): unknown => container.resolve(inj.token))\n }\n\n /**\n * Name a handler function so Hono's inspectRoutes() can identify it.\n * Format: `{type}:{Controller}.{method}` (e.g. `http:UsersController.create`)\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- intentionally accepting any function to set its name\n private nameHandler(fn: Function, controller: string, method: string, type: 'http' | 'ws' = 'http'): void {\n Object.defineProperty(fn, 'name', { value: `${type}:${controller}.${method}` })\n }\n\n /**\n * Create controller handler that resolves controller from request-scoped container\n * This ensures each request gets a fresh controller with request-scoped context\n */\n private createControllerHandler(\n ControllerClass: new (...args: unknown[]) => IController,\n methodName: string,\n responseSchema: ZodType | null = null,\n ): (c: Context<RouterEnv>) => Promise<Response> {\n const handler = async (c: Context<RouterEnv>) => {\n // Precognition short-circuit: HandlePrecognitiveRequests middleware\n // sets `validationSuccessResponse` for `Precognition: true` requests.\n // If we reach here, every request validator has passed — return the\n // 204 without invoking the controller body.\n const override = c.get('validationSuccessResponse')\n if (override) return override\n\n const ctx = new RouterContext(c)\n const requestContainer = ctx.getContainer()\n const controller = requestContainer.resolve<IController>(ControllerClass)\n\n const method = controller[methodName as keyof IController]\n if (typeof method === 'function') {\n const injectedArgs = this.resolveMethodInjections(ControllerClass.prototype as object, methodName, requestContainer)\n const response = await (method as (...args: unknown[]) => Promise<Response>).apply(controller, [ctx, ...injectedArgs])\n\n if (responseSchema && c.env.ENVIRONMENT !== 'production') {\n return this.validateResponse(response, responseSchema)\n }\n\n return response\n }\n\n throw new RouterError(`Method \"${methodName}\" not found on controller \"${ControllerClass.name}\"`)\n }\n\n this.nameHandler(handler, ControllerClass.name, methodName)\n return handler\n }\n\n /**\n * Extract the Zod schema from a RouteResponse definition.\n * Returns null for non-JSON content types or when no response is defined.\n */\n private extractResponseSchema(routeConfig: RouteConfig): ZodType | null {\n const responseDef = routeConfig.response\n if (!responseDef) return null\n\n if (this.isRouteResponseObject(responseDef)) {\n const contentType = responseDef.contentType ?? DEFAULT_CONTENT_TYPE\n if (!contentType.includes('application/json')) return null\n return responseDef.schema\n }\n\n return responseDef\n }\n\n /**\n * Check if a response definition is a RouteResponseObject (has schema key) vs bare ZodType\n */\n private isRouteResponseObject(response: RouteConfig['response']): response is RouteResponseObject {\n return typeof response === 'object' && 'schema' in response\n }\n\n /**\n * Validate a Response body against its declared Zod schema.\n *\n * Skips validation for:\n * - Non-JSON content types\n * - Empty bodies (204 No Content, 304 Not Modified)\n *\n * Clones the response to read the body without consuming the original stream.\n */\n private async validateResponse(response: Response, schema: ZodType): Promise<Response> {\n const contentType = response.headers.get('content-type')\n if (!contentType || !contentType.includes('application/json')) {\n return response\n }\n\n if (response.status === 204 || response.status === 304) {\n return response\n }\n\n const cloned = response.clone()\n\n let body: unknown\n try {\n body = await cloned.json()\n } catch {\n return response\n }\n\n const result = schema.safeParse(body)\n if (!result.success) {\n throw new ResponseValidationError(result.error)\n }\n\n return response\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"router-resolver-sUV_jTrU.mjs","names":["internal.getGroups","internal.getDefaultEntry","internal.getGlobalMiddleware"],"sources":["../src/router/router-resolver.ts"],"sourcesContent":["import type { ZodObject } from '../i18n/validation/zod'\nimport type { Constructor } from '../types'\nimport type { Middleware } from './middleware.interface'\nimport type { Router, RouterEntry } from './router'\nimport * as internal from './router.internals'\n\n/**\n * Resolved configuration for a single controller.\n * Merges Router default entry, sub-group overrides, and inheritance rules.\n */\nexport interface ResolvedRouterConfig {\n prefix?: string\n domain?: string\n name?: string\n middleware: Constructor<Middleware>[]\n version?: string | string[]\n hideFromDocs?: boolean\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ZodObject generics require any for flexible shape parameter\n params?: ZodObject<any>\n}\n\n/**\n * Internal resolver that computes the effective Router config for each controller.\n *\n * Inheritance rules:\n * - `middleware`: parent middleware runs first, then child (concatenated)\n * - `prefix`: concatenated (parent + child)\n * - `name`: concatenated (parent + child)\n * - `domain`: child overrides parent\n * - `version`: child overrides parent\n * - `hideFromDocs`: child overrides parent\n *\n * @internal — not exported from stratal/router\n */\nexport class RouterResolver {\n private readonly routers: { router: Router; controllers: Constructor[] }[]\n\n constructor(routers: { router: Router; controllers: Constructor[] }[]) {\n this.routers = routers\n }\n\n /**\n * Resolve the effective config for a given controller class.\n * Searches through all module routers to find the one owning this controller.\n */\n resolveForController(controller: Constructor): ResolvedRouterConfig {\n for (const { router, controllers: moduleControllers } of this.routers) {\n if (!moduleControllers.includes(controller)) continue\n\n // Check if controller is in a sub-group\n for (const group of router[internal.getGroups]()) {\n if (group.controllers?.includes(controller)) {\n return this.mergeEntries(router[internal.getDefaultEntry](), group)\n }\n }\n\n // Controller is in the default scope (not in any sub-group)\n // But only if it's not claimed by any sub-group in this module\n const groupedControllers = new Set(\n router[internal.getGroups]().flatMap(g => g.controllers ?? [])\n )\n if (!groupedControllers.has(controller)) {\n return this.entryToConfig(router[internal.getDefaultEntry]())\n }\n }\n\n // Controller not found in any module's router — return empty config\n return { middleware: [] }\n }\n\n /**\n * Collect all global middleware registered via `router.use()` across all modules.\n */\n getGlobalMiddleware(): Constructor<Middleware>[] {\n const global: Constructor<Middleware>[] = []\n for (const { router } of this.routers) {\n global.push(...router[internal.getGlobalMiddleware]())\n }\n return global\n }\n\n /**\n * Merge parent default entry with child group entry following inheritance rules.\n */\n private mergeEntries(parent: RouterEntry, child: RouterEntry): ResolvedRouterConfig {\n return {\n // Concatenate: parent prefix + child prefix\n prefix: this.concatPrefixes(parent.prefix, child.prefix),\n // Override: child domain wins\n domain: child.domain ?? parent.domain,\n // Concatenate: parent name + child name\n name: this.concatNames(parent.name, child.name),\n // Concatenate: parent middleware first, then child\n middleware: [...parent.middleware, ...child.middleware],\n // Override: child version wins\n version: child.version ?? parent.version,\n // Override: child hideFromDocs wins\n hideFromDocs: child.hideFromDocs ?? parent.hideFromDocs,\n // Extend: parent params extended with child params\n params: this.mergeParams(parent.params, child.params),\n }\n }\n\n private entryToConfig(entry: RouterEntry): ResolvedRouterConfig {\n return {\n prefix: entry.prefix,\n domain: entry.domain,\n name: entry.name,\n middleware: [...entry.middleware],\n version: entry.version,\n hideFromDocs: entry.hideFromDocs,\n params: entry.params,\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ZodObject generics require any for flexible shape parameter\n private mergeParams(parent?: ZodObject<any>, child?: ZodObject<any>): ZodObject<any> | undefined {\n if (!parent && !child) return undefined\n if (!parent) return child\n if (!child) return parent\n // oxlint-disable-next-line typescript/no-explicit-any\n return parent.extend(child.shape) as ZodObject<any>\n }\n\n private concatPrefixes(parent?: string, child?: string): string | undefined {\n if (!parent && !child) return undefined\n if (!parent) return child\n if (!child) return parent\n // Normalize: remove trailing slash from parent, ensure child starts with /\n const p = parent.endsWith('/') ? parent.slice(0, -1) : parent\n const c = child.startsWith('/') ? child : `/${child}`\n return `${p}${c}`\n }\n\n private concatNames(parent?: string, child?: string): string | undefined {\n if (!parent && !child) return undefined\n if (!parent) return child\n if (!child) return parent\n return `${parent}${child}`\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAkCA,IAAa,iBAAb,MAA4B;CAC1B;CAEA,YAAY,SAA2D;EACrE,KAAK,UAAU;CACjB;;;;;CAMA,qBAAqB,YAA+C;EAClE,KAAK,MAAM,EAAE,QAAQ,aAAa,uBAAuB,KAAK,SAAS;GACrE,IAAI,CAAC,kBAAkB,SAAS,UAAU,GAAG;GAG7C,KAAK,MAAM,SAAS,OAAOA,WAAoB,GAC7C,IAAI,MAAM,aAAa,SAAS,UAAU,GACxC,OAAO,KAAK,aAAa,OAAOC,iBAA0B,GAAG,KAAK;GAStE,IAAI,CAAC,IAH0B,IAC7B,OAAOD,WAAoB,EAAE,SAAQ,MAAK,EAAE,eAAe,CAAC,CAAC,CAEzC,EAAE,IAAI,UAAU,GACpC,OAAO,KAAK,cAAc,OAAOC,iBAA0B,CAAC;EAEhE;EAGA,OAAO,EAAE,YAAY,CAAC,EAAE;CAC1B;;;;CAKA,sBAAiD;EAC/C,MAAM,SAAoC,CAAC;EAC3C,KAAK,MAAM,EAAE,YAAY,KAAK,SAC5B,OAAO,KAAK,GAAG,OAAOC,qBAA8B,CAAC;EAEvD,OAAO;CACT;;;;CAKA,aAAqB,QAAqB,OAA0C;EAClF,OAAO;GAEL,QAAQ,KAAK,eAAe,OAAO,QAAQ,MAAM,MAAM;GAEvD,QAAQ,MAAM,UAAU,OAAO;GAE/B,MAAM,KAAK,YAAY,OAAO,MAAM,MAAM,IAAI;GAE9C,YAAY,CAAC,GAAG,OAAO,YAAY,GAAG,MAAM,UAAU;GAEtD,SAAS,MAAM,WAAW,OAAO;GAEjC,cAAc,MAAM,gBAAgB,OAAO;GAE3C,QAAQ,KAAK,YAAY,OAAO,QAAQ,MAAM,MAAM;EACtD;CACF;CAEA,cAAsB,OAA0C;EAC9D,OAAO;GACL,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,MAAM,MAAM;GACZ,YAAY,CAAC,GAAG,MAAM,UAAU;GAChC,SAAS,MAAM;GACf,cAAc,MAAM;GACpB,QAAQ,MAAM;EAChB;CACF;CAGA,YAAoB,QAAyB,OAAoD;EAC/F,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO,KAAA;EAC9B,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,CAAC,OAAO,OAAO;EAEnB,OAAO,OAAO,OAAO,MAAM,KAAK;CAClC;CAEA,eAAuB,QAAiB,OAAoC;EAC1E,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO,KAAA;EAC9B,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,CAAC,OAAO,OAAO;EAInB,OAAO,GAFG,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI,SAC7C,MAAM,WAAW,GAAG,IAAI,QAAQ,IAAI;CAEhD;CAEA,YAAoB,QAAiB,OAAoC;EACvE,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO,KAAA;EAC9B,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,CAAC,OAAO,OAAO;EACnB,OAAO,GAAG,SAAS;CACrB;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage-C30X81CS.mjs","names":[],"sources":["../src/storage/storage.tokens.ts","../src/storage/services/storage-manager.service.ts","../src/storage/services/storage.service.ts","../src/storage/storage.module.ts","../src/storage/errors/file-not-found.error.ts","../src/storage/controllers/storage.controller.ts","../src/storage/contracts/delete-file.input.ts","../src/storage/contracts/file-exists.input.ts","../src/storage/contracts/get-presigned-url.input.ts","../src/storage/contracts/upload-file.input.ts","../src/storage/errors/file-too-large.error.ts","../src/storage/errors/invalid-file-type.error.ts"],"sourcesContent":["/**\n * Dependency injection tokens for the Storage module\n * Using Symbol-based tokens to avoid magic strings\n */\nexport const STORAGE_TOKENS = {\n Options: Symbol.for('stratal:storage:options'),\n StorageService: Symbol.for('stratal:storage:service'),\n StorageManager: Symbol.for('stratal:storage:manager'),\n} as const\n","import { inject } from '../../di'\nimport { Singleton } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type StratalEnv } from '../../env'\nimport { StorageError } from '../storage.error'\nimport type { IStorageProvider } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig, StorageEntry } from '../types'\n\n/**\n * Storage Manager Service\n * Manages multiple storage providers (one per disk)\n * Handles lazy initialization and caching of R2 providers\n */\n@Singleton(STORAGE_TOKENS.StorageManager)\nexport class StorageManagerService {\n private readonly providers = new Map<string, IStorageProvider>()\n private readonly creationPromises = new Map<string, Promise<IStorageProvider>>()\n private readonly diskConfigs = new Map<string, StorageEntry>()\n\n constructor(\n @inject(STORAGE_TOKENS.Options)\n private readonly options: StorageConfig,\n @inject(DI_TOKENS.CloudflareEnv)\n private readonly env: StratalEnv\n ) {\n this.initializeDiskConfigs()\n }\n\n /**\n * Initialize disk configurations from options\n */\n private initializeDiskConfigs(): void {\n for (const entry of this.options.storage) {\n this.diskConfigs.set(entry.disk, entry)\n }\n }\n\n /**\n * Get provider for a specific disk\n * Lazily initializes provider on first access\n * @param diskName - Name of the disk\n * @returns Storage provider instance\n */\n async getProvider(diskName: string): Promise<IStorageProvider> {\n // Return cached provider if exists\n const cached = this.providers.get(diskName)\n if (cached) {\n return cached\n }\n\n // Return in-flight creation promise to deduplicate concurrent calls\n const inflight = this.creationPromises.get(diskName)\n if (inflight) {\n return inflight\n }\n\n // Get disk configuration\n const diskConfig = this.diskConfigs.get(diskName)\n if (!diskConfig) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n\n // Create provider and deduplicate concurrent calls\n const promise = this.createProvider(diskConfig).then((provider) => {\n this.providers.set(diskName, provider)\n this.creationPromises.delete(diskName)\n return provider\n }).catch((error: unknown) => {\n this.creationPromises.delete(diskName)\n throw error\n })\n\n this.creationPromises.set(diskName, promise)\n\n return promise\n }\n\n /**\n * Create an R2 provider instance\n * Dynamically imports R2StorageProvider to support code splitting\n * @param config - Storage entry configuration\n * @returns Storage provider instance\n */\n private async createProvider(config: StorageEntry): Promise<IStorageProvider> {\n const { R2StorageProvider } = await import('../providers/r2-storage.provider')\n const bucket = this.env[config.binding as keyof StratalEnv] as unknown as R2Bucket | undefined\n if (!bucket) {\n throw new StorageError(`R2 binding \"${config.binding}\" was not found in the environment`)\n }\n return new R2StorageProvider(config, bucket, this.env, this.options.route)\n }\n\n /**\n * Get disk configuration\n * @param diskName - Name of the disk\n * @returns Storage entry configuration\n */\n getDiskConfig(diskName: string): StorageEntry {\n const config = this.diskConfigs.get(diskName)\n if (!config) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n return config\n }\n\n /**\n * Check if a disk exists\n * @param diskName - Name of the disk\n * @returns True if disk exists, false otherwise\n */\n hasDisk(diskName: string): boolean {\n return this.diskConfigs.has(diskName)\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return Array.from(this.diskConfigs.keys())\n }\n}\n","import { inject } from '../../di'\nimport { Request } from '../../di/decorators'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport { StorageError } from '../storage.error'\nimport type { StreamingBlobPayloadInputTypes } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig } from '../types'\nimport { type StorageManagerService } from './storage-manager.service'\n\n/**\n * Storage Service\n *\n * Main facade for storage operations.\n * Request-scoped for proper isolation.\n *\n * @example\n * ```typescript\n * @inject(STORAGE_TOKENS.StorageService)\n * private readonly storage: StorageService\n *\n * await this.storage.upload(file, 'documents/report.pdf')\n * ```\n */\n@Request(STORAGE_TOKENS.StorageService)\nexport class StorageService {\n constructor(\n @inject(STORAGE_TOKENS.StorageManager)\n protected readonly storageManager: StorageManagerService,\n @inject(STORAGE_TOKENS.Options)\n protected readonly options: StorageConfig\n ) { }\n\n /**\n * Upload content to storage\n * @param body - Content to upload (stream, buffer, or string)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options including size and mime type\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async upload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: UploadOptions,\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.upload(body, fullPath, options)\n }\n\n /**\n * Download a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Download result with stream and metadata\n */\n async download(relativePath: string, disk?: string): Promise<DownloadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.download(fullPath)\n }\n\n /**\n * Delete a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n */\n async delete(relativePath: string, disk?: string): Promise<void> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n await provider.delete(fullPath)\n }\n\n /**\n * Check if a file exists in storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns True if file exists, false otherwise\n */\n async exists(relativePath: string, disk?: string): Promise<boolean> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.exists(fullPath)\n }\n\n /**\n * Generate a presigned download URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDownloadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'GET', expiresIn, disk)\n }\n\n /**\n * Generate a presigned upload URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedUploadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'PUT', expiresIn, disk)\n }\n\n /**\n * Generate a presigned delete URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDeleteUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'DELETE', expiresIn, disk)\n }\n\n /**\n * Generate a presigned URL for any method\n * @param relativePath - Relative path within the disk\n * @param method - HTTP method (GET, PUT, DELETE, HEAD)\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n protected async getPresignedUrl(\n relativePath: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n const validatedExpiresIn = this.validateExpiresIn(expiresIn)\n\n return provider.getPresignedUrl(fullPath, method, validatedExpiresIn)\n }\n\n /**\n * Resolve disk name (use default if not provided)\n * @param disk - Optional disk name\n * @returns Resolved disk name\n */\n protected resolveDisk(disk?: string): string {\n const diskName = disk ?? this.options.defaultStorageDisk\n\n if (!this.storageManager.hasDisk(diskName)) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n\n return diskName\n }\n\n /**\n * Build full path with disk root and path template substitution\n * @param relativePath - Relative path within the disk\n * @param diskName - Name of the disk\n * @returns Full path including disk root\n */\n protected buildFullPath(relativePath: string, diskName: string): string {\n const diskConfig = this.storageManager.getDiskConfig(diskName)\n let root = diskConfig.root || ''\n\n // Substitute template variables\n root = this.substituteTemplateVariables(root)\n\n // Combine root and relative path\n const fullPath = `${root}/${relativePath}`.replace(/\\/+/g, '/').replace(/^\\//, '')\n\n return fullPath\n }\n\n /**\n * Substitute template variables in path\n * Override this method in subclasses to add custom substitutions\n *\n * @param path - Path with template variables\n * @returns Path with substituted variables\n */\n protected substituteTemplateVariables(path: string): string {\n let result = path\n\n // Substitute {date}, {year}, {month}\n const now = new Date()\n result = result.replace(/{date}/g, now.toISOString().split('T')[0])\n result = result.replace(/{year}/g, now.getFullYear().toString())\n result = result.replace(/{month}/g, (now.getMonth() + 1).toString().padStart(2, '0'))\n\n return result\n }\n\n /**\n * Validate expiry time for presigned URLs\n * @param expiresIn - Optional expiry time in seconds\n * @returns Validated expiry time\n */\n protected validateExpiresIn(expiresIn?: number): number {\n const presignedUrlConfig = this.options.presignedUrl\n const validatedExpiresIn = expiresIn ?? presignedUrlConfig.defaultExpiry\n\n const minExpiry = 1\n const maxExpiry = presignedUrlConfig.maxExpiry\n\n if (validatedExpiresIn < minExpiry || validatedExpiresIn > maxExpiry) {\n throw new StorageError(`Presigned URL expiry ${validatedExpiresIn}s is out of range (${minExpiry}–${maxExpiry}s)`)\n }\n\n return validatedExpiresIn\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return this.storageManager.getAvailableDisks()\n }\n\n /**\n * Chunked upload for streaming data without known size\n * Uses multipart upload under the hood - handles retries and large files\n *\n * Use this method when:\n * - Content-Length is unknown or unreliable\n * - Uploading from streams that can't be rewound\n * - Need automatic retry handling for transient failures\n *\n * @param body - Content to upload (stream or buffer)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options (mimeType required, size optional)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: Omit<UploadOptions, 'size'> & { size?: number },\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.chunkedUpload(body, fullPath, options)\n }\n}\n","/**\n * Storage Module\n * Provides file storage capabilities using Cloudflare R2\n * Supports multiple disk configurations with dynamic path templates\n */\n\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport { StorageManagerService } from './services/storage-manager.service'\nimport { StorageService } from './services/storage.service'\nimport { STORAGE_TOKENS } from './storage.tokens'\nimport type { StorageConfig } from './types'\n\n/**\n * Storage module options\n * Same as StorageConfig from types.ts\n */\nexport type StorageModuleOptions = StorageConfig\n\n@Module({\n providers: [\n { provide: STORAGE_TOKENS.StorageManager, useClass: StorageManagerService },\n { provide: STORAGE_TOKENS.StorageService, useClass: StorageService },\n ],\n})\nexport class StorageModule {\n /**\n * Configure StorageModule with static options\n *\n * @example\n * ```typescript\n * StorageModule.forRoot({\n * storage: [{ disk: 'uploads', binding: 'MY_BUCKET', root: 'uploads' }],\n * defaultStorageDisk: 'uploads',\n * presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 }\n * })\n * ```\n */\n static forRoot(options: StorageModuleOptions): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n { provide: STORAGE_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n /**\n * Configure StorageModule with async factory\n *\n * Use when configuration depends on other services.\n *\n * @example\n * ```typescript\n * StorageModule.forRootAsync({\n * inject: [storageConfig.KEY],\n * useFactory: (storage) => ({\n * storage: storage.storage,\n * defaultStorageDisk: storage.defaultStorageDisk,\n * presignedUrl: storage.presignedUrl\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<StorageModuleOptions>): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n {\n provide: STORAGE_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n}\n","import { HttpException } from '../../errors'\n\nexport class FileNotFoundError extends HttpException {\n constructor(path?: string) {\n super(404, path ? `File not found: \"${path}\"` : 'File not found')\n }\n}\n","import { inject } from '../../di'\nimport { z } from '../../i18n/validation'\nimport { Controller } from '../../router/decorators/controller.decorator'\nimport { Delete, Get, Put } from '../../router/decorators/http-method.decorator'\nimport { type RouterContext } from '../../router/router-context'\nimport { FileNotFoundError } from '../errors/file-not-found.error'\nimport type { StorageService } from '../services/storage.service'\nimport { STORAGE_TOKENS } from '../storage.tokens'\n\nconst diskParam = z.object({\n disk: z.string(),\n})\n\n/**\n * Storage Controller\n *\n * Auto-registered controller that proxies R2 operations behind signed URLs.\n * Signature verification is applied via VerifySignatureMiddleware on the module's\n * configureRoutes() method.\n *\n * Routes:\n * - GET /storage/:disk/* → download file\n * - PUT /storage/:disk/* → upload file\n * - DELETE /storage/:disk/* → delete file\n */\n@Controller('/storage', { hideFromDocs: true })\nexport class StorageController {\n constructor(\n @inject(STORAGE_TOKENS.StorageService)\n private readonly storage: StorageService\n ) {}\n\n @Get('/:disk/*', { hideFromDocs: true, params: diskParam })\n async download(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n const result = await this.storage.download(path, disk)\n\n const stream = result.toStream()\n if (!stream) {\n throw new FileNotFoundError(path)\n }\n\n return new Response(stream, {\n headers: {\n 'Content-Type': result.contentType,\n 'Content-Length': String(result.size),\n 'Content-Disposition': 'inline',\n },\n })\n }\n\n @Put('/:disk/*', { hideFromDocs: true, params: diskParam })\n async upload(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n const body = ctx.c.req.raw.body\n const contentType = ctx.header('content-type') ?? 'application/octet-stream'\n const contentLength = ctx.header('content-length')\n\n await this.storage.upload(body, path, {\n mimeType: contentType,\n size: contentLength ? parseInt(contentLength, 10) : 0,\n }, disk)\n\n return ctx.json({ path, disk }, 200)\n }\n\n @Delete('/:disk/*', { hideFromDocs: true, params: diskParam })\n async destroy(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n await this.storage.delete(path, disk)\n\n return ctx.c.body(null, 204)\n }\n}\n\n/**\n * Extract the wildcard path from the Hono context.\n * Hono stores wildcard params under the key matching the path pattern.\n */\nfunction extractWildcardPath(ctx: RouterContext): string {\n // Hono exposes wildcard capture as the raw path after the matched prefix\n const url = new URL(ctx.c.req.url)\n const fullPath = url.pathname\n // Remove /storage/:disk/ prefix to get the file path\n const parts = fullPath.split('/')\n // ['', 'storage', 'disk', ...rest]\n return parts.slice(3).join('/')\n}\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const deleteFileInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type DeleteFileInput = z.infer<typeof deleteFileInputSchema>\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const fileExistsInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type FileExistsInput = z.infer<typeof fileExistsInputSchema>\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const getPresignedUrlInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']).default('GET'),\n expiresIn: z.number().int().min(1).max(604800).optional(),\n disk: z.string().optional(),\n})\n\nexport type GetPresignedUrlInput = z.infer<typeof getPresignedUrlInputSchema>\n\nexport const presignedUrlResultSchema = z.object({\n url: z.string().url(),\n expiresIn: z.number(),\n expiresAt: z.date(),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']),\n})\n\nexport type PresignedUrlResult = z.infer<typeof presignedUrlResultSchema>\n","import { z } from '../../i18n/validation'\n\n/**\n * Upload options for streaming uploads\n */\nexport interface UploadOptions {\n /**\n * Size of the content in bytes\n */\n size: number\n /**\n * MIME type of the content\n */\n mimeType?: string\n /**\n * Custom metadata to store with the object (S3-specific)\n * Stored as S3 object metadata headers\n */\n metadata?: Record<string, string>\n /**\n * Object tagging for lifecycle policies (S3-specific)\n * Format: key=value (e.g., \"Tus-Completed=true\")\n */\n tagging?: string\n}\n\nexport const uploadResultSchema = z.object({\n path: z.string(),\n disk: z.string(),\n fullPath: z.string(),\n size: z.number(),\n mimeType: z.string(),\n uploadedAt: z.date(),\n})\n\nexport type UploadResult = z.infer<typeof uploadResultSchema>\n","import { HttpException } from '../../errors'\n\nexport class FileTooLargeError extends HttpException {\n constructor(public readonly size?: number, public readonly maxSize?: number) {\n super(413, 'File too large')\n }\n}\n","import { HttpException } from '../../errors'\n\nexport class InvalidFileTypeError extends HttpException {\n constructor(public readonly mimeType?: string) {\n super(422, 'Invalid file type')\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { a as z } from "./zod-wecrEVAs.mjs";
|
|
2
|
-
//#region src/storage/types.d.ts
|
|
3
|
-
/**
|
|
4
|
-
* Storage entry configuration
|
|
5
|
-
* Represents a single storage disk backed by Cloudflare R2
|
|
6
|
-
*/
|
|
7
|
-
interface StorageEntry {
|
|
8
|
-
disk: string;
|
|
9
|
-
/** R2Bucket binding name from wrangler.toml */
|
|
10
|
-
binding: string;
|
|
11
|
-
root: string;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Presigned URL configuration
|
|
15
|
-
*/
|
|
16
|
-
interface PresignedUrlConfig {
|
|
17
|
-
defaultExpiry: number;
|
|
18
|
-
maxExpiry: number;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Storage route configuration
|
|
22
|
-
*/
|
|
23
|
-
interface StorageRouteConfig {
|
|
24
|
-
/** Base path for storage routes. Default: '/storage' */
|
|
25
|
-
basePath?: string;
|
|
26
|
-
/** Disable auto-registered storage routes. Default: false */
|
|
27
|
-
disabled?: boolean;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Storage configuration used by framework
|
|
31
|
-
*/
|
|
32
|
-
interface StorageConfig {
|
|
33
|
-
storage: StorageEntry[];
|
|
34
|
-
defaultStorageDisk: string;
|
|
35
|
-
presignedUrl: PresignedUrlConfig;
|
|
36
|
-
/** Config for auto-registered storage routes (presigned URL proxying) */
|
|
37
|
-
route?: StorageRouteConfig;
|
|
38
|
-
}
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/storage/contracts/delete-file.input.d.ts
|
|
41
|
-
declare const deleteFileInputSchema: z.ZodObject<{
|
|
42
|
-
path: z.ZodString;
|
|
43
|
-
disk: z.ZodOptional<z.ZodString>;
|
|
44
|
-
}, z.core.$strip>;
|
|
45
|
-
type DeleteFileInput = z.infer<typeof deleteFileInputSchema>;
|
|
46
|
-
//#endregion
|
|
47
|
-
//#region src/storage/contracts/download-result.d.ts
|
|
48
|
-
/**
|
|
49
|
-
* Download Result
|
|
50
|
-
*
|
|
51
|
-
* Result of downloading a file from storage.
|
|
52
|
-
* Includes the file stream and metadata.
|
|
53
|
-
*/
|
|
54
|
-
interface DownloadResult {
|
|
55
|
-
/**
|
|
56
|
-
* ReadableStream of file contents
|
|
57
|
-
*/
|
|
58
|
-
toStream(): undefined | ReadableStream<Uint8Array>;
|
|
59
|
-
toString(): undefined | Promise<string>;
|
|
60
|
-
toArrayBuffer(): undefined | Promise<Uint8Array>;
|
|
61
|
-
/**
|
|
62
|
-
* MIME type of the file (e.g., 'application/pdf', 'image/png')
|
|
63
|
-
*/
|
|
64
|
-
contentType: string;
|
|
65
|
-
/**
|
|
66
|
-
* Size of the file in bytes
|
|
67
|
-
*/
|
|
68
|
-
size: number;
|
|
69
|
-
/**
|
|
70
|
-
* Custom object metadata (S3-specific)
|
|
71
|
-
* Key-value pairs stored with the object
|
|
72
|
-
*/
|
|
73
|
-
metadata?: Record<string, string>;
|
|
74
|
-
}
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/storage/contracts/file-exists.input.d.ts
|
|
77
|
-
declare const fileExistsInputSchema: z.ZodObject<{
|
|
78
|
-
path: z.ZodString;
|
|
79
|
-
disk: z.ZodOptional<z.ZodString>;
|
|
80
|
-
}, z.core.$strip>;
|
|
81
|
-
type FileExistsInput = z.infer<typeof fileExistsInputSchema>;
|
|
82
|
-
//#endregion
|
|
83
|
-
//#region src/storage/contracts/get-presigned-url.input.d.ts
|
|
84
|
-
declare const getPresignedUrlInputSchema: z.ZodObject<{
|
|
85
|
-
path: z.ZodString;
|
|
86
|
-
method: z.ZodDefault<z.ZodEnum<{
|
|
87
|
-
GET: "GET";
|
|
88
|
-
PUT: "PUT";
|
|
89
|
-
DELETE: "DELETE";
|
|
90
|
-
HEAD: "HEAD";
|
|
91
|
-
}>>;
|
|
92
|
-
expiresIn: z.ZodOptional<z.ZodNumber>;
|
|
93
|
-
disk: z.ZodOptional<z.ZodString>;
|
|
94
|
-
}, z.core.$strip>;
|
|
95
|
-
type GetPresignedUrlInput = z.infer<typeof getPresignedUrlInputSchema>;
|
|
96
|
-
declare const presignedUrlResultSchema: z.ZodObject<{
|
|
97
|
-
url: z.ZodString;
|
|
98
|
-
expiresIn: z.ZodNumber;
|
|
99
|
-
expiresAt: z.ZodDate;
|
|
100
|
-
method: z.ZodEnum<{
|
|
101
|
-
GET: "GET";
|
|
102
|
-
PUT: "PUT";
|
|
103
|
-
DELETE: "DELETE";
|
|
104
|
-
HEAD: "HEAD";
|
|
105
|
-
}>;
|
|
106
|
-
}, z.core.$strip>;
|
|
107
|
-
type PresignedUrlResult = z.infer<typeof presignedUrlResultSchema>;
|
|
108
|
-
//#endregion
|
|
109
|
-
//#region src/storage/contracts/upload-file.input.d.ts
|
|
110
|
-
/**
|
|
111
|
-
* Upload options for streaming uploads
|
|
112
|
-
*/
|
|
113
|
-
interface UploadOptions {
|
|
114
|
-
/**
|
|
115
|
-
* Size of the content in bytes
|
|
116
|
-
*/
|
|
117
|
-
size: number;
|
|
118
|
-
/**
|
|
119
|
-
* MIME type of the content
|
|
120
|
-
*/
|
|
121
|
-
mimeType?: string;
|
|
122
|
-
/**
|
|
123
|
-
* Custom metadata to store with the object (S3-specific)
|
|
124
|
-
* Stored as S3 object metadata headers
|
|
125
|
-
*/
|
|
126
|
-
metadata?: Record<string, string>;
|
|
127
|
-
/**
|
|
128
|
-
* Object tagging for lifecycle policies (S3-specific)
|
|
129
|
-
* Format: key=value (e.g., "Tus-Completed=true")
|
|
130
|
-
*/
|
|
131
|
-
tagging?: string;
|
|
132
|
-
}
|
|
133
|
-
declare const uploadResultSchema: z.ZodObject<{
|
|
134
|
-
path: z.ZodString;
|
|
135
|
-
disk: z.ZodString;
|
|
136
|
-
fullPath: z.ZodString;
|
|
137
|
-
size: z.ZodNumber;
|
|
138
|
-
mimeType: z.ZodString;
|
|
139
|
-
uploadedAt: z.ZodDate;
|
|
140
|
-
}, z.core.$strip>;
|
|
141
|
-
type UploadResult = z.infer<typeof uploadResultSchema>;
|
|
142
|
-
//#endregion
|
|
143
|
-
//#region src/storage/providers/storage-provider.interface.d.ts
|
|
144
|
-
/**
|
|
145
|
-
* Streaming blob payload input types
|
|
146
|
-
* Represents the types that can be used as the body of an upload operation
|
|
147
|
-
*/
|
|
148
|
-
type StreamingBlobPayloadInputTypes = ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob | null;
|
|
149
|
-
/**
|
|
150
|
-
* Storage provider interface
|
|
151
|
-
* Defines the contract for storage implementations (R2, S3, GCS, etc.)
|
|
152
|
-
*/
|
|
153
|
-
interface IStorageProvider {
|
|
154
|
-
/**
|
|
155
|
-
* Upload content to storage
|
|
156
|
-
* @param body - Content to upload (stream, buffer, or string)
|
|
157
|
-
* @param path - Full path including disk root
|
|
158
|
-
* @param options - Upload options including size and mime type
|
|
159
|
-
* @returns Upload result with metadata
|
|
160
|
-
*/
|
|
161
|
-
upload(body: StreamingBlobPayloadInputTypes, path: string, options: UploadOptions): Promise<UploadResult>;
|
|
162
|
-
/**
|
|
163
|
-
* Download a file from storage
|
|
164
|
-
* @param path - Full path to the file
|
|
165
|
-
* @returns Download result with stream and metadata
|
|
166
|
-
*/
|
|
167
|
-
download(path: string): Promise<DownloadResult>;
|
|
168
|
-
/**
|
|
169
|
-
* Delete a file from storage
|
|
170
|
-
* @param path - Full path to the file
|
|
171
|
-
*/
|
|
172
|
-
delete(path: string): Promise<void>;
|
|
173
|
-
/**
|
|
174
|
-
* Check if a file exists in storage
|
|
175
|
-
* @param path - Full path to the file
|
|
176
|
-
* @returns True if file exists, false otherwise
|
|
177
|
-
*/
|
|
178
|
-
exists(path: string): Promise<boolean>;
|
|
179
|
-
/**
|
|
180
|
-
* Generate a presigned URL for temporary access
|
|
181
|
-
* @param path - Full path to the file
|
|
182
|
-
* @param method - HTTP method (GET, PUT, DELETE, HEAD)
|
|
183
|
-
* @param expiresIn - Expiry time in seconds (1-604800)
|
|
184
|
-
* @returns Presigned URL result
|
|
185
|
-
*/
|
|
186
|
-
getPresignedUrl(path: string, method: 'GET' | 'PUT' | 'DELETE' | 'HEAD', expiresIn: number): Promise<PresignedUrlResult>;
|
|
187
|
-
/**
|
|
188
|
-
* Chunked upload for streaming data without known size
|
|
189
|
-
* Uses multipart upload under the hood for reliability
|
|
190
|
-
* @param body - Content to upload (stream or buffer)
|
|
191
|
-
* @param path - Full path including disk root
|
|
192
|
-
* @param options - Upload options (mimeType required, size optional)
|
|
193
|
-
* @returns Upload result with metadata
|
|
194
|
-
*/
|
|
195
|
-
chunkedUpload(body: StreamingBlobPayloadInputTypes, path: string, options: Omit<UploadOptions, 'size'> & {
|
|
196
|
-
size?: number;
|
|
197
|
-
}): Promise<UploadResult>;
|
|
198
|
-
}
|
|
199
|
-
//#endregion
|
|
200
|
-
export { StorageEntry as _, uploadResultSchema as a, getPresignedUrlInputSchema as c, fileExistsInputSchema as d, DownloadResult as f, StorageConfig as g, PresignedUrlConfig as h, UploadResult as i, presignedUrlResultSchema as l, deleteFileInputSchema as m, StreamingBlobPayloadInputTypes as n, GetPresignedUrlInput as o, DeleteFileInput as p, UploadOptions as r, PresignedUrlResult as s, IStorageProvider as t, FileExistsInput as u, StorageRouteConfig as v };
|
|
201
|
-
//# sourceMappingURL=storage-provider.interface-ClUwxz4S.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage-provider.interface-ClUwxz4S.d.mts","names":[],"sources":["../src/storage/types.ts","../src/storage/contracts/delete-file.input.ts","../src/storage/contracts/download-result.ts","../src/storage/contracts/file-exists.input.ts","../src/storage/contracts/get-presigned-url.input.ts","../src/storage/contracts/upload-file.input.ts","../src/storage/providers/storage-provider.interface.ts"],"mappings":";;;;;;UAIiB,YAAA;EAChB,IAAA;;EAEA,OAAA;EACA,IAAA;AAAA;;;AAAI;UAMY,kBAAA;EAChB,aAAA;EACA,SAAS;AAAA;AAAA;AAMV;;AANU,UAMO,kBAAA;EAEhB;EAAA,QAAA;EAQgB;EANhB,QAAQ;AAAA;;;;UAMQ,aAAA;EAChB,OAAA,EAAS,YAAA;EACT,kBAAA;EACA,YAAA,EAAc,kBAAA;EADd;EAGA,KAAA,GAAQ,kBAAA;AAAA;;;cCnCI,qBAAA,EAAqB,CAAA,CAAA,SAAA;;;;KAKtB,eAAA,GAAkB,CAAA,CAAE,KAAK,QAAQ,qBAAA;;;;;;;ADH7C;;UEEiB,cAAA;EFFY;;;EEM3B,QAAA,gBAAwB,cAAA,CAAe,UAAA;EAEvC,QAAA,gBAAwB,OAAA;EAExB,aAAA,gBAA6B,OAAA,CAAQ,UAAA;EFAtB;;;EEKf,WAAA;EFHQ;AAMV;;EEEE,IAAA;EFAD;AAEQ;AAMT;;EEFE,QAAA,GAAW,MAAA;AAAA;;;cC5BA,qBAAA,EAAqB,CAAA,CAAA,SAAA;;;;KAKtB,eAAA,GAAkB,CAAA,CAAE,KAAK,QAAQ,qBAAA;;;cCLhC,0BAAA,EAA0B,CAAA,CAAA,SAAA;;;;;;;;;;;KAO3B,oBAAA,GAAuB,CAAA,CAAE,KAAK,QAAQ,0BAAA;AAAA,cAErC,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;KAOzB,kBAAA,GAAqB,CAAA,CAAE,KAAK,QAAQ,wBAAA;;;;;;UCb/B,aAAA;ELDY;;;EKK3B,IAAA;ELFD;;;EKMC,QAAA;ELCe;;;;EKIf,QAAA,GAAW,MAAM;ELIF;;;;EKCf,OAAA;AAAA;AAAA,cAGW,kBAAA,EAAkB,CAAA,CAAA,SAAA;;;;;;;;KASnB,YAAA,GAAe,CAAA,CAAE,KAAK,QAAQ,kBAAA;;;;;;AL/B1C;KMEY,8BAAA,GACR,cAAA,GACA,WAAA,GACA,eAAA,YAEA,IAAA;;;;;UAOa,gBAAA;ENVZ;AAAA;AAML;;;;AAEU;EMUR,MAAA,CAAO,IAAA,EAAM,8BAAA,EAAgC,IAAA,UAAc,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;ENJ3D;;;AAI1B;AAMT;EMCE,QAAA,CAAS,IAAA,WAAe,OAAA,CAAQ,cAAA;;;;;EAMhC,MAAA,CAAO,IAAA,WAAe,OAAA;ENFG;;;;;EMSzB,MAAA,CAAO,IAAA,WAAe,OAAA;ENTvB;;;AAA0B;;;;EMkBzB,eAAA,CACE,IAAA,UACA,MAAA,qCACA,SAAA,WACC,OAAA,CAAQ,kBAAA;ELtDX;;;;;;;;EKgEA,aAAA,CACE,IAAA,EAAM,8BAAA,EACN,IAAA,UACA,OAAA,EAAS,IAAA,CAAK,aAAA;IAA2B,IAAA;EAAA,IACxC,OAAA,CAAQ,YAAA;AAAA"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { a as ApplicationError } from "./container-storage-BmOJ4_Na.mjs";
|
|
2
|
-
import "./errors-C01O2T-n.mjs";
|
|
3
|
-
//#region src/storage/storage.error.ts
|
|
4
|
-
var StorageError = class extends ApplicationError {};
|
|
5
|
-
//#endregion
|
|
6
|
-
export { StorageError as t };
|
|
7
|
-
|
|
8
|
-
//# sourceMappingURL=storage.error-BStXPmO4.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage.error-BStXPmO4.mjs","names":[],"sources":["../src/storage/storage.error.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class StorageError extends ApplicationError {}\n"],"mappings":";;;AAEA,IAAa,eAAb,cAAkC,iBAAiB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stratal-BL6FKUM_.mjs","names":[],"sources":["../src/application.ts","../src/stratal.ts"],"sourcesContent":["import type { CronJob } from './cron/cron-job'\nimport type { CronManager } from './cron/cron-manager'\nimport { Container } from './di/container'\nimport { containerStorage, getContainer, runWithContainer } from './di/container-storage'\nimport { DI_TOKENS } from './di/tokens'\nimport { type StratalEnv } from './env'\nimport { DefaultExceptionHandler } from './errors/default-exception-handler'\nimport { createCliExceptionContext, createCronExceptionContext, createQueueExceptionContext } from './errors/exception-context'\nimport type { ExceptionHandler } from './errors/exception-handler'\nimport type { EventHandler, EventRegistry } from './events'\nimport { getListenerHandlers } from './events'\nimport type { StratalExecutionContext } from './execution-context'\nimport { JsonFormatter, LOGGER_TOKENS, LoggerService, LogLevel, PrettyFormatter } from './logger'\nimport { LazyModuleLoader } from './module/lazy-module-loader'\nimport { ModuleRegistry } from './module/module-registry'\nimport type { DynamicModule, ModuleClass } from './module/types'\nimport type { Command } from './quarry/command'\nimport type { QuarryRegistry } from './quarry/quarry-registry'\nimport type { CommandInput, CommandResult } from './quarry/types'\nimport type { ConsumerRegistry } from './queue/consumer-registry'\nimport type { IQueueConsumer, QueueMessage } from './queue/queue-consumer'\nimport type { QueueManager } from './queue/queue-manager'\nimport type { RouterContext } from './router'\nimport type { HonoApp } from './router/hono-app'\nimport { ROUTER_TOKENS } from './router/router.tokens'\nimport type { TrailingSlashConfig, VersioningOptions } from './router/types'\nimport type { Seeder } from './seeder/seeder'\nimport { SEEDER_TOKENS } from './seeder/seeder-registry'\nimport type { SeederRegistry } from './seeder/seeder-registry'\nimport type { Constructor } from './types'\n\nexport interface ApplicationConfig {\n module: ModuleClass | DynamicModule\n logging?: {\n level?: LogLevel\n formatter?: 'json' | 'pretty'\n }\n versioning?: VersioningOptions\n trailingSlash?: TrailingSlashConfig\n exceptionHandler?: Constructor<ExceptionHandler>\n}\n\nexport interface ApplicationOptions extends ApplicationConfig {\n env: StratalEnv\n ctx: StratalExecutionContext\n}\n\nexport class Application {\n private _container: Container\n private honoApp!: HonoApp\n private moduleRegistry: ModuleRegistry\n private consumerRegistry!: ConsumerRegistry\n private cronManager?: CronManager\n private quarry!: QuarryRegistry\n private initialized = false\n\n // Independently memoized lazy-init promises — each built-in subsystem is\n // loaded on demand at its trigger point to keep cold start lean.\n private routingInitPromise: Promise<void> | null = null\n private eventsInitPromise: Promise<void> | null = null\n private queueInitPromise: Promise<void> | null = null\n private i18nInitPromise: Promise<void> | null = null\n private cronInitPromise: Promise<void> | null = null\n\n readonly env: StratalEnv\n private readonly appConfig: ApplicationConfig\n\n constructor({ env, ctx, ...config }: ApplicationOptions) {\n this.env = env\n this.appConfig = config\n\n this._container = new Container()\n\n this._container.registerValue(DI_TOKENS.Application, this)\n this._container.registerValue(DI_TOKENS.CloudflareEnv, env)\n this._container.registerValue(DI_TOKENS.ExecutionContext, ctx)\n this._container.registerValue(ROUTER_TOKENS.RouterContext, null)\n\n this.registerLoggerService()\n this.registerCoreServices()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n this.moduleRegistry = new ModuleRegistry(this._container, logger)\n\n this._container.registerValue(DI_TOKENS.ModuleRegistry, this.moduleRegistry)\n }\n\n get container(): Container {\n return this._container\n }\n\n async ensureHono(): Promise<HonoApp> {\n await this.initializeRouting()\n return this.honoApp\n }\n\n get config(): ApplicationConfig {\n return this.appConfig\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return\n }\n\n try {\n await runWithContainer(this._container, () => this.initializeInternal())\n } catch (error) {\n // A failed boot can still hold live resources: every module whose\n // onInitialize already ran (DB pools, timers) and every container-cached\n // singleton. Tear them down here so callers never have to special-case\n // a failed initialize() — shutdown() stays a no-op for it.\n await this.teardown().catch((teardownError: unknown) => {\n console.error('[stratal] Teardown after failed initialization also failed:', teardownError)\n })\n throw error\n }\n }\n\n private async initializeInternal(): Promise<void> {\n // Eager subsystem modules — Quarry/Seeder are resolved synchronously\n // post-init (CLI runner, test harness), so they must be registered before\n // initialize() completes. Dynamic import keeps application.ts free of static\n // subsystem imports (uniform with the other built-ins). They load at boot\n // regardless, so this is for consistency, not cold-start deferral.\n const [{ QuarryModule }, { SeederModule }] = await Promise.all([\n import('./quarry/quarry.module'),\n import('./seeder/seeder.module'),\n ])\n this.moduleRegistry.registerAll([QuarryModule, SeederModule])\n\n // Register the user's root module (traverses imports). Other built-in\n // subsystems (i18n, queue, cache, openapi, cron, events, router) load on\n // demand at their trigger points.\n this.moduleRegistry.register(this.appConfig.module)\n\n // Initialize all modules (only those with lifecycle hooks)\n await this.moduleRegistry.initialize()\n\n // Initialize ExceptionHandler\n this.initializeExceptionHandler()\n\n // Register CLI commands + seeders (class refs only; no instantiation)\n this.registerCommands()\n this.registerSeeders()\n\n // Wire event listeners as part of initialization whenever the application\n // declares any, so emitted events reach their handlers regardless of which\n // entry point drives the app. Listener wiring otherwise happens lazily only\n // on the HTTP router (`initializeRouting`) and queue/scheduled/command\n // (`ensureScopedHandlers`) paths; any code that emits events without going\n // through one of those — a direct service or repository call, an RPC\n // entrypoint, or a Durable Object — would dispatch into a registry with no\n // handlers attached, silently dropping the event. Guarded on there being\n // listeners so applications with none still skip loading the events\n // subsystem; `initializeEventListeners` dedups, so any later lazy trigger\n // becomes a no-op.\n if (this.moduleRegistry.getAllListeners().length > 0) {\n await this.initializeEventListeners()\n }\n\n // Cron only loads when the app actually declares scheduled jobs\n if (this.moduleRegistry.getAllJobs().length > 0) {\n await this.ensureCron()\n }\n\n this.initialized = true\n }\n\n private async registerRoutingServices(): Promise<void> {\n const [\n { HonoApp },\n { RouteRegistry },\n { RouterResolver },\n { VersioningService },\n { LocalePathService },\n { LocaleUrlService },\n { RouteRegistrationService },\n { Uri },\n ] = await Promise.all([\n import('./router/hono-app'),\n import('./router/route-registry'),\n import('./router/router-resolver'),\n import('./router/services/versioning.service'),\n import('./router/services/locale-path.service'),\n import('./router/services/locale-url.service'),\n import('./router/services/route-registration.service'),\n import('./router/uri'),\n ])\n\n this._container.register(ROUTER_TOKENS.VersioningService, VersioningService)\n this._container.register(ROUTER_TOKENS.HonoApp, HonoApp)\n this._container.register(ROUTER_TOKENS.LocalePathService, LocalePathService)\n this._container.register(ROUTER_TOKENS.LocaleUrlService, LocaleUrlService)\n this._container.register(ROUTER_TOKENS.RouteRegistry, RouteRegistry)\n this._container.register(ROUTER_TOKENS.Uri, Uri)\n\n const routerConfigs = this.moduleRegistry.getAllRouterConfigs()\n if (routerConfigs.length > 0) {\n this._container.registerValue(ROUTER_TOKENS.RouterResolver, new RouterResolver(routerConfigs))\n }\n\n this._container.register(RouteRegistrationService, RouteRegistrationService)\n }\n\n /**\n * Load the events subsystem and wire listeners. Needed by the HTTP, queue,\n * and scheduled paths (handlers emit events). Independent of the queue\n * subsystem so HTTP-only apps never load queue code. EventsModule is\n * registered before the (possibly empty) listener wiring so `emit()` works\n * even with zero listeners; dedups against the framework's own load.\n */\n private initializeEventListeners(): Promise<void> {\n this.eventsInitPromise ??= runWithContainer(this._container, async () => {\n const { EventsModule } = await import('./events/events.module')\n await this.moduleRegistry.registerLazy(EventsModule)\n this.registerEventListeners()\n })\n return this.eventsInitPromise\n }\n\n /**\n * Wire event and queue handlers for any non-HTTP request scope — queue\n * batches, scheduled/cron runs, CLI commands, Durable Objects, Workflows,\n * WorkerEntrypoints — all of which may emit events or dispatch to queues from\n * arbitrary user code. This is the single source of truth for that wiring:\n * every non-HTTP entry point routes through it rather than open-coding which\n * subsystems to init, so a subsystem can't be silently dropped by a future\n * refactor (the queue half was once lost from the command path that way).\n *\n * HTTP (`fetch`) deliberately does NOT use this: a fetch worker only enqueues\n * to the async Cloudflare queue and never processes consumers inline, so it\n * skips queue init via `initializeRouting`.\n */\n async ensureScopedHandlers(): Promise<void> {\n await this.initializeEventListeners()\n await this.initializeQueue()\n }\n\n /**\n * Load the queue subsystem on demand (first queue/scheduled trigger). i18n is\n * ensured first because the queue registry depends on it.\n */\n private initializeQueue(): Promise<void> {\n this.queueInitPromise ??= runWithContainer(this._container, async () => {\n await this.ensureI18n()\n const { QueueModule } = await import('./queue/queue.module')\n this.moduleRegistry.register(QueueModule as unknown as ModuleClass)\n this.consumerRegistry = this._container.resolve<ConsumerRegistry>(DI_TOKENS.ConsumerRegistry)\n await this.registerQueueConsumers()\n })\n return this.queueInitPromise\n }\n\n /**\n * Load i18n on demand. Coupled to the request path (Zod validation error maps,\n * OpenAPI descriptions, queue registry), so it loads before routing/queue\n * handling. Uses `registerLazy` because I18nModule has an `onInitialize` hook\n * (configures the Zod error map). Dedups if the app already imported i18n.\n */\n private ensureI18n(): Promise<void> {\n this.i18nInitPromise ??= runWithContainer(this._container, async () => {\n const { I18nModule } = await import('./i18n/i18n.module')\n await this.moduleRegistry.registerLazy(I18nModule as unknown as ModuleClass)\n })\n return this.i18nInitPromise\n }\n\n /**\n * Load the cron subsystem on demand (first scheduled trigger, or at bootstrap\n * when the app declares jobs).\n */\n private ensureCron(): Promise<CronManager> {\n this.cronInitPromise ??= runWithContainer(this._container, async () => {\n const { CronModule } = await import('./cron/cron.module')\n this.moduleRegistry.register(CronModule)\n this.cronManager = this._container.resolve<CronManager>(DI_TOKENS.Cron)\n this.registerCronJobs(this.cronManager)\n })\n return this.cronInitPromise.then(() => this.cronManager!)\n }\n\n resolve<T>(token: symbol): T {\n try {\n return this._container.resolve(token)\n } catch (error) {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n const ctx = createCliExceptionContext('resolve')\n void handler.handle(error, ctx)\n throw error\n }\n }\n\n async handleQueue(batch: MessageBatch, queueName: string): Promise<void> {\n await this.ensureScopedHandlers()\n\n const firstMessage = batch.messages[0]?.body as QueueMessage | undefined\n const locale = firstMessage?.metadata?.locale ?? 'en'\n const mockRouterContext = this.createMockRouterContext(locale)\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n const queueManager = requestContainer.resolve<QueueManager>(DI_TOKENS.Queue)\n await queueManager.processBatch(queueName, batch)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createQueueExceptionContext(queueName))\n throw error\n }\n })\n }\n\n async handleScheduled(controller: ScheduledController): Promise<void> {\n const cronManager = await this.ensureCron()\n // A scheduled job is a non-HTTP scope that can emit events and dispatch to\n // queues (timeout/reminder/digest emails) — wire both so dispatches aren't\n // silently dropped by the sync provider. i18n is ensured independently for\n // the job execution context. All inits are memoized, so this is idempotent.\n await this.ensureScopedHandlers()\n await this.ensureI18n()\n\n const mockRouterContext = this.createMockRouterContext('en')\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n await cronManager.executeScheduled(controller, requestContainer)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createCronExceptionContext())\n throw error\n }\n })\n }\n\n createMockRouterContext(locale = 'en'): RouterContext {\n return {\n getLocale: () => locale,\n setLocale: () => { /* no-op */ },\n // Non-HTTP scopes have no request headers\n header: () => undefined,\n // Inside runInRequestScope the active container is the request-scoped child\n // (set in AsyncLocalStorage); return it so request-scoped providers resolve\n // against the scope, not the global container.\n getContainer: () => containerStorage.getStore() ?? this._container,\n } as unknown as RouterContext\n }\n\n async shutdown(): Promise<void> {\n // A failed initialize() has already torn itself down — only a fully\n // initialized application owns resources here.\n if (!this.initialized) return\n this.initialized = false\n\n await this.teardown()\n }\n\n /** Releases everything boot acquired: module resources, then the container. */\n private async teardown(): Promise<void> {\n await this.moduleRegistry.shutdown()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.info('Disposing container...')\n\n await this._container.dispose()\n }\n\n async handleCommand(name: string, input?: CommandInput): Promise<CommandResult> {\n // Routing first: commands generate URLs via route() (e.g. tenant:bootstrap\n // prints tenant URLs / builds email links).\n await this.initializeRouting()\n // A CLI command is a non-HTTP scope that can dispatch to queues and emit\n // events from arbitrary user code (e.g. tenant:bootstrap → email.send /\n // tenant.geo.seed). Without this the dev/CLI sync provider finds zero\n // consumers and silently drops every dispatched message. All inits are\n // memoized, so the events half (already wired by routing) is a no-op here.\n await this.ensureScopedHandlers()\n // Resolve QuarryRegistry lazily (deferred from bootstrap)\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const mockContext = this.createMockRouterContext('en')\n return this._container.runInRequestScope(mockContext, async () => {\n return this.quarry.call(name, input)\n })\n }\n\n private initializeRouting(): Promise<void> {\n this.routingInitPromise ??= runWithContainer(this._container, async () => {\n await this.initializeEventListeners()\n await this.ensureI18n()\n const { OpenAPIModule } = await import('./openapi')\n this.moduleRegistry.register(OpenAPIModule as unknown as ModuleClass)\n await this.registerRoutingServices()\n this.honoApp = this._container.resolve<HonoApp>(ROUTER_TOKENS.HonoApp)\n await this.honoApp.configure()\n })\n return this.routingInitPromise\n }\n\n private registerCommands(): void {\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const commands = this.moduleRegistry.getAllCommands()\n for (const CommandClass of commands) {\n this.quarry.register(CommandClass as Constructor<Command>)\n }\n }\n\n private registerSeeders(): void {\n const seeders = this.moduleRegistry.getAllSeeders()\n if (seeders.length === 0) return\n const registry = this._container.resolve<SeederRegistry>(SEEDER_TOKENS.SeederRegistry)\n for (const SeederClass of seeders) {\n registry.register(SeederClass as Constructor<Seeder>)\n }\n }\n\n private async registerQueueConsumers(): Promise<void> {\n const consumerClasses = this.moduleRegistry.getAllConsumers()\n if (consumerClasses.length === 0) return\n\n // Consumers are resolved fresh per message at dispatch time (see\n // QueueManager / SyncQueueProvider). Here we only need each consumer's\n // declared message types; read them from a throwaway instance built inside a\n // request scope so a consumer that injects request-scoped providers\n // (@InjectQueue, i18n, auth context, …) doesn't fail to instantiate at boot.\n const mockContext = this.createMockRouterContext('en')\n await this._container.runInRequestScope(mockContext, (requestContainer) => {\n for (const ConsumerClass of consumerClasses) {\n const consumer = requestContainer.resolve(ConsumerClass) as IQueueConsumer\n this.consumerRegistry.register(\n ConsumerClass as Constructor<IQueueConsumer>,\n consumer.messageTypes,\n )\n }\n })\n }\n\n private registerCronJobs(cronManager: CronManager): void {\n for (const JobClass of this.moduleRegistry.getAllJobs()) {\n const schedule = (JobClass as unknown as { schedule: string }).schedule\n if (schedule) {\n cronManager.registerJob(schedule, JobClass as Constructor<CronJob>)\n } else {\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.warn(`Cron job \"${JobClass.name}\" has no static schedule property — skipped`)\n }\n }\n }\n\n private registerEventListeners(): void {\n const listeners = this.moduleRegistry.getAllListeners()\n if (listeners.length === 0) {\n return\n }\n\n const eventRegistry = this._container.resolve<EventRegistry>(DI_TOKENS.EventRegistry)\n\n for (const ListenerClass of listeners) {\n const handlers = getListenerHandlers(ListenerClass)\n\n for (const { methodName, event, options } of handlers) {\n // Resolve the listener fresh per event from the active scope (events are\n // emitted inside an HTTP request or runInScope), so request-scoped\n // listener dependencies bind to the emitting request rather than being\n // frozen at boot. Listener metadata is read from the class statically —\n // no instance is created until an event actually fires.\n const handler: EventHandler = ((...args: unknown[]) => {\n const instance = getContainer().resolve(ListenerClass) as Record<\n string,\n (...a: unknown[]) => unknown\n >\n return instance[methodName](...args)\n }) as EventHandler\n\n eventRegistry.on(event, handler, options)\n }\n }\n }\n\n private registerLoggerService(): void {\n const logLevel = this.appConfig.logging?.level ?? LogLevel.INFO\n const formatter = this.appConfig.logging?.formatter ?? 'json'\n\n this._container.registerValue(LOGGER_TOKENS.LogLevelOptions, logLevel)\n\n this._container\n .when(() => formatter === 'pretty')\n .use(LOGGER_TOKENS.Formatter)\n .give(PrettyFormatter)\n .otherwise(JsonFormatter)\n\n this._container.registerSingleton(LOGGER_TOKENS.LoggerService, LoggerService)\n }\n\n /**\n * Bootstrap kernel — registered imperatively because they cannot be expressed\n * as ordinary module providers: ExceptionHandler is a user-overridable forced\n * singleton (a module ClassProvider derives scope from the class decorator,\n * which a user handler may not carry), and LazyModuleLoader is the loader\n * itself. Subsystem registries (events/cron/quarry/seeder) are modules.\n */\n private registerCoreServices(): void {\n this._container.registerSingleton(\n DI_TOKENS.ExceptionHandler,\n (this.appConfig.exceptionHandler ?? DefaultExceptionHandler) as Constructor,\n )\n this._container.registerSingleton(DI_TOKENS.LazyModuleLoader, LazyModuleLoader)\n }\n\n private initializeExceptionHandler(): void {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n handler.register()\n this.moduleRegistry.configureExceptionHandlers(handler)\n }\n}\n","import { Application, type ApplicationConfig } from './application'\nimport type { StratalEnv } from './env'\nimport { StratalNotInitializedError, StratalSupersededError } from './errors'\nimport type { HonoApp } from './router/hono-app'\n\n/**\n * Stratal — Hono-style entry point for Cloudflare Workers.\n *\n * Eagerly bootstraps the Application at construction time, dynamically\n * importing `cloudflare:workers` for env and waitUntil.\n *\n * @example\n * ```typescript\n * import { Stratal } from 'stratal'\n * import { AppModule } from './app.module'\n *\n * export default new Stratal({ module: AppModule })\n * ```\n */\nexport class Stratal<Env extends StratalEnv = StratalEnv> {\n private app: Application | null = null\n private initPromise: Promise<Application>\n\n private static _application: Promise<Application> | null = null\n private static _generation = 0\n private static _current: Stratal | null = null\n /**\n * Serializes generation teardown: each new instance appends the previous\n * instance's shutdown here, and `prepareApp` awaits the chain before\n * allocating the new Application. Old and new DI graphs never coexist,\n * so repeated Vite HMR reloads can't accumulate live object graphs.\n */\n private static _teardownChain: Promise<void> = Promise.resolve()\n\n constructor(config: ApplicationConfig) {\n this.fetch = this.fetch.bind(this)\n this.queue = this.queue.bind(this)\n this.scheduled = this.scheduled.bind(this)\n\n // Invalidate any in-flight initialization from a previous instance (Vite HMR reload)\n const generation = ++Stratal._generation\n\n const previous = Stratal._current\n Stratal._current = this\n if (previous) {\n Stratal._teardownChain = Stratal._teardownChain\n .then(() => previous.shutdown())\n .catch((error: unknown) => {\n console.error('[stratal] Failed to shut down superseded instance:', error)\n })\n }\n\n // Capture the chain as of THIS construction: it contains only strictly\n // older generations' teardowns. Reading the static inside prepareApp\n // instead would deadlock when this generation is superseded before its\n // first await — it would wait on a chain containing its own shutdown,\n // which waits on this initPromise.\n const teardownBarrier = Stratal._teardownChain\n\n this.initPromise = this.prepareApp(config, generation, teardownBarrier)\n Stratal._application = this.initPromise\n // A superseded generation rejects; without an awaiter (no in-flight\n // request during the reload) that would surface as an unhandled rejection.\n // Anything else is a real bootstrap failure — log it so it isn't invisible\n // until the first request arrives (awaiters still get the rejection).\n this.initPromise.catch((error: unknown) => {\n if (error instanceof StratalSupersededError) return\n console.error('[stratal] Initialization failed:', error)\n })\n }\n\n async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {\n const app = await this.ensureReady()\n const hono = await app.ensureHono()\n return hono.fetch(request, env, ctx)\n }\n\n async queue(batch: MessageBatch): Promise<void> {\n const app = await this.ensureReady()\n return app.handleQueue(batch, batch.queue)\n }\n\n async scheduled(controller: ScheduledController): Promise<void> {\n const app = await this.ensureReady()\n return app.handleScheduled(controller)\n }\n\n get hono(): Promise<HonoApp> {\n return this.ensureReady().then(app => app.ensureHono())\n }\n\n async shutdown(): Promise<void> {\n try { this.app = await this.initPromise } catch { /* ignore */ }\n if (this.app) {\n await this.app.shutdown()\n this.app = null\n }\n // If we're still the live generation (explicit shutdown, not supersession),\n // clear the statics so resolveApplication() throws StratalNotInitializedError\n // instead of handing out the disposed Application.\n if (Stratal._current === this) {\n Stratal._current = null\n Stratal._application = null\n }\n }\n\n /**\n * @internal\n * Resolves the Application instance from the static singleton.\n * Used by worker base classes (DurableObject, Workflow, WorkerEntrypoint)\n * to access the DI container without going through Cloudflare RPC.\n * Generations superseded mid-boot reject with {@link StratalSupersededError};\n * this retries against the replacing generation so callers always land on\n * the live Application.\n */\n static async resolveApplication(): Promise<Application> {\n let current = Stratal._application\n while (current) {\n try {\n return await current\n } catch (error) {\n // Superseded by a newer generation while booting — its constructor has\n // already swapped _application; retry against the replacement.\n if (error instanceof StratalSupersededError && Stratal._application !== current) {\n current = Stratal._application\n continue\n }\n throw error\n }\n }\n throw new StratalNotInitializedError()\n }\n\n private async ensureReady(): Promise<Application> {\n if (this.app) return this.app\n try {\n this.app = await this.initPromise\n return this.app\n } catch (error) {\n if (error instanceof StratalSupersededError) {\n // This instance was replaced mid-boot (Vite HMR reload) — serve the\n // request from the generation that replaced it.\n return Stratal.resolveApplication()\n }\n throw error\n }\n }\n\n private async prepareApp(\n config: ApplicationConfig,\n generation: number,\n teardownBarrier: Promise<void>,\n ): Promise<Application> {\n const { env, waitUntil } = await import('cloudflare:workers')\n\n // Let every previous generation finish tearing down before allocating the\n // new DI graph — concurrent old+new graphs are what leak across reloads.\n await teardownBarrier\n\n // After the awaits, check if a newer instance has replaced us (Vite HMR reload)\n if (generation !== Stratal._generation) {\n throw new StratalSupersededError(generation)\n }\n\n const app = new Application({ ...config, env: env as Env, ctx: { waitUntil } })\n await app.initialize()\n\n // Check again after initialization completes\n if (generation !== Stratal._generation) {\n await app.shutdown()\n throw new StratalSupersededError(generation)\n }\n\n return app\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stratal-D5j_I14G.d.mts","names":[],"sources":["../src/stratal.ts"],"mappings":";;;;;AAmBA;;;;;;;;;;;;;cAAa,OAAA,aAAoB,UAAA,GAAa,UAAA;EAAA,QACpC,GAAA;EAAA,QACA,WAAA;EAAA,eAEO,YAAA;EAAA,eACA,WAAA;EAAA,eACA,QAAA;EA0FoB;;;;;;EAAA,eAnFpB,cAAA;cAEH,MAAA,EAAQ,iBAAA;EAqCd,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,gBAAA,GAAmB,OAAA,CAAQ,QAAA;EAMlE,KAAA,CAAM,KAAA,EAAO,YAAA,GAAe,OAAA;EAK5B,SAAA,CAAU,UAAA,EAAY,mBAAA,GAAsB,OAAA;EAAA,IAK9C,IAAA,IAAQ,OAAA,CAAQ,OAAA;EAId,QAAA,IAAY,OAAA;EAzDE;;;;;;;;;EAAA,OAiFP,kBAAA,IAAsB,OAAA,CAAQ,WAAA;EAAA,QAkB7B,WAAA;EAAA,QAeA,UAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tiered-cache.service-Dv3BhxxE.d.mts","names":[],"sources":["../src/cache/services/tiered-cache.service.ts"],"mappings":";;;;;AA0DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cACa,kBAAA;EAAA,iBAO2C,KAAA;EA4FnD;EAAA,iBAjGc,EAAA;EAsHL;EAAA,iBApHK,QAAA;cAGqC,KAAA,EAAO,YAAA;EAkH1D;;;;;;;;EAvGH,OAAA,CAAQ,IAAA,WAAe,kBAAA;EAWjB,GAAA,CAAI,GAAA,UAAa,aAAA,YAAyB,qBAAA,WAAgC,OAAA;EAC1E,GAAA,0BAA6B,GAAA,UAAa,aAAA,WAAwB,qBAAA,WAAgC,OAAA,CAAQ,aAAA;EAC1G,GAAA,CAAI,GAAA,UAAa,aAAA,kBAA+B,qBAAA,kBAAuC,OAAA,CAAQ,WAAA;EAC/F,GAAA,CAAI,GAAA,UAAa,aAAA,aAA0B,qBAAA,aAAkC,OAAA,CAAQ,cAAA;EA+BrF,eAAA,qBACJ,GAAA,UACA,aAAA,YAAyB,qBAAA,WACxB,OAAA,CAAQ,gCAAA,SAAyC,QAAA;EAC9C,eAAA,8CACJ,GAAA,UACA,aAAA,WAAwB,qBAAA,WACvB,OAAA,CAAQ,gCAAA,CAAiC,aAAA,EAAe,QAAA;EACrD,eAAA,qBACJ,GAAA,UACA,aAAA,kBAA+B,qBAAA,kBAC9B,OAAA,CAAQ,gCAAA,CAAiC,WAAA,EAAa,QAAA;EACnD,eAAA,qBACJ,GAAA,UACA,aAAA,aAA0B,qBAAA,aACzB,OAAA,CAAQ,gCAAA,CAAiC,cAAA,EAAgB,QAAA;EAiBtD,GAAA,CACJ,GAAA,UACA,KAAA,WAAgB,WAAA,GAAc,eAAA,GAAkB,cAAA,EAChD,OAAA,GAAU,qBAAA,GACT,OAAA;EAaG,MAAA,CAAO,GAAA,WAAc,OAAA;EAOrB,IAAA,qBACJ,OAAA,GAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA,CAAsB,QAAA;EA3FuC;EAAA,QAkGhE,MAAA;EAlGgG;EAAA,QA6GhG,OAAA;EA5GF;EAAA,QAuHE,WAAA;AAAA"}
|