stratal 0.0.27 → 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 +73 -12
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-registry-lkQPDuQe.mjs","names":["internal.getDefaultEntry","internal.getGroups","internal.getGlobalMiddleware"],"sources":["../src/module/module-ref.ts","../src/module/lazy-module-loader.ts","../src/rate-limiter/errors.ts","../src/rate-limiter/rate-limiter.tokens.ts","../src/rate-limiter/throttle.middleware.ts","../src/router/router.error.ts","../src/router/router.internals.ts","../src/router/router.ts","../src/module/module-registry.ts"],"sourcesContent":["import type { Container } from '../di/container'\nimport type { InjectionToken } from '../di/types'\nimport type { Constructor } from '../types'\n\n/**\n * Handle to a lazily loaded module, returned by {@link LazyModuleLoader.load}.\n *\n * Resolves providers from the global container, so singletons registered by the\n * lazy module persist across requests.\n *\n * Note: request-scoped providers resolved via {@link ModuleRef.get} degrade to\n * transient instances (there is no ambient request scope here). To obtain a\n * true per-request instance, resolve the token from the request container\n * inside a request scope instead.\n */\nexport class ModuleRef {\n constructor(\n private readonly container: Container,\n /** The module class this ref was loaded from. */\n readonly moduleClass: Constructor,\n ) { }\n\n /** Resolve a provider from the lazily loaded module (global scope). */\n get<T>(token: InjectionToken<T>): T {\n return this.container.resolve(token)\n }\n\n /** Async variant of {@link ModuleRef.get}, matching the NestJS API shape. */\n resolve<T>(token: InjectionToken<T>): Promise<T> {\n return Promise.resolve(this.container.resolve(token))\n }\n}\n","import { inject } from '../di/decorators'\nimport type { Container } from '../di/container'\nimport { DI_TOKENS } from '../di/tokens'\nimport { LOGGER_TOKENS, type LoggerService } from '../logger'\nimport type { Constructor } from '../types'\nimport { ModuleRef } from './module-ref'\nimport type { ModuleRegistry } from './module-registry'\nimport type { DynamicModule, ModuleClass } from './types'\n\n/**\n * Loads modules on demand (NestJS-style), so optional features stay out of the\n * cold-start path until first use.\n *\n * Inject {@link DI_TOKENS.LazyModuleLoader} and call {@link load}:\n *\n * ```ts\n * const ref = await loader.load(() => import('./reports.module').then(m => m.ReportsModule))\n * const reports = ref.get(ReportService)\n * ```\n *\n * The loaded module's nested `imports` and `providers` are registered into the\n * global container and its `onInitialize` hook runs once. Controllers, queue\n * consumers, and cron jobs are skipped — that wiring is finalized at bootstrap.\n * Repeat loads of the same module return the cached {@link ModuleRef} without\n * re-registering.\n */\nexport class LazyModuleLoader {\n private readonly cache = new Map<Constructor, ModuleRef>()\n private readonly inFlight = new Map<Constructor, Promise<ModuleRef>>()\n\n constructor(\n @inject(DI_TOKENS.ModuleRegistry) private readonly registry: ModuleRegistry,\n @inject(DI_TOKENS.Container) private readonly container: Container,\n @inject(LOGGER_TOKENS.LoggerService) private readonly logger: LoggerService,\n ) { }\n\n async load(loaderFn: () => Promise<ModuleClass | DynamicModule>): Promise<ModuleRef> {\n const loaded = await loaderFn()\n const { moduleClass } = this.registry.resolveModule(loaded)\n\n const cached = this.cache.get(moduleClass)\n if (cached) return cached\n\n const inFlight = this.inFlight.get(moduleClass)\n if (inFlight) return inFlight\n\n const promise = this.registerAndBuild(moduleClass, loaded)\n this.inFlight.set(moduleClass, promise)\n try {\n return await promise\n } finally {\n this.inFlight.delete(moduleClass)\n }\n }\n\n private async registerAndBuild(\n moduleClass: Constructor,\n loaded: ModuleClass | DynamicModule,\n ): Promise<ModuleRef> {\n this.logger.debug(`Lazy loading module: ${moduleClass.name}`)\n await this.registry.registerLazy(loaded)\n const ref = new ModuleRef(this.container.getRootContainer(), moduleClass)\n this.cache.set(moduleClass, ref)\n return ref\n }\n}\n","import { ApplicationError, HttpException } from '../errors'\n\n/**\n * Thrown when a request exceeds a configured rate limit.\n *\n * HTTP Status: 429 Too Many Requests\n *\n * The {@link ExceptionHandler} renders the body via content negotiation\n * (HTML for HTML clients, JSON for everything else). Standard rate-limit\n * headers (`Retry-After`, `X-RateLimit-*`) are injected by the\n * `respond()` callback registered via `RateLimiterModule.onException`.\n */\nexport class TooManyRequestsError extends HttpException {\n constructor(\n public readonly info: { retryAfter: number; limit: number; resetAt: number },\n ) {\n super(429, 'Too many requests')\n }\n}\n\nexport class RateLimiterError extends ApplicationError {}\n","export const RATE_LIMITER_TOKENS = {\n Registry: Symbol.for('stratal:rate-limiter:registry'),\n Store: Symbol.for('stratal:rate-limiter:store'),\n StoreFactory: Symbol.for('stratal:rate-limiter:store-factory'),\n Options: Symbol.for('stratal:rate-limiter:options'),\n /**\n * Per-app marker registered by RateLimiterModule.onInitialize. Used by\n * ThrottleMiddleware to detect \"module not imported\".\n */\n ModuleMarker: Symbol.for('stratal:rate-limiter:module-marker'),\n} as const\n\nexport type RateLimiterToken = (typeof RATE_LIMITER_TOKENS)[keyof typeof RATE_LIMITER_TOKENS]\n","import { inject } from '../di'\nimport { CONTAINER_TOKEN, type Container } from '../di'\nimport { Transient } from '../di/decorators'\nimport type { Middleware, Next } from '../router/middleware.interface'\nimport type { RouterContext } from '../router/router-context'\nimport type { Constructor } from '../types'\nimport { RateLimiterError } from './errors'\nimport type { RateLimiterRegistry } from './rate-limiter-registry'\nimport { RATE_LIMITER_TOKENS } from './rate-limiter.tokens'\n\nconst cache = new Map<string, Constructor<Middleware>>()\n\n/**\n * Memoized factory that produces a Stratal `Middleware` class bound to a\n * named limiter. Calling twice with the same name returns the *same* class\n * — important for `Router.middleware` deduplication via class identity.\n *\n * Detection of \"module not imported\" works against a per-app marker\n * registered by `RateLimiterModule.onInitialize`. We check\n * `isRegistered(marker)` at request time before resolving Registry.\n */\nexport function createThrottleMiddleware(name: string): Constructor<Middleware> {\n const existing = cache.get(name)\n if (existing) return existing\n\n @Transient()\n class ThrottleMiddleware implements Middleware {\n constructor(\n @inject(CONTAINER_TOKEN) private readonly container: Container,\n ) {}\n\n handle(ctx: RouterContext, next: Next): Promise<Response | void> {\n if (!this.container.isRegistered(RATE_LIMITER_TOKENS.ModuleMarker)) {\n throw new RateLimiterError(`RateLimiterModule was not imported. Cannot resolve throttle \"${name}\". Import RateLimiterModule.forRoot({ store: ... }) in your AppModule.`)\n }\n const registry = this.container.resolve<RateLimiterRegistry>(RATE_LIMITER_TOKENS.Registry)\n return registry.handle(name, ctx, next)\n }\n }\n\n Object.defineProperty(ThrottleMiddleware, 'name', { value: `Throttle(${name})` })\n cache.set(name, ThrottleMiddleware)\n return ThrottleMiddleware\n}\n\n/**\n * Test-only escape hatch: clear the per-name middleware class cache.\n * Production code never needs this.\n */\nexport function _resetThrottleMiddlewareCache(): void {\n cache.clear()\n}\n","import { ApplicationError } from '../errors'\n\nexport class RouterError extends ApplicationError {}\n","/**\n * Symbol keys for Router internal accessors.\n *\n * These symbols are NOT exported from the public `stratal/router` barrel.\n * Only internal modules (RouterResolver) import them, keeping the Router's\n * public API clean — users never see these methods.\n *\n * Registered via `Symbol.for` so that a duplicate evaluation of this module\n * (e.g. under a bundler or SSR module runner) resolves to the same symbol,\n * keeping symbol-keyed dispatch between `Router` and `RouterResolver` stable.\n *\n * @internal\n */\n\n/** @internal */\nexport const getDefaultEntry: unique symbol = Symbol.for('stratal:router:getDefaultEntry')\n/** @internal */\nexport const getGroups: unique symbol = Symbol.for('stratal:router:getGroups')\n/** @internal */\nexport const getGlobalMiddleware: unique symbol = Symbol.for('stratal:router:getGlobalMiddleware')\n","import type { ZodObject } from '../i18n/validation/zod'\nimport { createThrottleMiddleware } from '../rate-limiter/throttle.middleware'\nimport type { Constructor } from '../types'\nimport { RouterError } from './router.error'\nimport type { Middleware } from './middleware.interface'\nimport * as internal from './router.internals'\n\n/**\n * Configuration for a sub-group created via `router.group()`.\n */\nexport interface RouterGroupConfig {\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 entry representing a sub-group or the default scope.\n * @internal — used by RouterResolver, not exported publicly.\n */\nexport interface RouterEntry {\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 /** Controllers in this entry. undefined = all controllers not in any sub-group */\n controllers?: Constructor[]\n}\n\n/**\n * Modules implement this to configure routes and middleware.\n * Replaces `MiddlewareConfigurable`.\n *\n * @example\n * ```typescript\n * @Module({ controllers: [UsersController, PostsController] })\n * export class ApiModule implements RouteConfigurable {\n * configureRoutes(router: Router): void {\n * router\n * .name('api.')\n * .middleware(CorsMiddleware)\n * .version('1')\n * }\n * }\n * ```\n */\nexport interface RouteConfigurable {\n configureRoutes(router: Router): void\n}\n\n/**\n * Fluent builder for route and middleware configuration.\n *\n * Scoped methods (`middleware()`, `prefix()`, `domain()`, `name()`, `version()`, `hideFromDocs()`)\n * apply only to this module's controllers or the sub-group's controllers.\n *\n * `use()` registers global middleware (all routes in the entire app).\n * Only callable on the root Router — throws inside `group()` callbacks.\n *\n * `group()` creates sub-groups for specific controllers with a callback.\n * Controllers in a sub-group are excluded from the parent scope.\n */\nexport class Router {\n private readonly _isChild: boolean\n private readonly _defaultEntry: RouterEntry = { middleware: [] }\n private readonly _groups: RouterEntry[] = []\n private readonly _globalMiddleware: Constructor<Middleware>[] = []\n\n constructor(isChild = false) {\n this._isChild = isChild\n }\n\n /** Dynamic path prefix. For shared segments like `/:companyId` */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ZodObject generics require any for flexible shape parameter\n prefix(path: string, params?: ZodObject<any>): this {\n this._defaultEntry.prefix = path\n this._defaultEntry.params = params\n return this\n }\n\n /** Domain pattern for controllers in this scope */\n domain(pattern: string): this {\n this._defaultEntry.domain = pattern\n return this\n }\n\n /** Name prefix for routes in this scope */\n name(prefix: string): this {\n this._defaultEntry.name = prefix\n return this\n }\n\n /** Middleware applied to controllers in this scope */\n middleware(...middlewares: Constructor<Middleware>[]): this {\n this._defaultEntry.middleware.push(...middlewares)\n return this\n }\n\n /**\n * Apply a named rate limiter to controllers in this scope.\n *\n * The named limiter must be registered via `RateLimiterRegistry.for(name, ...)`\n * (typically inside a module's `onInitialize` hook), and the user must\n * import `RateLimiterModule.forRoot({ store: ... })` in their AppModule.\n *\n * @example\n * ```typescript\n * router.prefix('/uploads').throttle('uploads')\n * router.group([AdminController], (admin) => admin.throttle('admin'))\n * ```\n */\n throttle(name: string): this {\n this._defaultEntry.middleware.push(createThrottleMiddleware(name))\n return this\n }\n\n /** API version for controllers in this scope */\n version(version: string | string[]): this {\n this._defaultEntry.version = version\n return this\n }\n\n /** Hide/show routes in this scope from OpenAPI docs */\n hideFromDocs(hide = true): this {\n this._defaultEntry.hideFromDocs = hide\n return this\n }\n\n /**\n * Global middleware — applied to ALL routes in the entire app.\n * Only callable on the root Router. Throws if called inside `group()`.\n */\n use(...middlewares: Constructor<Middleware>[]): this {\n if (this._isChild) {\n throw new RouterError('router.use() is only allowed on the root Router. It cannot be called inside a group() callback.')\n }\n this._globalMiddleware.push(...middlewares)\n return this\n }\n\n /**\n * Create a sub-group for specific controllers/gateways.\n * Controllers in a sub-group are excluded from the parent (default) scope.\n * The callback receives a new Router (without `use()`) for fluent configuration.\n */\n group(controllers: Constructor[], callback: (router: Omit<Router, 'use'>) => void): this {\n const childRouter = new Router(true)\n callback(childRouter)\n\n this._groups.push({\n ...childRouter._defaultEntry,\n controllers,\n })\n return this\n }\n\n // --- Internal accessors via symbol keys (invisible to consumers) ---\n\n [internal.getDefaultEntry](): RouterEntry {\n return this._defaultEntry\n }\n\n [internal.getGroups](): RouterEntry[] {\n return this._groups\n }\n\n [internal.getGlobalMiddleware](): Constructor<Middleware>[] {\n return this._globalMiddleware\n }\n}\n","import type { Container } from '../di/container'\nimport { isListener } from '../events'\nimport type { LoggerService } from '../logger'\nimport { Router, type RouteConfigurable } from '../router/router'\nimport { isCommand } from '../quarry/is-command'\nimport { isSeeder } from '../seeder/is-seeder'\nimport type { Constructor } from '../types'\nimport { getModuleOptions } from './module.decorator'\nimport type { ExceptionHandler } from '../errors/exception-handler'\nimport type {\n DynamicModule,\n InjectionToken,\n ModuleClass,\n ModuleContext,\n ModuleOptions,\n OnException,\n OnInitialize,\n OnShutdown,\n Provider,\n} from './types'\n\n\ninterface RegisteredModule {\n moduleClass: Constructor\n options: ModuleOptions\n instance: object | null\n hasLifecycle: boolean\n}\n\nexport class ModuleRegistry {\n private modules: RegisteredModule[] = []\n private registeredClasses = new Set<Constructor>()\n private initialized = false\n\n private allControllers: Constructor[] = []\n private allConsumers: Constructor[] = []\n private allJobs: Constructor[] = []\n private allListeners: Constructor[] = []\n private allCommands: Constructor[] = []\n private allSeeders: Constructor[] = []\n private allRouterConfigs: { router: Router; controllers: Constructor[] }[] = []\n\n constructor(\n private readonly container: Container,\n private readonly logger: LoggerService\n ) { }\n\n register(moduleOrDynamic: ModuleClass | DynamicModule): void {\n const { moduleClass, options } = this.resolveModule(moduleOrDynamic)\n\n if (this.handleAlreadyRegistered(moduleClass, moduleOrDynamic)) return\n\n this.registeredClasses.add(moduleClass)\n this.logger.info(`Registering module: ${moduleClass.name}`)\n\n for (const ImportedModule of options.imports ?? []) {\n this.register(ImportedModule)\n }\n\n const registered = this.registerModuleNode(moduleClass, options, { includeHttpWiring: true })\n\n // Eager register() is synchronous and assumes the bootstrap initialize()\n // batch will run lifecycle hooks. If it's called AFTER initialization (e.g. a\n // built-in subsystem registered on first use), that batch has finished and\n // won't revisit this module — so a lifecycle hook would be silently dropped.\n // Surface it instead of failing silently; load such modules via\n // LazyModuleLoader (registerLazy), which runs onInitialize immediately.\n if (this.initialized && registered.hasLifecycle) {\n this.logger.warn(\n `Module ${moduleClass.name} was registered eagerly after initialization; its onInitialize/onShutdown/configureRoutes hooks will not run. Load it via LazyModuleLoader instead.`,\n )\n }\n }\n\n /**\n * Register a module on demand (NestJS-style lazy loading). Registers nested\n * imports recursively and providers, then runs `onInitialize` immediately\n * (the bootstrap-time batch {@link initialize} has already completed).\n *\n * Controllers, queue consumers, and cron jobs are SKIPPED — route, queue, and\n * cron wiring is finalized at bootstrap and cannot be extended at runtime.\n */\n async registerLazy(moduleOrDynamic: ModuleClass | DynamicModule): Promise<void> {\n const { moduleClass, options } = this.resolveModule(moduleOrDynamic)\n\n if (this.handleAlreadyRegistered(moduleClass, moduleOrDynamic)) return\n\n this.registeredClasses.add(moduleClass)\n this.logger.info(`Lazily registering module: ${moduleClass.name}`)\n\n for (const ImportedModule of options.imports ?? []) {\n await this.registerLazy(ImportedModule)\n }\n\n const registered = this.registerModuleNode(moduleClass, options, { includeHttpWiring: false })\n\n if (registered.hasLifecycle) {\n const instance = new registered.moduleClass()\n registered.instance = instance\n if (this.hasOnInitialize(instance)) {\n const context: ModuleContext = { container: this.container, logger: this.logger }\n this.logger.info(`Initializing (lazy): ${registered.moduleClass.name}`)\n await instance.onInitialize(context)\n }\n }\n }\n\n registerAll(modules: (ModuleClass | DynamicModule)[]): void {\n for (const module of modules) {\n this.register(module)\n }\n }\n\n hasRegistered(moduleClass: Constructor): boolean {\n return this.registeredClasses.has(moduleClass)\n }\n\n /**\n * Handle re-registration of an already-known module. For a DynamicModule,\n * its extra providers are wired (without re-running lifecycle); for a plain\n * class it is a no-op. Returns true when the module was already registered.\n */\n private handleAlreadyRegistered(\n moduleClass: Constructor,\n moduleOrDynamic: ModuleClass | DynamicModule,\n ): boolean {\n if (!this.registeredClasses.has(moduleClass)) return false\n\n if (this.isDynamicModule(moduleOrDynamic)) {\n this.logger.debug(`Module ${moduleClass.name} already registered, registering DynamicModule providers`)\n const { module: _, ...dynamicRest } = moduleOrDynamic\n for (const provider of dynamicRest.providers ?? []) {\n this.registerProvider(provider)\n }\n } else {\n this.logger.debug(`Module ${moduleClass.name} already registered, skipping`)\n }\n return true\n }\n\n /**\n * Register a single module's providers (and, for eager registration, its\n * controllers/consumers/jobs), detect lifecycle hooks, and record it.\n * Assumes the module's `imports` have already been registered by the caller.\n */\n private registerModuleNode(\n moduleClass: Constructor,\n options: ModuleOptions,\n { includeHttpWiring }: { includeHttpWiring: boolean },\n ): RegisteredModule {\n for (const provider of options.providers ?? []) {\n this.registerProvider(provider, { lazy: !includeHttpWiring, moduleName: moduleClass.name })\n }\n\n if (includeHttpWiring) {\n for (const controller of options.controllers ?? []) {\n this.container.register(controller)\n this.allControllers.push(controller)\n }\n\n for (const consumer of options.consumers ?? []) {\n this.container.register(consumer)\n this.allConsumers.push(consumer)\n this.logger.info(`Collected consumer: ${consumer.name}`, { queueCount: this.allConsumers.length })\n }\n\n for (const job of options.jobs ?? []) {\n this.container.register(job)\n this.allJobs.push(job)\n }\n } else {\n const skipped: string[] = []\n if (options.controllers?.length) skipped.push('controllers')\n if (options.consumers?.length) skipped.push('consumers')\n if (options.jobs?.length) skipped.push('jobs')\n if ('configureRoutes' in moduleClass.prototype) skipped.push('configureRoutes')\n if ('onException' in moduleClass.prototype) skipped.push('onException')\n if (skipped.length) {\n this.logger.warn(\n `Lazy module ${moduleClass.name} declares ${skipped.join('/')} which are skipped — ` +\n `route, queue, cron, and exception-handler wiring is finalized at bootstrap`,\n )\n }\n }\n\n const hasLifecycle =\n 'onInitialize' in moduleClass.prototype ||\n 'onShutdown' in moduleClass.prototype ||\n 'onException' in moduleClass.prototype ||\n 'configureRoutes' in moduleClass.prototype\n\n const registered: RegisteredModule = { moduleClass, options, instance: null, hasLifecycle }\n this.modules.push(registered)\n return registered\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return\n\n this.logger.info('Initializing modules...')\n\n const context: ModuleContext = {\n container: this.container,\n logger: this.logger,\n }\n\n // Snapshot: a module's onInitialize may lazily load another module\n // (LazyModuleLoader.load → registerLazy), which pushes to this.modules and\n // initializes it itself. Iterating a snapshot prevents the batch loop from\n // re-visiting (and double-initializing) those lazily-added modules.\n for (const registered of [...this.modules]) {\n if (!registered.hasLifecycle) continue\n\n const instance = new registered.moduleClass()\n registered.instance = instance\n\n if (this.hasOnInitialize(instance)) {\n this.logger.info(`Initializing: ${registered.moduleClass.name}`)\n await instance.onInitialize(context)\n }\n }\n\n this.initialized = true\n this.logger.info('All modules initialized')\n }\n\n getAllControllers(): Constructor[] {\n return this.allControllers\n }\n\n getAllConsumers(): Constructor[] {\n return this.allConsumers\n }\n\n getAllJobs(): Constructor[] {\n return this.allJobs\n }\n\n getAllListeners(): Constructor[] {\n return this.allListeners\n }\n\n getAllCommands(): Constructor[] {\n return this.allCommands\n }\n\n getAllSeeders(): Constructor[] {\n return this.allSeeders\n }\n\n getAllRouterConfigs(): { router: Router; controllers: Constructor[] }[] {\n if (this.allRouterConfigs.length === 0) {\n for (const { moduleClass, options, instance } of this.modules) {\n if (instance && this.hasRouteConfigurable(instance)) {\n this.logger.debug(`Configuring routes for: ${moduleClass.name}`)\n const router = new Router()\n instance.configureRoutes(router)\n const moduleControllers = options.controllers ?? []\n this.allRouterConfigs.push({ router, controllers: moduleControllers })\n }\n }\n }\n return this.allRouterConfigs\n }\n\n configureExceptionHandlers(handler: ExceptionHandler): void {\n for (const { moduleClass, instance } of this.modules) {\n if (instance && this.hasOnException(instance)) {\n this.logger.debug(`Configuring exception handlers for: ${moduleClass.name}`)\n instance.onException(handler)\n }\n }\n }\n\n async shutdown(): Promise<void> {\n this.logger.info('Shutting down modules...')\n\n const context: ModuleContext = {\n container: this.container,\n logger: this.logger,\n }\n\n const reversed = [...this.modules].reverse()\n\n for (const { moduleClass, instance } of reversed) {\n if (instance && this.hasOnShutdown(instance)) {\n try {\n await instance.onShutdown(context)\n } catch (error) {\n this.logger.error(`Error shutting down ${moduleClass.name}:`, error as Error)\n }\n }\n }\n\n this.logger.info('All modules shut down')\n }\n\n private hasRouteConfigurable(instance: unknown): instance is RouteConfigurable {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'configureRoutes' in instance &&\n typeof (instance as RouteConfigurable).configureRoutes === 'function'\n )\n }\n\n private hasOnInitialize(instance: unknown): instance is OnInitialize {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'onInitialize' in instance &&\n typeof (instance as OnInitialize).onInitialize === 'function'\n )\n }\n\n private hasOnShutdown(instance: unknown): instance is OnShutdown {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'onShutdown' in instance &&\n typeof (instance as OnShutdown).onShutdown === 'function'\n )\n }\n\n private hasOnException(instance: unknown): instance is OnException {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n 'onException' in instance &&\n typeof (instance as OnException).onException === 'function'\n )\n }\n\n resolveModule(moduleOrDynamic: ModuleClass | DynamicModule): {\n moduleClass: Constructor\n options: ModuleOptions\n } {\n if (this.isDynamicModule(moduleOrDynamic)) {\n const { module: moduleClass, ...dynamicRest } = moduleOrDynamic\n\n const decoratorOptions = getModuleOptions(moduleClass) ?? {}\n const mergedOptions: ModuleOptions = {\n ...decoratorOptions,\n ...dynamicRest,\n providers: [...(decoratorOptions.providers ?? []), ...(dynamicRest.providers ?? [])],\n imports: [...(decoratorOptions.imports ?? [])],\n }\n\n return { moduleClass: moduleClass, options: mergedOptions }\n }\n\n const moduleClass = moduleOrDynamic as Constructor\n const options = getModuleOptions(moduleClass) ?? {}\n return { moduleClass, options }\n }\n\n isDynamicModule(value: unknown): value is DynamicModule {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'module' in value &&\n typeof (value as DynamicModule).module === 'function'\n )\n }\n\n private registerProvider(\n provider: Provider,\n { lazy = false, moduleName }: { lazy?: boolean; moduleName?: string } = {},\n ): void {\n if (lazy) {\n const token = this.providerToken(provider)\n // A lazy module registers onto the shared root container. If its token is\n // already bound (by an eagerly-registered or previously-loaded module),\n // overriding would silently clobber that binding — and break any\n // shared-singleton rendezvous. Keep the existing binding and warn.\n if (token !== null && this.container.isRegistered(token)) {\n this.logger.warn(\n `Lazy module ${moduleName ?? '(unknown)'} provides ${this.describeToken(token)}, ` +\n `which is already registered by another module — keeping the existing binding and ignoring the lazy provider.`,\n )\n return\n }\n }\n\n if (typeof provider === 'function') {\n this.container.register(provider as Constructor)\n this.collectIfListener(provider)\n this.collectIfCommand(provider)\n this.collectIfSeeder(provider)\n } else if ('useClass' in provider) {\n this.container.register(provider.provide, provider.useClass as Constructor)\n this.collectIfListener(provider.useClass)\n this.collectIfCommand(provider.useClass)\n this.collectIfSeeder(provider.useClass)\n } else if ('useValue' in provider) {\n this.container.registerValue(provider.provide, provider.useValue)\n } else if ('useFactory' in provider) {\n const { provide, useFactory, inject = [] } = provider\n this.container.registerFactory(provide, (c) => {\n const deps = inject.map((token) => c.resolve(token))\n return useFactory(...deps)\n })\n } else if ('useExisting' in provider) {\n this.container.registerExisting(provider.provide, provider.useExisting)\n }\n }\n\n /** The DI token a provider binds, for collision detection. */\n private providerToken(provider: Provider): InjectionToken | null {\n if (typeof provider === 'function') return provider\n if ('provide' in provider) return provider.provide\n return null\n }\n\n private describeToken(token: InjectionToken): string {\n if (typeof token === 'function') return token.name\n if (typeof token === 'symbol') return token.toString()\n if (typeof token === 'string') return token\n return 'lazy token'\n }\n\n private collectIfCommand(providerClass: Constructor): void {\n if (isCommand(providerClass) && !this.allCommands.includes(providerClass)) {\n this.allCommands.push(providerClass)\n this.logger.debug(`Collected command: ${providerClass.name}`)\n }\n }\n\n private collectIfSeeder(providerClass: Constructor): void {\n if (isSeeder(providerClass) && !this.allSeeders.includes(providerClass)) {\n this.allSeeders.push(providerClass)\n this.logger.debug(`Collected seeder: ${providerClass.name}`)\n }\n }\n\n private collectIfListener(providerClass: Constructor): void {\n if (isListener(providerClass)) {\n // Register with the listener's own scope (`@Listener()` applies\n // `@Transient()`) rather than forcing a singleton: listeners are resolved\n // fresh per event from the emitting request scope, so a listener may inject\n // request-scoped providers (i18n, queue senders, auth context, …).\n this.container.register(providerClass)\n this.allListeners.push(providerClass)\n this.logger.debug(`Collected listener: ${providerClass.name}`)\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as defineMetadata, v as getMetadata, y as hasMetadata } from "./di-DQ3xbjgf.mjs";
|
|
2
2
|
//#region src/module/module.decorator.ts
|
|
3
3
|
const MODULE_OPTIONS_KEY = Symbol.for("stratal:module:options");
|
|
4
4
|
function Module(options) {
|
|
@@ -16,4 +16,4 @@ function isModuleClass(target) {
|
|
|
16
16
|
//#endregion
|
|
17
17
|
export { isModuleClass as i, Module as n, getModuleOptions as r, MODULE_OPTIONS_KEY as t };
|
|
18
18
|
|
|
19
|
-
//# sourceMappingURL=module.decorator-
|
|
19
|
+
//# sourceMappingURL=module.decorator-CYbrOlX9.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.decorator-
|
|
1
|
+
{"version":3,"file":"module.decorator-CYbrOlX9.mjs","names":[],"sources":["../src/module/module.decorator.ts"],"sourcesContent":["import { defineMetadata, getMetadata, hasMetadata } from '../di/metadata'\nimport type { Constructor } from '../types'\nimport type { ModuleOptions } from './types'\n\nexport const MODULE_OPTIONS_KEY = Symbol.for('stratal:module:options')\n\nexport function Module(options: ModuleOptions) {\n return <TFunction extends abstract new (...args: never[]) => unknown>(target: TFunction): TFunction => {\n defineMetadata(MODULE_OPTIONS_KEY, options, target)\n return target\n }\n}\n\nexport function getModuleOptions(target: Constructor): ModuleOptions | undefined {\n return getMetadata<ModuleOptions>(MODULE_OPTIONS_KEY, target)\n}\n\nexport function isModuleClass(target: unknown): target is Constructor {\n return typeof target === 'function' && hasMetadata(MODULE_OPTIONS_KEY, target)\n}\n"],"mappings":";;AAIA,MAAa,qBAAqB,OAAO,IAAI,wBAAwB;AAErE,SAAgB,OAAO,SAAwB;CAC7C,QAAsE,WAAiC;EACrG,eAAe,oBAAoB,SAAS,MAAM;EAClD,OAAO;CACT;AACF;AAEA,SAAgB,iBAAiB,QAAgD;CAC/E,OAAO,YAA2B,oBAAoB,MAAM;AAC9D;AAEA,SAAgB,cAAc,QAAwC;CACpE,OAAO,OAAO,WAAW,cAAc,YAAY,oBAAoB,MAAM;AAC/E"}
|
package/dist/openapi/index.d.mts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as OpenAPIService } from "../openapi.service-
|
|
1
|
+
import { Jn as AsyncModuleOptions, Xn as DynamicModule, kr as RouterEnv, mn as RouteSchemaMeta } from "../index-B2WtEagR.mjs";
|
|
2
|
+
import { r as OpenAPIObject } from "../zod-BGisqLs3.mjs";
|
|
3
|
+
import { t as OpenAPIService } from "../openapi.service-Ds-R1tih.mjs";
|
|
4
4
|
import { MiddlewareHandler } from "hono/types";
|
|
5
|
-
|
|
6
5
|
//#region src/openapi/types.d.ts
|
|
7
6
|
/**
|
|
8
7
|
* OpenAPI Info section configuration
|
|
@@ -47,10 +46,11 @@ interface OpenAPIModuleOptions {
|
|
|
47
46
|
ui?: OpenAPIUIOptions | false;
|
|
48
47
|
}
|
|
49
48
|
/**
|
|
50
|
-
* Route
|
|
51
|
-
*
|
|
49
|
+
* Route visibility predicate for the OpenAPI document.
|
|
50
|
+
* Receives the route's schema metadata (path, method, tags, security, groups,
|
|
51
|
+
* meta) and returns true to include the route, false to exclude it.
|
|
52
52
|
*/
|
|
53
|
-
type RouteFilterFn = (
|
|
53
|
+
type RouteFilterFn = (route: RouteSchemaMeta) => boolean;
|
|
54
54
|
/**
|
|
55
55
|
* Runtime configuration override (set via middleware)
|
|
56
56
|
*/
|
|
@@ -97,7 +97,7 @@ interface IOpenAPIConfigService {
|
|
|
97
97
|
}
|
|
98
98
|
//#endregion
|
|
99
99
|
//#region src/openapi/openapi.module.d.ts
|
|
100
|
-
declare class OpenAPIModule {
|
|
100
|
+
export declare class OpenAPIModule {
|
|
101
101
|
/**
|
|
102
102
|
* Configure OpenAPI module with static options
|
|
103
103
|
*
|
|
@@ -112,15 +112,18 @@ declare class OpenAPIModule {
|
|
|
112
112
|
/**
|
|
113
113
|
* OpenAPI Module DI Tokens
|
|
114
114
|
*/
|
|
115
|
-
declare const OPENAPI_TOKENS: {
|
|
116
|
-
/** Static options provided via forRoot() */
|
|
115
|
+
export declare const OPENAPI_TOKENS: {
|
|
116
|
+
/** Static options provided via forRoot() */
|
|
117
|
+
readonly Options: symbol;
|
|
117
118
|
/**
|
|
118
119
|
* Singleton holder of the static base config. Resolvable outside a request
|
|
119
120
|
* (bootstrap endpoint mounting, CLI commands) for static config — never throws
|
|
120
121
|
* on resolve and carries no per-request state.
|
|
121
122
|
*/
|
|
122
|
-
readonly ConfigStore: symbol;
|
|
123
|
-
|
|
123
|
+
readonly ConfigStore: symbol;
|
|
124
|
+
/** Request-scoped config service that layers runtime overrides over the store */
|
|
125
|
+
readonly ConfigService: symbol;
|
|
126
|
+
/** OpenAPI service that generates specs and serves endpoints */
|
|
124
127
|
readonly OpenAPIService: symbol;
|
|
125
128
|
};
|
|
126
129
|
//#endregion
|
|
@@ -147,7 +150,7 @@ declare const OPENAPI_TOKENS: {
|
|
|
147
150
|
* }
|
|
148
151
|
* ```
|
|
149
152
|
*/
|
|
150
|
-
declare class OpenAPIConfigService implements IOpenAPIConfigService {
|
|
153
|
+
export declare class OpenAPIConfigService implements IOpenAPIConfigService {
|
|
151
154
|
private store;
|
|
152
155
|
private overrides;
|
|
153
156
|
constructor(store: IOpenAPIConfigStore);
|
|
@@ -178,7 +181,7 @@ declare class OpenAPIConfigService implements IOpenAPIConfigService {
|
|
|
178
181
|
* Per-request overrides live on {@link OpenAPIConfigService}, which reads the
|
|
179
182
|
* base config from here and merges its overrides on top.
|
|
180
183
|
*/
|
|
181
|
-
declare class OpenAPIConfigStore implements IOpenAPIConfigStore {
|
|
184
|
+
export declare class OpenAPIConfigStore implements IOpenAPIConfigStore {
|
|
182
185
|
private baseOptions?;
|
|
183
186
|
constructor(baseOptions?: OpenAPIModuleOptions | undefined);
|
|
184
187
|
getBaseConfig(): OpenAPIEffectiveConfig;
|
|
@@ -212,7 +215,7 @@ type Dispatcher = (method: string, url: string, options?: {
|
|
|
212
215
|
*
|
|
213
216
|
* Plain class (no DI) — reusable across MCP, CLI, and custom tooling.
|
|
214
217
|
*/
|
|
215
|
-
declare class OpenApiToolsService {
|
|
218
|
+
export declare class OpenApiToolsService {
|
|
216
219
|
private static readonly HTTP_METHODS;
|
|
217
220
|
private tools;
|
|
218
221
|
private toolMap;
|
|
@@ -234,5 +237,5 @@ declare class OpenApiToolsService {
|
|
|
234
237
|
private getOperation;
|
|
235
238
|
}
|
|
236
239
|
//#endregion
|
|
237
|
-
export { type Dispatcher, type IOpenAPIConfigService, type IOpenAPIConfigStore,
|
|
240
|
+
export { type Dispatcher, type IOpenAPIConfigService, type IOpenAPIConfigStore, type OpenAPIConfigOverride, type OpenAPIEffectiveConfig, type OpenAPIInfo, type OpenAPIModuleOptions, OpenAPIService, type OpenAPIUIContext, type OpenAPIUIOptions, type OpenAPIUIRenderer, type RouteFilterFn, type ToolDefinition, type ToolExecutionResult, type ToolFilter };
|
|
238
241
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/openapi/types.ts","../../src/openapi/openapi.module.ts","../../src/openapi/openapi.tokens.ts","../../src/openapi/services/openapi-config.service.ts","../../src/openapi/services/openapi-config.store.ts","../../src/openapi/services/openapi-tools.service.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/openapi/types.ts","../../src/openapi/openapi.module.ts","../../src/openapi/openapi.tokens.ts","../../src/openapi/services/openapi-config.service.ts","../../src/openapi/services/openapi-config.store.ts","../../src/openapi/services/openapi-tools.service.ts"],"mappings":";;;;;;;;UAOiB;EACf;EACA;EACA;;;;;UAMe;EACf;EACA;;;;;;KAOU,qBAAqB,SAAS,qBAAqB,kBAAkB;;;;UAKhE;;EAEf;;EAEA,WAAW;;;;;UAMI;;EAEf;;EAGA,OAAO;;EAGP,kBAAkB;;EAGlB,KAAK;;;;;;;KAQK,iBAAiB,OAAO;;;;UAKnB;;EAEf,OAAO,QAAQ;;EAGf,cAAc;;;;;UAMC;EACf;EACA,MAAM;EACN,kBAAkB;EAClB,cAAc;EACd,KAAK;;;;;;UAOU;;;;EAIf,iBAAiB;;;;;;UAOF;;;;;EAKf,SAAS,QAAQ;;;;EAKjB,sBAAsB;;;;qBC3CX;;;;;;;SAOJ,QAAQ,UAAS,uBAA4B;SAqB7C,aAAa,SAAS,mBAAmB,wBAAwB;;;;;;;qBC1F7D;;WAEX;;;;;;WAOA;;WAGA;;WAGA;;;;;;;;;;;;;;;;;;;;;;;;;;qBCeW,gCAAgC;UAIG;UAHtC;EAER,YAC8C,OAAO;;;;;EAOrD,SAAS,QAAQ;;EAKjB,sBAAsB;;;;;UAcd;;;;;;;;;;;;;;;;qBCzCG,8BAA8B;UAEuB;EADhE,YACgE,cAAc;EAG9E,iBAAiB;;;;KCxBd,aAAa;UAaD;EACf;EACA;EACA,aAAa;EACb;EACA;EACA;;UAGe;EACf;EACA;EACA,SAAS;;UAGM;EACf;EACA;;KAGU,cAAc,gBAAgB,aAAa;EACrD;EACA,UAAU;MACN,QAAQ;;;;;;qBA6BD;0BACa;UAEhB;UACA;UACA;UAEA;EAER,YAAY,MAAM,eAAe;IAAY,aAAa;;EAS1D,SAAS,SAAS,aAAa;EAmB/B,QAAQ,eAAe;EAIjB,YAAY,cAAc,MAAM,0BAA0B,QAAQ;UAmDhE;UAuBA;UASA;UAOA;UA+CA;UAYA;UA4BA;UAaA"}
|
package/dist/openapi/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as OPENAPI_TOKENS, t as OpenApiToolsService } from "../openapi-tools.service-
|
|
2
|
-
import { i as OpenAPIConfigService, n as OpenAPIService, r as OpenAPIConfigStore, t as OpenAPIModule } from "../openapi-
|
|
1
|
+
import { n as OPENAPI_TOKENS, t as OpenApiToolsService } from "../openapi-tools.service-VKZJlTIX.mjs";
|
|
2
|
+
import { i as OpenAPIConfigService, n as OpenAPIService, r as OpenAPIConfigStore, t as OpenAPIModule } from "../openapi-DBfBa5Qh.mjs";
|
|
3
3
|
export { OPENAPI_TOKENS, OpenAPIConfigService, OpenAPIConfigStore, OpenAPIModule, OpenAPIService, OpenApiToolsService };
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { c as Request, l as Singleton, p as inject } from "./di-
|
|
2
|
-
import { n as __decorateParam, t as __decorate } from "./decorate-
|
|
3
|
-
import { d as SECURITY_SCHEMES, l as ROUTER_CONTEXT_KEYS } from "./exception-context-
|
|
4
|
-
import { n as Module } from "./module.decorator-
|
|
5
|
-
import "./module/index.mjs";
|
|
1
|
+
import { C as ROUTER_TOKENS, c as Request, l as Singleton, p as inject } from "./di-DQ3xbjgf.mjs";
|
|
2
|
+
import { n as __decorateParam, t as __decorate } from "./decorate-C79yt3Wt.mjs";
|
|
3
|
+
import { d as SECURITY_SCHEMES, l as ROUTER_CONTEXT_KEYS } from "./exception-context-CdnGTQNF.mjs";
|
|
4
|
+
import { n as Module } from "./module.decorator-CYbrOlX9.mjs";
|
|
6
5
|
import { t as I18N_TOKENS } from "./i18n.tokens-CZ_v8oyS.mjs";
|
|
7
|
-
import { n as OPENAPI_TOKENS } from "./openapi-tools.service-
|
|
6
|
+
import { n as OPENAPI_TOKENS } from "./openapi-tools.service-VKZJlTIX.mjs";
|
|
8
7
|
//#region src/openapi/services/openapi-config.service.ts
|
|
9
8
|
let OpenAPIConfigService = class OpenAPIConfigService {
|
|
10
9
|
store;
|
|
@@ -70,26 +69,48 @@ OpenAPIConfigStore = __decorate([Singleton(OPENAPI_TOKENS.ConfigStore), __decora
|
|
|
70
69
|
//#region src/openapi/services/openapi.service.ts
|
|
71
70
|
let OpenAPIService = class OpenAPIService {
|
|
72
71
|
/**
|
|
73
|
-
* Generate a filtered OpenAPI
|
|
72
|
+
* Generate a filtered OpenAPI document from collected route metadata.
|
|
74
73
|
* Usable from both HTTP handlers and CLI commands.
|
|
75
74
|
*/
|
|
76
|
-
getSpec(
|
|
75
|
+
async getSpec(container) {
|
|
77
76
|
const configService = container.resolve(OPENAPI_TOKENS.ConfigService);
|
|
78
77
|
const i18n = container.resolve(I18N_TOKENS.I18nService);
|
|
78
|
+
const metadata = container.resolve(ROUTER_TOKENS.RouteMetadataRegistry);
|
|
79
79
|
const config = configService.getEffectiveConfig();
|
|
80
|
-
const
|
|
81
|
-
|
|
80
|
+
const [{ generateOpenAPIDocument }, { commonErrorSchemas }] = await Promise.all([import("./openapi-generator-CCwiu8AU.mjs"), import("./common.schemas-BigQCwYB.mjs").then((n) => n.n)]);
|
|
81
|
+
const routes = metadata.all().map((route) => this.withCommonErrors(route, commonErrorSchemas));
|
|
82
|
+
return generateOpenAPIDocument({
|
|
82
83
|
info: {
|
|
83
|
-
version: config.info.version,
|
|
84
84
|
title: config.info.title,
|
|
85
|
+
version: config.info.version,
|
|
85
86
|
description: config.info.description
|
|
86
|
-
}
|
|
87
|
+
},
|
|
88
|
+
routes,
|
|
89
|
+
securitySchemes: this.getSecuritySchemeDefinitions(i18n),
|
|
90
|
+
filter: config.routeFilter
|
|
87
91
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Append the standard error responses (400/401/403/404/409/500) a route
|
|
95
|
+
* doesn't already declare. Done here, in the lazy doc path, so the error
|
|
96
|
+
* schemas never load at registration time.
|
|
97
|
+
*/
|
|
98
|
+
withCommonErrors(route, common) {
|
|
99
|
+
const present = new Set(route.responses.map((r) => r.status));
|
|
100
|
+
const extra = [];
|
|
101
|
+
for (const [status, def] of Object.entries(common)) {
|
|
102
|
+
const code = Number(status);
|
|
103
|
+
if (!present.has(code)) extra.push({
|
|
104
|
+
status: code,
|
|
105
|
+
schema: def.schema,
|
|
106
|
+
contentType: "application/json",
|
|
107
|
+
description: def.description
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return extra.length ? {
|
|
111
|
+
...route,
|
|
112
|
+
responses: [...route.responses, ...extra]
|
|
113
|
+
} : route;
|
|
93
114
|
}
|
|
94
115
|
/**
|
|
95
116
|
* Setup OpenAPI documentation endpoints
|
|
@@ -98,9 +119,9 @@ let OpenAPIService = class OpenAPIService {
|
|
|
98
119
|
const config = container.resolve(OPENAPI_TOKENS.ConfigStore).getBaseConfig();
|
|
99
120
|
const jsonPath = config.jsonPath;
|
|
100
121
|
const ui = config.ui;
|
|
101
|
-
app.get(jsonPath, (c) => {
|
|
122
|
+
app.get(jsonPath, async (c) => {
|
|
102
123
|
const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER);
|
|
103
|
-
const fullSpec = this.getSpec(
|
|
124
|
+
const fullSpec = await this.getSpec(requestContainer);
|
|
104
125
|
const url = new URL(c.req.raw.url);
|
|
105
126
|
const i18n = requestContainer.resolve(I18N_TOKENS.I18nService);
|
|
106
127
|
fullSpec.servers = [{
|
|
@@ -155,51 +176,6 @@ let OpenAPIService = class OpenAPIService {
|
|
|
155
176
|
}
|
|
156
177
|
};
|
|
157
178
|
}
|
|
158
|
-
/**
|
|
159
|
-
* Filter OpenAPI paths using custom routeFilter
|
|
160
|
-
*/
|
|
161
|
-
filterRoutes(paths, config) {
|
|
162
|
-
const filteredPaths = {};
|
|
163
|
-
for (const [path, pathItem] of Object.entries(paths)) {
|
|
164
|
-
if (config.routeFilter && !config.routeFilter(path, pathItem)) continue;
|
|
165
|
-
filteredPaths[path] = pathItem;
|
|
166
|
-
}
|
|
167
|
-
return filteredPaths;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Filter unreferenced schemas from OpenAPI spec
|
|
171
|
-
*/
|
|
172
|
-
filterSchemas(spec) {
|
|
173
|
-
const referencedSchemas = /* @__PURE__ */ new Set();
|
|
174
|
-
this.collectSchemaRefs(spec.paths, referencedSchemas);
|
|
175
|
-
const filteredSchemas = {};
|
|
176
|
-
const components = spec.components;
|
|
177
|
-
if (components?.schemas) {
|
|
178
|
-
const allSchemas = components.schemas;
|
|
179
|
-
let prevSize = 0;
|
|
180
|
-
while (referencedSchemas.size > prevSize) {
|
|
181
|
-
prevSize = referencedSchemas.size;
|
|
182
|
-
for (const [schemaName, schemaValue] of Object.entries(allSchemas)) if (referencedSchemas.has(schemaName) && !filteredSchemas[schemaName]) {
|
|
183
|
-
filteredSchemas[schemaName] = schemaValue;
|
|
184
|
-
this.collectSchemaRefs(schemaValue, referencedSchemas);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return filteredSchemas;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Recursively collect all schema references from an object
|
|
192
|
-
*/
|
|
193
|
-
collectSchemaRefs(obj, refs) {
|
|
194
|
-
if (!obj || typeof obj !== "object") return;
|
|
195
|
-
const record = obj;
|
|
196
|
-
if (record.$ref && typeof record.$ref === "string") {
|
|
197
|
-
const match = /^#\/components\/schemas\/(.+)$/.exec(record.$ref);
|
|
198
|
-
if (match) refs.add(match[1]);
|
|
199
|
-
}
|
|
200
|
-
if (Array.isArray(obj)) for (const item of obj) this.collectSchemaRefs(item, refs);
|
|
201
|
-
else for (const value of Object.values(record)) this.collectSchemaRefs(value, refs);
|
|
202
|
-
}
|
|
203
179
|
};
|
|
204
180
|
OpenAPIService = __decorate([Singleton(OPENAPI_TOKENS.OpenAPIService)], OpenAPIService);
|
|
205
181
|
//#endregion
|
|
@@ -306,4 +282,4 @@ OpenAPIModule = _OpenAPIModule = __decorate([Module({ providers: [
|
|
|
306
282
|
//#endregion
|
|
307
283
|
export { OpenAPIConfigService as i, OpenAPIService as n, OpenAPIConfigStore as r, OpenAPIModule as t };
|
|
308
284
|
|
|
309
|
-
//# sourceMappingURL=openapi-
|
|
285
|
+
//# sourceMappingURL=openapi-DBfBa5Qh.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-DBfBa5Qh.mjs","names":[],"sources":["../src/openapi/services/openapi-config.service.ts","../src/openapi/services/openapi-config.store.ts","../src/openapi/services/openapi.service.ts","../src/openapi/openapi.module.ts"],"sourcesContent":["import { inject } from '../../di'\nimport { Request } from '../../di/decorators'\nimport { OPENAPI_TOKENS } from '../openapi.tokens'\nimport type {\n IOpenAPIConfigService,\n IOpenAPIConfigStore,\n OpenAPIConfigOverride,\n OpenAPIEffectiveConfig\n} from '../types'\n\n/**\n * OpenAPI Config Service\n *\n * Request-scoped service that manages OpenAPI configuration for the current request.\n * Supports runtime overrides via middleware while preserving base configuration.\n *\n * @example\n * ```typescript\n * // In middleware (e.g., RouteAccessMiddleware)\n * constructor(\n * @inject(OPENAPI_TOKENS.ConfigService) private openAPIConfig: IOpenAPIConfigService\n * ) {}\n *\n * async handle(ctx, next) {\n * this.openAPIConfig.override({\n * info: { title: 'Custom API' },\n * routeFilter: (path) => shouldInclude(path)\n * })\n * await next()\n * }\n * ```\n */\n@Request(OPENAPI_TOKENS.ConfigService)\nexport class OpenAPIConfigService implements IOpenAPIConfigService {\n private overrides: OpenAPIConfigOverride[] = []\n\n constructor(\n @inject(OPENAPI_TOKENS.ConfigStore) private store: IOpenAPIConfigStore\n ) { }\n\n /**\n * Add configuration override for this request.\n * Overrides are merged in the order they are added.\n */\n override(config: OpenAPIConfigOverride): void {\n this.overrides.push(config)\n }\n\n /** Get effective configuration (base merged with all overrides) */\n getEffectiveConfig(): OpenAPIEffectiveConfig {\n let effective = this.store.getBaseConfig()\n\n for (const override of this.overrides) {\n effective = this.mergeConfig(effective, override)\n }\n\n return effective\n }\n\n /**\n * Merge override into effective config.\n * Info is shallow-merged, routeFilter is replaced.\n */\n private mergeConfig(\n base: OpenAPIEffectiveConfig,\n override: OpenAPIConfigOverride\n ): OpenAPIEffectiveConfig {\n return {\n ...base,\n info: {\n ...base.info,\n ...(override.info && {\n title: override.info.title ?? base.info.title,\n version: override.info.version ?? base.info.version,\n description: override.info.description ?? base.info.description\n })\n },\n routeFilter: override.routeFilter ?? base.routeFilter\n }\n }\n}\n","import { inject } from '../../di'\nimport { Singleton } from '../../di/decorators'\nimport { OPENAPI_TOKENS } from '../openapi.tokens'\nimport type {\n IOpenAPIConfigStore,\n OpenAPIEffectiveConfig,\n OpenAPIModuleOptions\n} from '../types'\n\n/**\n * OpenAPI Config Store\n *\n * Singleton holder of the static base configuration (mount paths, base info,\n * security schemes) derived from `OpenAPIModule.forRoot()` options. It carries\n * no per-request state, so it is the thing to resolve when you need OpenAPI\n * config OUTSIDE a request scope — endpoint mounting at bootstrap, the\n * `mcp:serve` CLI command, etc. Resolving it never throws on its own.\n *\n * Per-request overrides live on {@link OpenAPIConfigService}, which reads the\n * base config from here and merges its overrides on top.\n */\n@Singleton(OPENAPI_TOKENS.ConfigStore)\nexport class OpenAPIConfigStore implements IOpenAPIConfigStore {\n constructor(\n @inject(OPENAPI_TOKENS.Options, { isOptional: true }) private baseOptions?: OpenAPIModuleOptions\n ) { }\n\n getBaseConfig(): OpenAPIEffectiveConfig {\n return {\n jsonPath: this.baseOptions?.jsonPath ?? '/api/openapi.json',\n ui: this.baseOptions?.ui,\n info: {\n title: this.baseOptions?.info?.title ?? 'API',\n version: this.baseOptions?.info?.version ?? '1.0.0',\n description: this.baseOptions?.info?.description\n },\n securitySchemes: this.baseOptions?.securitySchemes\n }\n }\n}\n","import type { Container } from '../../di/container'\nimport { Singleton } from '../../di/decorators'\nimport { I18N_TOKENS } from '../../i18n/i18n.tokens'\nimport type { II18nService } from '../../i18n/i18n.types'\nimport type { OpenAPIObject, ZodType } from '../../i18n/validation/zod'\nimport { ROUTER_CONTEXT_KEYS, SECURITY_SCHEMES } from '../../router/constants'\nimport type { HonoApp } from '../../router/hono-app'\nimport type { RouteMetadataRegistry, RouteResponseMeta, RouteSchemaMeta } from '../../router/route-metadata'\nimport { ROUTER_TOKENS } from '../../router/router.tokens'\nimport type { RouterEnv } from '../../router/types'\nimport { OPENAPI_TOKENS } from '../openapi.tokens'\nimport type { IOpenAPIConfigService, IOpenAPIConfigStore } from '../types'\n\n/**\n * OpenAPI Service\n *\n * Generates OpenAPI specifications lazily from the route metadata registry:\n * - Built on demand (per request to the spec endpoint), never at startup\n * - Schema → JSON Schema via zod v4's native `z.toJSONSchema()`\n * - Runtime configuration via OpenAPIConfigService\n * - Route visibility via a metadata predicate (`routeFilter`)\n * - i18n support for titles, descriptions, and security schemes\n *\n * Hidden routes (hideFromDocs) are flagged in the metadata and excluded by the\n * generator. The generator and the common error schemas are dynamically\n * imported so `zod`/`common.schemas` never reach the routing path — they load\n * only when the document is actually requested.\n */\n@Singleton(OPENAPI_TOKENS.OpenAPIService)\nexport class OpenAPIService {\n /**\n * Generate a filtered OpenAPI document from collected route metadata.\n * Usable from both HTTP handlers and CLI commands.\n */\n async getSpec(container: Container): Promise<OpenAPIObject> {\n const configService = container.resolve<IOpenAPIConfigService>(OPENAPI_TOKENS.ConfigService)\n const i18n = container.resolve<II18nService>(I18N_TOKENS.I18nService)\n const metadata = container.resolve<RouteMetadataRegistry>(ROUTER_TOKENS.RouteMetadataRegistry)\n const config = configService.getEffectiveConfig()\n\n const [{ generateOpenAPIDocument }, { commonErrorSchemas }] = await Promise.all([\n import('../openapi-generator'),\n import('../../router/schemas/common.schemas'),\n ])\n\n const routes = metadata.all().map((route) => this.withCommonErrors(route, commonErrorSchemas))\n\n return generateOpenAPIDocument({\n info: {\n title: config.info.title,\n version: config.info.version,\n description: config.info.description,\n },\n routes,\n securitySchemes: this.getSecuritySchemeDefinitions(i18n),\n filter: config.routeFilter,\n })\n }\n\n /**\n * Append the standard error responses (400/401/403/404/409/500) a route\n * doesn't already declare. Done here, in the lazy doc path, so the error\n * schemas never load at registration time.\n */\n private withCommonErrors(\n route: RouteSchemaMeta,\n common: Record<string, { schema: ZodType; description: string }>,\n ): RouteSchemaMeta {\n const present = new Set(route.responses.map((r) => r.status))\n const extra: RouteResponseMeta[] = []\n for (const [status, def] of Object.entries(common)) {\n const code = Number(status)\n if (!present.has(code)) {\n extra.push({ status: code, schema: def.schema, contentType: 'application/json', description: def.description })\n }\n }\n return extra.length ? { ...route, responses: [...route.responses, ...extra] } : route\n }\n\n /**\n * Setup OpenAPI documentation endpoints\n */\n setupEndpoints(app: HonoApp, container: Container): void {\n // Endpoints are mounted at bootstrap (no request scope), so read the static\n // mount paths from the singleton config store — request overrides (info /\n // routeFilter) never affect jsonPath/ui and are resolved per request inside\n // the handlers below via the request-scoped config service.\n const config = container.resolve<IOpenAPIConfigStore>(OPENAPI_TOKENS.ConfigStore).getBaseConfig()\n const jsonPath = config.jsonPath\n const ui = config.ui\n\n // OpenAPI JSON spec endpoint\n app.get(jsonPath, async (c) => {\n const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n const fullSpec = await this.getSpec(requestContainer)\n\n // Add servers (HTTP-specific — needs request URL context)\n const url = new URL(c.req.raw.url)\n const i18n = requestContainer.resolve<II18nService>(I18N_TOKENS.I18nService)\n fullSpec.servers = [{\n url: `${url.protocol}//${url.host}`,\n description: i18n.t('common.api.serverDescription'),\n }]\n\n return c.json(fullSpec)\n })\n this.nameLastHandler(app, 'OpenAPI', 'spec')\n\n // Docs UI endpoint\n if (ui !== false) {\n const uiPath = ui?.path ?? '/api/docs'\n const uiRenderer = ui?.renderer\n\n app.get(uiPath, async (c, next) => {\n const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n const requestConfigService = requestContainer.resolve<IOpenAPIConfigService>(\n OPENAPI_TOKENS.ConfigService\n )\n const effectiveConfig = requestConfigService.getEffectiveConfig()\n const uiContext = { specUrl: effectiveConfig.jsonPath, title: effectiveConfig.info.title }\n\n if (uiRenderer) {\n return uiRenderer(uiContext)(c, next)\n }\n\n const { swaggerUI } = await import('@hono/swagger-ui')\n return swaggerUI<RouterEnv>({ url: uiContext.specUrl })(c, next)\n })\n this.nameLastHandler(app, 'OpenAPI', 'docs')\n }\n }\n\n private nameLastHandler(app: HonoApp, controller: string, method: string): void {\n const last = app.routes[app.routes.length - 1]\n Object.defineProperty(last.handler, 'name', { value: `http:${controller}.${method}` })\n }\n\n /**\n * Get localized security scheme definitions\n */\n private getSecuritySchemeDefinitions(i18n: II18nService) {\n return {\n [SECURITY_SCHEMES.BEARER_AUTH]: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: i18n.t('common.api.security.bearerAuth')\n },\n [SECURITY_SCHEMES.API_KEY]: {\n type: 'apiKey',\n in: 'header',\n name: 'X-API-Key',\n description: i18n.t('common.api.security.apiKey')\n },\n [SECURITY_SCHEMES.SESSION_COOKIE]: {\n type: 'apiKey',\n in: 'cookie',\n name: 'session',\n description: i18n.t('common.api.security.sessionCookie')\n }\n } as const\n }\n}\n","/**\n * OpenAPI Module\n *\n * Provides configurable OpenAPI documentation endpoints with runtime override support.\n *\n * Features:\n * - Configurable paths for /openapi.json and /docs\n * - Runtime config overrides via middleware\n * - i18n support for titles and descriptions\n * - Route filtering via hideFromDocs and custom routeFilter\n *\n * @example Basic usage\n * ```typescript\n * @Module({\n * imports: [\n * OpenAPIModule.forRoot({\n * info: { title: 'My API', version: '1.0.0' }\n * })\n * ]\n * })\n * export class AppModule {}\n * ```\n *\n * @example With runtime override in middleware\n * ```typescript\n * // In RouteAccessMiddleware\n * constructor(\n * @inject(OPENAPI_TOKENS.ConfigService) private openAPIConfig: IOpenAPIConfigService\n * ) {}\n *\n * async handle(ctx, next) {\n * this.openAPIConfig.override({\n * info: { title: 'Custom API' },\n * routeFilter: (path) => this.shouldInclude(path)\n * })\n * await next()\n * }\n * ```\n */\n\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport { OPENAPI_TOKENS } from './openapi.tokens'\nimport { OpenAPIConfigService, OpenAPIConfigStore, OpenAPIService } from './services'\nimport type { OpenAPIModuleOptions } from './types'\n\n/** Default options when none provided */\nconst DEFAULT_OPTIONS: OpenAPIModuleOptions = {\n jsonPath: '/api/openapi.json',\n info: {\n title: 'API',\n version: '1.0.0'\n }\n}\n\n@Module({\n providers: [\n // Static base config (singleton — resolvable outside a request scope)\n { provide: OPENAPI_TOKENS.ConfigStore, useClass: OpenAPIConfigStore },\n // OpenAPI config service (request-scoped, supports runtime overrides)\n { provide: OPENAPI_TOKENS.ConfigService, useClass: OpenAPIConfigService },\n // OpenAPI service (singleton — serves spec and docs endpoints)\n { provide: OPENAPI_TOKENS.OpenAPIService, useClass: OpenAPIService },\n ],\n})\nexport class OpenAPIModule {\n /**\n * Configure OpenAPI module with static options\n *\n * @param options - OpenAPI configuration (paths, info, security schemes)\n * @returns DynamicModule with options provider\n */\n static forRoot(options: OpenAPIModuleOptions = {}): DynamicModule {\n // Merge with defaults\n const mergedOptions: OpenAPIModuleOptions = {\n ...DEFAULT_OPTIONS,\n ...options,\n info: {\n ...DEFAULT_OPTIONS.info,\n ...options.info,\n title: options.info?.title ?? DEFAULT_OPTIONS.info?.title ?? 'API',\n version: options.info?.version ?? DEFAULT_OPTIONS.info?.version ?? '1.0.0',\n }\n }\n\n return {\n module: OpenAPIModule,\n providers: [\n { provide: OPENAPI_TOKENS.Options, useValue: mergedOptions }\n ]\n }\n }\n\n static forRootAsync(options: AsyncModuleOptions<OpenAPIModuleOptions>): DynamicModule {\n return {\n module: OpenAPIModule,\n providers: [\n {\n provide: OPENAPI_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject\n },\n ]\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { globalRegistry, toJSONSchema } from "zod/mini";
|
|
2
|
+
//#region src/openapi/openapi-generator.ts
|
|
3
|
+
const REF_FROM = ["#/definitions/", "#/$defs/"];
|
|
4
|
+
const REF_TO = "#/components/schemas/";
|
|
5
|
+
/**
|
|
6
|
+
* Build an OpenAPI 3.0 document on demand from collected route metadata using
|
|
7
|
+
* zod v4's native `z.toJSONSchema()` — no `@hono/zod-openapi` /
|
|
8
|
+
* `@asteasolutions/zod-to-openapi`. This module is only ever loaded via dynamic
|
|
9
|
+
* import from the docs endpoint, so `zod` never reaches the routing-init path.
|
|
10
|
+
*/
|
|
11
|
+
function generateOpenAPIDocument(input) {
|
|
12
|
+
const paths = {};
|
|
13
|
+
const components = {};
|
|
14
|
+
for (const route of input.routes) {
|
|
15
|
+
if (route.hidden) continue;
|
|
16
|
+
if (input.filter && !input.filter(route)) continue;
|
|
17
|
+
const operation = buildOperation(route, components);
|
|
18
|
+
const pathItem = paths[route.path] ??= {};
|
|
19
|
+
pathItem[route.method] = operation;
|
|
20
|
+
}
|
|
21
|
+
const doc = {
|
|
22
|
+
openapi: "3.0.0",
|
|
23
|
+
info: input.info,
|
|
24
|
+
paths
|
|
25
|
+
};
|
|
26
|
+
if (input.servers?.length) doc.servers = input.servers;
|
|
27
|
+
if (input.securitySchemes || Object.keys(components).length > 0) {
|
|
28
|
+
doc.components = {};
|
|
29
|
+
if (input.securitySchemes) doc.components.securitySchemes = input.securitySchemes;
|
|
30
|
+
if (Object.keys(components).length > 0) doc.components.schemas = components;
|
|
31
|
+
}
|
|
32
|
+
return doc;
|
|
33
|
+
}
|
|
34
|
+
function buildOperation(route, components) {
|
|
35
|
+
const operation = { responses: buildResponses(route, components) };
|
|
36
|
+
if (route.tags.length) operation.tags = route.tags;
|
|
37
|
+
if (route.summary) operation.summary = route.summary;
|
|
38
|
+
if (route.description) operation.description = route.description;
|
|
39
|
+
if (route.security.length) operation.security = route.security;
|
|
40
|
+
const parameters = buildParameters(route, components);
|
|
41
|
+
if (parameters.length) operation.parameters = parameters;
|
|
42
|
+
if (route.request.body) {
|
|
43
|
+
const schema = convert(route.request.body.schema, "input", components);
|
|
44
|
+
operation.requestBody = {
|
|
45
|
+
required: true,
|
|
46
|
+
content: { [route.request.body.contentType]: { schema } }
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return operation;
|
|
50
|
+
}
|
|
51
|
+
function buildParameters(route, components) {
|
|
52
|
+
const parameters = [];
|
|
53
|
+
if (route.localeParam) parameters.push({
|
|
54
|
+
name: route.localeParam.name,
|
|
55
|
+
in: "path",
|
|
56
|
+
required: true,
|
|
57
|
+
schema: {
|
|
58
|
+
type: "string",
|
|
59
|
+
enum: route.localeParam.values
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
if (route.request.params) parameters.push(...expandObjectParams(route.request.params, "path", components));
|
|
63
|
+
if (route.request.query) parameters.push(...expandObjectParams(route.request.query, "query", components));
|
|
64
|
+
return parameters;
|
|
65
|
+
}
|
|
66
|
+
/** Split a zod object schema into individual OpenAPI parameter objects. */
|
|
67
|
+
function expandObjectParams(schema, location, components) {
|
|
68
|
+
const json = convertInline(schema, "input", components);
|
|
69
|
+
if (json.type !== "object" || !json.properties) return [];
|
|
70
|
+
const required = new Set(json.required ?? []);
|
|
71
|
+
return Object.entries(json.properties).map(([name, propSchema]) => ({
|
|
72
|
+
name,
|
|
73
|
+
in: location,
|
|
74
|
+
required: location === "path" ? true : required.has(name),
|
|
75
|
+
schema: propSchema
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
function buildResponses(route, components) {
|
|
79
|
+
const responses = {};
|
|
80
|
+
for (const res of route.responses) {
|
|
81
|
+
const entry = { description: res.description };
|
|
82
|
+
if (res.schema) entry.content = { [res.contentType]: { schema: convert(res.schema, "output", components) } };
|
|
83
|
+
responses[String(res.status)] = entry;
|
|
84
|
+
}
|
|
85
|
+
return responses;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Convert one zod schema to an OpenAPI 3.0 schema object. Schemas registered
|
|
89
|
+
* with an `id` (via the global registry) are emitted once into the shared
|
|
90
|
+
* components map and referenced with `$ref` — both when they appear nested (zod
|
|
91
|
+
* extracts them to `definitions`) and when passed at the top level.
|
|
92
|
+
*/
|
|
93
|
+
function convert(schema, io, components) {
|
|
94
|
+
const id = globalRegistry.get(schema)?.id;
|
|
95
|
+
if (id) {
|
|
96
|
+
if (!(id in components)) {
|
|
97
|
+
components[id] = {};
|
|
98
|
+
components[id] = convertInline(schema, io, components);
|
|
99
|
+
}
|
|
100
|
+
return { $ref: REF_TO + id };
|
|
101
|
+
}
|
|
102
|
+
return convertInline(schema, io, components);
|
|
103
|
+
}
|
|
104
|
+
/** Convert a schema inline, hoisting any reused `$defs` into shared components. */
|
|
105
|
+
function convertInline(schema, io, components) {
|
|
106
|
+
const json = toJSONSchema(schema, {
|
|
107
|
+
io,
|
|
108
|
+
target: "openapi-3.0",
|
|
109
|
+
reused: "ref",
|
|
110
|
+
unrepresentable: "any"
|
|
111
|
+
});
|
|
112
|
+
delete json.$schema;
|
|
113
|
+
const defs = json.definitions ?? json.$defs;
|
|
114
|
+
if (defs) {
|
|
115
|
+
delete json.definitions;
|
|
116
|
+
delete json.$defs;
|
|
117
|
+
for (const [name, def] of Object.entries(defs)) components[name] ??= rewriteRefs(def);
|
|
118
|
+
}
|
|
119
|
+
return rewriteRefs(json);
|
|
120
|
+
}
|
|
121
|
+
/** Rewrite JSON-Schema `#/definitions/x` and `#/$defs/x` refs to OpenAPI `#/components/schemas/x`. */
|
|
122
|
+
function rewriteRefs(node) {
|
|
123
|
+
if (Array.isArray(node)) return node.map(rewriteRefs);
|
|
124
|
+
if (node && typeof node === "object") {
|
|
125
|
+
const out = {};
|
|
126
|
+
for (const [key, value] of Object.entries(node)) out[key] = key === "$ref" && typeof value === "string" ? REF_FROM.reduce((ref, from) => ref.replace(from, REF_TO), value) : rewriteRefs(value);
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
return node;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { generateOpenAPIDocument };
|
|
133
|
+
|
|
134
|
+
//# sourceMappingURL=openapi-generator-CCwiu8AU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-generator-CCwiu8AU.mjs","names":[],"sources":["../src/openapi/openapi-generator.ts"],"sourcesContent":["import type {\n ComponentsObject,\n OpenAPIObject,\n OperationObject,\n ParameterObject,\n PathItemObject,\n ReferenceObject,\n ResponseObject,\n ResponsesObject,\n SchemaObject,\n} from 'openapi3-ts/oas30';\nimport type { ZodMiniType as ZodType } from 'zod/mini';\nimport { globalRegistry, toJSONSchema } from 'zod/mini';\nimport type { RouteSchemaMeta } from '../router/route-metadata';\n\n/**\n * Inputs for a single document build. `filter` scopes which routes appear:\n * callers pass a predicate over the route metadata (groups, path, method,\n * security) instead of imperatively mutating shared config.\n */\nexport interface OpenAPIDocumentInput {\n info: { title: string; version: string; description?: string }\n routes: readonly RouteSchemaMeta[]\n securitySchemes?: ComponentsObject['securitySchemes']\n servers?: { url: string }[]\n filter?: (route: RouteSchemaMeta) => boolean\n}\n\n// Definition containers differ by JSON Schema draft: draft-07 emits\n// `definitions`, draft-2020 emits `$defs`. Both map to OpenAPI components.\nconst REF_FROM = ['#/definitions/', '#/$defs/']\nconst REF_TO = '#/components/schemas/'\n\n/**\n * Build an OpenAPI 3.0 document on demand from collected route metadata using\n * zod v4's native `z.toJSONSchema()` — no `@hono/zod-openapi` /\n * `@asteasolutions/zod-to-openapi`. This module is only ever loaded via dynamic\n * import from the docs endpoint, so `zod` never reaches the routing-init path.\n */\nexport function generateOpenAPIDocument(input: OpenAPIDocumentInput): OpenAPIObject {\n const paths: Record<string, PathItemObject> = {}\n const components: Record<string, SchemaObject> = {}\n\n for (const route of input.routes) {\n if (route.hidden) continue\n if (input.filter && !input.filter(route)) continue\n\n const operation = buildOperation(route, components)\n const pathItem = (paths[route.path] ??= {})\n ;(pathItem as Record<string, OperationObject>)[route.method] = operation\n }\n\n const doc: OpenAPIObject = {\n openapi: '3.0.0',\n info: input.info,\n paths,\n }\n if (input.servers?.length) doc.servers = input.servers\n if (input.securitySchemes || Object.keys(components).length > 0) {\n doc.components = {}\n if (input.securitySchemes) doc.components.securitySchemes = input.securitySchemes\n if (Object.keys(components).length > 0) doc.components.schemas = components\n }\n return doc\n}\n\nfunction buildOperation(route: RouteSchemaMeta, components: Record<string, SchemaObject>): OperationObject {\n const operation: OperationObject = { responses: buildResponses(route, components) }\n\n if (route.tags.length) operation.tags = route.tags\n if (route.summary) operation.summary = route.summary\n if (route.description) operation.description = route.description\n if (route.security.length) operation.security = route.security\n\n const parameters = buildParameters(route, components)\n if (parameters.length) operation.parameters = parameters\n\n if (route.request.body) {\n const schema = convert(route.request.body.schema, 'input', components)\n operation.requestBody = {\n required: true,\n content: { [route.request.body.contentType]: { schema } },\n }\n }\n\n return operation\n}\n\nfunction buildParameters(route: RouteSchemaMeta, components: Record<string, SchemaObject>): ParameterObject[] {\n const parameters: ParameterObject[] = []\n\n if (route.localeParam) {\n parameters.push({\n name: route.localeParam.name,\n in: 'path',\n required: true,\n schema: { type: 'string', enum: route.localeParam.values },\n })\n }\n if (route.request.params) {\n parameters.push(...expandObjectParams(route.request.params, 'path', components))\n }\n if (route.request.query) {\n parameters.push(...expandObjectParams(route.request.query, 'query', components))\n }\n\n return parameters\n}\n\n/** Split a zod object schema into individual OpenAPI parameter objects. */\nfunction expandObjectParams(schema: ZodType, location: 'path' | 'query', components: Record<string, SchemaObject>): ParameterObject[] {\n // Always inline: OpenAPI parameters are split per-property and cannot be a\n // `$ref` to the whole object, even when that object is a named component.\n const json = convertInline(schema, 'input', components)\n if (json.type !== 'object' || !json.properties) return []\n\n const required = new Set(json.required ?? [])\n return Object.entries(json.properties).map(([name, propSchema]) => ({\n name,\n in: location,\n // Path params are always required; query follows the schema.\n required: location === 'path' ? true : required.has(name),\n schema: propSchema as SchemaObject,\n }))\n}\n\nfunction buildResponses(route: RouteSchemaMeta, components: Record<string, SchemaObject>): ResponsesObject {\n const responses: ResponsesObject = {}\n for (const res of route.responses) {\n const entry: ResponseObject = { description: res.description }\n if (res.schema) {\n entry.content = { [res.contentType]: { schema: convert(res.schema, 'output', components) } }\n }\n responses[String(res.status)] = entry\n }\n return responses\n}\n\n/**\n * Convert one zod schema to an OpenAPI 3.0 schema object. Schemas registered\n * with an `id` (via the global registry) are emitted once into the shared\n * components map and referenced with `$ref` — both when they appear nested (zod\n * extracts them to `definitions`) and when passed at the top level.\n */\nfunction convert(schema: ZodType, io: 'input' | 'output', components: Record<string, SchemaObject>): SchemaObject | ReferenceObject {\n const id = globalRegistry.get(schema)?.id\n if (id) {\n if (!(id in components)) {\n components[id] = {} // placeholder breaks recursion before the body is built\n components[id] = convertInline(schema, io, components)\n }\n return { $ref: REF_TO + id }\n }\n return convertInline(schema, io, components)\n}\n\n/** Convert a schema inline, hoisting any reused `$defs` into shared components. */\nfunction convertInline(schema: ZodType, io: 'input' | 'output', components: Record<string, SchemaObject>): SchemaObject {\n // Types with no JSON Schema representation (custom, transform, function, map,\n // set, etc.) emit an empty schema (\"any\") instead of aborting the document.\n const json = toJSONSchema(schema, { io, target: 'openapi-3.0', reused: 'ref', unrepresentable: 'any' }) as Record<string, unknown>\n delete json.$schema\n\n const defs = (json.definitions ?? json.$defs) as Record<string, unknown> | undefined\n if (defs) {\n delete json.definitions\n delete json.$defs\n for (const [name, def] of Object.entries(defs)) {\n components[name] ??= rewriteRefs(def) as SchemaObject\n }\n }\n return rewriteRefs(json) as SchemaObject\n}\n\n/** Rewrite JSON-Schema `#/definitions/x` and `#/$defs/x` refs to OpenAPI `#/components/schemas/x`. */\nfunction rewriteRefs(node: unknown): unknown {\n if (Array.isArray(node)) return node.map(rewriteRefs)\n if (node && typeof node === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(node as Record<string, unknown>)) {\n out[key] = key === '$ref' && typeof value === 'string'\n ? REF_FROM.reduce((ref, from) => ref.replace(from, REF_TO), value)\n : rewriteRefs(value)\n }\n return out\n }\n return node\n}\n"],"mappings":"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"}
|
|
@@ -19,12 +19,20 @@ const OPENAPI_TOKENS = {
|
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/openapi/services/openapi-tools.service.ts
|
|
21
21
|
/**
|
|
22
|
+
* Serialize a path/query parameter value for use in a URL.
|
|
23
|
+
* Objects/arrays are JSON-encoded; primitives are coerced to string.
|
|
24
|
+
*/
|
|
25
|
+
function stringifyParamValue(value) {
|
|
26
|
+
if (value != null && typeof value === "object") return JSON.stringify(value);
|
|
27
|
+
return String(value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
22
30
|
* Converts an OpenAPI 3.0 spec into callable tool definitions.
|
|
23
31
|
*
|
|
24
32
|
* Plain class (no DI) — reusable across MCP, CLI, and custom tooling.
|
|
25
33
|
*/
|
|
26
34
|
var OpenApiToolsService = class OpenApiToolsService {
|
|
27
|
-
static HTTP_METHODS = new Set([
|
|
35
|
+
static HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
28
36
|
"get",
|
|
29
37
|
"post",
|
|
30
38
|
"put",
|
|
@@ -69,13 +77,13 @@ var OpenApiToolsService = class OpenApiToolsService {
|
|
|
69
77
|
for (const param of tool.pathParams) {
|
|
70
78
|
const value = args[`path_${param}`];
|
|
71
79
|
if (value === void 0) throw new Error(`Missing required path parameter: ${param}`);
|
|
72
|
-
url = url.replace(`{${param}}`, encodeURIComponent(
|
|
80
|
+
url = url.replace(`{${param}}`, encodeURIComponent(stringifyParamValue(value)));
|
|
73
81
|
}
|
|
74
82
|
const queryParts = [];
|
|
75
83
|
for (const key of Object.keys(args)) if (key.startsWith("query_")) {
|
|
76
84
|
const paramName = key.slice(6);
|
|
77
85
|
const value = args[key];
|
|
78
|
-
queryParts.push(`${encodeURIComponent(paramName)}=${encodeURIComponent(
|
|
86
|
+
queryParts.push(`${encodeURIComponent(paramName)}=${encodeURIComponent(stringifyParamValue(value))}`);
|
|
79
87
|
}
|
|
80
88
|
if (queryParts.length > 0) url += `?${queryParts.join("&")}`;
|
|
81
89
|
const body = args.body;
|
|
@@ -203,4 +211,4 @@ var OpenApiToolsService = class OpenApiToolsService {
|
|
|
203
211
|
//#endregion
|
|
204
212
|
export { OPENAPI_TOKENS as n, OpenApiToolsService as t };
|
|
205
213
|
|
|
206
|
-
//# sourceMappingURL=openapi-tools.service-
|
|
214
|
+
//# sourceMappingURL=openapi-tools.service-VKZJlTIX.mjs.map
|