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.
Files changed (284) hide show
  1. package/CHANGELOG.md +912 -0
  2. package/README.md +96 -14
  3. package/dist/bin/cloudflare-workers-loader.mjs +18 -3
  4. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  5. package/dist/bin/quarry.mjs +73 -12
  6. package/dist/bin/quarry.mjs.map +1 -1
  7. package/dist/cache/index.d.mts +8 -9
  8. package/dist/cache/index.d.mts.map +1 -1
  9. package/dist/cache/index.mjs +56 -17
  10. package/dist/cache/index.mjs.map +1 -1
  11. package/dist/{cache.service-uElmBtdS.d.mts → cache.service-Dg0Hj0gp.d.mts} +27 -12
  12. package/dist/cache.service-Dg0Hj0gp.d.mts.map +1 -0
  13. package/dist/{command-DoBD2Cwl.d.mts → command-BXMccV_9.d.mts} +6 -3
  14. package/dist/command-BXMccV_9.d.mts.map +1 -0
  15. package/dist/{command-BvmUAPPQ.mjs → command-Bb820B6W.mjs} +36 -13
  16. package/dist/command-Bb820B6W.mjs.map +1 -0
  17. package/dist/common.schemas-BigQCwYB.mjs +89 -0
  18. package/dist/common.schemas-BigQCwYB.mjs.map +1 -0
  19. package/dist/config/index.d.mts +15 -15
  20. package/dist/config/index.d.mts.map +1 -1
  21. package/dist/config/index.mjs +4 -6
  22. package/dist/config/index.mjs.map +1 -1
  23. package/dist/{consumer-registry-D3iMTSdy.d.mts → consumer-registry-i9oCdarv.d.mts} +2 -3
  24. package/dist/consumer-registry-i9oCdarv.d.mts.map +1 -0
  25. package/dist/{container-storage-BmOJ4_Na.mjs → container-storage-DfF7SXdV.mjs} +11 -1
  26. package/dist/container-storage-DfF7SXdV.mjs.map +1 -0
  27. package/dist/{controller.decorator-YSTPQntu.mjs → controller.decorator-BB0JcwMg.mjs} +18 -9
  28. package/dist/controller.decorator-BB0JcwMg.mjs.map +1 -0
  29. package/dist/cron/index.d.mts +6 -7
  30. package/dist/cron/index.d.mts.map +1 -1
  31. package/dist/cron/index.mjs +1 -1
  32. package/dist/{cron-job-NesZRk8F.d.mts → cron-job-DAgNw4Bn.d.mts} +1 -2
  33. package/dist/cron-job-DAgNw4Bn.d.mts.map +1 -0
  34. package/dist/{cron.module-C81HTzR7.mjs → cron.module-Dnyl9sFk.mjs} +6 -7
  35. package/dist/{cron.module-C81HTzR7.mjs.map → cron.module-Dnyl9sFk.mjs.map} +1 -1
  36. package/dist/{decorate-CuAoSZvs.mjs → decorate-C79yt3Wt.mjs} +2 -2
  37. package/dist/di/index.d.mts +1 -1
  38. package/dist/di/index.mjs +2 -3
  39. package/dist/{di-D7qmrAir.mjs → di-DQ3xbjgf.mjs} +48 -6
  40. package/dist/di-DQ3xbjgf.mjs.map +1 -0
  41. package/dist/email/index.d.mts +158 -133
  42. package/dist/email/index.d.mts.map +1 -1
  43. package/dist/email/index.mjs +66 -78
  44. package/dist/email/index.mjs.map +1 -1
  45. package/dist/{en-CDZBMcc1.mjs → en-CCL84z4k.mjs} +2 -2
  46. package/dist/{en-CDZBMcc1.mjs.map → en-CCL84z4k.mjs.map} +1 -1
  47. package/dist/env-ug22bJj7.d.mts.map +1 -1
  48. package/dist/errors/index.d.mts +1 -1
  49. package/dist/errors/index.mjs +3 -3
  50. package/dist/{errors-C01O2T-n.mjs → errors-CrOXI_y5.mjs} +6 -8
  51. package/dist/errors-CrOXI_y5.mjs.map +1 -0
  52. package/dist/events/index.d.mts +8 -9
  53. package/dist/events/index.d.mts.map +1 -1
  54. package/dist/events/index.mjs +1 -1
  55. package/dist/{events-BhEQuT1X.mjs → events-GtwGEHyT.mjs} +5 -6
  56. package/dist/events-GtwGEHyT.mjs.map +1 -0
  57. package/dist/{exception-context-D-kvney-.mjs → exception-context-CdnGTQNF.mjs} +13 -7
  58. package/dist/exception-context-CdnGTQNF.mjs.map +1 -0
  59. package/dist/{gateway-context-m7kEzRa2.mjs → gateway-context-Cmc1NRS0.mjs} +7 -7
  60. package/dist/{gateway-context-m7kEzRa2.mjs.map → gateway-context-Cmc1NRS0.mjs.map} +1 -1
  61. package/dist/gateway-mode-0rUaeW3g.mjs +53 -0
  62. package/dist/gateway-mode-0rUaeW3g.mjs.map +1 -0
  63. package/dist/guards/index.d.mts +38 -20
  64. package/dist/guards/index.d.mts.map +1 -1
  65. package/dist/guards/index.mjs +2 -2
  66. package/dist/{guards-DALPXy3_.mjs → guards-DOGly9IF.mjs} +34 -11
  67. package/dist/guards-DOGly9IF.mjs.map +1 -0
  68. package/dist/hono-app-IhdqL0Fk.mjs +399 -0
  69. package/dist/hono-app-IhdqL0Fk.mjs.map +1 -0
  70. package/dist/{http-method.decorator-BljM8BDj.mjs → http-method.decorator-BDi9LTWj.mjs} +9 -8
  71. package/dist/http-method.decorator-BDi9LTWj.mjs.map +1 -0
  72. package/dist/i18n/index.d.mts +26 -137
  73. package/dist/i18n/index.d.mts.map +1 -1
  74. package/dist/i18n/index.mjs +4 -5
  75. package/dist/i18n/index.mjs.map +1 -1
  76. package/dist/i18n/messages/en/index.d.mts +1 -1
  77. package/dist/i18n/messages/en/index.mjs +1 -1
  78. package/dist/i18n/utils/index.d.mts +1 -2
  79. package/dist/i18n/utils/index.d.mts.map +1 -1
  80. package/dist/i18n/validation/index.d.mts +80 -5
  81. package/dist/i18n/validation/index.d.mts.map +1 -0
  82. package/dist/i18n/validation/index.mjs +4 -5
  83. package/dist/{i18n.module-B2DvWUPa.mjs → i18n.module-Bm3xlViA.mjs} +28 -13
  84. package/dist/i18n.module-Bm3xlViA.mjs.map +1 -0
  85. package/dist/index-0ItCjaqw.d.mts.map +1 -1
  86. package/dist/{index-CNuFQSNj.d.mts → index-3HJfhLlE.d.mts} +9 -11
  87. package/dist/index-3HJfhLlE.d.mts.map +1 -0
  88. package/dist/{index-uybm0bhQ.d.mts → index-B2WtEagR.d.mts} +1187 -234
  89. package/dist/index-B2WtEagR.d.mts.map +1 -0
  90. package/dist/index-BUt92sAE.d.mts.map +1 -1
  91. package/dist/index-CAHNCpvr.d.mts +203 -0
  92. package/dist/index-CAHNCpvr.d.mts.map +1 -0
  93. package/dist/index.d.mts +4 -4
  94. package/dist/index.mjs +2 -2
  95. package/dist/{is-command-CEPO9n8c.mjs → is-command-CSVv14wh.mjs} +2 -2
  96. package/dist/{is-command-CEPO9n8c.mjs.map → is-command-CSVv14wh.mjs.map} +1 -1
  97. package/dist/{is-seeder-Gvh_AM71.mjs → is-seeder-gLgXYbqC.mjs} +2 -2
  98. package/dist/is-seeder-gLgXYbqC.mjs.map +1 -0
  99. package/dist/{lazy-module-loader-M6YKudNL.d.mts → lazy-module-loader-C3OHoUnR.d.mts} +4 -5
  100. package/dist/lazy-module-loader-C3OHoUnR.d.mts.map +1 -0
  101. package/dist/limit-BE0HNAF6.mjs +116 -0
  102. package/dist/limit-BE0HNAF6.mjs.map +1 -0
  103. package/dist/locale-path.service-BqzDIrgy.mjs +213 -0
  104. package/dist/locale-path.service-BqzDIrgy.mjs.map +1 -0
  105. package/dist/locale-url-nZrZxqJP.mjs.map +1 -1
  106. package/dist/{locale-url.service-6bgia24_.mjs → locale-url.service-H8PH6GGh.mjs} +15 -5
  107. package/dist/{locale-url.service-6bgia24_.mjs.map → locale-url.service-H8PH6GGh.mjs.map} +1 -1
  108. package/dist/logger/index.mjs +3 -5
  109. package/dist/logger/index.mjs.map +1 -1
  110. package/dist/macroable-cvDTFZ_A.mjs.map +1 -1
  111. package/dist/metadata-Ba8Fxs1F.mjs +48 -0
  112. package/dist/metadata-Ba8Fxs1F.mjs.map +1 -0
  113. package/dist/module/index.d.mts +8 -9
  114. package/dist/module/index.d.mts.map +1 -1
  115. package/dist/module/index.mjs +3 -4
  116. package/dist/module/index.mjs.map +1 -1
  117. package/dist/{module-registry-NxX5O0Qk.mjs → module-registry-lkQPDuQe.mjs} +17 -15
  118. package/dist/module-registry-lkQPDuQe.mjs.map +1 -0
  119. package/dist/{module.decorator-CYHY6pG5.mjs → module.decorator-CYbrOlX9.mjs} +2 -2
  120. package/dist/{module.decorator-CYHY6pG5.mjs.map → module.decorator-CYbrOlX9.mjs.map} +1 -1
  121. package/dist/openapi/index.d.mts +19 -16
  122. package/dist/openapi/index.d.mts.map +1 -1
  123. package/dist/openapi/index.mjs +2 -2
  124. package/dist/{openapi-CMwuCp31.mjs → openapi-DBfBa5Qh.mjs} +41 -65
  125. package/dist/openapi-DBfBa5Qh.mjs.map +1 -0
  126. package/dist/openapi-generator-CCwiu8AU.mjs +134 -0
  127. package/dist/openapi-generator-CCwiu8AU.mjs.map +1 -0
  128. package/dist/{openapi-tools.service-BC5EC3R3.mjs → openapi-tools.service-VKZJlTIX.mjs} +12 -4
  129. package/dist/openapi-tools.service-VKZJlTIX.mjs.map +1 -0
  130. package/dist/openapi.service-Ds-R1tih.d.mts +43 -0
  131. package/dist/openapi.service-Ds-R1tih.d.mts.map +1 -0
  132. package/dist/quarry/index.d.mts +10 -11
  133. package/dist/quarry/index.d.mts.map +1 -1
  134. package/dist/quarry/index.mjs +4 -4
  135. package/dist/quarry/runner.d.mts +19 -20
  136. package/dist/quarry/runner.d.mts.map +1 -1
  137. package/dist/quarry/runner.mjs +16 -13
  138. package/dist/quarry/runner.mjs.map +1 -1
  139. package/dist/{quarry-registry-DRnV-DDa.d.mts → quarry-registry-Ci4k58FZ.d.mts} +3 -4
  140. package/dist/quarry-registry-Ci4k58FZ.d.mts.map +1 -0
  141. package/dist/{quarry.module-CcGxU2dJ.mjs → quarry.module-BssRR8f0.mjs} +20 -37
  142. package/dist/quarry.module-BssRR8f0.mjs.map +1 -0
  143. package/dist/queue/index.d.mts +2 -2
  144. package/dist/queue/index.mjs +3 -3
  145. package/dist/queue/index.mjs.map +1 -1
  146. package/dist/{queue.module-CEs4_kEM.mjs → queue.module-DnAdSbR-.mjs} +40 -41
  147. package/dist/queue.module-DnAdSbR-.mjs.map +1 -0
  148. package/dist/{r2-storage.provider-BoZmR6Ut.mjs → r2-storage.provider-wU_6api4.mjs} +45 -6
  149. package/dist/r2-storage.provider-wU_6api4.mjs.map +1 -0
  150. package/dist/{rate-limit.decorator-Djs4oYDB.mjs → rate-limit.decorator-PYQ9EYqs.mjs} +10 -5
  151. package/dist/{rate-limit.decorator-Djs4oYDB.mjs.map → rate-limit.decorator-PYQ9EYqs.mjs.map} +1 -1
  152. package/dist/rate-limiter/index.d.mts +64 -19
  153. package/dist/rate-limiter/index.d.mts.map +1 -1
  154. package/dist/rate-limiter/index.mjs +67 -83
  155. package/dist/rate-limiter/index.mjs.map +1 -1
  156. package/dist/response-cache/index.d.mts +326 -0
  157. package/dist/response-cache/index.d.mts.map +1 -0
  158. package/dist/response-cache/index.mjs +511 -0
  159. package/dist/response-cache/index.mjs.map +1 -0
  160. package/dist/response-cache.error-B4D0zy9W.mjs +24 -0
  161. package/dist/response-cache.error-B4D0zy9W.mjs.map +1 -0
  162. package/dist/response-headers-BqA57S_X.mjs +158 -0
  163. package/dist/response-headers-BqA57S_X.mjs.map +1 -0
  164. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  165. package/dist/route-cache-binding-CrLh8tB6.mjs +390 -0
  166. package/dist/route-cache-binding-CrLh8tB6.mjs.map +1 -0
  167. package/dist/route-metadata-QQhbed1i.mjs +17 -0
  168. package/dist/route-metadata-QQhbed1i.mjs.map +1 -0
  169. package/dist/{route-name-DGoBOfPg.mjs → route-name-BslSbmMP.mjs} +15 -11
  170. package/dist/route-name-BslSbmMP.mjs.map +1 -0
  171. package/dist/route-registration.service-COKBZTsn.mjs +1265 -0
  172. package/dist/route-registration.service-COKBZTsn.mjs.map +1 -0
  173. package/dist/{route-registry-BvLJisvK.mjs → route-registry-DFz9_-N6.mjs} +6 -6
  174. package/dist/{route-registry-BvLJisvK.mjs.map → route-registry-DFz9_-N6.mjs.map} +1 -1
  175. package/dist/router/index.d.mts +2 -2
  176. package/dist/router/index.mjs +16 -15
  177. package/dist/{router-resolver-sUV_jTrU.mjs → router-resolver-B-dMSMIG.mjs} +7 -3
  178. package/dist/router-resolver-B-dMSMIG.mjs.map +1 -0
  179. package/dist/{router-DwyqEXgf.mjs → router-wlsVkGkM.mjs} +5 -14
  180. package/dist/{router-DwyqEXgf.mjs.map → router-wlsVkGkM.mjs.map} +1 -1
  181. package/dist/seeder/index.d.mts +12 -13
  182. package/dist/seeder/index.d.mts.map +1 -1
  183. package/dist/seeder/index.mjs +4 -4
  184. package/dist/{seeder-BPGY5rUb.mjs → seeder-BrAk7XIj.mjs} +5 -6
  185. package/dist/{seeder-BPGY5rUb.mjs.map → seeder-BrAk7XIj.mjs.map} +1 -1
  186. package/dist/{seeder-registry-DEvCycsT.mjs → seeder-registry-Bo5BgLbC.mjs} +5 -6
  187. package/dist/{seeder-registry-DEvCycsT.mjs.map → seeder-registry-Bo5BgLbC.mjs.map} +1 -1
  188. package/dist/{seeder.module-CIwQbdN4.mjs → seeder.module-DOoVgTsK.mjs} +5 -5
  189. package/dist/{seeder.module-CIwQbdN4.mjs.map → seeder.module-DOoVgTsK.mjs.map} +1 -1
  190. package/dist/signed-url-DIU0sK_6.mjs.map +1 -1
  191. package/dist/storage/index.d.mts +63 -13
  192. package/dist/storage/index.d.mts.map +1 -1
  193. package/dist/storage/index.mjs +2 -2
  194. package/dist/storage/providers/index.d.mts +18 -16
  195. package/dist/storage/providers/index.d.mts.map +1 -1
  196. package/dist/storage/providers/index.mjs +1 -1
  197. package/dist/{storage-C30X81CS.mjs → storage-DBWVYMnn.mjs} +167 -49
  198. package/dist/storage-DBWVYMnn.mjs.map +1 -0
  199. package/dist/storage-provider.interface-CinHpjl1.d.mts +315 -0
  200. package/dist/storage-provider.interface-CinHpjl1.d.mts.map +1 -0
  201. package/dist/storage.error-Ao38Qdjx.mjs +7 -0
  202. package/dist/storage.error-Ao38Qdjx.mjs.map +1 -0
  203. package/dist/{stratal-D5j_I14G.d.mts → stratal-Bs1_E8GQ.d.mts} +13 -3
  204. package/dist/stratal-Bs1_E8GQ.d.mts.map +1 -0
  205. package/dist/{stratal-BL6FKUM_.mjs → stratal-CiwNpWy2.mjs} +56 -23
  206. package/dist/stratal-CiwNpWy2.mjs.map +1 -0
  207. package/dist/{tiered-cache.service-Dv3BhxxE.d.mts → tiered-cache.service-Bz8bbvyb.d.mts} +16 -3
  208. package/dist/tiered-cache.service-Bz8bbvyb.d.mts.map +1 -0
  209. package/dist/trailing-slash-2SctvePW.mjs.map +1 -1
  210. package/dist/types-CmV_9xBD.d.mts.map +1 -1
  211. package/dist/{uri-iwofWJ_T.mjs → uri-BrOq148R.mjs} +13 -9
  212. package/dist/{uri-iwofWJ_T.mjs.map → uri-BrOq148R.mjs.map} +1 -1
  213. package/dist/{usage-generator-DAWYasuP.mjs → usage-generator-BW7EcO-C.mjs} +3 -3
  214. package/dist/{usage-generator-DAWYasuP.mjs.map → usage-generator-BW7EcO-C.mjs.map} +1 -1
  215. package/dist/{zod-eKqqhZ5_.mjs → validation-CZ_B0aXY.mjs} +38 -17
  216. package/dist/validation-CZ_B0aXY.mjs.map +1 -0
  217. package/dist/{validation.context-CRvmrhq7.mjs → validation.context-CdbwQWIr.mjs} +2 -2
  218. package/dist/{validation.context-CRvmrhq7.mjs.map → validation.context-CdbwQWIr.mjs.map} +1 -1
  219. package/dist/{versioning.service-CCa2oYMJ.mjs → versioning.service-DnF_4Lnd.mjs} +5 -5
  220. package/dist/{versioning.service-CCa2oYMJ.mjs.map → versioning.service-DnF_4Lnd.mjs.map} +1 -1
  221. package/dist/websocket/index.d.mts +12 -13
  222. package/dist/websocket/index.d.mts.map +1 -1
  223. package/dist/websocket/index.mjs +1 -1
  224. package/dist/workers/index.d.mts +70 -7
  225. package/dist/workers/index.d.mts.map +1 -1
  226. package/dist/workers/index.mjs +72 -3
  227. package/dist/workers/index.mjs.map +1 -1
  228. package/dist/{zod-wecrEVAs.d.mts → zod-BGisqLs3.d.mts} +9 -16
  229. package/dist/zod-BGisqLs3.d.mts.map +1 -0
  230. package/package.json +33 -25
  231. package/dist/cache.service-uElmBtdS.d.mts.map +0 -1
  232. package/dist/chunk-BBjsoOtd.mjs +0 -27
  233. package/dist/command-BvmUAPPQ.mjs.map +0 -1
  234. package/dist/command-DoBD2Cwl.d.mts.map +0 -1
  235. package/dist/consumer-registry-D3iMTSdy.d.mts.map +0 -1
  236. package/dist/container-storage-BmOJ4_Na.mjs.map +0 -1
  237. package/dist/controller.decorator-YSTPQntu.mjs.map +0 -1
  238. package/dist/cron-job-NesZRk8F.d.mts.map +0 -1
  239. package/dist/di-D7qmrAir.mjs.map +0 -1
  240. package/dist/errors-C01O2T-n.mjs.map +0 -1
  241. package/dist/events-BhEQuT1X.mjs.map +0 -1
  242. package/dist/exception-context-D-kvney-.mjs.map +0 -1
  243. package/dist/guards-DALPXy3_.mjs.map +0 -1
  244. package/dist/hono-app-COAgmutc.mjs +0 -168
  245. package/dist/hono-app-COAgmutc.mjs.map +0 -1
  246. package/dist/http-method.decorator-BljM8BDj.mjs.map +0 -1
  247. package/dist/i18n.module-B2DvWUPa.mjs.map +0 -1
  248. package/dist/index-CNuFQSNj.d.mts.map +0 -1
  249. package/dist/index-DtBNIFuP.d.mts +0 -42
  250. package/dist/index-DtBNIFuP.d.mts.map +0 -1
  251. package/dist/index-HgOLNruQ.d.mts +0 -203
  252. package/dist/index-HgOLNruQ.d.mts.map +0 -1
  253. package/dist/index-uybm0bhQ.d.mts.map +0 -1
  254. package/dist/is-seeder-Gvh_AM71.mjs.map +0 -1
  255. package/dist/lazy-module-loader-M6YKudNL.d.mts.map +0 -1
  256. package/dist/locale-path.service-CH0CaxwH.mjs +0 -165
  257. package/dist/locale-path.service-CH0CaxwH.mjs.map +0 -1
  258. package/dist/metadata-DzzprcID.mjs +0 -39
  259. package/dist/metadata-DzzprcID.mjs.map +0 -1
  260. package/dist/module-registry-NxX5O0Qk.mjs.map +0 -1
  261. package/dist/openapi-CMwuCp31.mjs.map +0 -1
  262. package/dist/openapi-tools.service-BC5EC3R3.mjs.map +0 -1
  263. package/dist/openapi.service-2rvJBCEg.d.mts +0 -50
  264. package/dist/openapi.service-2rvJBCEg.d.mts.map +0 -1
  265. package/dist/quarry-registry-DRnV-DDa.d.mts.map +0 -1
  266. package/dist/quarry.module-CcGxU2dJ.mjs.map +0 -1
  267. package/dist/queue.module-CEs4_kEM.mjs.map +0 -1
  268. package/dist/r2-storage.provider-BoZmR6Ut.mjs.map +0 -1
  269. package/dist/route-name-DGoBOfPg.mjs.map +0 -1
  270. package/dist/route-registration.service-CDPQKpm4.mjs +0 -918
  271. package/dist/route-registration.service-CDPQKpm4.mjs.map +0 -1
  272. package/dist/router-resolver-sUV_jTrU.mjs.map +0 -1
  273. package/dist/storage-C30X81CS.mjs.map +0 -1
  274. package/dist/storage-provider.interface-ClUwxz4S.d.mts +0 -201
  275. package/dist/storage-provider.interface-ClUwxz4S.d.mts.map +0 -1
  276. package/dist/storage.error-BStXPmO4.mjs +0 -8
  277. package/dist/storage.error-BStXPmO4.mjs.map +0 -1
  278. package/dist/stratal-BL6FKUM_.mjs.map +0 -1
  279. package/dist/stratal-D5j_I14G.d.mts.map +0 -1
  280. package/dist/tiered-cache.service-Dv3BhxxE.d.mts.map +0 -1
  281. package/dist/validation-CpOjviyT.mjs +0 -49
  282. package/dist/validation-CpOjviyT.mjs.map +0 -1
  283. package/dist/zod-eKqqhZ5_.mjs.map +0 -1
  284. package/dist/zod-wecrEVAs.d.mts.map +0 -1
