stratal 0.0.22 → 0.0.23

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 (270) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/cloudflare-workers-loader.mjs +80 -7
  3. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  4. package/dist/bin/quarry.mjs +41 -54
  5. package/dist/bin/quarry.mjs.map +1 -1
  6. package/dist/cache/index.d.mts +5 -3
  7. package/dist/cache/index.d.mts.map +1 -1
  8. package/dist/cache/index.mjs +123 -39
  9. package/dist/cache/index.mjs.map +1 -1
  10. package/dist/{cache.service-e34gV6tz.d.mts → cache.service-uElmBtdS.d.mts} +24 -34
  11. package/dist/cache.service-uElmBtdS.d.mts.map +1 -0
  12. package/dist/{command-BU4ApTo5.mjs → command-BvmUAPPQ.mjs} +15 -3
  13. package/dist/command-BvmUAPPQ.mjs.map +1 -0
  14. package/dist/{command-wXfvHbBZ.d.mts → command-CPhFHjG3.d.mts} +2 -2
  15. package/dist/command-CPhFHjG3.d.mts.map +1 -0
  16. package/dist/command-not-found.error-ONAZ2Bpk.mjs +14 -0
  17. package/dist/command-not-found.error-ONAZ2Bpk.mjs.map +1 -0
  18. package/dist/config/index.d.mts +3 -3
  19. package/dist/config/index.d.mts.map +1 -1
  20. package/dist/config/index.mjs +7 -6
  21. package/dist/config/index.mjs.map +1 -1
  22. package/dist/{consumer-registry-DHQtypr1.d.mts → consumer-registry-D3iMTSdy.d.mts} +54 -22
  23. package/dist/consumer-registry-D3iMTSdy.d.mts.map +1 -0
  24. package/dist/{container-storage-GpNNz79X.mjs → container-storage-BmOJ4_Na.mjs} +1 -1
  25. package/dist/{container-storage-GpNNz79X.mjs.map → container-storage-BmOJ4_Na.mjs.map} +1 -1
  26. package/dist/{controller.decorator-DIUazNU7.mjs → controller.decorator-C5UVeJS3.mjs} +4 -4
  27. package/dist/{controller.decorator-DIUazNU7.mjs.map → controller.decorator-C5UVeJS3.mjs.map} +1 -1
  28. package/dist/cron/index.d.mts +79 -4
  29. package/dist/cron/index.d.mts.map +1 -1
  30. package/dist/cron/index.mjs +2 -2
  31. package/dist/cron-job-NesZRk8F.d.mts +58 -0
  32. package/dist/cron-job-NesZRk8F.d.mts.map +1 -0
  33. package/dist/{cron-manager-9bpN9bu4.mjs → cron.module-Bgzq5hiT.mjs} +17 -7
  34. package/dist/cron.module-Bgzq5hiT.mjs.map +1 -0
  35. package/dist/{decorate-HgTKAYK8.mjs → decorate-CuAoSZvs.mjs} +2 -2
  36. package/dist/{deep-merge-C8NgcXw4.mjs → deep-merge-ByiAOZ3r.mjs} +1 -1
  37. package/dist/{deep-merge-C8NgcXw4.mjs.map → deep-merge-ByiAOZ3r.mjs.map} +1 -1
  38. package/dist/di/index.d.mts +2 -2
  39. package/dist/di/index.mjs +3 -3
  40. package/dist/{di-BO1QIb5H.mjs → di-DseMn-z9.mjs} +244 -135
  41. package/dist/di-DseMn-z9.mjs.map +1 -0
  42. package/dist/email/index.d.mts +33 -40
  43. package/dist/email/index.d.mts.map +1 -1
  44. package/dist/email/index.mjs +456 -41
  45. package/dist/email/index.mjs.map +1 -1
  46. package/dist/{en-BPP6h6y5.mjs → en-CDZBMcc1.mjs} +2 -2
  47. package/dist/{en-BPP6h6y5.mjs.map → en-CDZBMcc1.mjs.map} +1 -1
  48. package/dist/{env-DKSbuBi5.d.mts → env-ug22bJj7.d.mts} +1 -1
  49. package/dist/env-ug22bJj7.d.mts.map +1 -0
  50. package/dist/errors/index.d.mts +1 -1
  51. package/dist/errors/index.mjs +3 -3
  52. package/dist/{errors-BBZTnjdq.mjs → errors-mXYxG0XB.mjs} +5 -5
  53. package/dist/{errors-BBZTnjdq.mjs.map → errors-mXYxG0XB.mjs.map} +1 -1
  54. package/dist/events/index.d.mts +14 -3
  55. package/dist/events/index.d.mts.map +1 -1
  56. package/dist/events/index.mjs +2 -2
  57. package/dist/{events-D1KdDaiP.mjs → events-BXJGZjpG.mjs} +16 -6
  58. package/dist/events-BXJGZjpG.mjs.map +1 -0
  59. package/dist/{exception-context-B4kM-M53.mjs → exception-context-kEoMFwze.mjs} +3 -3
  60. package/dist/{exception-context-B4kM-M53.mjs.map → exception-context-kEoMFwze.mjs.map} +1 -1
  61. package/dist/{gateway-context-CFe6a9gz.mjs → gateway-context-TMu_AlJt.mjs} +25 -6
  62. package/dist/{gateway-context-CFe6a9gz.mjs.map → gateway-context-TMu_AlJt.mjs.map} +1 -1
  63. package/dist/guards/index.d.mts +3 -3
  64. package/dist/guards/index.d.mts.map +1 -1
  65. package/dist/guards/index.mjs +1 -1
  66. package/dist/{guards-Ced-uNIF.mjs → guards-DALPXy3_.mjs} +2 -2
  67. package/dist/{guards-Ced-uNIF.mjs.map → guards-DALPXy3_.mjs.map} +1 -1
  68. package/dist/hono-app-CvV3hOfT.mjs +161 -0
  69. package/dist/hono-app-CvV3hOfT.mjs.map +1 -0
  70. package/dist/{http-method.decorator-CdjKFJZZ.mjs → http-method.decorator-ByWZb9DO.mjs} +4 -4
  71. package/dist/{http-method.decorator-CdjKFJZZ.mjs.map → http-method.decorator-ByWZb9DO.mjs.map} +1 -1
  72. package/dist/i18n/index.d.mts +4 -4
  73. package/dist/i18n/index.d.mts.map +1 -1
  74. package/dist/i18n/index.mjs +5 -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.mjs +1 -1
  79. package/dist/i18n/validation/index.d.mts +3 -3
  80. package/dist/i18n/validation/index.mjs +3 -3
  81. package/dist/{i18n.module-BlXrtAlV.mjs → i18n.module-DRQAZoSZ.mjs} +14 -11
  82. package/dist/{i18n.module-BlXrtAlV.mjs.map → i18n.module-DRQAZoSZ.mjs.map} +1 -1
  83. package/dist/{i18n.tokens-hwRpmjRq.mjs → i18n.tokens-CZ_v8oyS.mjs} +1 -1
  84. package/dist/{i18n.tokens-hwRpmjRq.mjs.map → i18n.tokens-CZ_v8oyS.mjs.map} +1 -1
  85. package/dist/{index-B4UBK-2T.d.mts → index-0ItCjaqw.d.mts} +1 -1
  86. package/dist/index-0ItCjaqw.d.mts.map +1 -0
  87. package/dist/{index-CW1YHSft.d.mts → index-B5JBRcWD.d.mts} +249 -103
  88. package/dist/index-B5JBRcWD.d.mts.map +1 -0
  89. package/dist/{index-BtlE9RuO.d.mts → index-BUt92sAE.d.mts} +1 -1
  90. package/dist/index-BUt92sAE.d.mts.map +1 -0
  91. package/dist/{index-DEncMcC6.d.mts → index-B_JoEl3V.d.mts} +221 -16
  92. package/dist/index-B_JoEl3V.d.mts.map +1 -0
  93. package/dist/{index-Dj5IMwtr.d.mts → index-DtBNIFuP.d.mts} +4 -6
  94. package/dist/index-DtBNIFuP.d.mts.map +1 -0
  95. package/dist/{index-KMgSCSM7.d.mts → index-HgOLNruQ.d.mts} +1 -1
  96. package/dist/{index-KMgSCSM7.d.mts.map → index-HgOLNruQ.d.mts.map} +1 -1
  97. package/dist/index.d.mts +6 -5
  98. package/dist/index.mjs +3 -2
  99. package/dist/{is-command-CX5rAfZW.mjs → is-command-CEPO9n8c.mjs} +2 -2
  100. package/dist/{is-command-CX5rAfZW.mjs.map → is-command-CEPO9n8c.mjs.map} +1 -1
  101. package/dist/{is-seeder-CYCtELlm.mjs → is-seeder-Gvh_AM71.mjs} +1 -1
  102. package/dist/{is-seeder-CYCtELlm.mjs.map → is-seeder-Gvh_AM71.mjs.map} +1 -1
  103. package/dist/lazy-module-loader-Ib383jH_.d.mts +60 -0
  104. package/dist/lazy-module-loader-Ib383jH_.d.mts.map +1 -0
  105. package/dist/locale-path.service-D-dHiIPc.mjs +165 -0
  106. package/dist/locale-path.service-D-dHiIPc.mjs.map +1 -0
  107. package/dist/locale-url-nZrZxqJP.mjs +44 -0
  108. package/dist/locale-url-nZrZxqJP.mjs.map +1 -0
  109. package/dist/locale-url.service-C2EWmGdq.mjs +41 -0
  110. package/dist/locale-url.service-C2EWmGdq.mjs.map +1 -0
  111. package/dist/logger/index.d.mts +1 -1
  112. package/dist/logger/index.mjs +2 -2
  113. package/dist/logger/index.mjs.map +1 -1
  114. package/dist/macroable/index.d.mts +2 -2
  115. package/dist/macroable/index.mjs +1 -1
  116. package/dist/{macroable-DzlfzT50.mjs → macroable-cvDTFZ_A.mjs} +1 -1
  117. package/dist/{macroable-DzlfzT50.mjs.map → macroable-cvDTFZ_A.mjs.map} +1 -1
  118. package/dist/{metadata-BVkc4aUu.mjs → metadata-DzzprcID.mjs} +1 -1
  119. package/dist/{metadata-BVkc4aUu.mjs.map → metadata-DzzprcID.mjs.map} +1 -1
  120. package/dist/module/index.d.mts +4 -3
  121. package/dist/module/index.d.mts.map +1 -1
  122. package/dist/module/index.mjs +10 -2
  123. package/dist/module/index.mjs.map +1 -0
  124. package/dist/{module-xYoHba6B.mjs → module-registry-Dm-pqHd3.mjs} +189 -57
  125. package/dist/module-registry-Dm-pqHd3.mjs.map +1 -0
  126. package/dist/module.decorator-CYHY6pG5.mjs +19 -0
  127. package/dist/module.decorator-CYHY6pG5.mjs.map +1 -0
  128. package/dist/openapi/index.d.mts +44 -8
  129. package/dist/openapi/index.d.mts.map +1 -1
  130. package/dist/openapi/index.mjs +3 -2
  131. package/dist/{openapi-C6lm0RmV.mjs → openapi-CstuTM8S.mjs} +55 -229
  132. package/dist/openapi-CstuTM8S.mjs.map +1 -0
  133. package/dist/openapi-tools.service-BC5EC3R3.mjs +206 -0
  134. package/dist/openapi-tools.service-BC5EC3R3.mjs.map +1 -0
  135. package/dist/{openapi.service-CrLlsXAd.d.mts → openapi.service-YhTiJ1bO.d.mts} +3 -3
  136. package/dist/{openapi.service-CrLlsXAd.d.mts.map → openapi.service-YhTiJ1bO.d.mts.map} +1 -1
  137. package/dist/quarry/index.d.mts +14 -5
  138. package/dist/quarry/index.d.mts.map +1 -1
  139. package/dist/quarry/index.mjs +6 -5
  140. package/dist/quarry/runner.d.mts +11 -11
  141. package/dist/quarry/runner.d.mts.map +1 -1
  142. package/dist/quarry/runner.mjs +192 -22
  143. package/dist/quarry/runner.mjs.map +1 -1
  144. package/dist/{quarry-registry-D4hIGScf.d.mts → quarry-registry-CXg0RFXq.d.mts} +4 -4
  145. package/dist/quarry-registry-CXg0RFXq.d.mts.map +1 -0
  146. package/dist/{quarry-registry-DkraZNwn.mjs → quarry.module-BuRPGMDm.mjs} +22 -21
  147. package/dist/quarry.module-BuRPGMDm.mjs.map +1 -0
  148. package/dist/queue/index.d.mts +3 -3
  149. package/dist/queue/index.mjs +42 -31
  150. package/dist/queue/index.mjs.map +1 -1
  151. package/dist/queue.module-nddvxzCB.mjs +613 -0
  152. package/dist/queue.module-nddvxzCB.mjs.map +1 -0
  153. package/dist/queue.tokens-DjHnFmre.mjs +11 -0
  154. package/dist/queue.tokens-DjHnFmre.mjs.map +1 -0
  155. package/dist/{r2-storage.provider-Hfm6LdZQ.mjs → r2-storage.provider-DCxQt9dD.mjs} +4 -4
  156. package/dist/{r2-storage.provider-Hfm6LdZQ.mjs.map → r2-storage.provider-DCxQt9dD.mjs.map} +1 -1
  157. package/dist/{rate-limit.decorator-D69zdZbp.mjs → rate-limit.decorator-BPAie_p3.mjs} +3 -3
  158. package/dist/{rate-limit.decorator-D69zdZbp.mjs.map → rate-limit.decorator-BPAie_p3.mjs.map} +1 -1
  159. package/dist/rate-limiter/index.d.mts +5 -5
  160. package/dist/rate-limiter/index.d.mts.map +1 -1
  161. package/dist/rate-limiter/index.mjs +26 -21
  162. package/dist/rate-limiter/index.mjs.map +1 -1
  163. package/dist/route-name-DGoBOfPg.mjs +171 -0
  164. package/dist/route-name-DGoBOfPg.mjs.map +1 -0
  165. package/dist/route-registration.service-D6vSwiKP.mjs +918 -0
  166. package/dist/route-registration.service-D6vSwiKP.mjs.map +1 -0
  167. package/dist/route-registry-CYqLp2Nj.mjs +123 -0
  168. package/dist/route-registry-CYqLp2Nj.mjs.map +1 -0
  169. package/dist/router/index.d.mts +2 -2
  170. package/dist/router/index.mjs +18 -8
  171. package/dist/router-CWGBD-Bg.mjs +78 -0
  172. package/dist/router-CWGBD-Bg.mjs.map +1 -0
  173. package/dist/router-resolver-D4YlPNlm.mjs +88 -0
  174. package/dist/router-resolver-D4YlPNlm.mjs.map +1 -0
  175. package/dist/seeder/index.d.mts +14 -4
  176. package/dist/seeder/index.d.mts.map +1 -1
  177. package/dist/seeder/index.mjs +5 -3
  178. package/dist/{seeder-BADTig4n.mjs → seeder-7ubkms-Y.mjs} +7 -56
  179. package/dist/seeder-7ubkms-Y.mjs.map +1 -0
  180. package/dist/seeder-registry-CyUmKsJq.mjs +57 -0
  181. package/dist/seeder-registry-CyUmKsJq.mjs.map +1 -0
  182. package/dist/seeder.module-CYYwk3Qk.mjs +15 -0
  183. package/dist/seeder.module-CYYwk3Qk.mjs.map +1 -0
  184. package/dist/{signed-url-BqUqt5dF.mjs → signed-url-DIU0sK_6.mjs} +1 -1
  185. package/dist/{signed-url-BqUqt5dF.mjs.map → signed-url-DIU0sK_6.mjs.map} +1 -1
  186. package/dist/storage/index.d.mts +3 -3
  187. package/dist/storage/index.d.mts.map +1 -1
  188. package/dist/storage/index.mjs +2 -2
  189. package/dist/storage/providers/index.d.mts +2 -2
  190. package/dist/storage/providers/index.d.mts.map +1 -1
  191. package/dist/storage/providers/index.mjs +1 -1
  192. package/dist/{storage-BA3ppVYM.mjs → storage-MDZypIE9.mjs} +12 -11
  193. package/dist/{storage-BA3ppVYM.mjs.map → storage-MDZypIE9.mjs.map} +1 -1
  194. package/dist/{storage-provider.interface-DQMtT42e.d.mts → storage-provider.interface-ClUwxz4S.d.mts} +2 -2
  195. package/dist/storage-provider.interface-ClUwxz4S.d.mts.map +1 -0
  196. package/dist/storage.error-Dnib4VHc.mjs +8 -0
  197. package/dist/{storage.error-C6FY037a.mjs.map → storage.error-Dnib4VHc.mjs.map} +1 -1
  198. package/dist/{stratal-Bdq4IdB3.mjs → stratal-DL9M38_s.mjs} +142 -140
  199. package/dist/stratal-DL9M38_s.mjs.map +1 -0
  200. package/dist/{stratal-BsKmvP6J.d.mts → stratal-DwDJPY9N.d.mts} +3 -3
  201. package/dist/{stratal-BsKmvP6J.d.mts.map → stratal-DwDJPY9N.d.mts.map} +1 -1
  202. package/dist/tiered-cache.service-Dv3BhxxE.d.mts +79 -0
  203. package/dist/tiered-cache.service-Dv3BhxxE.d.mts.map +1 -0
  204. package/dist/trailing-slash-CFyw8nYu.mjs +34 -0
  205. package/dist/trailing-slash-CFyw8nYu.mjs.map +1 -0
  206. package/dist/{types-BaeHi67f.d.mts → types-CmV_9xBD.d.mts} +1 -1
  207. package/dist/types-CmV_9xBD.d.mts.map +1 -0
  208. package/dist/uri-h7Q8Jug9.mjs +251 -0
  209. package/dist/uri-h7Q8Jug9.mjs.map +1 -0
  210. package/dist/{usage-generator-DTqaUMR9.mjs → usage-generator-DAWYasuP.mjs} +4 -4
  211. package/dist/usage-generator-DAWYasuP.mjs.map +1 -0
  212. package/dist/{validation-DUzcjb8Q.mjs → validation-CpOjviyT.mjs} +6 -6
  213. package/dist/{validation-DUzcjb8Q.mjs.map → validation-CpOjviyT.mjs.map} +1 -1
  214. package/dist/{validation.context-XTysWJ3b.mjs → validation.context-CRvmrhq7.mjs} +3 -3
  215. package/dist/{validation.context-XTysWJ3b.mjs.map → validation.context-CRvmrhq7.mjs.map} +1 -1
  216. package/dist/versioning.service-C6aHky8-.mjs +36 -0
  217. package/dist/versioning.service-C6aHky8-.mjs.map +1 -0
  218. package/dist/websocket/index.d.mts +11 -2
  219. package/dist/websocket/index.d.mts.map +1 -1
  220. package/dist/websocket/index.mjs +1 -1
  221. package/dist/workers/index.d.mts +2 -2
  222. package/dist/workers/index.d.mts.map +1 -1
  223. package/dist/workers/index.mjs +3 -3
  224. package/dist/workers/index.mjs.map +1 -1
  225. package/dist/{zod-hMa3rSHV.mjs → zod-eKqqhZ5_.mjs} +2 -2
  226. package/dist/{zod-hMa3rSHV.mjs.map → zod-eKqqhZ5_.mjs.map} +1 -1
  227. package/dist/{zod-DvWTfRpI.d.mts → zod-wecrEVAs.d.mts} +8 -3
  228. package/dist/zod-wecrEVAs.d.mts.map +1 -0
  229. package/package.json +19 -30
  230. package/dist/base-email.provider-BWZHIjt8.mjs +0 -42
  231. package/dist/base-email.provider-BWZHIjt8.mjs.map +0 -1
  232. package/dist/cache.service-e34gV6tz.d.mts.map +0 -1
  233. package/dist/cache.tokens-ovi_c52J.mjs +0 -6
  234. package/dist/cache.tokens-ovi_c52J.mjs.map +0 -1
  235. package/dist/colors-axmupKdp.mjs +0 -16
  236. package/dist/colors-axmupKdp.mjs.map +0 -1
  237. package/dist/command-BU4ApTo5.mjs.map +0 -1
  238. package/dist/command-wXfvHbBZ.d.mts.map +0 -1
  239. package/dist/consumer-registry-DHQtypr1.d.mts.map +0 -1
  240. package/dist/cron-manager-9bpN9bu4.mjs.map +0 -1
  241. package/dist/cron-manager-CSTIBPcM.d.mts +0 -124
  242. package/dist/cron-manager-CSTIBPcM.d.mts.map +0 -1
  243. package/dist/di-BO1QIb5H.mjs.map +0 -1
  244. package/dist/env-DKSbuBi5.d.mts.map +0 -1
  245. package/dist/events-D1KdDaiP.mjs.map +0 -1
  246. package/dist/index-B4UBK-2T.d.mts.map +0 -1
  247. package/dist/index-BtlE9RuO.d.mts.map +0 -1
  248. package/dist/index-CW1YHSft.d.mts.map +0 -1
  249. package/dist/index-DEncMcC6.d.mts.map +0 -1
  250. package/dist/index-Dj5IMwtr.d.mts.map +0 -1
  251. package/dist/module-xYoHba6B.mjs.map +0 -1
  252. package/dist/openapi-C6lm0RmV.mjs.map +0 -1
  253. package/dist/quarry-registry-D4hIGScf.d.mts.map +0 -1
  254. package/dist/quarry-registry-DkraZNwn.mjs.map +0 -1
  255. package/dist/queue.module-DeWJ0tQM.mjs +0 -355
  256. package/dist/queue.module-DeWJ0tQM.mjs.map +0 -1
  257. package/dist/resend.provider-Ur6tU7fK.mjs +0 -68
  258. package/dist/resend.provider-Ur6tU7fK.mjs.map +0 -1
  259. package/dist/router-Cy6DjkvP.mjs +0 -1852
  260. package/dist/router-Cy6DjkvP.mjs.map +0 -1
  261. package/dist/seeder-BADTig4n.mjs.map +0 -1
  262. package/dist/smtp.provider-C129sNBT.mjs +0 -76
  263. package/dist/smtp.provider-C129sNBT.mjs.map +0 -1
  264. package/dist/storage-provider.interface-DQMtT42e.d.mts.map +0 -1
  265. package/dist/storage.error-C6FY037a.mjs +0 -8
  266. package/dist/stratal-Bdq4IdB3.mjs.map +0 -1
  267. package/dist/types-BaeHi67f.d.mts.map +0 -1
  268. package/dist/usage-generator-DTqaUMR9.mjs.map +0 -1
  269. package/dist/zod-DvWTfRpI.d.mts.map +0 -1
  270. /package/dist/{chunk-D1SwGrFN.mjs → chunk-BBjsoOtd.mjs} +0 -0