@@ -0,0 +1,213 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { C as ROUTER_TOKENS, l as Singleton, n as CONTAINER_TOKEN, p as inject } from "./di-DQ3xbjgf.mjs";
3
+ import { n as __decorateParam, t as __decorate } from "./decorate-C79yt3Wt.mjs";
4
+ import { l as ROUTER_CONTEXT_KEYS } from "./exception-context-CdnGTQNF.mjs";
5
+ import { t as I18N_TOKENS } from "./i18n.tokens-CZ_v8oyS.mjs";
6
+ import { languageDetector } from "hono/language";
7
+ //#region src/i18n/i18n.options.ts
8
+ /** The cookie (and query-string key) the `locale` is read from / written to. */
9
+ const LOCALE_COOKIE = "locale";
10
+ /** Apply defaults to a single {@link LanguageDetectionOptions}. */
11
+ function resolveDetectionOptions(detection) {
12
+ return {
13
+ enabled: detection ? detection.enabled !== false : true,
14
+ strategy: detection && "strategy" in detection ? detection.strategy ?? "cookie" : "cookie",
15
+ prefixDefaultLocale: detection && "prefixDefaultLocale" in detection && detection.prefixDefaultLocale !== void 0 ? detection.prefixDefaultLocale : false,
16
+ cookieOptions: detection && "cookieOptions" in detection ? detection.cookieOptions : void 0
17
+ };
18
+ }
19
+ /**
20
+ * Resolve the detection options for a given request path, evaluating the
21
+ * per-path resolver when `detection` is a function.
22
+ */
23
+ function resolveDetectionForPath(detection, path) {
24
+ return resolveDetectionOptions(typeof detection === "function" ? detection(path) : detection);
25
+ }
26
+ /**
27
+ * Resolve I18n options with defaults. `detection` reflects the application root
28
+ * (`/`); use {@link resolveDetectionForPath} for per-path detail.
29
+ */
30
+ function resolveI18nOptions(options) {
31
+ return {
32
+ defaultLocale: options?.defaultLocale ?? "en",
33
+ fallbackLocale: options?.fallbackLocale ?? "en",
34
+ locales: options?.locales ?? ["en"],
35
+ detection: resolveDetectionForPath(options?.detection, "/")
36
+ };
37
+ }
38
+ /**
39
+ * Build Hono languageDetector options for a resolved detection + locale config.
40
+ *
41
+ * Cookie strategy persists the detected locale to the `locale` cookie
42
+ * (`caches: ['cookie']`) using the resolved `cookieOptions` — so a per-path
43
+ * cookie area scopes the write (e.g. `{ path: '/admin' }`) instead of writing
44
+ * at the default `Path=/` and leaking across the app. Other strategies don't
45
+ * write any cookie.
46
+ */
47
+ function buildDetectorOptions(detection, locales, defaultLocale) {
48
+ const detectorOptions = {
49
+ order: [detection.strategy],
50
+ fallbackLanguage: defaultLocale,
51
+ supportedLanguages: locales,
52
+ lookupCookie: LOCALE_COOKIE,
53
+ lookupQueryString: LOCALE_COOKIE,
54
+ lookupFromPathIndex: 0,
55
+ ignoreCase: true
56
+ };
57
+ if (detection.strategy === "cookie") {
58
+ detectorOptions.caches = ["cookie"];
59
+ if (detection.cookieOptions) detectorOptions.cookieOptions = detection.cookieOptions;
60
+ } else detectorOptions.caches = false;
61
+ return detectorOptions;
62
+ }
63
+ //#endregion
64
+ //#region src/router/services/locale-path.service.ts
65
+ var locale_path_service_exports = /* @__PURE__ */ __exportAll({ LocalePathService: () => LocalePathService });
66
+ let LocalePathService = class LocalePathService {
67
+ honoApp;
68
+ _locales;
69
+ _defaultLocale;
70
+ _detection;
71
+ /** Detection + config resolved at the application root (`/`) — the primary mode. */
72
+ _primary;
73
+ _primaryConfig;
74
+ constructor(container, honoApp) {
75
+ this.honoApp = honoApp;
76
+ const i18nOptions = container.isRegistered(I18N_TOKENS.Options) ? container.resolve(I18N_TOKENS.Options) : void 0;
77
+ this._locales = i18nOptions?.locales ?? ["en"];
78
+ this._defaultLocale = i18nOptions?.defaultLocale ?? "en";
79
+ this._detection = i18nOptions?.detection;
80
+ this._primary = resolveDetectionForPath(this._detection, "/");
81
+ this._primaryConfig = this.buildConfig(this._primary);
82
+ if (typeof this._detection === "function" || this._primary.enabled) this.setupLanguageDetection();
83
+ if (this._primaryConfig?.defaultLocale && this._primary.prefixDefaultLocale === "redirect") this.setupDefaultLocaleRedirect(this._primaryConfig.defaultLocale);
84
+ }
85
+ /** Whether the app uses path-based locale URLs (resolved at the root). */
86
+ get enabled() {
87
+ return this._primaryConfig !== null;
88
+ }
89
+ /** The primary (root) locale path config, or null if the root isn't path-localized. */
90
+ get localePathConfig() {
91
+ return this._primaryConfig;
92
+ }
93
+ /** The primary (root) prefixDefaultLocale setting (false, true, or 'redirect'). */
94
+ get prefixDefaultLocale() {
95
+ return this._primary.prefixDefaultLocale;
96
+ }
97
+ /** Resolve the detection options for a given request path. */
98
+ detectionFor(path) {
99
+ return resolveDetectionForPath(this._detection, path);
100
+ }
101
+ /**
102
+ * Whether a pathname is path-localized — served with a `/:locale` segment.
103
+ * `false` for paths resolving to a non-`path` strategy (localized by cookie)
104
+ * or with detection disabled. Redirect middleware should skip non-localized
105
+ * paths so it never prepends a locale segment.
106
+ */
107
+ isPathLocalized(pathname) {
108
+ return this.buildConfig(this.detectionFor(pathname)) !== null;
109
+ }
110
+ /**
111
+ * Expand a path into primary + locale-prefixed variants.
112
+ *
113
+ * @param path - The base route path to expand
114
+ * @returns Array of resolved paths with locale metadata
115
+ */
116
+ resolve(path) {
117
+ const config = this.buildConfig(this.detectionFor(path));
118
+ if (!config) return [{
119
+ path,
120
+ isLocaleVariant: false
121
+ }];
122
+ const constraint = `{(?:${(config.defaultLocale === null ? config.allLocales : config.prefixedLocales).join("|")})}`;
123
+ const suffix = path === "/" ? "" : path;
124
+ if (config.defaultLocale === null) return [{
125
+ path: `/:locale${constraint}${suffix}`,
126
+ isLocaleVariant: true
127
+ }];
128
+ const result = [{
129
+ path,
130
+ isLocaleVariant: false
131
+ }];
132
+ if (config.prefixedLocales.length > 0) result.push({
133
+ path: `/:locale${constraint}${suffix}`,
134
+ isLocaleVariant: true
135
+ });
136
+ return result;
137
+ }
138
+ /** Build the locale path config for a resolved detection, or null if not path-localized. */
139
+ buildConfig(detection) {
140
+ if (!detection.enabled || detection.strategy !== "path") return null;
141
+ const allLocales = this._locales;
142
+ const defaultLocale = this._defaultLocale;
143
+ return detection.prefixDefaultLocale === true ? {
144
+ allLocales,
145
+ prefixedLocales: allLocales,
146
+ defaultLocale: null
147
+ } : {
148
+ allLocales,
149
+ prefixedLocales: allLocales.filter((l) => l !== defaultLocale),
150
+ defaultLocale
151
+ };
152
+ }
153
+ /**
154
+ * Apply Hono's languageDetector and bridge the detected language to Stratal's
155
+ * LOCALE context variable. With a per-path resolver, the detector is chosen
156
+ * per request (cached by strategy); with a static config, one detector is
157
+ * built up front.
158
+ */
159
+ setupLanguageDetection() {
160
+ const buildDetector = (detection) => languageDetector(buildDetectorOptions(detection, this._locales, this._defaultLocale));
161
+ let detector;
162
+ if (typeof this._detection === "function") {
163
+ const resolver = this._detection;
164
+ const cache = /* @__PURE__ */ new Map();
165
+ const detectorFor = (detection) => {
166
+ const key = `${detection.strategy}:${JSON.stringify(detection.cookieOptions ?? {})}`;
167
+ let d = cache.get(key);
168
+ if (!d) {
169
+ d = buildDetector(detection);
170
+ cache.set(key, d);
171
+ }
172
+ return d;
173
+ };
174
+ detector = (c, next) => {
175
+ const detection = resolveDetectionForPath(resolver, new URL(c.req.url).pathname);
176
+ if (!detection.enabled) return next();
177
+ return detectorFor(detection)(c, next);
178
+ };
179
+ } else detector = buildDetector(this._primary);
180
+ this.honoApp.use("*", detector);
181
+ this.honoApp.use("*", async (c, next) => {
182
+ const language = c.get("language");
183
+ if (language) c.set(ROUTER_CONTEXT_KEYS.LOCALE, language);
184
+ await next();
185
+ });
186
+ }
187
+ /**
188
+ * Redirect requests that include the default locale prefix to the unprefixed path.
189
+ * For example, `/en/users` → 301 redirect to `/users`.
190
+ *
191
+ * Only active when the root `prefixDefaultLocale` is `'redirect'`.
192
+ */
193
+ setupDefaultLocaleRedirect(defaultLocale) {
194
+ const prefix = `/${defaultLocale}`;
195
+ this.honoApp.use("*", async (c, next) => {
196
+ const path = new URL(c.req.url).pathname;
197
+ if (path === prefix || path.startsWith(`${prefix}/`)) {
198
+ const stripped = path.slice(prefix.length) || "/";
199
+ return c.redirect(stripped, 301);
200
+ }
201
+ await next();
202
+ });
203
+ }
204
+ };
205
+ LocalePathService = __decorate([
206
+ Singleton(),
207
+ __decorateParam(0, inject(CONTAINER_TOKEN)),
208
+ __decorateParam(1, inject(ROUTER_TOKENS.HonoApp))
209
+ ], LocalePathService);
210
+ //#endregion
211
+ export { resolveDetectionForPath as a, buildDetectorOptions as i, locale_path_service_exports as n, resolveI18nOptions as o, LOCALE_COOKIE as r, LocalePathService as t };
212
+
213
+ //# sourceMappingURL=locale-path.service-BqzDIrgy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locale-path.service-BqzDIrgy.mjs","names":[],"sources":["../src/i18n/i18n.options.ts","../src/router/services/locale-path.service.ts"],"sourcesContent":["/**\n * I18n Module Options\n *\n * Configuration options for the I18n dynamic module.\n * Use with I18nModule.forRoot() / forRootAsync() to configure locale settings.\n * Use I18nModule.registerMessages() to add translations.\n */\n\nimport type { DetectorOptions } from 'hono/language';\n\n/**\n * Detection strategy for locale resolution\n *\n * - `'cookie'` — reads from the `locale` cookie (default)\n * - `'header'` — reads from the `Accept-Language` header\n * - `'querystring'` — reads from the `?locale=` query parameter\n * - `'path'` — reads from the first URL path segment (e.g., `/en/api/users`)\n */\nexport type DetectionStrategy = 'cookie' | 'header' | 'querystring' | 'path'\n\n/** The cookie (and query-string key) the `locale` is read from / written to. */\nexport const LOCALE_COOKIE = 'locale'\n\ninterface BaseDetection {\n /** Set to false to disable language detection entirely. @default true */\n enabled?: boolean\n}\n\n/**\n * Language detection options (discriminated by strategy)\n *\n * @example Cookie detection (default)\n * ```typescript\n * { strategy: 'cookie' }\n * ```\n *\n * @example Header detection\n * ```typescript\n * { strategy: 'header' }\n * ```\n *\n * @example Path detection\n * ```typescript\n * { strategy: 'path' }\n * ```\n *\n * @example Disable detection\n * ```typescript\n * { enabled: false }\n * ```\n */\nexport type LanguageDetectionOptions =\n | (BaseDetection & { strategy?: 'cookie'; cookieOptions?: DetectorOptions['cookieOptions'] })\n | (BaseDetection & { strategy: 'header' })\n | (BaseDetection & { strategy: 'querystring' })\n | (BaseDetection & {\n strategy: 'path'\n /**\n * Controls whether the default locale gets a URL path prefix.\n *\n * - `false` (default) — The default locale has no prefix (`/users`), other locales\n * are prefixed (`/fr/users`). Requests to the prefixed default locale (`/en/users`) return 404.\n * - `'redirect'` — Same as `false`, but requests to the prefixed default locale\n * (`/en/users`) are 301-redirected to the unprefixed path (`/users`).\n * - `true` — All locales are prefixed (`/en/users`, `/fr/users`).\n *\n * @default false\n */\n prefixDefaultLocale?: false | true | 'redirect'\n })\n | { enabled: false }\n\n/**\n * A per-path detection resolver.\n *\n * Returns the detection options for a given request path. It **must be a pure\n * function of the path**: route `/:locale` variant expansion runs it at boot,\n * once per registered route pattern, where no request exists — so the decision\n * (e.g. \"`/admin` is cookie-localized, everything else is path-localized\") has\n * to be derivable from the path alone, and must agree at boot and per request.\n *\n * @example\n * ```typescript\n * detection: (path) =>\n * path.startsWith('/admin')\n * ? { strategy: 'cookie', cookieOptions: { path: '/admin' } }\n * : { strategy: 'path' }\n * ```\n */\nexport type DetectionResolver = (path: string) => LanguageDetectionOptions\n\n/** Detection config: a single static option set, or a per-path resolver. */\nexport type DetectionConfig = LanguageDetectionOptions | DetectionResolver\n\n/**\n * Options for configuring the I18n module\n *\n * @example\n * ```typescript\n * I18nModule.forRoot({\n * defaultLocale: 'en',\n * fallbackLocale: 'en',\n * locales: ['en', 'fr'],\n * detection: { strategy: 'header' },\n * })\n * ```\n */\nexport interface I18nModuleOptions {\n /**\n * Default locale for the application\n * @default 'en'\n */\n defaultLocale?: string\n\n /**\n * Fallback locale when translation is missing\n * @default 'en'\n */\n fallbackLocale?: string\n\n /**\n * List of supported locales\n * Request locales not in this list will fall back to defaultLocale\n */\n locales?: string[]\n\n /**\n * Language detection configuration. Either a single option set applied to\n * every request, or a {@link DetectionResolver} — a pure function of the\n * request path — so different areas can use different strategies (e.g. a\n * path-localized public site with a cookie-localized `/admin` panel).\n */\n detection?: DetectionConfig\n}\n\n/**\n * Detection options resolved for a single path (all defaults applied).\n */\nexport interface ResolvedDetection {\n enabled: boolean\n strategy: DetectionStrategy\n /** Only meaningful when `strategy` is `'path'`. */\n prefixDefaultLocale: false | true | 'redirect'\n /**\n * Cookie attributes for the persisted `locale` cookie under cookie strategy\n * (e.g. `{ path: '/admin' }` to scope a panel's locale so it isn't written at\n * the default `Path=/`).\n */\n cookieOptions?: DetectorOptions['cookieOptions']\n}\n\n/**\n * Resolved options with all defaults applied\n * Used internally by I18n services\n */\nexport interface ResolvedI18nOptions {\n defaultLocale: string\n fallbackLocale: string\n locales: string[]\n /**\n * Detection resolved at the application root (`/`) — the app's primary mode.\n * For per-path detail (when `detection` is a resolver) use\n * {@link resolveDetectionForPath}.\n */\n detection: ResolvedDetection\n}\n\n/** Apply defaults to a single {@link LanguageDetectionOptions}. */\nfunction resolveDetectionOptions(detection: LanguageDetectionOptions | undefined): ResolvedDetection {\n const enabled = detection ? detection.enabled !== false : true\n const strategy: DetectionStrategy = (detection && 'strategy' in detection) ? detection.strategy ?? 'cookie' : 'cookie'\n const prefixDefaultLocale: false | true | 'redirect' =\n (detection && 'prefixDefaultLocale' in detection && detection.prefixDefaultLocale !== undefined)\n ? detection.prefixDefaultLocale\n : false\n const cookieOptions = (detection && 'cookieOptions' in detection) ? detection.cookieOptions : undefined\n return { enabled, strategy, prefixDefaultLocale, cookieOptions }\n}\n\n/**\n * Resolve the detection options for a given request path, evaluating the\n * per-path resolver when `detection` is a function.\n */\nexport function resolveDetectionForPath(detection: DetectionConfig | undefined, path: string): ResolvedDetection {\n return resolveDetectionOptions(typeof detection === 'function' ? detection(path) : detection)\n}\n\n/**\n * Resolve I18n options with defaults. `detection` reflects the application root\n * (`/`); use {@link resolveDetectionForPath} for per-path detail.\n */\nexport function resolveI18nOptions(options?: I18nModuleOptions): ResolvedI18nOptions {\n return {\n defaultLocale: options?.defaultLocale ?? 'en',\n fallbackLocale: options?.fallbackLocale ?? 'en',\n locales: options?.locales ?? ['en'],\n detection: resolveDetectionForPath(options?.detection, '/'),\n }\n}\n\n/**\n * Build Hono languageDetector options for a resolved detection + locale config.\n *\n * Cookie strategy persists the detected locale to the `locale` cookie\n * (`caches: ['cookie']`) using the resolved `cookieOptions` — so a per-path\n * cookie area scopes the write (e.g. `{ path: '/admin' }`) instead of writing\n * at the default `Path=/` and leaking across the app. Other strategies don't\n * write any cookie.\n */\nexport function buildDetectorOptions(\n detection: ResolvedDetection,\n locales: string[],\n defaultLocale: string,\n): Partial<DetectorOptions> {\n const detectorOptions: Partial<DetectorOptions> = {\n order: [detection.strategy],\n fallbackLanguage: defaultLocale,\n supportedLanguages: locales,\n lookupCookie: LOCALE_COOKIE,\n lookupQueryString: LOCALE_COOKIE,\n lookupFromPathIndex: 0,\n ignoreCase: true,\n }\n\n if (detection.strategy === 'cookie') {\n detectorOptions.caches = ['cookie']\n if (detection.cookieOptions) {\n detectorOptions.cookieOptions = detection.cookieOptions\n }\n } else {\n detectorOptions.caches = false\n }\n\n return detectorOptions\n}\n","import type { Context, MiddlewareHandler } from 'hono'\nimport { languageDetector } from 'hono/language'\nimport { inject } from '../../di'\nimport type { Container } from '../../di/container'\nimport { Singleton } from '../../di/decorators'\nimport { CONTAINER_TOKEN } from '../../di/tokens'\nimport {\n buildDetectorOptions,\n type DetectionConfig,\n type I18nModuleOptions,\n type ResolvedDetection,\n resolveDetectionForPath,\n} from '../../i18n/i18n.options'\nimport { I18N_TOKENS } from '../../i18n/i18n.tokens'\nimport { ROUTER_CONTEXT_KEYS } from '../constants'\nimport type { HonoApp } from '../hono-app'\nimport { ROUTER_TOKENS } from '../router.tokens'\nimport type { LocalePathConfig, RouterEnv } from '../types'\n\n/**\n * A resolved path with locale variant metadata.\n */\nexport interface ResolvedPath {\n /** The fully resolved path (may include /:locale prefix) */\n path: string\n /** Whether this path is a locale-prefixed variant */\n isLocaleVariant: boolean\n}\n\n/**\n * Resolves locale path variants for route paths.\n *\n * Detection can be static or a per-path resolver (see {@link DetectionConfig}),\n * so this evaluates it **per path**: which routes get a `/:locale` variant\n * (at registration) and which detector runs (per request). A path resolving to\n * a non-`path` strategy gets no variant and is localized out-of-band (cookie).\n *\n * Also applies language detection and default-locale redirect middleware to\n * HonoApp when resolved from the container. Registered as a singleton.\n */\n@Singleton()\nexport class LocalePathService {\n private readonly _locales: string[]\n private readonly _defaultLocale: string\n private readonly _detection: DetectionConfig | undefined\n /** Detection + config resolved at the application root (`/`) — the primary mode. */\n private readonly _primary: ResolvedDetection\n private readonly _primaryConfig: LocalePathConfig | null\n\n constructor(\n @inject(CONTAINER_TOKEN) container: Container,\n @inject(ROUTER_TOKENS.HonoApp) private readonly honoApp: HonoApp,\n ) {\n const i18nOptions = container.isRegistered(I18N_TOKENS.Options)\n ? container.resolve<I18nModuleOptions>(I18N_TOKENS.Options)\n : undefined\n\n this._locales = i18nOptions?.locales ?? ['en']\n this._defaultLocale = i18nOptions?.defaultLocale ?? 'en'\n this._detection = i18nOptions?.detection\n this._primary = resolveDetectionForPath(this._detection, '/')\n this._primaryConfig = this.buildConfig(this._primary)\n\n // Install detection: always when `detection` is a resolver (it decides\n // per request), otherwise only when the static config keeps it enabled.\n if (typeof this._detection === 'function' || this._primary.enabled) {\n this.setupLanguageDetection()\n }\n if (this._primaryConfig?.defaultLocale && this._primary.prefixDefaultLocale === 'redirect') {\n this.setupDefaultLocaleRedirect(this._primaryConfig.defaultLocale)\n }\n }\n\n /** Whether the app uses path-based locale URLs (resolved at the root). */\n get enabled(): boolean {\n return this._primaryConfig !== null\n }\n\n /** The primary (root) locale path config, or null if the root isn't path-localized. */\n get localePathConfig(): LocalePathConfig | null {\n return this._primaryConfig\n }\n\n /** The primary (root) prefixDefaultLocale setting (false, true, or 'redirect'). */\n get prefixDefaultLocale(): false | true | 'redirect' {\n return this._primary.prefixDefaultLocale\n }\n\n /** Resolve the detection options for a given request path. */\n detectionFor(path: string): ResolvedDetection {\n return resolveDetectionForPath(this._detection, path)\n }\n\n /**\n * Whether a pathname is path-localized — served with a `/:locale` segment.\n * `false` for paths resolving to a non-`path` strategy (localized by cookie)\n * or with detection disabled. Redirect middleware should skip non-localized\n * paths so it never prepends a locale segment.\n */\n isPathLocalized(pathname: string): boolean {\n return this.buildConfig(this.detectionFor(pathname)) !== null\n }\n\n /**\n * Expand a path into primary + locale-prefixed variants.\n *\n * @param path - The base route path to expand\n * @returns Array of resolved paths with locale metadata\n */\n resolve(path: string): ResolvedPath[] {\n const config = this.buildConfig(this.detectionFor(path))\n if (!config) {\n // Not path-localized → no `/:locale` variant; served at the bare path.\n return [{ path, isLocaleVariant: false }]\n }\n\n const constraintLocales = config.defaultLocale === null ? config.allLocales : config.prefixedLocales\n // Wrap the alternation in a non-capturing group. An unparenthesised\n // `:locale{en|fr}` leaks the `|` boundary in Hono's RegExpRouter, so the\n // single-segment locale param greedily swallows multi-segment paths\n // (`/en/foo/bar` matches `/:locale{en|fr}` instead of `/:locale{en|fr}/:rest`).\n // Grouping keeps the alternation a single token; a lone locale (no `|`) is\n // an identical no-op group, so it's grouped too for a uniform pattern.\n const constraint = `{(?:${constraintLocales.join('|')})}`\n const suffix = path === '/' ? '' : path\n\n // All locales prefixed (prefixDefaultLocale: true)\n if (config.defaultLocale === null) {\n return [{ path: `/:locale${constraint}${suffix}`, isLocaleVariant: true }]\n }\n\n // Default locale unprefixed, other locales prefixed\n const result: ResolvedPath[] = [{ path, isLocaleVariant: false }]\n\n // Only add /:locale route when there are non-default locales to match\n // (z.enum requires at least one value)\n if (config.prefixedLocales.length > 0) {\n result.push({ path: `/:locale${constraint}${suffix}`, isLocaleVariant: true })\n }\n\n return result\n }\n\n /** Build the locale path config for a resolved detection, or null if not path-localized. */\n private buildConfig(detection: ResolvedDetection): LocalePathConfig | null {\n if (!detection.enabled || detection.strategy !== 'path') return null\n const allLocales = this._locales\n const defaultLocale = this._defaultLocale\n return detection.prefixDefaultLocale === true\n ? { allLocales, prefixedLocales: allLocales, defaultLocale: null }\n : { allLocales, prefixedLocales: allLocales.filter(l => l !== defaultLocale), defaultLocale }\n }\n\n /**\n * Apply Hono's languageDetector and bridge the detected language to Stratal's\n * LOCALE context variable. With a per-path resolver, the detector is chosen\n * per request (cached by strategy); with a static config, one detector is\n * built up front.\n */\n private setupLanguageDetection(): void {\n const buildDetector = (detection: ResolvedDetection): MiddlewareHandler<RouterEnv> =>\n languageDetector(buildDetectorOptions(detection, this._locales, this._defaultLocale)) as MiddlewareHandler<RouterEnv>\n\n let detector: MiddlewareHandler<RouterEnv>\n if (typeof this._detection === 'function') {\n const resolver = this._detection\n // Memoize by strategy + cookie write options (which can both differ per\n // path) — a resolver typically returns only a handful of distinct configs.\n const cache = new Map<string, MiddlewareHandler<RouterEnv>>()\n const detectorFor = (detection: ResolvedDetection): MiddlewareHandler<RouterEnv> => {\n const key = `${detection.strategy}:${JSON.stringify(detection.cookieOptions ?? {})}`\n let d = cache.get(key)\n if (!d) {\n d = buildDetector(detection)\n cache.set(key, d)\n }\n return d\n }\n detector = (c, next) => {\n const detection = resolveDetectionForPath(resolver, new URL(c.req.url).pathname)\n if (!detection.enabled) return next()\n return detectorFor(detection)(c, next)\n }\n } else {\n detector = buildDetector(this._primary)\n }\n\n this.honoApp.use('*', detector)\n\n // Bridge: sync Hono's 'language' variable to Stratal's LOCALE context key\n this.honoApp.use('*', async (c: Context<RouterEnv>, next: () => Promise<void>) => {\n const language = c.get('language')\n if (language) {\n c.set(ROUTER_CONTEXT_KEYS.LOCALE, language)\n }\n await next()\n })\n }\n\n /**\n * Redirect requests that include the default locale prefix to the unprefixed path.\n * For example, `/en/users` → 301 redirect to `/users`.\n *\n * Only active when the root `prefixDefaultLocale` is `'redirect'`.\n */\n private setupDefaultLocaleRedirect(defaultLocale: string): void {\n const prefix = `/${defaultLocale}`\n this.honoApp.use('*', async (c: Context<RouterEnv>, next: () => Promise<void>) => {\n const path = new URL(c.req.url).pathname\n if (path === prefix || path.startsWith(`${prefix}/`)) {\n const stripped = path.slice(prefix.length) || '/'\n return c.redirect(stripped, 301)\n }\n await next()\n })\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"locale-url-nZrZxqJP.mjs","names":[],"sources":["../src/router/locale-url.ts"],"sourcesContent":["import type { LocaleUrlConfig } from './types'\n\n/**\n * Pure helpers for locale-aware URL path manipulation.\n *\n * Each function takes config explicitly so it can run anywhere (no DI, no request\n * context). For ergonomic DI-driven access, see {@link LocaleUrlService} which\n * binds these to the resolved {@link LocalePathService} config.\n */\n\n/**\n * Whether a locale should be URL-prefixed under the given config.\n *\n * - No config → always prefix (caller didn't opt into locale-aware URLs).\n * - `prefixDefaultLocale: true` → every locale, including the default, is prefixed.\n * - Otherwise → only non-default locales are prefixed.\n */\nexport function shouldPrefixLocale(locale: string, config: LocaleUrlConfig | undefined): boolean {\n if (!config) return true\n if (config.prefixDefaultLocale === true) return true\n return locale !== config.defaultLocale\n}\n\n/**\n * Prepend `/{locale}` to a pathname, respecting `prefixDefaultLocale`.\n * Returns the pathname unchanged when the locale shouldn't be prefixed.\n */\nexport function applyLocalePrefix(pathname: string, locale: string, config: LocaleUrlConfig | undefined): string {\n if (!shouldPrefixLocale(locale, config)) return pathname\n return pathname === '/' ? `/${locale}` : `/${locale}${pathname}`\n}\n\n/**\n * Strip a known-locale prefix from the start of a pathname.\n * Returns the pathname unchanged if the first segment isn't in `knownLocales`.\n */\nexport function stripLocalePrefix(pathname: string, knownLocales: readonly string[]): string {\n const segments = pathname.split('/').filter(Boolean)\n if (segments.length > 0 && knownLocales.includes(segments[0])) {\n const rest = segments.slice(1).join('/')\n return rest ? `/${rest}` : '/'\n }\n return pathname\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,SAAgB,mBAAmB,QAAgB,QAA8C;CAC/F,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,OAAO,wBAAwB,MAAM,OAAO;CAChD,OAAO,WAAW,OAAO;AAC3B;;;;;AAMA,SAAgB,kBAAkB,UAAkB,QAAgB,QAA6C;CAC/G,IAAI,CAAC,mBAAmB,QAAQ,MAAM,GAAG,OAAO;CAChD,OAAO,aAAa,MAAM,IAAI,WAAW,IAAI,SAAS;AACxD;;;;;AAMA,SAAgB,kBAAkB,UAAkB,cAAyC;CAC3F,MAAM,WAAW,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;CACnD,IAAI,SAAS,SAAS,KAAK,aAAa,SAAS,SAAS,EAAE,GAAG;EAC7D,MAAM,OAAO,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;EACvC,OAAO,OAAO,IAAI,SAAS;CAC7B;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"locale-url-nZrZxqJP.mjs","names":[],"sources":["../src/router/locale-url.ts"],"sourcesContent":["import type { LocaleUrlConfig } from './types'\n\n/**\n * Pure helpers for locale-aware URL path manipulation.\n *\n * Each function takes config explicitly so it can run anywhere (no DI, no request\n * context). For ergonomic DI-driven access, see {@link LocaleUrlService} which\n * binds these to the resolved {@link LocalePathService} config.\n */\n\n/**\n * Whether a locale should be URL-prefixed under the given config.\n *\n * - No config → always prefix (caller didn't opt into locale-aware URLs).\n * - `prefixDefaultLocale: true` → every locale, including the default, is prefixed.\n * - Otherwise → only non-default locales are prefixed.\n */\nexport function shouldPrefixLocale(locale: string, config: LocaleUrlConfig | undefined): boolean {\n if (!config) return true\n if (config.prefixDefaultLocale === true) return true\n return locale !== config.defaultLocale\n}\n\n/**\n * Prepend `/{locale}` to a pathname, respecting `prefixDefaultLocale`.\n * Returns the pathname unchanged when the locale shouldn't be prefixed.\n */\nexport function applyLocalePrefix(pathname: string, locale: string, config: LocaleUrlConfig | undefined): string {\n if (!shouldPrefixLocale(locale, config)) return pathname\n return pathname === '/' ? `/${locale}` : `/${locale}${pathname}`\n}\n\n/**\n * Strip a known-locale prefix from the start of a pathname.\n * Returns the pathname unchanged if the first segment isn't in `knownLocales`.\n */\nexport function stripLocalePrefix(pathname: string, knownLocales: readonly string[]): string {\n const segments = pathname.split('/').filter(Boolean)\n if (segments.length > 0 && knownLocales.includes(segments[0])) {\n const rest = segments.slice(1).join('/')\n return rest ? `/${rest}` : '/'\n }\n return pathname\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,SAAgB,mBAAmB,QAAgB,QAA8C;CAC/F,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,OAAO,wBAAwB,MAAM,OAAO;CAChD,OAAO,WAAW,OAAO;AAC3B;;;;;AAMA,SAAgB,kBAAkB,UAAkB,QAAgB,QAA6C;CAC/G,IAAI,CAAC,mBAAmB,QAAQ,MAAM,GAAG,OAAO;CAChD,OAAO,aAAa,MAAM,IAAI,WAAW,IAAI,SAAS;AACxD;;;;;AAMA,SAAgB,kBAAkB,UAAkB,cAAyC;CAC3F,MAAM,WAAW,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CACnD,IAAI,SAAS,SAAS,KAAK,aAAa,SAAS,SAAS,EAAE,GAAG;EAC7D,MAAM,OAAO,SAAS,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG;EACvC,OAAO,OAAO,IAAI,SAAS;CAC7B;CACA,OAAO;AACT"}
@@ -1,6 +1,6 @@
1
- import { t as __exportAll } from "./chunk-BBjsoOtd.mjs";
2
- import { b as ROUTER_TOKENS, l as Singleton, p as inject } from "./di-D7qmrAir.mjs";
3
- import { n as __decorateParam, t as __decorate } from "./decorate-CuAoSZvs.mjs";
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { C as ROUTER_TOKENS, l as Singleton, p as inject } from "./di-DQ3xbjgf.mjs";
3
+ import { n as __decorateParam, t as __decorate } from "./decorate-C79yt3Wt.mjs";
4
4
  import { n as shouldPrefixLocale, r as stripLocalePrefix, t as applyLocalePrefix } from "./locale-url-nZrZxqJP.mjs";
5
5
  //#region src/router/services/locale-url.service.ts
6
6
  var locale_url_service_exports = /* @__PURE__ */ __exportAll({ LocaleUrlService: () => LocaleUrlService });
@@ -23,7 +23,17 @@ let LocaleUrlService = class LocaleUrlService {
23
23
  }
24
24
  /** Strip a known-locale prefix from the start of a pathname. */
25
25
  stripPrefix(pathname) {
26
- return stripLocalePrefix(pathname, this.localePath.localePathConfig?.allLocales ?? []);
26
+ const known = this.localePath.localePathConfig?.allLocales ?? [];
27
+ return stripLocalePrefix(pathname, known);
28
+ }
29
+ /**
30
+ * Whether a pathname is path-localized — served with a `/:locale` segment.
31
+ * `false` for paths localized out-of-band (cookie strategy) or with detection
32
+ * disabled. Redirect middleware should skip non-localized paths so it never
33
+ * prepends a locale segment to a path that isn't path-localized.
34
+ */
35
+ isPathLocalized(pathname) {
36
+ return this.localePath.isPathLocalized(pathname);
27
37
  }
28
38
  toUrlConfig() {
29
39
  const config = this.localePath.localePathConfig;
@@ -38,4 +48,4 @@ LocaleUrlService = __decorate([Singleton(), __decorateParam(0, inject(ROUTER_TOK
38
48
  //#endregion
39
49
  export { locale_url_service_exports as n, LocaleUrlService as t };
40
50
 
41
- //# sourceMappingURL=locale-url.service-6bgia24_.mjs.map
51
+ //# sourceMappingURL=locale-url.service-H8PH6GGh.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"locale-url.service-6bgia24_.mjs","names":[],"sources":["../src/router/services/locale-url.service.ts"],"sourcesContent":["import { inject } from '../../di'\nimport { Singleton } from '../../di/decorators'\nimport { applyLocalePrefix, shouldPrefixLocale, stripLocalePrefix } from '../locale-url'\nimport { ROUTER_TOKENS } from '../router.tokens'\nimport type { LocaleUrlConfig } from '../types'\nimport { type LocalePathService } from './locale-path.service'\n\n/**\n * DI-friendly wrapper around the pure locale-url helpers.\n *\n * Binds {@link LocalePathService} config so consumers can call `applyPrefix`,\n * `stripPrefix`, and `shouldPrefix` without passing config every time.\n *\n * Useful for canonical URL generation, sitemap builders, redirect middleware,\n * hreflang link emission, and anywhere else a request handler needs to compute\n * locale-aware path variants.\n */\n@Singleton()\nexport class LocaleUrlService {\n constructor(\n @inject(ROUTER_TOKENS.LocalePathService) private readonly localePath: LocalePathService,\n ) { }\n\n /** Whether path-based locale detection is enabled — i.e., locales have URL-distinct path variants. */\n get pathEnabled(): boolean {\n return this.localePath.enabled\n }\n\n /** Whether the given locale should get a URL prefix under the current config. */\n shouldPrefix(locale: string): boolean {\n return shouldPrefixLocale(locale, this.toUrlConfig())\n }\n\n /** Prepend `/{locale}` to a pathname, respecting `prefixDefaultLocale`. */\n applyPrefix(pathname: string, locale: string): string {\n return applyLocalePrefix(pathname, locale, this.toUrlConfig())\n }\n\n /** Strip a known-locale prefix from the start of a pathname. */\n stripPrefix(pathname: string): string {\n const known = this.localePath.localePathConfig?.allLocales ?? []\n return stripLocalePrefix(pathname, known)\n }\n\n private toUrlConfig(): LocaleUrlConfig | undefined {\n const config = this.localePath.localePathConfig\n if (!config) return undefined\n return { defaultLocale: config.defaultLocale, prefixDefaultLocale: this.localePath.prefixDefaultLocale }\n }\n}\n"],"mappings":";;;;;;AAkBO,IAAA,mBAAA,MAAM,iBAAiB;CAEgC;CAD5D,YACE,YACA;EAD0D,KAAA,aAAA;CACxD;;CAGJ,IAAI,cAAuB;EACzB,OAAO,KAAK,WAAW;CACzB;;CAGA,aAAa,QAAyB;EACpC,OAAO,mBAAmB,QAAQ,KAAK,YAAY,CAAC;CACtD;;CAGA,YAAY,UAAkB,QAAwB;EACpD,OAAO,kBAAkB,UAAU,QAAQ,KAAK,YAAY,CAAC;CAC/D;;CAGA,YAAY,UAA0B;EAEpC,OAAO,kBAAkB,UADX,KAAK,WAAW,kBAAkB,cAAc,CAAC,CACvB;CAC1C;CAEA,cAAmD;EACjD,MAAM,SAAS,KAAK,WAAW;EAC/B,IAAI,CAAC,QAAQ,OAAO,KAAA;EACpB,OAAO;GAAE,eAAe,OAAO;GAAe,qBAAqB,KAAK,WAAW;EAAoB;CACzG;AACF;+BAhCC,UAAU,GAAA,gBAAA,GAGN,OAAO,cAAc,iBAAiB,CAAA,CAAA,GAAA,gBAAA"}
1
+ {"version":3,"file":"locale-url.service-H8PH6GGh.mjs","names":[],"sources":["../src/router/services/locale-url.service.ts"],"sourcesContent":["import { inject } from '../../di'\nimport { Singleton } from '../../di/decorators'\nimport { applyLocalePrefix, shouldPrefixLocale, stripLocalePrefix } from '../locale-url'\nimport { ROUTER_TOKENS } from '../router.tokens'\nimport type { LocaleUrlConfig } from '../types'\nimport { type LocalePathService } from './locale-path.service'\n\n/**\n * DI-friendly wrapper around the pure locale-url helpers.\n *\n * Binds {@link LocalePathService} config so consumers can call `applyPrefix`,\n * `stripPrefix`, and `shouldPrefix` without passing config every time.\n *\n * Useful for canonical URL generation, sitemap builders, redirect middleware,\n * hreflang link emission, and anywhere else a request handler needs to compute\n * locale-aware path variants.\n */\n@Singleton()\nexport class LocaleUrlService {\n constructor(\n @inject(ROUTER_TOKENS.LocalePathService) private readonly localePath: LocalePathService,\n ) { }\n\n /** Whether path-based locale detection is enabled — i.e., locales have URL-distinct path variants. */\n get pathEnabled(): boolean {\n return this.localePath.enabled\n }\n\n /** Whether the given locale should get a URL prefix under the current config. */\n shouldPrefix(locale: string): boolean {\n return shouldPrefixLocale(locale, this.toUrlConfig())\n }\n\n /** Prepend `/{locale}` to a pathname, respecting `prefixDefaultLocale`. */\n applyPrefix(pathname: string, locale: string): string {\n return applyLocalePrefix(pathname, locale, this.toUrlConfig())\n }\n\n /** Strip a known-locale prefix from the start of a pathname. */\n stripPrefix(pathname: string): string {\n const known = this.localePath.localePathConfig?.allLocales ?? []\n return stripLocalePrefix(pathname, known)\n }\n\n /**\n * Whether a pathname is path-localized — served with a `/:locale` segment.\n * `false` for paths localized out-of-band (cookie strategy) or with detection\n * disabled. Redirect middleware should skip non-localized paths so it never\n * prepends a locale segment to a path that isn't path-localized.\n */\n isPathLocalized(pathname: string): boolean {\n return this.localePath.isPathLocalized(pathname)\n }\n\n private toUrlConfig(): LocaleUrlConfig | undefined {\n const config = this.localePath.localePathConfig\n if (!config) return undefined\n return { defaultLocale: config.defaultLocale, prefixDefaultLocale: this.localePath.prefixDefaultLocale }\n }\n}\n"],"mappings":";;;;;;AAkBO,IAAM,mBAAN,MAAM,iBAAiB;CAEgC;CAD5D,YACE,YACA;EAD0D,KAAA,aAAA;CACxD;;CAGJ,IAAI,cAAuB;EACzB,OAAO,KAAK,WAAW;CACzB;;CAGA,aAAa,QAAyB;EACpC,OAAO,mBAAmB,QAAQ,KAAK,YAAY,CAAC;CACtD;;CAGA,YAAY,UAAkB,QAAwB;EACpD,OAAO,kBAAkB,UAAU,QAAQ,KAAK,YAAY,CAAC;CAC/D;;CAGA,YAAY,UAA0B;EACpC,MAAM,QAAQ,KAAK,WAAW,kBAAkB,cAAc,CAAC;EAC/D,OAAO,kBAAkB,UAAU,KAAK;CAC1C;;;;;;;CAQA,gBAAgB,UAA2B;EACzC,OAAO,KAAK,WAAW,gBAAgB,QAAQ;CACjD;CAEA,cAAmD;EACjD,MAAM,SAAS,KAAK,WAAW;EAC/B,IAAI,CAAC,QAAQ,OAAO,KAAA;EACpB,OAAO;GAAE,eAAe,OAAO;GAAe,qBAAqB,KAAK,WAAW;EAAoB;CACzG;AACF;AA1CC,mBAAA,WAAA,CAAA,UAAU,GAAA,gBAAA,GAGN,OAAO,cAAc,iBAAiB,CAAA,CAAA,GAAA,gBAAA"}
@@ -1,5 +1,5 @@
1
- import { p as inject, u as Transient } from "../di-D7qmrAir.mjs";
2
- import { n as __decorateParam, t as __decorate } from "../decorate-CuAoSZvs.mjs";
1
+ import { p as inject, u as Transient } from "../di-DQ3xbjgf.mjs";
2
+ import { n as __decorateParam, t as __decorate } from "../decorate-C79yt3Wt.mjs";
3
3
  //#region src/logger/logger.tokens.ts
4
4
  const LOGGER_TOKENS = {
5
5
  LoggerService: Symbol.for("stratal:logger:service"),
@@ -78,9 +78,7 @@ let LoggerService = class LoggerService {
78
78
  case "warn":
79
79
  console.warn(formatted);
80
80
  break;
81
- case "error":
82
- console.error(formatted);
83
- break;
81
+ case "error": console.error(formatted);
84
82
  }
85
83
  }
86
84
  enrichContext(userContext) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/logger/logger.tokens.ts","../../src/logger/contracts/log-level.ts","../../src/logger/services/logger.service.ts","../../src/logger/formatters/json-formatter.ts","../../src/logger/formatters/pretty-formatter.ts"],"sourcesContent":["export const LOGGER_TOKENS = {\n LoggerService: Symbol.for('stratal:logger:service'),\n Formatter: Symbol.for('stratal:logger:formatter'),\n LogLevelOptions: Symbol.for('stratal:logger:log:level:options'),\n} as const\n","/**\n * Log severity levels\n * Ordered from least to most severe\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Map log levels to numeric priorities (for filtering)\n * Higher numbers = more severe = higher priority\n */\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n [LogLevel.DEBUG]: 0,\n [LogLevel.INFO]: 1,\n [LogLevel.WARN]: 2,\n [LogLevel.ERROR]: 3,\n}\n","import { inject } from '../../di'\nimport { Transient } from '../../di/decorators'\nimport type { InternalLogContext, LogContext, LogEntry } from '../contracts'\nimport { LOG_LEVEL_PRIORITY, LogLevel } from '../contracts/log-level'\nimport type { ILogFormatter } from '../formatters/formatter.interface'\nimport { LOGGER_TOKENS } from '../logger.tokens'\n\n@Transient()\nexport class LoggerService {\n constructor(\n @inject(LOGGER_TOKENS.LogLevelOptions)\n private readonly logLevel: LogLevel,\n\n @inject(LOGGER_TOKENS.Formatter)\n private readonly formatter: ILogFormatter,\n ) { }\n\n debug(message: string, context?: LogContext): void {\n this.log(LogLevel.DEBUG, message, context)\n }\n\n info(message: string, context?: LogContext): void {\n this.log(LogLevel.INFO, message, context)\n }\n\n warn(message: string, context?: LogContext): void {\n this.log(LogLevel.WARN, message, context)\n }\n\n error(message: string, error: Error, context?: LogContext): void\n error(message: string, context?: LogContext): void\n error(message: string, errorOrContext?: Error | LogContext, maybeContext?: LogContext): void {\n let context: LogContext | undefined\n let error: Error | undefined\n\n if (errorOrContext instanceof Error) {\n error = errorOrContext\n context = maybeContext\n } else {\n context = errorOrContext\n }\n\n this.log(LogLevel.ERROR, message, context, error)\n }\n\n private log(\n level: LogLevel,\n message: string,\n userContext?: LogContext,\n error?: Error\n ): void {\n if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.logLevel]) {\n return\n }\n\n const entry: LogEntry = {\n level,\n message,\n context: this.enrichContext(userContext ?? {}),\n error: error ? this.serializeError(error) : undefined,\n }\n\n const formatted = this.formatter.format(entry)\n this.writeToConsole(level, formatted)\n }\n\n private writeToConsole(level: LogLevel, formatted: string): void {\n switch (level) {\n case LogLevel.DEBUG:\n console.debug(formatted)\n break\n case LogLevel.INFO:\n console.info(formatted)\n break\n case LogLevel.WARN:\n console.warn(formatted)\n break\n case LogLevel.ERROR:\n console.error(formatted)\n break\n }\n }\n\n private enrichContext(userContext: LogContext): InternalLogContext {\n return {\n ...userContext,\n timestamp: Date.now(),\n }\n }\n\n private serializeError(error: Error): { message: string; stack?: string; name?: string } {\n return {\n message: error.message,\n stack: error.stack,\n name: error.name,\n }\n }\n}\n","import type { LogEntry } from '../contracts'\nimport type { ILogFormatter } from './formatter.interface'\n\n/**\n * JSON Formatter\n *\n * Produces structured JSON logs for production environments.\n * Optimized for log aggregation systems (Cloudflare Analytics, Datadog, etc.)\n *\n * Output format:\n * {\n * \"level\": \"info\",\n * \"message\": \"User logged in\",\n * \"timestamp\": 1234567890,\n * \"userId\": \"user_456\",\n * \"error\": { \"message\": \"...\", \"stack\": \"...\" }\n * }\n */\nexport class JsonFormatter implements ILogFormatter {\n format(entry: LogEntry): string {\n const output = {\n level: entry.level,\n message: entry.message,\n ...entry.context,\n ...(entry.error && { error: entry.error }),\n }\n\n return JSON.stringify(output)\n }\n}\n","import type { LogEntry } from '../contracts'\nimport { LogLevel } from '../contracts'\nimport type { ILogFormatter } from './formatter.interface'\n\n/**\n * Pretty Formatter\n *\n * Human-readable colored output for development environments.\n * Uses ANSI color codes for terminal output.\n *\n * Output format:\n * [2024-01-15 10:30:45] INFO: User logged in\n * userId: user_456\n */\nexport class PrettyFormatter implements ILogFormatter {\n private readonly colors: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: '\\x1b[36m', // Cyan\n [LogLevel.INFO]: '\\x1b[32m', // Green\n [LogLevel.WARN]: '\\x1b[33m', // Yellow\n [LogLevel.ERROR]: '\\x1b[31m', // Red\n }\n\n private readonly reset = '\\x1b[0m'\n\n format(entry: LogEntry): string {\n const color = this.colors[entry.level]\n const timestamp = new Date(entry.context.timestamp).toISOString()\n const levelStr = entry.level.toUpperCase().padEnd(5)\n\n let output = `${color}[${timestamp}] ${levelStr}${this.reset}: ${entry.message}`\n\n // Add context (exclude timestamp)\n const { timestamp: _, ...contextWithoutTimestamp } = entry.context\n const contextEntries = Object.entries(contextWithoutTimestamp)\n\n if (contextEntries.length > 0) {\n output += '\\n'\n contextEntries.forEach(([key, value]) => {\n output += ` ${key}: ${JSON.stringify(value)}\\n`\n })\n }\n\n // Add error stack if present\n if (entry.error?.stack) {\n output += `\\n${entry.error.stack}\\n`\n }\n\n return output.trimEnd()\n }\n}\n"],"mappings":";;;AAAA,MAAa,gBAAgB;CAC3B,eAAe,OAAO,IAAI,wBAAwB;CAClD,WAAW,OAAO,IAAI,0BAA0B;CAChD,iBAAiB,OAAO,IAAI,kCAAkC;AAChE;;;;;;;ACAA,IAAY,WAAL,yBAAA,UAAA;CACL,SAAA,WAAA;CACA,SAAA,UAAA;CACA,SAAA,UAAA;CACA,SAAA,WAAA;;AACF,EAAA,CAAA,CAAA;;;;;AAMA,MAAa,qBAA+C;YACxC;WACD;WACA;YACC;AACpB;;;ACZO,IAAA,gBAAA,MAAM,cAAc;CAGN;CAGA;CALnB,YACE,UAGA,WAEA;EAJiB,KAAA,WAAA;EAGA,KAAA,YAAA;CACf;CAEJ,MAAM,SAAiB,SAA4B;EACjD,KAAK,IAAA,SAAoB,SAAS,OAAO;CAC3C;CAEA,KAAK,SAAiB,SAA4B;EAChD,KAAK,IAAA,QAAmB,SAAS,OAAO;CAC1C;CAEA,KAAK,SAAiB,SAA4B;EAChD,KAAK,IAAA,QAAmB,SAAS,OAAO;CAC1C;CAIA,MAAM,SAAiB,gBAAqC,cAAiC;EAC3F,IAAI;EACJ,IAAI;EAEJ,IAAI,0BAA0B,OAAO;GACnC,QAAQ;GACR,UAAU;EACZ,OACE,UAAU;EAGZ,KAAK,IAAA,SAAoB,SAAS,SAAS,KAAK;CAClD;CAEA,IACE,OACA,SACA,aACA,OACM;EACN,IAAI,mBAAmB,SAAS,mBAAmB,KAAK,WACtD;EAGF,MAAM,QAAkB;GACtB;GACA;GACA,SAAS,KAAK,cAAc,eAAe,CAAC,CAAC;GAC7C,OAAO,QAAQ,KAAK,eAAe,KAAK,IAAI,KAAA;EAC9C;EAEA,MAAM,YAAY,KAAK,UAAU,OAAO,KAAK;EAC7C,KAAK,eAAe,OAAO,SAAS;CACtC;CAEA,eAAuB,OAAiB,WAAyB;EAC/D,QAAQ,OAAR;GACE,KAAA;IACE,QAAQ,MAAM,SAAS;IACvB;GACF,KAAA;IACE,QAAQ,KAAK,SAAS;IACtB;GACF,KAAA;IACE,QAAQ,KAAK,SAAS;IACtB;GACF,KAAA;IACE,QAAQ,MAAM,SAAS;IACvB;EACJ;CACF;CAEA,cAAsB,aAA6C;EACjE,OAAO;GACL,GAAG;GACH,WAAW,KAAK,IAAI;EACtB;CACF;CAEA,eAAuB,OAAkE;EACvF,OAAO;GACL,SAAS,MAAM;GACf,OAAO,MAAM;GACb,MAAM,MAAM;EACd;CACF;AACF;;CA1FC,UAAU;oBAGN,OAAO,cAAc,eAAe,CAAA;oBAGpC,OAAO,cAAc,SAAS,CAAA;;;;;;;;;;;;;;;;;;;ACKnC,IAAa,gBAAb,MAAoD;CAClD,OAAO,OAAyB;EAC9B,MAAM,SAAS;GACb,OAAO,MAAM;GACb,SAAS,MAAM;GACf,GAAG,MAAM;GACT,GAAI,MAAM,SAAS,EAAE,OAAO,MAAM,MAAM;EAC1C;EAEA,OAAO,KAAK,UAAU,MAAM;CAC9B;AACF;;;;;;;;;;;;;ACfA,IAAa,kBAAb,MAAsD;CACpD,SAAoD;aAChC;YACD;YACA;aACC;CACpB;CAEA,QAAyB;CAEzB,OAAO,OAAyB;EAK9B,IAAI,SAAS,GAJC,KAAK,OAAO,MAAM,OAIV,GAHJ,IAAI,KAAK,MAAM,QAAQ,SAAS,EAAE,YAGnB,EAAE,IAFlB,MAAM,MAAM,YAAY,EAAE,OAAO,CAEJ,IAAI,KAAK,MAAM,IAAI,MAAM;EAGvE,MAAM,EAAE,WAAW,GAAG,GAAG,4BAA4B,MAAM;EAC3D,MAAM,iBAAiB,OAAO,QAAQ,uBAAuB;EAE7D,IAAI,eAAe,SAAS,GAAG;GAC7B,UAAU;GACV,eAAe,SAAS,CAAC,KAAK,WAAW;IACvC,UAAU,KAAK,IAAI,IAAI,KAAK,UAAU,KAAK,EAAE;GAC/C,CAAC;EACH;EAGA,IAAI,MAAM,OAAO,OACf,UAAU,KAAK,MAAM,MAAM,MAAM;EAGnC,OAAO,OAAO,QAAQ;CACxB;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/logger/logger.tokens.ts","../../src/logger/contracts/log-level.ts","../../src/logger/services/logger.service.ts","../../src/logger/formatters/json-formatter.ts","../../src/logger/formatters/pretty-formatter.ts"],"sourcesContent":["export const LOGGER_TOKENS = {\n LoggerService: Symbol.for('stratal:logger:service'),\n Formatter: Symbol.for('stratal:logger:formatter'),\n LogLevelOptions: Symbol.for('stratal:logger:log:level:options'),\n} as const\n","/**\n * Log severity levels\n * Ordered from least to most severe\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Map log levels to numeric priorities (for filtering)\n * Higher numbers = more severe = higher priority\n */\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n [LogLevel.DEBUG]: 0,\n [LogLevel.INFO]: 1,\n [LogLevel.WARN]: 2,\n [LogLevel.ERROR]: 3,\n}\n","import { inject } from '../../di'\nimport { Transient } from '../../di/decorators'\nimport type { InternalLogContext, LogContext, LogEntry } from '../contracts'\nimport { LOG_LEVEL_PRIORITY, LogLevel } from '../contracts/log-level'\nimport type { ILogFormatter } from '../formatters/formatter.interface'\nimport { LOGGER_TOKENS } from '../logger.tokens'\n\n@Transient()\nexport class LoggerService {\n constructor(\n @inject(LOGGER_TOKENS.LogLevelOptions)\n private readonly logLevel: LogLevel,\n\n @inject(LOGGER_TOKENS.Formatter)\n private readonly formatter: ILogFormatter,\n ) { }\n\n debug(message: string, context?: LogContext): void {\n this.log(LogLevel.DEBUG, message, context)\n }\n\n info(message: string, context?: LogContext): void {\n this.log(LogLevel.INFO, message, context)\n }\n\n warn(message: string, context?: LogContext): void {\n this.log(LogLevel.WARN, message, context)\n }\n\n error(message: string, error: Error, context?: LogContext): void\n error(message: string, context?: LogContext): void\n error(message: string, errorOrContext?: Error | LogContext, maybeContext?: LogContext): void {\n let context: LogContext | undefined\n let error: Error | undefined\n\n if (errorOrContext instanceof Error) {\n error = errorOrContext\n context = maybeContext\n } else {\n context = errorOrContext\n }\n\n this.log(LogLevel.ERROR, message, context, error)\n }\n\n private log(\n level: LogLevel,\n message: string,\n userContext?: LogContext,\n error?: Error\n ): void {\n if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.logLevel]) {\n return\n }\n\n const entry: LogEntry = {\n level,\n message,\n context: this.enrichContext(userContext ?? {}),\n error: error ? this.serializeError(error) : undefined,\n }\n\n const formatted = this.formatter.format(entry)\n this.writeToConsole(level, formatted)\n }\n\n private writeToConsole(level: LogLevel, formatted: string): void {\n switch (level) {\n case LogLevel.DEBUG:\n console.debug(formatted)\n break\n case LogLevel.INFO:\n console.info(formatted)\n break\n case LogLevel.WARN:\n console.warn(formatted)\n break\n case LogLevel.ERROR:\n console.error(formatted)\n break\n }\n }\n\n private enrichContext(userContext: LogContext): InternalLogContext {\n return {\n ...userContext,\n timestamp: Date.now(),\n }\n }\n\n private serializeError(error: Error): { message: string; stack?: string; name?: string } {\n return {\n message: error.message,\n stack: error.stack,\n name: error.name,\n }\n }\n}\n","import type { LogEntry } from '../contracts'\nimport type { ILogFormatter } from './formatter.interface'\n\n/**\n * JSON Formatter\n *\n * Produces structured JSON logs for production environments.\n * Optimized for log aggregation systems (Cloudflare Analytics, Datadog, etc.)\n *\n * Output format:\n * {\n * \"level\": \"info\",\n * \"message\": \"User logged in\",\n * \"timestamp\": 1234567890,\n * \"userId\": \"user_456\",\n * \"error\": { \"message\": \"...\", \"stack\": \"...\" }\n * }\n */\nexport class JsonFormatter implements ILogFormatter {\n format(entry: LogEntry): string {\n const output = {\n level: entry.level,\n message: entry.message,\n ...entry.context,\n ...(entry.error && { error: entry.error }),\n }\n\n return JSON.stringify(output)\n }\n}\n","import type { LogEntry } from '../contracts'\nimport { LogLevel } from '../contracts'\nimport type { ILogFormatter } from './formatter.interface'\n\n/**\n * Pretty Formatter\n *\n * Human-readable colored output for development environments.\n * Uses ANSI color codes for terminal output.\n *\n * Output format:\n * [2024-01-15 10:30:45] INFO: User logged in\n * userId: user_456\n */\nexport class PrettyFormatter implements ILogFormatter {\n private readonly colors: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: '\\x1b[36m', // Cyan\n [LogLevel.INFO]: '\\x1b[32m', // Green\n [LogLevel.WARN]: '\\x1b[33m', // Yellow\n [LogLevel.ERROR]: '\\x1b[31m', // Red\n }\n\n private readonly reset = '\\x1b[0m'\n\n format(entry: LogEntry): string {\n const color = this.colors[entry.level]\n const timestamp = new Date(entry.context.timestamp).toISOString()\n const levelStr = entry.level.toUpperCase().padEnd(5)\n\n let output = `${color}[${timestamp}] ${levelStr}${this.reset}: ${entry.message}`\n\n // Add context (exclude timestamp)\n const { timestamp: _, ...contextWithoutTimestamp } = entry.context\n const contextEntries = Object.entries(contextWithoutTimestamp)\n\n if (contextEntries.length > 0) {\n output += '\\n'\n contextEntries.forEach(([key, value]) => {\n output += ` ${key}: ${JSON.stringify(value)}\\n`\n })\n }\n\n // Add error stack if present\n if (entry.error?.stack) {\n output += `\\n${entry.error.stack}\\n`\n }\n\n return output.trimEnd()\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"macroable-cvDTFZ_A.mjs","names":[],"sources":["../src/macroable/macroable.ts"],"sourcesContent":["import type { MacroFunction } from './types'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Constructor = abstract new (...args: any[]) => any\n\n/**\n * Abstract base class for adding macros, instance properties, and getters\n * to classes at runtime.\n *\n * Inspired by [@poppinss/macroable](https://github.com/poppinss/macroable)\n * and Laravel's Macroable trait.\n *\n * @example\n * ```typescript\n * // Register a macro\n * RouterContext.macro('flash', function (this: RouterContext, key: string, value: unknown) {\n * const session = this.getContainer().resolve(SessionService)\n * session.flash(key, value)\n * })\n *\n * // Register a getter\n * RouterContext.getter('requestId', function (this: RouterContext) {\n * return this.header('x-request-id') ?? crypto.randomUUID()\n * }, true)\n *\n * // Register a per-instance property (safe for destructuring)\n * RouterContext.instanceProperty('getUser', function (this: RouterContext) {\n * return this.getContainer().resolve(AuthContext).user\n * })\n * ```\n */\nexport abstract class Macroable {\n [key: string | symbol]: unknown\n /**\n * Per-instance properties. Each entry is applied and bound\n * to `this` inside the constructor so destructuring stays safe.\n */\n protected static instanceMacros = new Set<{ key: string | symbol; value: unknown }>()\n\n /**\n * Names registered via macro() — used by hasMacro() and flushMacros().\n */\n protected static macroNames = new Set<string | symbol>()\n\n /**\n * Names registered via getter() — used by hasMacro() and flushMacros().\n */\n protected static getterNames = new Set<string | symbol>()\n\n /**\n * Original prototype values saved before macro() overrides them.\n * Used by flushMacros() to restore native methods.\n */\n private static _originals = new Map<string | symbol, { existed: boolean; value: unknown }>()\n\n // ── Macros (prototype-level) ──────────────────────────────\n\n /**\n * Register a macro on the class prototype.\n * Can override existing methods.\n *\n * When the name matches an existing property, the value type is auto-derived.\n *\n * @param name - Method or property name\n * @param value - Function or value to assign\n */\n static macro<T extends Constructor, K extends keyof InstanceType<T>>(\n this: T,\n name: K,\n value: InstanceType<T>[K],\n ): void\n static macro(name: string | symbol, value: unknown): void\n static macro(name: string | symbol, value: unknown): void {\n if (!Object.prototype.hasOwnProperty.call(this, 'macroNames')) {\n this.macroNames = new Set(this.macroNames)\n }\n if (!Object.prototype.hasOwnProperty.call(this, '_originals')) {\n this._originals = new Map(this._originals)\n }\n\n // Save the original value before first override so flushMacros() can restore it\n if (!this._originals.has(name)) {\n const existed = Object.prototype.hasOwnProperty.call(this.prototype, name)\n this._originals.set(name, {\n existed,\n value: existed ? (this.prototype as Record<string | symbol, unknown>)[name] : undefined,\n })\n }\n\n this.macroNames.add(name);\n (this.prototype as Record<string | symbol, unknown>)[name] = value\n }\n\n // ── Instance properties (bound per-instance) ─────────────\n\n /**\n * Register a per-instance property that is bound to `this`\n * in the constructor. Safe for destructuring.\n *\n * When the name matches an existing property, the value type is auto-derived.\n *\n * @param name - Property name\n * @param value - Function (will be bound) or value\n */\n static instanceProperty<T extends Constructor, K extends keyof InstanceType<T>>(\n this: T,\n name: K,\n value: InstanceType<T>[K],\n ): void\n static instanceProperty(name: string | symbol, value: unknown): void\n static instanceProperty(name: string | symbol, value: unknown): void {\n if (!Object.prototype.hasOwnProperty.call(this, 'instanceMacros')) {\n this.instanceMacros = new Set(this.instanceMacros)\n }\n this.instanceMacros.add({ key: name, value })\n }\n\n // ── Getters (Object.defineProperty on prototype) ──────────\n\n /**\n * Register a computed getter on the class prototype.\n *\n * @param name - Property name\n * @param accumulator - Function that computes the value (called with instance as `this`)\n * @param singleton - If true, cache the value after first access\n */\n static getter<T extends Constructor, K extends keyof InstanceType<T>>(\n this: T,\n name: K,\n accumulator: (this: InstanceType<T>) => InstanceType<T>[K],\n singleton?: boolean,\n ): void\n static getter(name: string | symbol, accumulator: MacroFunction, singleton?: boolean): void\n static getter(\n name: string | symbol,\n accumulator: MacroFunction,\n singleton = false,\n ): void {\n if (!Object.prototype.hasOwnProperty.call(this, 'getterNames')) {\n this.getterNames = new Set(this.getterNames)\n }\n this.getterNames.add(name)\n\n Object.defineProperty(this.prototype, name, {\n get(): unknown {\n const value: unknown = accumulator.call(this)\n if (singleton) {\n Object.defineProperty(this, name, {\n value,\n configurable: false,\n enumerable: false,\n writable: false,\n })\n }\n return value\n },\n configurable: true,\n enumerable: false,\n })\n }\n\n // ── Introspection ─────────────────────────────────────────\n\n /**\n * Check if a macro, instance property, or getter is registered.\n *\n * @param name - Name to check\n */\n static hasMacro(name: string | symbol): boolean {\n return (\n this.macroNames.has(name) ||\n [...this.instanceMacros].some((m) => m.key === name) ||\n this.getterNames.has(name)\n )\n }\n\n // ── Cleanup ───────────────────────────────────────────────\n\n /**\n * Remove all macros, instance properties, and getters\n * registered on this class. Does not affect parent classes.\n */\n static flushMacros(): void {\n const proto = this.prototype as Record<string | symbol, unknown>\n\n // Restore original prototype values or delete if they didn't exist before\n for (const name of this.macroNames) {\n const original = this._originals.get(name)\n if (original?.existed) {\n proto[name] = original.value\n } else {\n Reflect.deleteProperty(proto, name)\n }\n }\n for (const name of this.getterNames) {\n Reflect.deleteProperty(proto, name)\n }\n\n if (Object.prototype.hasOwnProperty.call(this, 'macroNames')) {\n this.macroNames.clear()\n }\n if (Object.prototype.hasOwnProperty.call(this, 'instanceMacros')) {\n this.instanceMacros.clear()\n }\n if (Object.prototype.hasOwnProperty.call(this, 'getterNames')) {\n this.getterNames.clear()\n }\n if (Object.prototype.hasOwnProperty.call(this, '_originals')) {\n this._originals.clear()\n }\n }\n\n // ── Constructor (applies instance properties) ─────────────\n\n constructor() {\n const Constructor = this.constructor as typeof Macroable\n const self = this as Record<string | symbol, unknown>\n Constructor.instanceMacros.forEach(({ key, value }) => {\n if (typeof value === 'function') {\n self[key] = value.bind(this)\n } else {\n self[key] = value\n }\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAsB,YAAtB,MAAgC;;;;;CAM9B,OAAiB,iCAAiB,IAAI,IAA8C;;;;CAKpF,OAAiB,6BAAa,IAAI,IAAqB;;;;CAKvD,OAAiB,8BAAc,IAAI,IAAqB;;;;;CAMxD,OAAe,6BAAa,IAAI,IAA2D;CAmB3F,OAAO,MAAM,MAAuB,OAAsB;EACxD,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GAC1D,KAAK,aAAa,IAAI,IAAI,KAAK,UAAU;EAE3C,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GAC1D,KAAK,aAAa,IAAI,IAAI,KAAK,UAAU;EAI3C,IAAI,CAAC,KAAK,WAAW,IAAI,IAAI,GAAG;GAC9B,MAAM,UAAU,OAAO,UAAU,eAAe,KAAK,KAAK,WAAW,IAAI;GACzE,KAAK,WAAW,IAAI,MAAM;IACxB;IACA,OAAO,UAAW,KAAK,UAA+C,QAAQ,KAAA;GAChF,CAAC;EACH;EAEA,KAAK,WAAW,IAAI,IAAI;EACxB,KAAM,UAA+C,QAAQ;CAC/D;CAmBA,OAAO,iBAAiB,MAAuB,OAAsB;EACnE,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,gBAAgB,GAC9D,KAAK,iBAAiB,IAAI,IAAI,KAAK,cAAc;EAEnD,KAAK,eAAe,IAAI;GAAE,KAAK;GAAM;EAAM,CAAC;CAC9C;CAkBA,OAAO,OACL,MACA,aACA,YAAY,OACN;EACN,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,aAAa,GAC3D,KAAK,cAAc,IAAI,IAAI,KAAK,WAAW;EAE7C,KAAK,YAAY,IAAI,IAAI;EAEzB,OAAO,eAAe,KAAK,WAAW,MAAM;GAC1C,MAAe;IACb,MAAM,QAAiB,YAAY,KAAK,IAAI;IAC5C,IAAI,WACF,OAAO,eAAe,MAAM,MAAM;KAChC;KACA,cAAc;KACd,YAAY;KACZ,UAAU;IACZ,CAAC;IAEH,OAAO;GACT;GACA,cAAc;GACd,YAAY;EACd,CAAC;CACH;;;;;;CASA,OAAO,SAAS,MAAgC;EAC9C,OACE,KAAK,WAAW,IAAI,IAAI,KACxB,CAAC,GAAG,KAAK,cAAc,EAAE,MAAM,MAAM,EAAE,QAAQ,IAAI,KACnD,KAAK,YAAY,IAAI,IAAI;CAE7B;;;;;CAQA,OAAO,cAAoB;EACzB,MAAM,QAAQ,KAAK;EAGnB,KAAK,MAAM,QAAQ,KAAK,YAAY;GAClC,MAAM,WAAW,KAAK,WAAW,IAAI,IAAI;GACzC,IAAI,UAAU,SACZ,MAAM,QAAQ,SAAS;QAEvB,QAAQ,eAAe,OAAO,IAAI;EAEtC;EACA,KAAK,MAAM,QAAQ,KAAK,aACtB,QAAQ,eAAe,OAAO,IAAI;EAGpC,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GACzD,KAAK,WAAW,MAAM;EAExB,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,gBAAgB,GAC7D,KAAK,eAAe,MAAM;EAE5B,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,aAAa,GAC1D,KAAK,YAAY,MAAM;EAEzB,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GACzD,KAAK,WAAW,MAAM;CAE1B;CAIA,cAAc;EACZ,MAAM,cAAc,KAAK;EACzB,MAAM,OAAO;EACb,YAAY,eAAe,SAAS,EAAE,KAAK,YAAY;GACrD,IAAI,OAAO,UAAU,YACnB,KAAK,OAAO,MAAM,KAAK,IAAI;QAE3B,KAAK,OAAO;EAEhB,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"macroable-cvDTFZ_A.mjs","names":[],"sources":["../src/macroable/macroable.ts"],"sourcesContent":["import type { MacroFunction } from './types'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Constructor = abstract new (...args: any[]) => any\n\n/**\n * Abstract base class for adding macros, instance properties, and getters\n * to classes at runtime.\n *\n * Inspired by [@poppinss/macroable](https://github.com/poppinss/macroable)\n * and Laravel's Macroable trait.\n *\n * @example\n * ```typescript\n * // Register a macro\n * RouterContext.macro('flash', function (this: RouterContext, key: string, value: unknown) {\n * const session = this.getContainer().resolve(SessionService)\n * session.flash(key, value)\n * })\n *\n * // Register a getter\n * RouterContext.getter('requestId', function (this: RouterContext) {\n * return this.header('x-request-id') ?? crypto.randomUUID()\n * }, true)\n *\n * // Register a per-instance property (safe for destructuring)\n * RouterContext.instanceProperty('getUser', function (this: RouterContext) {\n * return this.getContainer().resolve(AuthContext).user\n * })\n * ```\n */\nexport abstract class Macroable {\n [key: string | symbol]: unknown\n /**\n * Per-instance properties. Each entry is applied and bound\n * to `this` inside the constructor so destructuring stays safe.\n */\n protected static instanceMacros = new Set<{ key: string | symbol; value: unknown }>()\n\n /**\n * Names registered via macro() — used by hasMacro() and flushMacros().\n */\n protected static macroNames = new Set<string | symbol>()\n\n /**\n * Names registered via getter() — used by hasMacro() and flushMacros().\n */\n protected static getterNames = new Set<string | symbol>()\n\n /**\n * Original prototype values saved before macro() overrides them.\n * Used by flushMacros() to restore native methods.\n */\n private static _originals = new Map<string | symbol, { existed: boolean; value: unknown }>()\n\n // ── Macros (prototype-level) ──────────────────────────────\n\n /**\n * Register a macro on the class prototype.\n * Can override existing methods.\n *\n * When the name matches an existing property, the value type is auto-derived.\n *\n * @param name - Method or property name\n * @param value - Function or value to assign\n */\n static macro<T extends Constructor, K extends keyof InstanceType<T>>(\n this: T,\n name: K,\n value: InstanceType<T>[K],\n ): void\n static macro(name: string | symbol, value: unknown): void\n static macro(name: string | symbol, value: unknown): void {\n if (!Object.prototype.hasOwnProperty.call(this, 'macroNames')) {\n this.macroNames = new Set(this.macroNames)\n }\n if (!Object.prototype.hasOwnProperty.call(this, '_originals')) {\n this._originals = new Map(this._originals)\n }\n\n // Save the original value before first override so flushMacros() can restore it\n if (!this._originals.has(name)) {\n const existed = Object.prototype.hasOwnProperty.call(this.prototype, name)\n this._originals.set(name, {\n existed,\n value: existed ? (this.prototype as Record<string | symbol, unknown>)[name] : undefined,\n })\n }\n\n this.macroNames.add(name);\n (this.prototype as Record<string | symbol, unknown>)[name] = value\n }\n\n // ── Instance properties (bound per-instance) ─────────────\n\n /**\n * Register a per-instance property that is bound to `this`\n * in the constructor. Safe for destructuring.\n *\n * When the name matches an existing property, the value type is auto-derived.\n *\n * @param name - Property name\n * @param value - Function (will be bound) or value\n */\n static instanceProperty<T extends Constructor, K extends keyof InstanceType<T>>(\n this: T,\n name: K,\n value: InstanceType<T>[K],\n ): void\n static instanceProperty(name: string | symbol, value: unknown): void\n static instanceProperty(name: string | symbol, value: unknown): void {\n if (!Object.prototype.hasOwnProperty.call(this, 'instanceMacros')) {\n this.instanceMacros = new Set(this.instanceMacros)\n }\n this.instanceMacros.add({ key: name, value })\n }\n\n // ── Getters (Object.defineProperty on prototype) ──────────\n\n /**\n * Register a computed getter on the class prototype.\n *\n * @param name - Property name\n * @param accumulator - Function that computes the value (called with instance as `this`)\n * @param singleton - If true, cache the value after first access\n */\n static getter<T extends Constructor, K extends keyof InstanceType<T>>(\n this: T,\n name: K,\n accumulator: (this: InstanceType<T>) => InstanceType<T>[K],\n singleton?: boolean,\n ): void\n static getter(name: string | symbol, accumulator: MacroFunction, singleton?: boolean): void\n static getter(\n name: string | symbol,\n accumulator: MacroFunction,\n singleton = false,\n ): void {\n if (!Object.prototype.hasOwnProperty.call(this, 'getterNames')) {\n this.getterNames = new Set(this.getterNames)\n }\n this.getterNames.add(name)\n\n Object.defineProperty(this.prototype, name, {\n get(): unknown {\n const value: unknown = accumulator.call(this)\n if (singleton) {\n Object.defineProperty(this, name, {\n value,\n configurable: false,\n enumerable: false,\n writable: false,\n })\n }\n return value\n },\n configurable: true,\n enumerable: false,\n })\n }\n\n // ── Introspection ─────────────────────────────────────────\n\n /**\n * Check if a macro, instance property, or getter is registered.\n *\n * @param name - Name to check\n */\n static hasMacro(name: string | symbol): boolean {\n return (\n this.macroNames.has(name) ||\n [...this.instanceMacros].some((m) => m.key === name) ||\n this.getterNames.has(name)\n )\n }\n\n // ── Cleanup ───────────────────────────────────────────────\n\n /**\n * Remove all macros, instance properties, and getters\n * registered on this class. Does not affect parent classes.\n */\n static flushMacros(): void {\n const proto = this.prototype as Record<string | symbol, unknown>\n\n // Restore original prototype values or delete if they didn't exist before\n for (const name of this.macroNames) {\n const original = this._originals.get(name)\n if (original?.existed) {\n proto[name] = original.value\n } else {\n Reflect.deleteProperty(proto, name)\n }\n }\n for (const name of this.getterNames) {\n Reflect.deleteProperty(proto, name)\n }\n\n if (Object.prototype.hasOwnProperty.call(this, 'macroNames')) {\n this.macroNames.clear()\n }\n if (Object.prototype.hasOwnProperty.call(this, 'instanceMacros')) {\n this.instanceMacros.clear()\n }\n if (Object.prototype.hasOwnProperty.call(this, 'getterNames')) {\n this.getterNames.clear()\n }\n if (Object.prototype.hasOwnProperty.call(this, '_originals')) {\n this._originals.clear()\n }\n }\n\n // ── Constructor (applies instance properties) ─────────────\n\n constructor() {\n const Constructor = this.constructor as typeof Macroable\n const self = this as Record<string | symbol, unknown>\n Constructor.instanceMacros.forEach(({ key, value }) => {\n if (typeof value === 'function') {\n self[key] = value.bind(this)\n } else {\n self[key] = value\n }\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAsB,YAAtB,MAAgC;;;;;CAM9B,OAAiB,iCAAiB,IAAI,IAA8C;;;;CAKpF,OAAiB,6BAAa,IAAI,IAAqB;;;;CAKvD,OAAiB,8BAAc,IAAI,IAAqB;;;;;CAMxD,OAAe,6BAAa,IAAI,IAA2D;CAmB3F,OAAO,MAAM,MAAuB,OAAsB;EACxD,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GAC1D,KAAK,aAAa,IAAI,IAAI,KAAK,UAAU;EAE3C,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GAC1D,KAAK,aAAa,IAAI,IAAI,KAAK,UAAU;EAI3C,IAAI,CAAC,KAAK,WAAW,IAAI,IAAI,GAAG;GAC9B,MAAM,UAAU,OAAO,UAAU,eAAe,KAAK,KAAK,WAAW,IAAI;GACzE,KAAK,WAAW,IAAI,MAAM;IACxB;IACA,OAAO,UAAW,KAAK,UAA+C,QAAQ,KAAA;GAChF,CAAC;EACH;EAEA,KAAK,WAAW,IAAI,IAAI;EACxB,KAAM,UAA+C,QAAQ;CAC/D;CAmBA,OAAO,iBAAiB,MAAuB,OAAsB;EACnE,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,gBAAgB,GAC9D,KAAK,iBAAiB,IAAI,IAAI,KAAK,cAAc;EAEnD,KAAK,eAAe,IAAI;GAAE,KAAK;GAAM;EAAM,CAAC;CAC9C;CAkBA,OAAO,OACL,MACA,aACA,YAAY,OACN;EACN,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,aAAa,GAC3D,KAAK,cAAc,IAAI,IAAI,KAAK,WAAW;EAE7C,KAAK,YAAY,IAAI,IAAI;EAEzB,OAAO,eAAe,KAAK,WAAW,MAAM;GAC1C,MAAe;IACb,MAAM,QAAiB,YAAY,KAAK,IAAI;IAC5C,IAAI,WACF,OAAO,eAAe,MAAM,MAAM;KAChC;KACA,cAAc;KACd,YAAY;KACZ,UAAU;IACZ,CAAC;IAEH,OAAO;GACT;GACA,cAAc;GACd,YAAY;EACd,CAAC;CACH;;;;;;CASA,OAAO,SAAS,MAAgC;EAC9C,OACE,KAAK,WAAW,IAAI,IAAI,KACxB,CAAC,GAAG,KAAK,cAAc,CAAC,CAAC,MAAM,MAAM,EAAE,QAAQ,IAAI,KACnD,KAAK,YAAY,IAAI,IAAI;CAE7B;;;;;CAQA,OAAO,cAAoB;EACzB,MAAM,QAAQ,KAAK;EAGnB,KAAK,MAAM,QAAQ,KAAK,YAAY;GAClC,MAAM,WAAW,KAAK,WAAW,IAAI,IAAI;GACzC,IAAI,UAAU,SACZ,MAAM,QAAQ,SAAS;QAEvB,QAAQ,eAAe,OAAO,IAAI;EAEtC;EACA,KAAK,MAAM,QAAQ,KAAK,aACtB,QAAQ,eAAe,OAAO,IAAI;EAGpC,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GACzD,KAAK,WAAW,MAAM;EAExB,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,gBAAgB,GAC7D,KAAK,eAAe,MAAM;EAE5B,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,aAAa,GAC1D,KAAK,YAAY,MAAM;EAEzB,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,YAAY,GACzD,KAAK,WAAW,MAAM;CAE1B;CAIA,cAAc;EACZ,MAAM,cAAc,KAAK;EACzB,MAAM,OAAO;EACb,YAAY,eAAe,SAAS,EAAE,KAAK,YAAY;GACrD,IAAI,OAAO,UAAU,YACnB,KAAK,OAAO,MAAM,KAAK,IAAI;QAE3B,KAAK,OAAO;EAEhB,CAAC;CACH;AACF"}
@@ -0,0 +1,48 @@
1
+ import { globalRegistry } from "zod/mini";
2
+ //#region src/i18n/validation/metadata.ts
3
+ const toMeta = (input) => typeof input === "string" ? { description: input } : input;
4
+ /**
5
+ * Attach metadata to a schema. zod/mini has no chainable `.describe()`, so
6
+ * metadata is registered in the global registry; the OpenAPI generator reads it
7
+ * back when building the document. Pass a string for a description only, or an
8
+ * object to set `example`, `examples`, `title`, `deprecated`, etc.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { string } from 'zod/mini'
13
+ * import { describe } from 'stratal/validation'
14
+ *
15
+ * const name = describe(string(), 'The display name')
16
+ * const id = describe(string(), { description: 'The id', example: '1212121' })
17
+ * ```
18
+ */
19
+ function describe(schema, meta) {
20
+ globalRegistry.add(schema, toMeta(meta));
21
+ return schema;
22
+ }
23
+ /**
24
+ * Register a schema as a reusable OpenAPI component. The generator emits it once
25
+ * under `components.schemas.<id>` and references it with `$ref` everywhere it is
26
+ * used. Pass a string for a description only, or an object for further metadata;
27
+ * the explicit `id` always wins.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { object, string } from 'zod/mini'
32
+ * import { named } from 'stratal/validation'
33
+ *
34
+ * const User = named(object({ id: string() }), 'User', 'A user record')
35
+ * const Money = named(object({ cents: number() }), 'Money', { example: { cents: 500 } })
36
+ * ```
37
+ */
38
+ function named(schema, id, meta) {
39
+ globalRegistry.add(schema, meta === void 0 ? { id } : {
40
+ ...toMeta(meta),
41
+ id
42
+ });
43
+ return schema;
44
+ }
45
+ //#endregion
46
+ export { named as n, describe as t };
47
+
48
+ //# sourceMappingURL=metadata-Ba8Fxs1F.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-Ba8Fxs1F.mjs","names":[],"sources":["../src/i18n/validation/metadata.ts"],"sourcesContent":["import { globalRegistry } from 'zod/mini'\nimport type { GlobalMeta, ZodType } from './zod'\n\n/**\n * Schema metadata read by the OpenAPI generator: `description`, `title`,\n * `example`, `examples`, `deprecated`, and any extra keys zod passes through to\n * the generated JSON Schema.\n */\nexport type SchemaMeta = GlobalMeta\n\n/** A bare description string, or a full metadata object. */\nexport type SchemaMetaInput = string | SchemaMeta\n\nconst toMeta = (input: SchemaMetaInput): SchemaMeta =>\n typeof input === 'string' ? { description: input } : input\n\n/**\n * Attach metadata to a schema. zod/mini has no chainable `.describe()`, so\n * metadata is registered in the global registry; the OpenAPI generator reads it\n * back when building the document. Pass a string for a description only, or an\n * object to set `example`, `examples`, `title`, `deprecated`, etc.\n *\n * @example\n * ```ts\n * import { string } from 'zod/mini'\n * import { describe } from 'stratal/validation'\n *\n * const name = describe(string(), 'The display name')\n * const id = describe(string(), { description: 'The id', example: '1212121' })\n * ```\n */\nexport function describe<T extends ZodType>(schema: T, meta: SchemaMetaInput): T {\n globalRegistry.add(schema, toMeta(meta))\n return schema\n}\n\n/**\n * Register a schema as a reusable OpenAPI component. The generator emits it once\n * under `components.schemas.<id>` and references it with `$ref` everywhere it is\n * used. Pass a string for a description only, or an object for further metadata;\n * the explicit `id` always wins.\n *\n * @example\n * ```ts\n * import { object, string } from 'zod/mini'\n * import { named } from 'stratal/validation'\n *\n * const User = named(object({ id: string() }), 'User', 'A user record')\n * const Money = named(object({ cents: number() }), 'Money', { example: { cents: 500 } })\n * ```\n */\nexport function named<T extends ZodType>(schema: T, id: string, meta?: SchemaMetaInput): T {\n globalRegistry.add(schema, meta === undefined ? { id } : { ...toMeta(meta), id })\n return schema\n}\n"],"mappings":";;AAaA,MAAM,UAAU,UACd,OAAO,UAAU,WAAW,EAAE,aAAa,MAAM,IAAI;;;;;;;;;;;;;;;;AAiBvD,SAAgB,SAA4B,QAAW,MAA0B;CAC/E,eAAe,IAAI,QAAQ,OAAO,IAAI,CAAC;CACvC,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,SAAgB,MAAyB,QAAW,IAAY,MAA2B;CACzF,eAAe,IAAI,QAAQ,SAAS,KAAA,IAAY,EAAE,GAAG,IAAI;EAAE,GAAG,OAAO,IAAI;EAAG;CAAG,CAAC;CAChF,OAAO;AACT"}
@@ -1,15 +1,14 @@
1
- import { Bn as ModuleOptions, Fn as DynamicModule, Gn as ValueProvider, Hn as OnInitialize, In as ExistingProvider, Ir as ApplicationError, Jn as InjectionToken, Ln as FactoryProvider, Nn as AsyncModuleOptions, Pn as ClassProvider, Rn as ModuleClass, Un as OnShutdown, Vn as OnException, Wn as Provider, hn as ModuleRegistry, zn as ModuleContext } from "../index-uybm0bhQ.mjs";
1
+ import { $n as ModuleClass, Jn as AsyncModuleOptions, Qn as FactoryProvider, Xn as DynamicModule, Yn as ClassProvider, Zn as ExistingProvider, ar as Provider, er as ModuleContext, ir as OnShutdown, li as ApplicationError, lr as InjectionToken, nr as OnException, or as ValueProvider, rr as OnInitialize, tr as ModuleOptions, xn as ModuleRegistry } from "../index-B2WtEagR.mjs";
2
2
  import { t as Constructor } from "../types-CmV_9xBD.mjs";
3
- import { n as ModuleRef, t as LazyModuleLoader } from "../lazy-module-loader-M6YKudNL.mjs";
4
-
3
+ import { n as ModuleRef, t as LazyModuleLoader } from "../lazy-module-loader-C3OHoUnR.mjs";
5
4
  //#region src/module/module.error.d.ts
6
- declare class ModuleError extends ApplicationError {}
5
+ export declare class ModuleError extends ApplicationError {}
7
6
  //#endregion
8
7
  //#region src/module/module.decorator.d.ts
9
- declare const MODULE_OPTIONS_KEY: unique symbol;
10
- declare function Module(options: ModuleOptions): <TFunction extends abstract new (...args: never[]) => unknown>(target: TFunction) => TFunction;
11
- declare function getModuleOptions(target: Constructor): ModuleOptions | undefined;
12
- declare function isModuleClass(target: unknown): target is Constructor;
8
+ export declare const MODULE_OPTIONS_KEY: unique symbol;
9
+ export declare function Module(options: ModuleOptions): <TFunction extends abstract new (...args: never[]) => unknown>(target: TFunction) => TFunction;
10
+ export declare function getModuleOptions(target: Constructor): ModuleOptions | undefined;
11
+ export declare function isModuleClass(target: unknown): target is Constructor;
13
12
  //#endregion
14
- export { AsyncModuleOptions, ClassProvider, DynamicModule, ExistingProvider, FactoryProvider, type InjectionToken, LazyModuleLoader, MODULE_OPTIONS_KEY, Module, ModuleClass, ModuleContext, ModuleError, ModuleOptions, ModuleRef, ModuleRegistry, OnException, OnInitialize, OnShutdown, Provider, ValueProvider, getModuleOptions, isModuleClass };
13
+ export { AsyncModuleOptions, ClassProvider, DynamicModule, ExistingProvider, FactoryProvider, type InjectionToken, LazyModuleLoader, ModuleClass, ModuleContext, ModuleOptions, ModuleRef, ModuleRegistry, OnException, OnInitialize, OnShutdown, Provider, ValueProvider };
15
14
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/module/module.error.ts","../../src/module/module.decorator.ts"],"mappings":";;;;;cAEa,WAAA,SAAoB,gBAAgB;;;cCEpC,kBAAA;AAAA,iBAEG,MAAA,CAAO,OAAA,EAAS,aAAA,uCACa,IAAA,uBAA2B,MAAA,EAAQ,SAAA,KAAY,SAAA;AAAA,iBAM5E,gBAAA,CAAiB,MAAA,EAAQ,WAAA,GAAc,aAAa;AAAA,iBAIpD,aAAA,CAAc,MAAA,YAAkB,MAAA,IAAU,WAAW"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/module/module.error.ts","../../src/module/module.decorator.ts"],"mappings":";;;;qBAEa,oBAAoB;;;qBCEpB;wBAEG,OAAO,SAAS,iBACtB,mCAAmC,2BAAyB,QAAU,cAAY;wBAM5E,iBAAiB,QAAQ,cAAc;wBAIvC,cAAc,kBAAkB,UAAU"}
@@ -1,7 +1,6 @@
1
- import { a as ApplicationError } from "../container-storage-BmOJ4_Na.mjs";
2
- import "../errors-C01O2T-n.mjs";
3
- import { i as isModuleClass, n as Module, r as getModuleOptions, t as MODULE_OPTIONS_KEY } from "../module.decorator-CYHY6pG5.mjs";
4
- import { d as LazyModuleLoader, f as ModuleRef, t as ModuleRegistry } from "../module-registry-NxX5O0Qk.mjs";
1
+ import { a as ApplicationError } from "../container-storage-DfF7SXdV.mjs";
2
+ import { i as isModuleClass, n as Module, r as getModuleOptions, t as MODULE_OPTIONS_KEY } from "../module.decorator-CYbrOlX9.mjs";
3
+ import { d as LazyModuleLoader, f as ModuleRef, t as ModuleRegistry } from "../module-registry-lkQPDuQe.mjs";
5
4
  //#region src/module/module.error.ts
6
5
  var ModuleError = class extends ApplicationError {};
7
6
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/module/module.error.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class ModuleError extends ApplicationError {}\n"],"mappings":";;;;;AAEA,IAAa,cAAb,cAAiC,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/module/module.error.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class ModuleError extends ApplicationError {}\n"],"mappings":";;;;AAEA,IAAa,cAAb,cAAiC,iBAAiB,CAAC"}
@@ -1,12 +1,12 @@
1
- import { n as CONTAINER_TOKEN, p as inject, r as DI_TOKENS, u as Transient } from "./di-D7qmrAir.mjs";
2
- import { a as ApplicationError } from "./container-storage-BmOJ4_Na.mjs";
3
- import { n as __decorateParam, t as __decorate } from "./decorate-CuAoSZvs.mjs";
1
+ import { n as CONTAINER_TOKEN, p as inject, r as DI_TOKENS, u as Transient } from "./di-DQ3xbjgf.mjs";
2
+ import { a as ApplicationError } from "./container-storage-DfF7SXdV.mjs";
3
+ import { n as __decorateParam, t as __decorate } from "./decorate-C79yt3Wt.mjs";
4
4
  import { LOGGER_TOKENS } from "./logger/index.mjs";
5
- import { d as HttpException } from "./errors-C01O2T-n.mjs";
6
- import { s as isListener } from "./events-BhEQuT1X.mjs";
7
- import { r as getModuleOptions } from "./module.decorator-CYHY6pG5.mjs";
8
- import { t as isCommand } from "./is-command-CEPO9n8c.mjs";
9
- import { t as isSeeder } from "./is-seeder-Gvh_AM71.mjs";
5
+ import { d as HttpException } from "./errors-CrOXI_y5.mjs";
6
+ import { s as isListener } from "./events-GtwGEHyT.mjs";
7
+ import { r as getModuleOptions } from "./module.decorator-CYbrOlX9.mjs";
8
+ import { t as isCommand } from "./is-command-CSVv14wh.mjs";
9
+ import { t as isSeeder } from "./is-seeder-gLgXYbqC.mjs";
10
10
  //#region src/module/module-ref.ts
11
11
  /**
12
12
  * Handle to a lazily loaded module, returned by {@link LazyModuleLoader.load}.
@@ -151,17 +151,18 @@ var RouterError = class extends ApplicationError {};
151
151
  * Only internal modules (RouterResolver) import them, keeping the Router's
152
152
  * public API clean — users never see these methods.
153
153
  *
154
- * Declared as individual unique symbols so TypeScript can distinguish
155
- * their return types in computed property access.
154
+ * Registered via `Symbol.for` so that a duplicate evaluation of this module
155
+ * (e.g. under a bundler or SSR module runner) resolves to the same symbol,
156
+ * keeping symbol-keyed dispatch between `Router` and `RouterResolver` stable.
156
157
  *
157
158
  * @internal
158
159
  */
159
160
  /** @internal */
160
- const getDefaultEntry = Symbol("Router.getDefaultEntry");
161
+ const getDefaultEntry = Symbol.for("stratal:router:getDefaultEntry");
161
162
  /** @internal */
162
- const getGroups = Symbol("Router.getGroups");
163
+ const getGroups = Symbol.for("stratal:router:getGroups");
163
164
  /** @internal */
164
- const getGlobalMiddleware = Symbol("Router.getGlobalMiddleware");
165
+ const getGlobalMiddleware = Symbol.for("stratal:router:getGlobalMiddleware");
165
166
  //#endregion
166
167
  //#region src/router/router.ts
167
168
  /**
@@ -512,7 +513,8 @@ var ModuleRegistry = class {
512
513
  else if ("useFactory" in provider) {
513
514
  const { provide, useFactory, inject = [] } = provider;
514
515
  this.container.registerFactory(provide, (c) => {
515
- return useFactory(...inject.map((token) => c.resolve(token)));
516
+ const deps = inject.map((token) => c.resolve(token));
517
+ return useFactory(...deps);
516
518
  });
517
519
  } else if ("useExisting" in provider) this.container.registerExisting(provider.provide, provider.useExisting);
518
520
  }
@@ -551,4 +553,4 @@ var ModuleRegistry = class {
551
553
  //#endregion
552
554
  export { getGroups as a, RATE_LIMITER_TOKENS as c, LazyModuleLoader as d, ModuleRef as f, getGlobalMiddleware as i, RateLimiterError as l, Router as n, RouterError as o, getDefaultEntry as r, createThrottleMiddleware as s, ModuleRegistry as t, TooManyRequestsError as u };
553
555
 
554
- //# sourceMappingURL=module-registry-NxX5O0Qk.mjs.map
556
+ //# sourceMappingURL=module-registry-lkQPDuQe.mjs.map