@@ -1,102 +1,11 @@
1
- import { r as runWithContainer } from "./container-storage-GpNNz79X.mjs";
2
- import { c as DI_TOKENS, o as ROUTER_TOKENS, t as Container } from "./di-BO1QIb5H.mjs";
1
+ import { r as DI_TOKENS, t as Container, v as ROUTER_TOKENS } from "./di-DseMn-z9.mjs";
2
+ import { n as getContainer, r as runWithContainer, t as containerStorage } from "./container-storage-BmOJ4_Na.mjs";
3
3
  import { JsonFormatter, LOGGER_TOKENS, LoggerService, PrettyFormatter } from "./logger/index.mjs";
4
- import { a as DefaultExceptionHandler, r as StratalNotInitializedError } from "./errors-BBZTnjdq.mjs";
5
- import { i as createQueueExceptionContext, n as createCronExceptionContext, t as createCliExceptionContext } from "./exception-context-B4kM-M53.mjs";
6
- import { a as getGroups, i as getGlobalMiddleware, r as getDefaultEntry, t as ModuleRegistry } from "./module-xYoHba6B.mjs";
7
- import { r as getListenerHandlers, t as EventRegistry } from "./events-D1KdDaiP.mjs";
8
- import { CacheModule } from "./cache/index.mjs";
9
- import { t as CronManager } from "./cron-manager-9bpN9bu4.mjs";
10
- import { n as OpenAPIModule } from "./openapi-C6lm0RmV.mjs";
11
- import { a as RouteRegistry, d as RouteRegistrationService, n as Uri, o as VersioningService, s as LocalePathService, u as HonoApp } from "./router-Cy6DjkvP.mjs";
12
- import { t as I18nModule } from "./i18n.module-BlXrtAlV.mjs";
13
- import { t as QuarryRegistry } from "./quarry-registry-DkraZNwn.mjs";
14
- import { t as QueueModule } from "./queue.module-DeWJ0tQM.mjs";
15
- import { i as SeederRegistry, r as SEEDER_TOKENS } from "./seeder-BADTig4n.mjs";
16
- //#region src/router/router-resolver.ts
17
- /**
18
- * Internal resolver that computes the effective Router config for each controller.
19
- *
20
- * Inheritance rules:
21
- * - `middleware`: parent middleware runs first, then child (concatenated)
22
- * - `prefix`: concatenated (parent + child)
23
- * - `name`: concatenated (parent + child)
24
- * - `domain`: child overrides parent
25
- * - `version`: child overrides parent
26
- * - `hideFromDocs`: child overrides parent
27
- *
28
- * @internal — not exported from stratal/router
29
- */
30
- var RouterResolver = class {
31
- routers;
32
- constructor(routers) {
33
- this.routers = routers;
34
- }
35
- /**
36
- * Resolve the effective config for a given controller class.
37
- * Searches through all module routers to find the one owning this controller.
38
- */
39
- resolveForController(controller) {
40
- for (const { router, controllers: moduleControllers } of this.routers) {
41
- if (!moduleControllers.includes(controller)) continue;
42
- for (const group of router[getGroups]()) if (group.controllers?.includes(controller)) return this.mergeEntries(router[getDefaultEntry](), group);
43
- if (!new Set(router[getGroups]().flatMap((g) => g.controllers ?? [])).has(controller)) return this.entryToConfig(router[getDefaultEntry]());
44
- }
45
- return { middleware: [] };
46
- }
47
- /**
48
- * Collect all global middleware registered via `router.use()` across all modules.
49
- */
50
- getGlobalMiddleware() {
51
- const global = [];
52
- for (const { router } of this.routers) global.push(...router[getGlobalMiddleware]());
53
- return global;
54
- }
55
- /**
56
- * Merge parent default entry with child group entry following inheritance rules.
57
- */
58
- mergeEntries(parent, child) {
59
- return {
60
- prefix: this.concatPrefixes(parent.prefix, child.prefix),
61
- domain: child.domain ?? parent.domain,
62
- name: this.concatNames(parent.name, child.name),
63
- middleware: [...parent.middleware, ...child.middleware],
64
- version: child.version ?? parent.version,
65
- hideFromDocs: child.hideFromDocs ?? parent.hideFromDocs,
66
- params: this.mergeParams(parent.params, child.params)
67
- };
68
- }
69
- entryToConfig(entry) {
70
- return {
71
- prefix: entry.prefix,
72
- domain: entry.domain,
73
- name: entry.name,
74
- middleware: [...entry.middleware],
75
- version: entry.version,
76
- hideFromDocs: entry.hideFromDocs,
77
- params: entry.params
78
- };
79
- }
80
- mergeParams(parent, child) {
81
- if (!parent && !child) return void 0;
82
- if (!parent) return child;
83
- if (!child) return parent;
84
- return parent.extend(child.shape);
85
- }
86
- concatPrefixes(parent, child) {
87
- if (!parent && !child) return void 0;
88
- if (!parent) return child;
89
- if (!child) return parent;
90
- return `${parent.endsWith("/") ? parent.slice(0, -1) : parent}${child.startsWith("/") ? child : `/${child}`}`;
91
- }
92
- concatNames(parent, child) {
93
- if (!parent && !child) return void 0;
94
- if (!parent) return child;
95
- if (!child) return parent;
96
- return `${parent}${child}`;
97
- }
98
- };
99
- //#endregion
4
+ import { a as DefaultExceptionHandler, r as StratalNotInitializedError } from "./errors-mXYxG0XB.mjs";
5
+ import { i as createQueueExceptionContext, n as createCronExceptionContext, t as createCliExceptionContext } from "./exception-context-kEoMFwze.mjs";
6
+ import { a as getListenerHandlers } from "./events-BXJGZjpG.mjs";
7
+ import { d as LazyModuleLoader, t as ModuleRegistry } from "./module-registry-Dm-pqHd3.mjs";
8
+ import { t as SEEDER_TOKENS } from "./seeder-registry-CyUmKsJq.mjs";
100
9
  //#region src/application.ts
101
10
  var Application = class {
102
11
  _container;
@@ -107,7 +16,10 @@ var Application = class {
107
16
  quarry;
108
17
  initialized = false;
109
18
  routingInitPromise = null;
110
- handlerInitPromise = null;
19
+ eventsInitPromise = null;
20
+ queueInitPromise = null;
21
+ i18nInitPromise = null;
22
+ cronInitPromise = null;
111
23
  env;
112
24
  appConfig;
113
25
  constructor({ env, ctx, ...config }) {
@@ -139,48 +51,109 @@ var Application = class {
139
51
  await runWithContainer(this._container, () => this.initializeInternal());
140
52
  }
141
53
  async initializeInternal() {
142
- this.moduleRegistry.registerAll([
143
- I18nModule,
144
- QueueModule,
145
- CacheModule
146
- ]);
54
+ const [{ QuarryModule }, { SeederModule }] = await Promise.all([import("./quarry.module-BuRPGMDm.mjs").then((n) => n.n), import("./seeder.module-CYYwk3Qk.mjs").then((n) => n.n)]);
55
+ this.moduleRegistry.registerAll([QuarryModule, SeederModule]);
147
56
  this.moduleRegistry.register(this.appConfig.module);
148
57
  await this.moduleRegistry.initialize();
149
58
  this.initializeExceptionHandler();
150
- this.cronManager = this._container.resolve(DI_TOKENS.Cron);
151
- this.registerCronJobs();
152
- this.registerSeeders();
153
59
  this.registerCommands();
60
+ this.registerSeeders();
61
+ if (this.moduleRegistry.getAllListeners().length > 0) await this.initializeEventListeners();
62
+ if (this.moduleRegistry.getAllJobs().length > 0) await this.ensureCron();
154
63
  this.initialized = true;
155
64
  }
156
- registerRoutingServices() {
65
+ async registerRoutingServices() {
66
+ const [{ HonoApp }, { RouteRegistry }, { RouterResolver }, { VersioningService }, { LocalePathService }, { LocaleUrlService }, { RouteRegistrationService }, { Uri }] = await Promise.all([
67
+ import("./hono-app-CvV3hOfT.mjs").then((n) => n.n),
68
+ import("./route-registry-CYqLp2Nj.mjs").then((n) => n.n),
69
+ import("./router-resolver-D4YlPNlm.mjs"),
70
+ import("./versioning.service-C6aHky8-.mjs").then((n) => n.n),
71
+ import("./locale-path.service-D-dHiIPc.mjs").then((n) => n.n),
72
+ import("./locale-url.service-C2EWmGdq.mjs").then((n) => n.n),
73
+ import("./route-registration.service-D6vSwiKP.mjs").then((n) => n.n),
74
+ import("./uri-h7Q8Jug9.mjs").then((n) => n.r)
75
+ ]);
157
76
  this._container.register(ROUTER_TOKENS.VersioningService, VersioningService);
158
77
  this._container.register(ROUTER_TOKENS.HonoApp, HonoApp);
159
78
  this._container.register(ROUTER_TOKENS.LocalePathService, LocalePathService);
79
+ this._container.register(ROUTER_TOKENS.LocaleUrlService, LocaleUrlService);
160
80
  this._container.register(ROUTER_TOKENS.RouteRegistry, RouteRegistry);
161
81
  this._container.register(ROUTER_TOKENS.Uri, Uri);
162
82
  const routerConfigs = this.moduleRegistry.getAllRouterConfigs();
163
83
  if (routerConfigs.length > 0) this._container.registerValue(ROUTER_TOKENS.RouterResolver, new RouterResolver(routerConfigs));
164
84
  this._container.register(RouteRegistrationService, RouteRegistrationService);
165
85
  }
166
- async initializeHandlers() {
167
- this.handlerInitPromise ??= runWithContainer(this._container, () => {
168
- this.consumerRegistry = this._container.resolve(DI_TOKENS.ConsumerRegistry);
169
- this.registerQueueConsumers();
86
+ /**
87
+ * Load the events subsystem and wire listeners. Needed by the HTTP, queue,
88
+ * and scheduled paths (handlers emit events). Independent of the queue
89
+ * subsystem so HTTP-only apps never load queue code. EventsModule is
90
+ * registered before the (possibly empty) listener wiring so `emit()` works
91
+ * even with zero listeners; dedups against the framework's own load.
92
+ */
93
+ initializeEventListeners() {
94
+ this.eventsInitPromise ??= runWithContainer(this._container, async () => {
95
+ const { EventsModule } = await import("./events-BXJGZjpG.mjs").then((n) => n.n);
96
+ await this.moduleRegistry.registerLazy(EventsModule);
170
97
  this.registerEventListeners();
171
- return Promise.resolve();
172
98
  });
173
- return this.handlerInitPromise;
99
+ return this.eventsInitPromise;
174
100
  }
175
- initializeRouting() {
176
- this.routingInitPromise ??= runWithContainer(this._container, async () => {
177
- await this.initializeHandlers();
178
- this.moduleRegistry.register(OpenAPIModule);
179
- this.registerRoutingServices();
180
- this.honoApp = this._container.resolve(ROUTER_TOKENS.HonoApp);
181
- await this.honoApp.configure();
101
+ /**
102
+ * Wire event and queue handlers for any non-HTTP request scope — queue
103
+ * batches, scheduled/cron runs, CLI commands, Durable Objects, Workflows,
104
+ * WorkerEntrypoints — all of which may emit events or dispatch to queues from
105
+ * arbitrary user code. This is the single source of truth for that wiring:
106
+ * every non-HTTP entry point routes through it rather than open-coding which
107
+ * subsystems to init, so a subsystem can't be silently dropped by a future
108
+ * refactor (the queue half was once lost from the command path that way).
109
+ *
110
+ * HTTP (`fetch`) deliberately does NOT use this: a fetch worker only enqueues
111
+ * to the async Cloudflare queue and never processes consumers inline, so it
112
+ * skips queue init via `initializeRouting`.
113
+ */
114
+ async ensureScopedHandlers() {
115
+ await this.initializeEventListeners();
116
+ await this.initializeQueue();
117
+ }
118
+ /**
119
+ * Load the queue subsystem on demand (first queue/scheduled trigger). i18n is
120
+ * ensured first because the queue registry depends on it.
121
+ */
122
+ initializeQueue() {
123
+ this.queueInitPromise ??= runWithContainer(this._container, async () => {
124
+ await this.ensureI18n();
125
+ const { QueueModule } = await import("./queue.module-nddvxzCB.mjs").then((n) => n.n);
126
+ this.moduleRegistry.register(QueueModule);
127
+ this.consumerRegistry = this._container.resolve(DI_TOKENS.ConsumerRegistry);
128
+ await this.registerQueueConsumers();
182
129
  });
183
- return this.routingInitPromise;
130
+ return this.queueInitPromise;
131
+ }
132
+ /**
133
+ * Load i18n on demand. Coupled to the request path (Zod validation error maps,
134
+ * OpenAPI descriptions, queue registry), so it loads before routing/queue
135
+ * handling. Uses `registerLazy` because I18nModule has an `onInitialize` hook
136
+ * (configures the Zod error map). Dedups if the app already imported i18n.
137
+ */
138
+ ensureI18n() {
139
+ this.i18nInitPromise ??= runWithContainer(this._container, async () => {
140
+ const { I18nModule } = await import("./i18n.module-DRQAZoSZ.mjs").then((n) => n.n);
141
+ await this.moduleRegistry.registerLazy(I18nModule);
142
+ });
143
+ return this.i18nInitPromise;
144
+ }
145
+ /**
146
+ * Load the cron subsystem on demand (first scheduled trigger, or at bootstrap
147
+ * when the app declares jobs).
148
+ */
149
+ ensureCron() {
150
+ this.cronInitPromise ??= runWithContainer(this._container, async () => {
151
+ const { CronModule } = await import("./cron.module-Bgzq5hiT.mjs").then((n) => n.n);
152
+ this.moduleRegistry.register(CronModule);
153
+ this.cronManager = this._container.resolve(DI_TOKENS.Cron);
154
+ this.registerCronJobs(this.cronManager);
155
+ });
156
+ return this.cronInitPromise.then(() => this.cronManager);
184
157
  }
185
158
  resolve(token) {
186
159
  try {
@@ -193,7 +166,7 @@ var Application = class {
193
166
  }
194
167
  }
195
168
  async handleQueue(batch, queueName) {
196
- await this.initializeHandlers();
169
+ await this.ensureScopedHandlers();
197
170
  const locale = (batch.messages[0]?.body)?.metadata?.locale ?? "en";
198
171
  const mockRouterContext = this.createMockRouterContext(locale);
199
172
  await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {
@@ -206,11 +179,13 @@ var Application = class {
206
179
  });
207
180
  }
208
181
  async handleScheduled(controller) {
209
- await this.initializeHandlers();
182
+ const cronManager = await this.ensureCron();
183
+ await this.ensureScopedHandlers();
184
+ await this.ensureI18n();
210
185
  const mockRouterContext = this.createMockRouterContext("en");
211
186
  await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {
212
187
  try {
213
- await this.cronManager.executeScheduled(controller, requestContainer);
188
+ await cronManager.executeScheduled(controller, requestContainer);
214
189
  } catch (error) {
215
190
  await requestContainer.resolve(DI_TOKENS.ExceptionHandler).handle(error, createCronExceptionContext());
216
191
  throw error;
@@ -221,7 +196,7 @@ var Application = class {
221
196
  return {
222
197
  getLocale: () => locale,
223
198
  setLocale: () => {},
224
- getContainer: () => this._container
199
+ getContainer: () => containerStorage.getStore() ?? this._container
225
200
  };
226
201
  }
227
202
  async shutdown() {
@@ -233,12 +208,25 @@ var Application = class {
233
208
  }
234
209
  async handleCommand(name, input) {
235
210
  await this.initializeRouting();
211
+ await this.ensureScopedHandlers();
236
212
  this.quarry ??= this._container.resolve(DI_TOKENS.Quarry);
237
213
  const mockContext = this.createMockRouterContext("en");
238
214
  return this._container.runInRequestScope(mockContext, async () => {
239
215
  return this.quarry.call(name, input);
240
216
  });
241
217
  }
218
+ initializeRouting() {
219
+ this.routingInitPromise ??= runWithContainer(this._container, async () => {
220
+ await this.initializeEventListeners();
221
+ await this.ensureI18n();
222
+ const { OpenAPIModule } = await import("./openapi/index.mjs");
223
+ this.moduleRegistry.register(OpenAPIModule);
224
+ await this.registerRoutingServices();
225
+ this.honoApp = this._container.resolve(ROUTER_TOKENS.HonoApp);
226
+ await this.honoApp.configure();
227
+ });
228
+ return this.routingInitPromise;
229
+ }
242
230
  registerCommands() {
243
231
  this.quarry ??= this._container.resolve(DI_TOKENS.Quarry);
244
232
  const commands = this.moduleRegistry.getAllCommands();
@@ -250,16 +238,22 @@ var Application = class {
250
238
  const registry = this._container.resolve(SEEDER_TOKENS.SeederRegistry);
251
239
  for (const SeederClass of seeders) registry.register(SeederClass);
252
240
  }
253
- registerQueueConsumers() {
254
- for (const ConsumerClass of this.moduleRegistry.getAllConsumers()) {
255
- const consumer = this._container.resolve(ConsumerClass);
256
- this.consumerRegistry.register(consumer);
257
- }
241
+ async registerQueueConsumers() {
242
+ const consumerClasses = this.moduleRegistry.getAllConsumers();
243
+ if (consumerClasses.length === 0) return;
244
+ const mockContext = this.createMockRouterContext("en");
245
+ await this._container.runInRequestScope(mockContext, (requestContainer) => {
246
+ for (const ConsumerClass of consumerClasses) {
247
+ const consumer = requestContainer.resolve(ConsumerClass);
248
+ this.consumerRegistry.register(ConsumerClass, consumer.messageTypes);
249
+ }
250
+ });
258
251
  }
259
- registerCronJobs() {
252
+ registerCronJobs(cronManager) {
260
253
  for (const JobClass of this.moduleRegistry.getAllJobs()) {
261
254
  const schedule = JobClass.schedule;
262
- if (schedule) this.cronManager.registerJob(schedule, JobClass);
255
+ if (schedule) cronManager.registerJob(schedule, JobClass);
256
+ else this._container.resolve(LOGGER_TOKENS.LoggerService).warn(`Cron job "${JobClass.name}" has no static schedule property — skipped`);
263
257
  }
264
258
  }
265
259
  registerEventListeners() {
@@ -267,9 +261,13 @@ var Application = class {
267
261
  if (listeners.length === 0) return;
268
262
  const eventRegistry = this._container.resolve(DI_TOKENS.EventRegistry);
269
263
  for (const ListenerClass of listeners) {
270
- const instance = this._container.resolve(ListenerClass);
271
264
  const handlers = getListenerHandlers(ListenerClass);
272
- for (const { methodName, event, options } of handlers) eventRegistry.on(event, instance[methodName].bind(instance), options);
265
+ for (const { methodName, event, options } of handlers) {
266
+ const handler = ((...args) => {
267
+ return getContainer().resolve(ListenerClass)[methodName](...args);
268
+ });
269
+ eventRegistry.on(event, handler, options);
270
+ }
273
271
  }
274
272
  }
275
273
  registerLoggerService() {
@@ -279,12 +277,16 @@ var Application = class {
279
277
  this._container.when(() => formatter === "pretty").use(LOGGER_TOKENS.Formatter).give(PrettyFormatter).otherwise(JsonFormatter);
280
278
  this._container.registerSingleton(LOGGER_TOKENS.LoggerService, LoggerService);
281
279
  }
280
+ /**
281
+ * Bootstrap kernel — registered imperatively because they cannot be expressed
282
+ * as ordinary module providers: ExceptionHandler is a user-overridable forced
283
+ * singleton (a module ClassProvider derives scope from the class decorator,
284
+ * which a user handler may not carry), and LazyModuleLoader is the loader
285
+ * itself. Subsystem registries (events/cron/quarry/seeder) are modules.
286
+ */
282
287
  registerCoreServices() {
283
- this._container.registerSingleton(DI_TOKENS.Cron, CronManager);
284
288
  this._container.registerSingleton(DI_TOKENS.ExceptionHandler, this.appConfig.exceptionHandler ?? DefaultExceptionHandler);
285
- this._container.registerSingleton(DI_TOKENS.EventRegistry, EventRegistry);
286
- this._container.registerSingleton(DI_TOKENS.Quarry, QuarryRegistry);
287
- this._container.registerValue(SEEDER_TOKENS.SeederRegistry, new SeederRegistry(this));
289
+ this._container.registerSingleton(DI_TOKENS.LazyModuleLoader, LazyModuleLoader);
288
290
  }
289
291
  initializeExceptionHandler() {
290
292
  const handler = this._container.resolve(DI_TOKENS.ExceptionHandler);
@@ -378,4 +380,4 @@ var Stratal = class Stratal {
378
380
  //#endregion
379
381
  export { Application as n, Stratal as t };
380
382
 
381
- //# sourceMappingURL=stratal-Bdq4IdB3.mjs.map
383
+ //# sourceMappingURL=stratal-DL9M38_s.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stratal-DL9M38_s.mjs","names":[],"sources":["../src/application.ts","../src/stratal.ts"],"sourcesContent":["import type { CronJob } from './cron/cron-job'\nimport type { CronManager } from './cron/cron-manager'\nimport { Container } from './di/container'\nimport { containerStorage, getContainer, runWithContainer } from './di/container-storage'\nimport { DI_TOKENS } from './di/tokens'\nimport { type StratalEnv } from './env'\nimport { DefaultExceptionHandler } from './errors/default-exception-handler'\nimport { createCliExceptionContext, createCronExceptionContext, createQueueExceptionContext } from './errors/exception-context'\nimport type { ExceptionHandler } from './errors/exception-handler'\nimport type { EventHandler, EventRegistry } from './events'\nimport { getListenerHandlers } from './events'\nimport type { StratalExecutionContext } from './execution-context'\nimport { JsonFormatter, LOGGER_TOKENS, LoggerService, LogLevel, PrettyFormatter } from './logger'\nimport { LazyModuleLoader } from './module/lazy-module-loader'\nimport { ModuleRegistry } from './module/module-registry'\nimport type { DynamicModule, ModuleClass } from './module/types'\nimport type { Command } from './quarry/command'\nimport type { QuarryRegistry } from './quarry/quarry-registry'\nimport type { CommandInput, CommandResult } from './quarry/types'\nimport type { ConsumerRegistry } from './queue/consumer-registry'\nimport type { IQueueConsumer, QueueMessage } from './queue/queue-consumer'\nimport type { QueueManager } from './queue/queue-manager'\nimport type { RouterContext } from './router'\nimport type { HonoApp } from './router/hono-app'\nimport { ROUTER_TOKENS } from './router/router.tokens'\nimport type { TrailingSlashMode, VersioningOptions } from './router/types'\nimport type { Seeder } from './seeder/seeder'\nimport { SEEDER_TOKENS } from './seeder/seeder-registry'\nimport type { SeederRegistry } from './seeder/seeder-registry'\nimport type { Constructor } from './types'\n\nexport interface ApplicationConfig {\n module: ModuleClass | DynamicModule\n logging?: {\n level?: LogLevel\n formatter?: 'json' | 'pretty'\n }\n versioning?: VersioningOptions\n trailingSlash?: TrailingSlashMode\n exceptionHandler?: Constructor<ExceptionHandler>\n}\n\nexport interface ApplicationOptions extends ApplicationConfig {\n env: StratalEnv\n ctx: StratalExecutionContext\n}\n\nexport class Application {\n private _container: Container\n private honoApp!: HonoApp\n private moduleRegistry: ModuleRegistry\n private consumerRegistry!: ConsumerRegistry\n private cronManager?: CronManager\n private quarry!: QuarryRegistry\n private initialized = false\n\n // Independently memoized lazy-init promises — each built-in subsystem is\n // loaded on demand at its trigger point to keep cold start lean.\n private routingInitPromise: Promise<void> | null = null\n private eventsInitPromise: Promise<void> | null = null\n private queueInitPromise: Promise<void> | null = null\n private i18nInitPromise: Promise<void> | null = null\n private cronInitPromise: Promise<void> | null = null\n\n readonly env: StratalEnv\n private readonly appConfig: ApplicationConfig\n\n constructor({ env, ctx, ...config }: ApplicationOptions) {\n this.env = env\n this.appConfig = config\n\n this._container = new Container()\n\n this._container.registerValue(DI_TOKENS.Application, this)\n this._container.registerValue(DI_TOKENS.CloudflareEnv, env)\n this._container.registerValue(DI_TOKENS.ExecutionContext, ctx)\n this._container.registerValue(ROUTER_TOKENS.RouterContext, null)\n\n this.registerLoggerService()\n this.registerCoreServices()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n this.moduleRegistry = new ModuleRegistry(this._container, logger)\n\n this._container.registerValue(DI_TOKENS.ModuleRegistry, this.moduleRegistry)\n }\n\n get container(): Container {\n return this._container\n }\n\n async ensureHono(): Promise<HonoApp> {\n await this.initializeRouting()\n return this.honoApp\n }\n\n get config(): ApplicationConfig {\n return this.appConfig\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return\n }\n\n await runWithContainer(this._container, () => this.initializeInternal())\n }\n\n private async initializeInternal(): Promise<void> {\n // Eager subsystem modules — Quarry/Seeder are resolved synchronously\n // post-init (CLI runner, test harness), so they must be registered before\n // initialize() completes. Dynamic import keeps application.ts free of static\n // subsystem imports (uniform with the other built-ins). They load at boot\n // regardless, so this is for consistency, not cold-start deferral.\n const [{ QuarryModule }, { SeederModule }] = await Promise.all([\n import('./quarry/quarry.module'),\n import('./seeder/seeder.module'),\n ])\n this.moduleRegistry.registerAll([QuarryModule, SeederModule])\n\n // Register the user's root module (traverses imports). Other built-in\n // subsystems (i18n, queue, cache, openapi, cron, events, router) load on\n // demand at their trigger points.\n this.moduleRegistry.register(this.appConfig.module)\n\n // Initialize all modules (only those with lifecycle hooks)\n await this.moduleRegistry.initialize()\n\n // Initialize ExceptionHandler\n this.initializeExceptionHandler()\n\n // Register CLI commands + seeders (class refs only; no instantiation)\n this.registerCommands()\n this.registerSeeders()\n\n // Wire event listeners as part of initialization whenever the application\n // declares any, so emitted events reach their handlers regardless of which\n // entry point drives the app. Listener wiring otherwise happens lazily only\n // on the HTTP router (`initializeRouting`) and queue/scheduled/command\n // (`ensureScopedHandlers`) paths; any code that emits events without going\n // through one of those — a direct service or repository call, an RPC\n // entrypoint, or a Durable Object — would dispatch into a registry with no\n // handlers attached, silently dropping the event. Guarded on there being\n // listeners so applications with none still skip loading the events\n // subsystem; `initializeEventListeners` dedups, so any later lazy trigger\n // becomes a no-op.\n if (this.moduleRegistry.getAllListeners().length > 0) {\n await this.initializeEventListeners()\n }\n\n // Cron only loads when the app actually declares scheduled jobs\n if (this.moduleRegistry.getAllJobs().length > 0) {\n await this.ensureCron()\n }\n\n this.initialized = true\n }\n\n private async registerRoutingServices(): Promise<void> {\n const [\n { HonoApp },\n { RouteRegistry },\n { RouterResolver },\n { VersioningService },\n { LocalePathService },\n { LocaleUrlService },\n { RouteRegistrationService },\n { Uri },\n ] = await Promise.all([\n import('./router/hono-app'),\n import('./router/route-registry'),\n import('./router/router-resolver'),\n import('./router/services/versioning.service'),\n import('./router/services/locale-path.service'),\n import('./router/services/locale-url.service'),\n import('./router/services/route-registration.service'),\n import('./router/uri'),\n ])\n\n this._container.register(ROUTER_TOKENS.VersioningService, VersioningService)\n this._container.register(ROUTER_TOKENS.HonoApp, HonoApp)\n this._container.register(ROUTER_TOKENS.LocalePathService, LocalePathService)\n this._container.register(ROUTER_TOKENS.LocaleUrlService, LocaleUrlService)\n this._container.register(ROUTER_TOKENS.RouteRegistry, RouteRegistry)\n this._container.register(ROUTER_TOKENS.Uri, Uri)\n\n const routerConfigs = this.moduleRegistry.getAllRouterConfigs()\n if (routerConfigs.length > 0) {\n this._container.registerValue(ROUTER_TOKENS.RouterResolver, new RouterResolver(routerConfigs))\n }\n\n this._container.register(RouteRegistrationService, RouteRegistrationService)\n }\n\n /**\n * Load the events subsystem and wire listeners. Needed by the HTTP, queue,\n * and scheduled paths (handlers emit events). Independent of the queue\n * subsystem so HTTP-only apps never load queue code. EventsModule is\n * registered before the (possibly empty) listener wiring so `emit()` works\n * even with zero listeners; dedups against the framework's own load.\n */\n private initializeEventListeners(): Promise<void> {\n this.eventsInitPromise ??= runWithContainer(this._container, async () => {\n const { EventsModule } = await import('./events/events.module')\n await this.moduleRegistry.registerLazy(EventsModule)\n this.registerEventListeners()\n })\n return this.eventsInitPromise\n }\n\n /**\n * Wire event and queue handlers for any non-HTTP request scope — queue\n * batches, scheduled/cron runs, CLI commands, Durable Objects, Workflows,\n * WorkerEntrypoints — all of which may emit events or dispatch to queues from\n * arbitrary user code. This is the single source of truth for that wiring:\n * every non-HTTP entry point routes through it rather than open-coding which\n * subsystems to init, so a subsystem can't be silently dropped by a future\n * refactor (the queue half was once lost from the command path that way).\n *\n * HTTP (`fetch`) deliberately does NOT use this: a fetch worker only enqueues\n * to the async Cloudflare queue and never processes consumers inline, so it\n * skips queue init via `initializeRouting`.\n */\n async ensureScopedHandlers(): Promise<void> {\n await this.initializeEventListeners()\n await this.initializeQueue()\n }\n\n /**\n * Load the queue subsystem on demand (first queue/scheduled trigger). i18n is\n * ensured first because the queue registry depends on it.\n */\n private initializeQueue(): Promise<void> {\n this.queueInitPromise ??= runWithContainer(this._container, async () => {\n await this.ensureI18n()\n const { QueueModule } = await import('./queue/queue.module')\n this.moduleRegistry.register(QueueModule as unknown as ModuleClass)\n this.consumerRegistry = this._container.resolve<ConsumerRegistry>(DI_TOKENS.ConsumerRegistry)\n await this.registerQueueConsumers()\n })\n return this.queueInitPromise\n }\n\n /**\n * Load i18n on demand. Coupled to the request path (Zod validation error maps,\n * OpenAPI descriptions, queue registry), so it loads before routing/queue\n * handling. Uses `registerLazy` because I18nModule has an `onInitialize` hook\n * (configures the Zod error map). Dedups if the app already imported i18n.\n */\n private ensureI18n(): Promise<void> {\n this.i18nInitPromise ??= runWithContainer(this._container, async () => {\n const { I18nModule } = await import('./i18n/i18n.module')\n await this.moduleRegistry.registerLazy(I18nModule as unknown as ModuleClass)\n })\n return this.i18nInitPromise\n }\n\n /**\n * Load the cron subsystem on demand (first scheduled trigger, or at bootstrap\n * when the app declares jobs).\n */\n private ensureCron(): Promise<CronManager> {\n this.cronInitPromise ??= runWithContainer(this._container, async () => {\n const { CronModule } = await import('./cron/cron.module')\n this.moduleRegistry.register(CronModule)\n this.cronManager = this._container.resolve<CronManager>(DI_TOKENS.Cron)\n this.registerCronJobs(this.cronManager)\n })\n return this.cronInitPromise.then(() => this.cronManager!)\n }\n\n resolve<T>(token: symbol): T {\n try {\n return this._container.resolve(token)\n } catch (error) {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n const ctx = createCliExceptionContext('resolve')\n void handler.handle(error, ctx)\n throw error\n }\n }\n\n async handleQueue(batch: MessageBatch, queueName: string): Promise<void> {\n await this.ensureScopedHandlers()\n\n const firstMessage = batch.messages[0]?.body as QueueMessage | undefined\n const locale = firstMessage?.metadata?.locale ?? 'en'\n const mockRouterContext = this.createMockRouterContext(locale)\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n const queueManager = requestContainer.resolve<QueueManager>(DI_TOKENS.Queue)\n await queueManager.processBatch(queueName, batch)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createQueueExceptionContext(queueName))\n throw error\n }\n })\n }\n\n async handleScheduled(controller: ScheduledController): Promise<void> {\n const cronManager = await this.ensureCron()\n // A scheduled job is a non-HTTP scope that can emit events and dispatch to\n // queues (timeout/reminder/digest emails) — wire both so dispatches aren't\n // silently dropped by the sync provider. i18n is ensured independently for\n // the job execution context. All inits are memoized, so this is idempotent.\n await this.ensureScopedHandlers()\n await this.ensureI18n()\n\n const mockRouterContext = this.createMockRouterContext('en')\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n await cronManager.executeScheduled(controller, requestContainer)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createCronExceptionContext())\n throw error\n }\n })\n }\n\n createMockRouterContext(locale = 'en'): RouterContext {\n return {\n getLocale: () => locale,\n setLocale: () => { /* no-op */ },\n // Inside runInRequestScope the active container is the request-scoped child\n // (set in AsyncLocalStorage); return it so request-scoped providers resolve\n // against the scope, not the global container.\n getContainer: () => containerStorage.getStore() ?? this._container,\n } as unknown as RouterContext\n }\n\n async shutdown(): Promise<void> {\n if (!this.initialized) return\n this.initialized = false\n\n await this.moduleRegistry.shutdown()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.info('Disposing container...')\n\n this._container.dispose()\n }\n\n async handleCommand(name: string, input?: CommandInput): Promise<CommandResult> {\n // Routing first: commands generate URLs via route() (e.g. tenant:bootstrap\n // prints tenant URLs / builds email links).\n await this.initializeRouting()\n // A CLI command is a non-HTTP scope that can dispatch to queues and emit\n // events from arbitrary user code (e.g. tenant:bootstrap → email.send /\n // tenant.geo.seed). Without this the dev/CLI sync provider finds zero\n // consumers and silently drops every dispatched message. All inits are\n // memoized, so the events half (already wired by routing) is a no-op here.\n await this.ensureScopedHandlers()\n // Resolve QuarryRegistry lazily (deferred from bootstrap)\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const mockContext = this.createMockRouterContext('en')\n return this._container.runInRequestScope(mockContext, async () => {\n return this.quarry.call(name, input)\n })\n }\n\n private initializeRouting(): Promise<void> {\n this.routingInitPromise ??= runWithContainer(this._container, async () => {\n await this.initializeEventListeners()\n await this.ensureI18n()\n const { OpenAPIModule } = await import('./openapi')\n this.moduleRegistry.register(OpenAPIModule as unknown as ModuleClass)\n await this.registerRoutingServices()\n this.honoApp = this._container.resolve<HonoApp>(ROUTER_TOKENS.HonoApp)\n await this.honoApp.configure()\n })\n return this.routingInitPromise\n }\n\n private registerCommands(): void {\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const commands = this.moduleRegistry.getAllCommands()\n for (const CommandClass of commands) {\n this.quarry.register(CommandClass as Constructor<Command>)\n }\n }\n\n private registerSeeders(): void {\n const seeders = this.moduleRegistry.getAllSeeders()\n if (seeders.length === 0) return\n const registry = this._container.resolve<SeederRegistry>(SEEDER_TOKENS.SeederRegistry)\n for (const SeederClass of seeders) {\n registry.register(SeederClass as Constructor<Seeder>)\n }\n }\n\n private async registerQueueConsumers(): Promise<void> {\n const consumerClasses = this.moduleRegistry.getAllConsumers()\n if (consumerClasses.length === 0) return\n\n // Consumers are resolved fresh per message at dispatch time (see\n // QueueManager / SyncQueueProvider). Here we only need each consumer's\n // declared message types; read them from a throwaway instance built inside a\n // request scope so a consumer that injects request-scoped providers\n // (@InjectQueue, i18n, auth context, …) doesn't fail to instantiate at boot.\n const mockContext = this.createMockRouterContext('en')\n await this._container.runInRequestScope(mockContext, (requestContainer) => {\n for (const ConsumerClass of consumerClasses) {\n const consumer = requestContainer.resolve(ConsumerClass) as IQueueConsumer\n this.consumerRegistry.register(\n ConsumerClass as Constructor<IQueueConsumer>,\n consumer.messageTypes,\n )\n }\n })\n }\n\n private registerCronJobs(cronManager: CronManager): void {\n for (const JobClass of this.moduleRegistry.getAllJobs()) {\n const schedule = (JobClass as unknown as { schedule: string }).schedule\n if (schedule) {\n cronManager.registerJob(schedule, JobClass as Constructor<CronJob>)\n } else {\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.warn(`Cron job \"${JobClass.name}\" has no static schedule property — skipped`)\n }\n }\n }\n\n private registerEventListeners(): void {\n const listeners = this.moduleRegistry.getAllListeners()\n if (listeners.length === 0) {\n return\n }\n\n const eventRegistry = this._container.resolve<EventRegistry>(DI_TOKENS.EventRegistry)\n\n for (const ListenerClass of listeners) {\n const handlers = getListenerHandlers(ListenerClass)\n\n for (const { methodName, event, options } of handlers) {\n // Resolve the listener fresh per event from the active scope (events are\n // emitted inside an HTTP request or runInScope), so request-scoped\n // listener dependencies bind to the emitting request rather than being\n // frozen at boot. Listener metadata is read from the class statically —\n // no instance is created until an event actually fires.\n const handler: EventHandler = ((...args: unknown[]) => {\n const instance = getContainer().resolve(ListenerClass) as Record<\n string,\n (...a: unknown[]) => unknown\n >\n return instance[methodName](...args)\n }) as EventHandler\n\n eventRegistry.on(event, handler, options)\n }\n }\n }\n\n private registerLoggerService(): void {\n const logLevel = this.appConfig.logging?.level ?? LogLevel.INFO\n const formatter = this.appConfig.logging?.formatter ?? 'json'\n\n this._container.registerValue(LOGGER_TOKENS.LogLevelOptions, logLevel)\n\n this._container\n .when(() => formatter === 'pretty')\n .use(LOGGER_TOKENS.Formatter)\n .give(PrettyFormatter)\n .otherwise(JsonFormatter)\n\n this._container.registerSingleton(LOGGER_TOKENS.LoggerService, LoggerService)\n }\n\n /**\n * Bootstrap kernel — registered imperatively because they cannot be expressed\n * as ordinary module providers: ExceptionHandler is a user-overridable forced\n * singleton (a module ClassProvider derives scope from the class decorator,\n * which a user handler may not carry), and LazyModuleLoader is the loader\n * itself. Subsystem registries (events/cron/quarry/seeder) are modules.\n */\n private registerCoreServices(): void {\n this._container.registerSingleton(\n DI_TOKENS.ExceptionHandler,\n (this.appConfig.exceptionHandler ?? DefaultExceptionHandler) as Constructor,\n )\n this._container.registerSingleton(DI_TOKENS.LazyModuleLoader, LazyModuleLoader)\n }\n\n private initializeExceptionHandler(): void {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n handler.register()\n this.moduleRegistry.configureExceptionHandlers(handler)\n }\n}\n","import { Application, type ApplicationConfig } from './application'\nimport type { StratalEnv } from './env'\nimport { StratalNotInitializedError } from './errors'\nimport type { HonoApp } from './router/hono-app'\n\n/**\n * Stratal — Hono-style entry point for Cloudflare Workers.\n *\n * Eagerly bootstraps the Application at construction time, dynamically\n * importing `cloudflare:workers` for env and waitUntil.\n *\n * @example\n * ```typescript\n * import { Stratal } from 'stratal'\n * import { AppModule } from './app.module'\n *\n * export default new Stratal({ module: AppModule })\n * ```\n */\nexport class Stratal<Env extends StratalEnv = StratalEnv> {\n private app: Application | null = null\n private initPromise: Promise<Application>\n\n private static _application: Promise<Application> | null = null\n private static _generation = 0\n private static _previousInstance: Stratal | null = null\n\n constructor(config: ApplicationConfig) {\n this.fetch = this.fetch.bind(this)\n this.queue = this.queue.bind(this)\n this.scheduled = this.scheduled.bind(this)\n\n // Invalidate any in-flight initialization from a previous instance (Vite HMR reload)\n const generation = ++Stratal._generation\n\n if (Stratal._previousInstance) {\n void Stratal._previousInstance.shutdown()\n }\n Stratal._previousInstance = this\n\n this.initPromise = this.prepareApp(config, generation)\n Stratal._application = this.initPromise\n }\n\n async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {\n const app = await this.ensureReady()\n const hono = await app.ensureHono()\n return hono.fetch(request, env, ctx)\n }\n\n async queue(batch: MessageBatch): Promise<void> {\n const app = await this.ensureReady()\n return app.handleQueue(batch, batch.queue)\n }\n\n async scheduled(controller: ScheduledController): Promise<void> {\n const app = await this.ensureReady()\n return app.handleScheduled(controller)\n }\n\n get hono(): Promise<HonoApp> {\n return this.initPromise.then(app => app.ensureHono())\n }\n\n async shutdown(): Promise<void> {\n try { this.app = await this.initPromise } catch { /* ignore */ }\n if (this.app) {\n await this.app.shutdown()\n this.app = null\n }\n }\n\n /**\n * @internal\n * Resolves the Application instance from the static singleton.\n * Used by worker base classes (DurableObject, Workflow, WorkerEntrypoint)\n * to access the DI container without going through Cloudflare RPC.\n */\n static resolveApplication(): Promise<Application> {\n if (!Stratal._application) {\n throw new StratalNotInitializedError()\n }\n return Stratal._application\n }\n\n private async ensureReady(): Promise<Application> {\n this.app ??= await this.initPromise;\n return this.app\n }\n\n private async prepareApp(config: ApplicationConfig, generation: number): Promise<Application> {\n const { env, waitUntil } = await import('cloudflare:workers')\n\n // After async import, check if a newer instance has replaced us (Vite HMR reload)\n if (generation !== Stratal._generation) {\n return new Promise<Application>(() => {\n //\n }) // Never resolves — avoids cross-request promise warning\n }\n\n const app = new Application({ ...config, env: env as Env, ctx: { waitUntil } })\n await app.initialize()\n\n // Check again after initialization completes\n if (generation !== Stratal._generation) {\n await app.shutdown()\n return new Promise<Application>(() => {\n //\n })\n }\n\n return app\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { ct as Application, dn as HonoApp, lt as ApplicationConfig } from "./index-DEncMcC6.mjs";
2
- import { t as StratalEnv } from "./env-DKSbuBi5.mjs";
1
+ import { ct as Application, gn as HonoApp, lt as ApplicationConfig } from "./index-B_JoEl3V.mjs";
2
+ import { t as StratalEnv } from "./env-ug22bJj7.mjs";
3
3
 
4
4
  //#region src/stratal.d.ts
5
5
  /**
@@ -40,4 +40,4 @@ declare class Stratal<Env extends StratalEnv = StratalEnv> {
40
40
  }
41
41
  //#endregion
42
42
  export { Stratal as t };
43
- //# sourceMappingURL=stratal-BsKmvP6J.d.mts.map
43
+ //# sourceMappingURL=stratal-DwDJPY9N.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stratal-BsKmvP6J.d.mts","names":[],"sources":["../src/stratal.ts"],"mappings":";;;;;AAmBA;;;;;;;;;;;;;cAAa,OAAA,aAAoB,UAAA,GAAa,UAAA;EAAA,QACpC,GAAA;EAAA,QACA,WAAA;EAAA,eAEO,YAAA;EAAA,eACA,WAAA;EAAA,eACA,iBAAA;cAEH,MAAA,EAAQ,iBAAA;EAiBd,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,gBAAA,GAAmB,OAAA,CAAQ,QAAA;EAMlE,KAAA,CAAM,KAAA,EAAO,YAAA,GAAe,OAAA;EAK5B,SAAA,CAAU,UAAA,EAAY,mBAAA,GAAsB,OAAA;EAAA,IAK9C,IAAA,CAAA,GAAQ,OAAA,CAAQ,OAAA;EAId,QAAA,CAAA,GAAY,OAAA;EA5CV;;;;;;EAAA,OA0DD,kBAAA,CAAA,GAAsB,OAAA,CAAQ,WAAA;EAAA,QAOvB,WAAA;EAAA,QAKA,UAAA;AAAA"}
1
+ {"version":3,"file":"stratal-DwDJPY9N.d.mts","names":[],"sources":["../src/stratal.ts"],"mappings":";;;;;AAmBA;;;;;;;;;;;;;cAAa,OAAA,aAAoB,UAAA,GAAa,UAAA;EAAA,QACpC,GAAA;EAAA,QACA,WAAA;EAAA,eAEO,YAAA;EAAA,eACA,WAAA;EAAA,eACA,iBAAA;cAEH,MAAA,EAAQ,iBAAA;EAiBd,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,gBAAA,GAAmB,OAAA,CAAQ,QAAA;EAMlE,KAAA,CAAM,KAAA,EAAO,YAAA,GAAe,OAAA;EAK5B,SAAA,CAAU,UAAA,EAAY,mBAAA,GAAsB,OAAA;EAAA,IAK9C,IAAA,IAAQ,OAAA,CAAQ,OAAA;EAId,QAAA,IAAY,OAAA;EA5CV;;;;;;EAAA,OA0DD,kBAAA,IAAsB,OAAA,CAAQ,WAAA;EAAA,QAOvB,WAAA;EAAA,QAKA,UAAA;AAAA"}
@@ -0,0 +1,79 @@
1
+ import { t as CacheService } from "./cache.service-uElmBtdS.mjs";
2
+
3
+ //#region src/cache/services/tiered-cache.service.d.ts
4
+ /**
5
+ * Tiered Cache Service
6
+ *
7
+ * Layers an **isolate-local in-memory L1** over {@link CacheService} (KV, the
8
+ * L2). Opt-in — inject `CACHE_TOKENS.TieredCacheService` instead of
9
+ * `CACHE_TOKENS.CacheService` when you want it.
10
+ *
11
+ * **Why:** KV reads are eventually consistent (a `get` can return an
12
+ * edge-cached value for up to ~60s after a `put`). The L1 makes writes made
13
+ * through *this isolate* immediately and consistently visible to subsequent
14
+ * reads on the same isolate — closing the read-after-write gap KV alone cannot.
15
+ * This is what makes set-once patterns (e.g. queue idempotency markers) reliable
16
+ * within an isolate, even inside KV's consistency window.
17
+ *
18
+ * **Use it for:** set-once / read-mostly values (idempotency claims, immutable
19
+ * lookups). **Do not use it for** read-modify-write counters that need
20
+ * cross-edge freshness (e.g. rate limiting): an isolate that wrote a key reads
21
+ * its own value until the L1 entry expires, so concurrent increments from other
22
+ * isolates are missed and overwritten. Use plain {@link CacheService} (KV) or a
23
+ * Durable-Object store for those.
24
+ *
25
+ * **Semantics:**
26
+ * - L1 caches string-backed values only (`text`/`json`). `arrayBuffer`/`stream`
27
+ * reads and non-string writes bypass and invalidate L1.
28
+ * - `put`/`delete` are write-through: KV and L1 update in lock-step, honoring
29
+ * `expirationTtl`/`expiration` for the L1 entry's own expiry.
30
+ * - `get` populates L1 from `text` reads (the only path that yields the raw
31
+ * string). `json` reads are served from L1 when the string was cached by a
32
+ * prior `put`/`text` read, otherwise they go straight to KV.
33
+ * - `getWithMetadata` and `list` always read KV directly (metadata is not held
34
+ * in L1).
35
+ *
36
+ * **Cross-isolate caveat:** the L1 only observes writes made through its own
37
+ * isolate. A value mutated by another isolate is not seen until the local entry
38
+ * expires or is invalidated by a local write/delete.
39
+ *
40
+ * **Bindings:** `binding(name)` returns a memoized tiered instance per binding,
41
+ * so each KV namespace has its own stable, isolate-lifetime L1.
42
+ */
43
+ declare class TieredCacheService {
44
+ private readonly cache;
45
+ /** Isolate-local L1 tier. Per-instance, so each binding has its own. */
46
+ private readonly l1;
47
+ /** Memoized tiered instances per binding name (each with its own L1). */
48
+ private readonly children;
49
+ constructor(cache: CacheService);
50
+ /**
51
+ * Get the tiered cache bound to a KV namespace by its binding name. Memoized:
52
+ * repeated calls with the same name return the same instance, preserving its
53
+ * isolate-local L1 across requests/messages.
54
+ *
55
+ * @param name - KV namespace binding name (e.g. `'UPLOADS_CACHE'`)
56
+ * @throws {CacheError} If no binding with that name exists in the environment
57
+ */
58
+ binding(name: string): TieredCacheService;
59
+ get(key: string, typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>): Promise<string | null>;
60
+ get<ExpectedValue = unknown>(key: string, typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>): Promise<ExpectedValue | null>;
61
+ get(key: string, typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>): Promise<ArrayBuffer | null>;
62
+ get(key: string, typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>): Promise<ReadableStream | null>;
63
+ getWithMetadata<Metadata = unknown>(key: string, typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>): Promise<KVNamespaceGetWithMetadataResult<string, Metadata>>;
64
+ getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(key: string, typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>): Promise<KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>;
65
+ getWithMetadata<Metadata = unknown>(key: string, typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>): Promise<KVNamespaceGetWithMetadataResult<ArrayBuffer, Metadata>>;
66
+ getWithMetadata<Metadata = unknown>(key: string, typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>): Promise<KVNamespaceGetWithMetadataResult<ReadableStream, Metadata>>;
67
+ put(key: string, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise<void>;
68
+ delete(key: string): Promise<void>;
69
+ list<Metadata = unknown>(options?: KVNamespaceListOptions): Promise<KVNamespaceListResult<Metadata>>;
70
+ /** Read a fresh L1 entry, evicting it if expired. Returns `null` on miss. */
71
+ private l1Read;
72
+ /** Write an L1 entry, refreshing its recency and evicting the oldest at cap. */
73
+ private l1Write;
74
+ /** Compute an absolute L1 expiry (epoch ms) from KV put options. */
75
+ private l1ExpiresAt;
76
+ }
77
+ //#endregion
78
+ export { TieredCacheService as t };
79
+ //# sourceMappingURL=tiered-cache.service-Dv3BhxxE.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiered-cache.service-Dv3BhxxE.d.mts","names":[],"sources":["../src/cache/services/tiered-cache.service.ts"],"mappings":";;;;;AA0DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cACa,kBAAA;EAAA,iBAO2C,KAAA;EA4FnD;EAAA,iBAjGc,EAAA;EAsHL;EAAA,iBApHK,QAAA;cAGqC,KAAA,EAAO,YAAA;EAkH1D;;;;;;;;EAvGH,OAAA,CAAQ,IAAA,WAAe,kBAAA;EAWjB,GAAA,CAAI,GAAA,UAAa,aAAA,YAAyB,qBAAA,WAAgC,OAAA;EAC1E,GAAA,0BAA6B,GAAA,UAAa,aAAA,WAAwB,qBAAA,WAAgC,OAAA,CAAQ,aAAA;EAC1G,GAAA,CAAI,GAAA,UAAa,aAAA,kBAA+B,qBAAA,kBAAuC,OAAA,CAAQ,WAAA;EAC/F,GAAA,CAAI,GAAA,UAAa,aAAA,aAA0B,qBAAA,aAAkC,OAAA,CAAQ,cAAA;EA+BrF,eAAA,qBACJ,GAAA,UACA,aAAA,YAAyB,qBAAA,WACxB,OAAA,CAAQ,gCAAA,SAAyC,QAAA;EAC9C,eAAA,8CACJ,GAAA,UACA,aAAA,WAAwB,qBAAA,WACvB,OAAA,CAAQ,gCAAA,CAAiC,aAAA,EAAe,QAAA;EACrD,eAAA,qBACJ,GAAA,UACA,aAAA,kBAA+B,qBAAA,kBAC9B,OAAA,CAAQ,gCAAA,CAAiC,WAAA,EAAa,QAAA;EACnD,eAAA,qBACJ,GAAA,UACA,aAAA,aAA0B,qBAAA,aACzB,OAAA,CAAQ,gCAAA,CAAiC,cAAA,EAAgB,QAAA;EAiBtD,GAAA,CACJ,GAAA,UACA,KAAA,WAAgB,WAAA,GAAc,eAAA,GAAkB,cAAA,EAChD,OAAA,GAAU,qBAAA,GACT,OAAA;EAaG,MAAA,CAAO,GAAA,WAAc,OAAA;EAOrB,IAAA,qBACJ,OAAA,GAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA,CAAsB,QAAA;EA3FuC;EAAA,QAkGhE,MAAA;EAlGgG;EAAA,QA6GhG,OAAA;EA5GF;EAAA,QAuHE,WAAA;AAAA"}
@@ -0,0 +1,34 @@
1
+ //#region src/router/trailing-slash.ts
2
+ /**
3
+ * Apply a trailing-slash mode to a URL or path.
4
+ *
5
+ * - `'ignore'` — return as-is.
6
+ * - `'always'` — append `/` to the pathname unless it already has one.
7
+ * Skipped when the last segment contains `.` (file-like paths) and for the
8
+ * root `/` path.
9
+ * - `'never'` — strip a trailing `/` from the pathname. Skipped for root.
10
+ *
11
+ * Preserves query string and hash. Handles both relative paths
12
+ * (`/foo?x=1`) and absolute URLs (`https://host/foo?x=1`).
13
+ *
14
+ * Used by URL-generation helpers and the redirect middleware so canonical
15
+ * form is computed in one place.
16
+ */
17
+ function applyTrailingSlash(url, mode) {
18
+ if (mode === "ignore") return url;
19
+ const isAbsolute = /^https?:\/\//i.test(url);
20
+ const parsed = isAbsolute ? new URL(url) : new URL(url, "http://placeholder.local");
21
+ const path = parsed.pathname;
22
+ if (path === "/") return url;
23
+ const hasTrailing = path.endsWith("/");
24
+ if (mode === "always" && !hasTrailing) {
25
+ if (path.slice(path.lastIndexOf("/") + 1).includes(".")) return url;
26
+ parsed.pathname = `${path}/`;
27
+ } else if (mode === "never" && hasTrailing) parsed.pathname = path.slice(0, -1);
28
+ else return url;
29
+ return isAbsolute ? parsed.toString() : `${parsed.pathname}${parsed.search}${parsed.hash}`;
30
+ }
31
+ //#endregion
32
+ export { applyTrailingSlash as t };
33
+
34
+ //# sourceMappingURL=trailing-slash-CFyw8nYu.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trailing-slash-CFyw8nYu.mjs","names":[],"sources":["../src/router/trailing-slash.ts"],"sourcesContent":["import type { TrailingSlashMode } from './types'\n\n/**\n * Apply a trailing-slash mode to a URL or path.\n *\n * - `'ignore'` — return as-is.\n * - `'always'` — append `/` to the pathname unless it already has one.\n * Skipped when the last segment contains `.` (file-like paths) and for the\n * root `/` path.\n * - `'never'` — strip a trailing `/` from the pathname. Skipped for root.\n *\n * Preserves query string and hash. Handles both relative paths\n * (`/foo?x=1`) and absolute URLs (`https://host/foo?x=1`).\n *\n * Used by URL-generation helpers and the redirect middleware so canonical\n * form is computed in one place.\n */\nexport function applyTrailingSlash(url: string, mode: TrailingSlashMode): string {\n if (mode === 'ignore') return url\n\n const isAbsolute = /^https?:\\/\\//i.test(url)\n const parsed = isAbsolute\n ? new URL(url)\n : new URL(url, 'http://placeholder.local')\n\n const path = parsed.pathname\n if (path === '/') return url\n\n const hasTrailing = path.endsWith('/')\n\n if (mode === 'always' && !hasTrailing) {\n const lastSegment = path.slice(path.lastIndexOf('/') + 1)\n if (lastSegment.includes('.')) return url\n parsed.pathname = `${path}/`\n } else if (mode === 'never' && hasTrailing) {\n parsed.pathname = path.slice(0, -1)\n } else {\n return url\n }\n\n return isAbsolute\n ? parsed.toString()\n : `${parsed.pathname}${parsed.search}${parsed.hash}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,SAAgB,mBAAmB,KAAa,MAAiC;CAC/E,IAAI,SAAS,UAAU,OAAO;CAE9B,MAAM,aAAa,gBAAgB,KAAK,GAAG;CAC3C,MAAM,SAAS,aACX,IAAI,IAAI,GAAG,IACX,IAAI,IAAI,KAAK,0BAA0B;CAE3C,MAAM,OAAO,OAAO;CACpB,IAAI,SAAS,KAAK,OAAO;CAEzB,MAAM,cAAc,KAAK,SAAS,GAAG;CAErC,IAAI,SAAS,YAAY,CAAC,aAAa;EAErC,IADoB,KAAK,MAAM,KAAK,YAAY,GAAG,IAAI,CACzC,EAAE,SAAS,GAAG,GAAG,OAAO;EACtC,OAAO,WAAW,GAAG,KAAK;CAC5B,OAAO,IAAI,SAAS,WAAW,aAC7B,OAAO,WAAW,KAAK,MAAM,GAAG,EAAE;MAElC,OAAO;CAGT,OAAO,aACH,OAAO,SAAS,IAChB,GAAG,OAAO,WAAW,OAAO,SAAS,OAAO;AAClD"}
@@ -16,4 +16,4 @@
16
16
  type Constructor<T = object> = new (...args: any[]) => T;
17
17
  //#endregion
18
18
  export { Constructor as t };
19
- //# sourceMappingURL=types-BaeHi67f.d.mts.map
19
+ //# sourceMappingURL=types-CmV_9xBD.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-CmV_9xBD.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;AAeA;;;;;;;;AAA+D;;;;;KAAnD,WAAA,uBAAkC,IAAA,YAAgB,CAAC"}