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,158 @@
1
+ //#region src/response-cache/cached-entrypoint.ts
2
+ /** Methods Workers Caching will ever store. Everything else always runs inline. */
3
+ const CACHEABLE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
4
+ /**
5
+ * Whether the gateway should forward this request into the cached entrypoint.
6
+ *
7
+ * Anything that returns `false` runs inline in the gateway and never reaches
8
+ * the cached entrypoint at all — which is what keeps RFC 9111 heuristic
9
+ * freshness (a `200` with no `Cache-Control` cached for two hours) away from
10
+ * un-annotated routes.
11
+ */
12
+ function shouldLoopback(method, cacheable) {
13
+ if (!cacheable) return false;
14
+ return CACHEABLE_METHODS.has(method.toUpperCase());
15
+ }
16
+ //#endregion
17
+ //#region src/response-cache/representation.ts
18
+ /**
19
+ * Folding a response's *representation* into the cache key.
20
+ *
21
+ * A route may answer one URL with more than one body — an HTML document and a
22
+ * JSON payload for the same page, say — chosen from a request header. HTTP's
23
+ * answer is `Vary`, and Workers Caching documents it as honoured, so a
24
+ * response that declares `Vary: X-Inertia` should be stored once per distinct
25
+ * value of that header.
26
+ *
27
+ * Measured, it is not reliable: on a URL carrying a query string, a variant
28
+ * stored for one value of the header is served to a request sending another,
29
+ * so a browser navigation receives the JSON meant for a client-side visit.
30
+ * `Vary` is a matching pass applied to an entry that has already been located
31
+ * by key; `ctx.props` IS part of the key, and Cloudflare documents it as
32
+ * impossible to bypass — the same property that makes it the mechanism for
33
+ * per-caller partitioning.
34
+ *
35
+ * So the representation goes in the key. Two representations become two
36
+ * entries, and nothing downstream has to match correctly for the right one to
37
+ * come back. `Vary` is still emitted, because it is what tells the *browser*
38
+ * cache the same thing and the Inertia protocol requires it — it is simply no
39
+ * longer load-bearing here.
40
+ */
41
+ /**
42
+ * The `ctx.props` key the representation is carried under.
43
+ *
44
+ * `$`-prefixed so it cannot collide with a partition name: partitions are
45
+ * referenced from `partitionBy: ['user']` and are written as identifiers, and
46
+ * `assertPartitionNames` rejects the prefix outright rather than letting a
47
+ * collision silently overwrite one or the other.
48
+ */
49
+ const REPRESENTATION_PROP = "$representation";
50
+ /**
51
+ * Headers the platform varies on by itself, which therefore need no key of
52
+ * ours.
53
+ *
54
+ * Compression is negotiated and stored by Cloudflare independently of the
55
+ * response body the Worker returns, so `Vary: Accept-Encoding` — which almost
56
+ * every framework and proxy emits — describes something already handled. Left
57
+ * out of this list it would make nearly every response uncacheable.
58
+ */
59
+ const PLATFORM_HANDLED = /* @__PURE__ */ new Set(["accept-encoding"]);
60
+ /**
61
+ * The request's values for `keyBy`, as one deterministic string.
62
+ *
63
+ * Names are lowercased and sorted so that reordering `keyBy` does not
64
+ * re-partition a live cache, and each value is carried as `null` when the
65
+ * header is absent — a header that is missing and one sent empty are different
66
+ * requests and must not collapse onto one entry.
67
+ *
68
+ * Returns `undefined` when nothing is declared, so the common case adds no
69
+ * prop at all and leaves the key exactly as it was.
70
+ */
71
+ function representationOf(headers, keyBy) {
72
+ if (!keyBy || keyBy.length === 0) return void 0;
73
+ const names = [...new Set(keyBy.map((name) => name.toLowerCase()))].sort();
74
+ return JSON.stringify(names.map((name) => [name, headers.get(name)]));
75
+ }
76
+ /**
77
+ * The names a response says it varies on that are NOT in the cache key.
78
+ *
79
+ * Each one is a way for two different bodies to share one entry, so a response
80
+ * naming any of them is refused rather than stored — see `CacheabilityService`.
81
+ * `*` counts as unkeyed: Cloudflare treats `Vary: *` as uncacheable anyway, and
82
+ * it cannot be satisfied from request headers by definition.
83
+ */
84
+ function unkeyedVary(varyHeader, keyBy) {
85
+ if (!varyHeader) return [];
86
+ const keyed = new Set((keyBy ?? []).map((name) => name.toLowerCase()));
87
+ return varyHeader.split(",").map((name) => name.trim().toLowerCase()).filter(Boolean).filter((name) => !keyed.has(name) && !PLATFORM_HANDLED.has(name));
88
+ }
89
+ //#endregion
90
+ //#region src/response-cache/response-headers.ts
91
+ /**
92
+ * Set (or delete, via `null`) one or more headers on a `Response`, mutating
93
+ * in place whenever the header list allows it.
94
+ *
95
+ * A `Response`'s header list has a "guard" — per the Fetch spec, plain
96
+ * `Response`s (anything built with `new Response(...)`, which is what every
97
+ * Stratal controller returns) get guard `"response"`, which is mutable.
98
+ * `.set()`/`.delete()` only throw for guard `"immutable"`, which shows up for
99
+ * responses obtained from a `fetch()` subrequest or otherwise already handed
100
+ * off — not for a response this Worker constructed itself. That's the
101
+ * overwhelming majority of what flows through this function, so mutating in
102
+ * place avoids cloning the response (and re-wrapping its body stream) on
103
+ * every request just to add a `Cache-Control` header.
104
+ *
105
+ * Falls back to reconstructing the `Response` only when a `.set()`/`.delete()`
106
+ * call actually throws. Skips touching the response entirely outside the
107
+ * `[200, 599]` status range: the `Response` constructor throws a `RangeError`
108
+ * for a status outside that range (notably `101`), and — for a `101`
109
+ * WebSocket-upgrade response specifically — reconstructing would drop
110
+ * Cloudflare's `webSocket`/`cf` init fields the original carried, which
111
+ * `{ status, statusText, headers }` alone can't reproduce.
112
+ */
113
+ function setResponseHeaders(response, headers) {
114
+ if (response.status < 200 || response.status > 599) return response;
115
+ try {
116
+ for (const [name, value] of Object.entries(headers)) if (value === null) response.headers.delete(name);
117
+ else response.headers.set(name, value);
118
+ return response;
119
+ } catch {
120
+ const merged = new Headers(response.headers);
121
+ for (const [name, value] of Object.entries(headers)) if (value === null) merged.delete(name);
122
+ else merged.set(name, value);
123
+ return new Response(response.body, {
124
+ status: response.status,
125
+ statusText: response.statusText,
126
+ headers: merged
127
+ });
128
+ }
129
+ }
130
+ /** `public` as a whole directive, not the substring inside `no-cache="public"` or a field name. */
131
+ const PUBLIC_DIRECTIVE = /(?:^|,)\s*public\s*(?:,|$)/;
132
+ /**
133
+ * Whether a SHARED cache is permitted to store this response and serve it to a
134
+ * different caller.
135
+ *
136
+ * `public` and `s-maxage` are the two directives that say so outright (RFC 9111
137
+ * §5.2.2). Everything else is treated as not shared-cacheable: `private` and
138
+ * `no-store` say so explicitly, and a response with no `Cache-Control` at all
139
+ * never reaches a caller in a Stratal app without one — `createNoStoreFallback\
140
+ Middleware` stamps it first.
141
+ *
142
+ * `CDN-Cache-Control` is read the same way and counts on its own. It is the
143
+ * header a CDN honours in preference to `Cache-Control`, so a response can be
144
+ * stored and replayed to other callers on the strength of it alone — reading
145
+ * only `Cache-Control` would call such a response private and leave whatever
146
+ * this guards, per-caller headers included, to be served to everyone.
147
+ */
148
+ function isSharedCacheable(response) {
149
+ const says = (header) => {
150
+ const directives = response.headers.get(header)?.toLowerCase() ?? "";
151
+ return PUBLIC_DIRECTIVE.test(directives) || directives.includes("s-maxage");
152
+ };
153
+ return says("Cache-Control") || says("CDN-Cache-Control");
154
+ }
155
+ //#endregion
156
+ export { unkeyedVary as a, representationOf as i, setResponseHeaders as n, shouldLoopback as o, REPRESENTATION_PROP as r, isSharedCacheable as t };
157
+
158
+ //# sourceMappingURL=response-headers-BqA57S_X.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-headers-BqA57S_X.mjs","names":[],"sources":["../src/response-cache/cached-entrypoint.ts","../src/response-cache/representation.ts","../src/response-cache/response-headers.ts"],"sourcesContent":["import type { CacheableOptions } from './types'\n\n/** Methods Workers Caching will ever store. Everything else always runs inline. */\nconst CACHEABLE_METHODS = new Set(['GET', 'HEAD'])\n\n/**\n * Whether the gateway should forward this request into the cached entrypoint.\n *\n * Anything that returns `false` runs inline in the gateway and never reaches\n * the cached entrypoint at all — which is what keeps RFC 9111 heuristic\n * freshness (a `200` with no `Cache-Control` cached for two hours) away from\n * un-annotated routes.\n */\nexport function shouldLoopback(method: string, cacheable: CacheableOptions | undefined): boolean {\n if (!cacheable) return false\n return CACHEABLE_METHODS.has(method.toUpperCase())\n}\n","/**\n * Folding a response's *representation* into the cache key.\n *\n * A route may answer one URL with more than one body — an HTML document and a\n * JSON payload for the same page, say — chosen from a request header. HTTP's\n * answer is `Vary`, and Workers Caching documents it as honoured, so a\n * response that declares `Vary: X-Inertia` should be stored once per distinct\n * value of that header.\n *\n * Measured, it is not reliable: on a URL carrying a query string, a variant\n * stored for one value of the header is served to a request sending another,\n * so a browser navigation receives the JSON meant for a client-side visit.\n * `Vary` is a matching pass applied to an entry that has already been located\n * by key; `ctx.props` IS part of the key, and Cloudflare documents it as\n * impossible to bypass — the same property that makes it the mechanism for\n * per-caller partitioning.\n *\n * So the representation goes in the key. Two representations become two\n * entries, and nothing downstream has to match correctly for the right one to\n * come back. `Vary` is still emitted, because it is what tells the *browser*\n * cache the same thing and the Inertia protocol requires it — it is simply no\n * longer load-bearing here.\n */\n\n/**\n * The `ctx.props` key the representation is carried under.\n *\n * `$`-prefixed so it cannot collide with a partition name: partitions are\n * referenced from `partitionBy: ['user']` and are written as identifiers, and\n * `assertPartitionNames` rejects the prefix outright rather than letting a\n * collision silently overwrite one or the other.\n */\nexport const REPRESENTATION_PROP = '$representation'\n\n/**\n * Headers the platform varies on by itself, which therefore need no key of\n * ours.\n *\n * Compression is negotiated and stored by Cloudflare independently of the\n * response body the Worker returns, so `Vary: Accept-Encoding` — which almost\n * every framework and proxy emits — describes something already handled. Left\n * out of this list it would make nearly every response uncacheable.\n */\nconst PLATFORM_HANDLED = new Set(['accept-encoding'])\n\n/**\n * The request's values for `keyBy`, as one deterministic string.\n *\n * Names are lowercased and sorted so that reordering `keyBy` does not\n * re-partition a live cache, and each value is carried as `null` when the\n * header is absent — a header that is missing and one sent empty are different\n * requests and must not collapse onto one entry.\n *\n * Returns `undefined` when nothing is declared, so the common case adds no\n * prop at all and leaves the key exactly as it was.\n */\nexport function representationOf(\n headers: Headers,\n keyBy: readonly string[] | undefined,\n): string | undefined {\n if (!keyBy || keyBy.length === 0) return undefined\n\n const names = [...new Set(keyBy.map((name) => name.toLowerCase()))].sort()\n\n return JSON.stringify(names.map((name) => [name, headers.get(name)]))\n}\n\n/**\n * The names a response says it varies on that are NOT in the cache key.\n *\n * Each one is a way for two different bodies to share one entry, so a response\n * naming any of them is refused rather than stored — see `CacheabilityService`.\n * `*` counts as unkeyed: Cloudflare treats `Vary: *` as uncacheable anyway, and\n * it cannot be satisfied from request headers by definition.\n */\nexport function unkeyedVary(\n varyHeader: string | null,\n keyBy: readonly string[] | undefined,\n): string[] {\n if (!varyHeader) return []\n\n const keyed = new Set((keyBy ?? []).map((name) => name.toLowerCase()))\n\n return varyHeader\n .split(',')\n .map((name) => name.trim().toLowerCase())\n .filter(Boolean)\n .filter((name) => !keyed.has(name) && !PLATFORM_HANDLED.has(name))\n}\n","/**\n * Set (or delete, via `null`) one or more headers on a `Response`, mutating\n * in place whenever the header list allows it.\n *\n * A `Response`'s header list has a \"guard\" — per the Fetch spec, plain\n * `Response`s (anything built with `new Response(...)`, which is what every\n * Stratal controller returns) get guard `\"response\"`, which is mutable.\n * `.set()`/`.delete()` only throw for guard `\"immutable\"`, which shows up for\n * responses obtained from a `fetch()` subrequest or otherwise already handed\n * off — not for a response this Worker constructed itself. That's the\n * overwhelming majority of what flows through this function, so mutating in\n * place avoids cloning the response (and re-wrapping its body stream) on\n * every request just to add a `Cache-Control` header.\n *\n * Falls back to reconstructing the `Response` only when a `.set()`/`.delete()`\n * call actually throws. Skips touching the response entirely outside the\n * `[200, 599]` status range: the `Response` constructor throws a `RangeError`\n * for a status outside that range (notably `101`), and — for a `101`\n * WebSocket-upgrade response specifically — reconstructing would drop\n * Cloudflare's `webSocket`/`cf` init fields the original carried, which\n * `{ status, statusText, headers }` alone can't reproduce.\n */\nexport function setResponseHeaders(response: Response, headers: Record<string, string | null>): Response {\n if (response.status < 200 || response.status > 599) return response\n\n try {\n for (const [name, value] of Object.entries(headers)) {\n if (value === null) response.headers.delete(name)\n else response.headers.set(name, value)\n }\n return response\n } catch {\n const merged = new Headers(response.headers)\n for (const [name, value] of Object.entries(headers)) {\n if (value === null) merged.delete(name)\n else merged.set(name, value)\n }\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: merged,\n })\n }\n}\n\n/** `public` as a whole directive, not the substring inside `no-cache=\"public\"` or a field name. */\nconst PUBLIC_DIRECTIVE = /(?:^|,)\\s*public\\s*(?:,|$)/\n\n/**\n * Whether a SHARED cache is permitted to store this response and serve it to a\n * different caller.\n *\n * `public` and `s-maxage` are the two directives that say so outright (RFC 9111\n * §5.2.2). Everything else is treated as not shared-cacheable: `private` and\n * `no-store` say so explicitly, and a response with no `Cache-Control` at all\n * never reaches a caller in a Stratal app without one — `createNoStoreFallback\\\nMiddleware` stamps it first.\n *\n * `CDN-Cache-Control` is read the same way and counts on its own. It is the\n * header a CDN honours in preference to `Cache-Control`, so a response can be\n * stored and replayed to other callers on the strength of it alone — reading\n * only `Cache-Control` would call such a response private and leave whatever\n * this guards, per-caller headers included, to be served to everyone.\n */\nexport function isSharedCacheable(response: Response): boolean {\n const says = (header: string): boolean => {\n const directives = response.headers.get(header)?.toLowerCase() ?? ''\n return PUBLIC_DIRECTIVE.test(directives) || directives.includes('s-maxage')\n }\n\n return says('Cache-Control') || says('CDN-Cache-Control')\n}\n"],"mappings":";;AAGA,MAAM,oCAAoB,IAAI,IAAI,CAAC,OAAO,MAAM,CAAC;;;;;;;;;AAUjD,SAAgB,eAAe,QAAgB,WAAkD;CAC/F,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO,kBAAkB,IAAI,OAAO,YAAY,CAAC;AACnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACgBA,MAAa,sBAAsB;;;;;;;;;;AAWnC,MAAM,mCAAmB,IAAI,IAAI,CAAC,iBAAiB,CAAC;;;;;;;;;;;;AAapD,SAAgB,iBACd,SACA,OACoB;CACpB,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,KAAA;CAEzC,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;CAEzE,OAAO,KAAK,UAAU,MAAM,KAAK,SAAS,CAAC,MAAM,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC;AACtE;;;;;;;;;AAUA,SAAgB,YACd,YACA,OACU;CACV,IAAI,CAAC,YAAY,OAAO,CAAC;CAEzB,MAAM,QAAQ,IAAI,KAAK,SAAS,CAAC,EAAA,CAAG,KAAK,SAAS,KAAK,YAAY,CAAC,CAAC;CAErE,OAAO,WACJ,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CACxC,OAAO,OAAO,CAAC,CACf,QAAQ,SAAS,CAAC,MAAM,IAAI,IAAI,KAAK,CAAC,iBAAiB,IAAI,IAAI,CAAC;AACrE;;;;;;;;;;;;;;;;;;;;;;;;;AClEA,SAAgB,mBAAmB,UAAoB,SAAkD;CACvG,IAAI,SAAS,SAAS,OAAO,SAAS,SAAS,KAAK,OAAO;CAE3D,IAAI;EACF,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,GAChD,IAAI,UAAU,MAAM,SAAS,QAAQ,OAAO,IAAI;OAC3C,SAAS,QAAQ,IAAI,MAAM,KAAK;EAEvC,OAAO;CACT,QAAQ;EACN,MAAM,SAAS,IAAI,QAAQ,SAAS,OAAO;EAC3C,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,GAChD,IAAI,UAAU,MAAM,OAAO,OAAO,IAAI;OACjC,OAAO,IAAI,MAAM,KAAK;EAE7B,OAAO,IAAI,SAAS,SAAS,MAAM;GACjC,QAAQ,SAAS;GACjB,YAAY,SAAS;GACrB,SAAS;EACX,CAAC;CACH;AACF;;AAGA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;AAkBzB,SAAgB,kBAAkB,UAA6B;CAC7D,MAAM,QAAQ,WAA4B;EACxC,MAAM,aAAa,SAAS,QAAQ,IAAI,MAAM,CAAC,EAAE,YAAY,KAAK;EAClE,OAAO,iBAAiB,KAAK,UAAU,KAAK,WAAW,SAAS,UAAU;CAC5E;CAEA,OAAO,KAAK,eAAe,KAAK,KAAK,mBAAmB;AAC1D"}
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -0,0 +1,390 @@
1
+ import { _ as defineMetadata, v as getMetadata } from "./di-DQ3xbjgf.mjs";
2
+ import { u as ROUTE_METADATA_KEYS } from "./exception-context-CdnGTQNF.mjs";
3
+ import { r as ResponseCacheConfigError } from "./response-cache.error-B4D0zy9W.mjs";
4
+ //#region src/response-cache/gateway-binding.ts
5
+ /**
6
+ * Look up the configured cached entrypoint on `ctx.exports`, failing loudly if
7
+ * it isn't there.
8
+ *
9
+ * A typo in `gateway: { entrypoint: 'Cachd' }`, a missing `export const
10
+ * Cached = ...`, or a missing `enable_ctx_exports` compatibility flag would
11
+ * otherwise all present identically: every partitioned route quietly runs
12
+ * inline in the gateway forever, never caching, with no signal anywhere. The
13
+ * whole point of `partitionBy` refusing to boot without a gateway is that a
14
+ * declared partition is either honoured or it is an error — so this is an
15
+ * error too.
16
+ *
17
+ * @throws {ResponseCacheConfigError} `ctx.exports` is unavailable, or carries
18
+ * no export under `name`.
19
+ */
20
+ function resolveCachedEntrypoint(ctx, name) {
21
+ let exports;
22
+ try {
23
+ exports = ctx?.exports;
24
+ } catch (error) {
25
+ throw new ResponseCacheConfigError(`reading \`ctx.exports\` failed (${error instanceof Error ? error.message : String(error)}), so the cached entrypoint "${name}" cannot be reached. Add "enable_ctx_exports" to \`compatibility_flags\` in your Wrangler config.`);
26
+ }
27
+ if (typeof exports !== "object" || exports === null) throw new ResponseCacheConfigError(`\`ctx.exports\` is unavailable on this entrypoint, so the cached entrypoint "${name}" configured via \`ResponseCacheModule.forRoot({ gateway: { entrypoint } })\` cannot be reached. Add "enable_ctx_exports" to \`compatibility_flags\` in your Wrangler config, and set \`compatibility_date\` to 2026-07-06 or later.`);
28
+ const binding = exports[name];
29
+ if (binding === void 0 || binding === null) {
30
+ const available = Object.keys(exports);
31
+ throw new ResponseCacheConfigError(`the cached entrypoint "${name}" is not exported by this Worker. \`ResponseCacheModule.forRoot({ gateway: { entrypoint } })\` must name a top-level export — \`export const ${name} = cachedEntrypoint(stratal)\` — that is also declared in your Wrangler \`exports\` block with \`cache: { enabled: true }\`. Exports visible on \`ctx.exports\`: ${available.length > 0 ? available.join(", ") : "(none)"}.`);
32
+ }
33
+ return binding;
34
+ }
35
+ /**
36
+ * A `WorkersCache` that purges through the cached entrypoint over RPC.
37
+ *
38
+ * Handed to `ResponseCacheService.purge()` in place of the gateway's own
39
+ * `ctx.cache` whenever a gateway is configured and this request is running as
40
+ * the gateway. See `cachedEntrypoint` for why that redirection is required
41
+ * rather than merely tidy.
42
+ */
43
+ function createLoopbackPurgeTarget(ctx, name) {
44
+ return { purge: async (spec) => resolveCachedEntrypoint(ctx, name).purge(spec) };
45
+ }
46
+ //#endregion
47
+ //#region src/response-cache/response-cache.tokens.ts
48
+ const RESPONSE_CACHE_TOKENS = {
49
+ Options: Symbol.for("stratal:response-cache:options"),
50
+ ResponseCacheService: Symbol.for("stratal:response-cache:service"),
51
+ CacheabilityService: Symbol.for("stratal:response-cache:cacheability"),
52
+ PartitionResolverService: Symbol.for("stratal:response-cache:partition-resolver"),
53
+ GatewayPrimerService: Symbol.for("stratal:response-cache:gateway-primer"),
54
+ GatewayPrimers: Symbol.for("stratal:response-cache:gateway-primers"),
55
+ GatewayRouteTable: Symbol.for("stratal:response-cache:gateway-route-table")
56
+ };
57
+ //#endregion
58
+ //#region src/response-cache/decorators/cacheable.decorator.ts
59
+ const KEY$1 = ROUTE_METADATA_KEYS.CACHEABLE;
60
+ /**
61
+ * Mark a route's response as cacheable by Cloudflare Workers Caching.
62
+ *
63
+ * Only `GET` and `HEAD` are ever cached. Omitted fields fall back to the
64
+ * module's `defaults` block; an explicit value always wins.
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * @Get('/blog/:slug')
69
+ * @Cacheable({ ttl: 300, swr: 60, tags: ['post:{param.slug}'] })
70
+ * async show(ctx: RouterContext) { ... }
71
+ * ```
72
+ *
73
+ * Module defaults fill in whatever the route leaves out:
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * ResponseCacheModule.forRoot({ defaults: { ttl: 300, swr: 60 } })
78
+ *
79
+ * @Get('/pricing')
80
+ * @Cacheable() // ttl 300, swr 60
81
+ * async pricing(ctx: RouterContext) { ... }
82
+ * ```
83
+ *
84
+ * **A guarded route needs a `partitionBy`.** A guarded route's response differs
85
+ * per caller, so caching it under one shared entry would serve one user's
86
+ * response to another. `@Cacheable` on a route carrying `@UseGuards` is a boot
87
+ * error unless it declares a non-empty `partitionBy` (or inherits one from
88
+ * `defaults.partitionBy`) — and per-caller keying additionally requires
89
+ * `ResponseCacheModule.forRoot({ gateway: { entrypoint } })`, without which
90
+ * `partitionBy` itself is rejected at boot.
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * @Get('/dashboard')
95
+ * @UseGuards(AuthGuard)
96
+ * @Cacheable({ ttl: 60, partitionBy: ['user'] })
97
+ * async dashboard(ctx: RouterContext) { ... }
98
+ * ```
99
+ */
100
+ function Cacheable(options = {}) {
101
+ return (target, propertyKey) => {
102
+ defineMetadata(KEY$1, options, target, propertyKey);
103
+ };
104
+ }
105
+ /** Read the `@Cacheable` options for a method. Returns `undefined` when absent. */
106
+ function getCacheable(target, methodName) {
107
+ return getMetadata(KEY$1, target, methodName);
108
+ }
109
+ //#endregion
110
+ //#region src/response-cache/decorators/purges-cache.decorator.ts
111
+ const KEY = ROUTE_METADATA_KEYS.PURGES_CACHE;
112
+ /**
113
+ * Invalidate cached responses after this route mutates data.
114
+ *
115
+ * Runs after the handler returns a 2xx or 3xx. `pathPrefixes` matches the
116
+ * request path only, so `['/blog']` also clears `/blog?page=2`.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * @Post('/posts/:slug/publish')
121
+ * @PurgesCache({ tags: ['post:{param.slug}', 'category:{data.post.categoryId}'] })
122
+ * async publish(ctx: RouterContext) { ... }
123
+ * ```
124
+ */
125
+ function PurgesCache(options) {
126
+ const hasTargets = (options.tags?.length ?? 0) > 0 || (options.pathPrefixes?.length ?? 0) > 0;
127
+ if (options.purgeEverything && hasTargets) throw new ResponseCacheConfigError("@PurgesCache: `purgeEverything` is exclusive — remove `tags` and `pathPrefixes`.");
128
+ if (!options.purgeEverything && !hasTargets) throw new ResponseCacheConfigError("@PurgesCache: specify at least one of `tags`, `pathPrefixes`, or `purgeEverything`.");
129
+ return (target, propertyKey) => {
130
+ defineMetadata(KEY, options, target, propertyKey);
131
+ };
132
+ }
133
+ /** Read the `@PurgesCache` options for a method. Returns `undefined` when absent. */
134
+ function getPurgesCache(target, methodName) {
135
+ return getMetadata(KEY, target, methodName);
136
+ }
137
+ //#endregion
138
+ //#region src/response-cache/boot-check.ts
139
+ /**
140
+ * Why this entrypoint cannot cache, or `undefined` when it can.
141
+ *
142
+ * A Worker cannot read its own Wrangler configuration, but the presence of
143
+ * `ctx.cache` tells us whether Workers Caching is enabled for this entrypoint.
144
+ *
145
+ * Reported rather than thrown, and the distinction is the whole point. The
146
+ * hazard is a route stamping `public, max-age=…` while nothing stores the
147
+ * response: a claim the app makes and no layer honours. Refusing to make that
148
+ * claim removes the hazard — the response falls through to
149
+ * `createNoStoreFallbackMiddleware` and is stamped `private, no-store`, which
150
+ * is both true and visible in the response itself. Failing the request
151
+ * removes it too, but takes down every route in the isolate to do it,
152
+ * including the ones that never asked to be cached.
153
+ *
154
+ * The answer belongs to an entrypoint rather than to the app, because a
155
+ * gateway topology runs two of them — one with `cache.enabled` false by
156
+ * design, one with it true — inside a single isolate.
157
+ */
158
+ function cachingUnavailableReason(cacheableRouteCount, cache) {
159
+ if (cacheableRouteCount === 0) return void 0;
160
+ if (cache) return void 0;
161
+ return `${cacheableRouteCount} route(s) declare @Cacheable, but Workers Caching is not available on this entrypoint, so their responses are served uncached and stamped \`private, no-store\`. Set \`"cache": { "enabled": true }\` in your Wrangler config, use Wrangler >= 4.69.0, and set \`compatibility_date\` to 2026-07-06 or later.`;
162
+ }
163
+ /**
164
+ * The one export name a gateway can never forward to: its own.
165
+ *
166
+ * `ctx.exports.default` is the default export — the `Stratal` instance whose
167
+ * `fetch` marks the context as the gateway and dispatches. Forwarding there
168
+ * re-enters the gateway, which marks the *new* context and forwards again,
169
+ * recursing until the runtime's subrequest limit kills the request. Rejecting
170
+ * it at boot is what makes "the cached entrypoint never re-dispatches, so no
171
+ * loop is possible" true rather than merely true-if-configured-correctly.
172
+ */
173
+ const SELF_ENTRYPOINT = "default";
174
+ /**
175
+ * Validate the configured entrypoint name itself.
176
+ *
177
+ * Split from the reachability check in `resolveCachedEntrypoint` because this
178
+ * part needs no runtime: a name that is empty, or names the gateway itself, is
179
+ * wrong on inspection and should never reach a first request.
180
+ */
181
+ function assertValidGatewayEntrypoint(options) {
182
+ const gateway = options.gateway;
183
+ if (!gateway) return;
184
+ const entrypoint = gateway.entrypoint;
185
+ if (typeof entrypoint !== "string" || entrypoint.trim() === "") throw new ResponseCacheConfigError("ResponseCacheModule: `gateway.entrypoint` must be a non-empty export name, e.g. `gateway: { entrypoint: 'Cached' }` matching `export const Cached = cachedEntrypoint(stratal)`.");
186
+ if (entrypoint === SELF_ENTRYPOINT) throw new ResponseCacheConfigError("ResponseCacheModule: `gateway.entrypoint` cannot be \"default\" — that is the gateway itself. Forwarding to it would re-enter the gateway, which would forward again, recursing until the Worker hits its subrequest limit. Export a separate cached entrypoint (`export const Cached = cachedEntrypoint(stratal)`) and name that instead, with `cache: { enabled: false }` on `default` and `true` on the named export.");
187
+ }
188
+ /**
189
+ * Keep partition names out of the `$`-prefixed space the framework reserves.
190
+ *
191
+ * Partitions and the representation both become `ctx.props` entries, and props
192
+ * is a flat object — a partition named `$representation` would overwrite the
193
+ * representation or be overwritten by it, either way silently collapsing two
194
+ * cache keys into one. The prefix is rejected wholesale rather than just the
195
+ * one name in use today, so a later reserved key cannot break an app that had
196
+ * already shipped a partition called it.
197
+ */
198
+ function assertPartitionNames(options) {
199
+ const reserved = Object.keys(options.partitions ?? {}).filter((name) => name.startsWith("$"));
200
+ if (reserved.length > 0) throw new ResponseCacheConfigError(`ResponseCacheModule: partition name(s) ${reserved.join(", ")} start with "$", which is reserved for values the framework puts in \`ctx.props\` itself. Rename them.`);
201
+ }
202
+ /**
203
+ * Reject the two module options only the gateway entrypoint can act on, when
204
+ * no gateway entrypoint is configured.
205
+ *
206
+ * `partitions` is looked up by `partitionBy`, which `bindRouteCache` rejects
207
+ * under the same condition; `primers` is read only by `GatewayPrimerService`,
208
+ * which the dispatch middleware is the sole caller of. Without
209
+ * `gateway.entrypoint` neither is ever consulted, and accepting them silently
210
+ * is the "option a consumer sets that does nothing" failure this feature is
211
+ * otherwise careful to avoid — an author would reasonably read a configured
212
+ * `partitions` block as proof that per-user keying is in effect. Fail at boot
213
+ * instead, in the same breath as `partitionBy`.
214
+ *
215
+ * Validates the entrypoint name first, so `gateway: { entrypoint: '' }` is
216
+ * reported as the malformed name it is rather than falling through to a
217
+ * "you need a gateway" message about the wrong thing.
218
+ */
219
+ function assertNoGatewayOptions(options) {
220
+ assertValidGatewayEntrypoint(options);
221
+ assertPartitionNames(options);
222
+ if (options.gateway !== void 0) return;
223
+ if (Object.keys(options.partitions ?? {}).length > 0) throw new ResponseCacheConfigError("ResponseCacheModule: `partitions` requires `gateway: { entrypoint }`. Partition resolvers only run in the gateway, which forwards partitioned reads to a cached entrypoint over `ctx.exports` — without one, the resolvers configured here would never be called and every `partitionBy` route would cache publicly. Add `gateway: { entrypoint: 'Cached' }` and `export const Cached = cachedEntrypoint(stratal)`, or remove `partitions` (and any `partitionBy`).");
224
+ if ((options.primers?.length ?? 0) > 0) throw new ResponseCacheConfigError("ResponseCacheModule: `primers` requires `gateway: { entrypoint }`. Primers exist to populate the request container before partition resolution runs in the gateway — without a cached entrypoint nothing would ever run them, including `AUTH_GATEWAY_PRIMERS`. Add `gateway: { entrypoint }`, or remove `primers`.");
225
+ }
226
+ //#endregion
227
+ //#region src/response-cache/resolve-cacheable.ts
228
+ /**
229
+ * Fold module defaults into a route's `@Cacheable` options.
230
+ *
231
+ * `partitionBy` replaces rather than merges: a route must be able to declare
232
+ * itself public (`[]`) or re-partition without inheriting a stale default.
233
+ * Reading the route alone must be enough to know how it is keyed.
234
+ */
235
+ function resolveCacheable(route, defaults, context) {
236
+ const where = `${context.controller}.${context.method}`;
237
+ const ttl = route.ttl ?? defaults.ttl;
238
+ if (ttl === void 0) throw new ResponseCacheConfigError(`${where}: @Cacheable needs a \`ttl\`, either on the route or via ResponseCacheModule.forRoot({ defaults: { ttl } }).`);
239
+ if (ttl <= 0 || !Number.isFinite(ttl)) throw new ResponseCacheConfigError(`${where}: @Cacheable \`ttl\` must be a positive number of seconds.`);
240
+ const browserTtl = route.browserTtl ?? defaults.browserTtl ?? ttl;
241
+ if (browserTtl < 0 || !Number.isFinite(browserTtl)) throw new ResponseCacheConfigError(`${where}: @Cacheable \`browserTtl\` must be a non-negative finite number of seconds.`);
242
+ const swr = route.swr ?? defaults.swr;
243
+ if (swr !== void 0 && (swr < 0 || !Number.isFinite(swr))) throw new ResponseCacheConfigError(`${where}: @Cacheable \`swr\` must be a non-negative finite number of seconds.`);
244
+ const partitionBy = route.partitionBy ?? defaults.partitionBy ?? [];
245
+ if (context.guarded && partitionBy.length === 0) throw new ResponseCacheConfigError(context.gatewayConfigured ? `${where}: @Cacheable on a guarded route needs a non-empty \`partitionBy\`. A guarded route's response differs per caller, so caching it under one shared entry would serve one user's response to another. Declare the partitions it varies by (\`@Cacheable({ partitionBy: ['user'] })\`), or set a module-level \`defaults.partitionBy\`. \`partitionBy: []\` is not accepted here even explicitly.` : `${where}: @Cacheable cannot be used on a guarded route without a gateway entrypoint. A guarded route's response differs per caller, so caching it would serve one user's response to another. Per-caller cache keying needs \`ResponseCacheModule.forRoot({ gateway: { entrypoint } })\` plus a \`partitionBy\` — configure both, or remove @Cacheable from this route.`);
246
+ return {
247
+ ttl,
248
+ browserTtl,
249
+ swr,
250
+ tags: route.tags ?? [],
251
+ partitionBy,
252
+ vary: [.../* @__PURE__ */ new Set([...defaults.vary ?? [], ...route.vary ?? []])]
253
+ };
254
+ }
255
+ //#endregion
256
+ //#region src/response-cache/services/route-cache-binding.ts
257
+ /** Matches a `{body.*}` cache tag placeholder (any path under the `body` scope). */
258
+ const BODY_SCOPE_TAG = /\{body\.[^}]+\}/;
259
+ /**
260
+ * Reject any tag that reads the `{body.*}` scope: the HTTP router never
261
+ * populates `TagScopes.body` (see `RouteRegistrationService.buildTagScopes`)
262
+ * — the parsed request body isn't cheaply available at the point tags are
263
+ * rendered, and re-parsing it on every request just to support a rarely used
264
+ * tag scope was deliberately rejected. A `{body.*}` tag can therefore never
265
+ * resolve.
266
+ *
267
+ * Left unchecked, this doesn't fail quietly: on a `@PurgesCache` route,
268
+ * `buildPurgeSpec(...)` is evaluated as an *argument* to `purge(...)`, so
269
+ * `renderTags` throwing `InvalidCacheTagError` happens before `purge`'s own
270
+ * try/catch exists — and `applyCacheDecision` has no catch of its own either,
271
+ * so the request 500s *after* its mutation already committed. Catching it
272
+ * here, at registration, turns a request-time failure the author only
273
+ * discovers in production into a boot-time error they see before deploying.
274
+ */
275
+ function assertNoBodyScopeTags(tags, decorator, context) {
276
+ const offending = tags?.find((tag) => BODY_SCOPE_TAG.test(tag));
277
+ if (!offending) return;
278
+ throw new ResponseCacheConfigError(`${context.controller}.${context.method}: ${decorator} tag "${offending}" uses the \`{body.*}\` scope, which can never resolve — the parsed request body is not available when cache tags are rendered. Use \`{param.*}\`, \`{query.*}\`, or \`{data.*}\` instead.`);
279
+ }
280
+ /** Matches a `{param.X}` cache tag placeholder, capturing the param name `X`. */
281
+ const PARAM_SCOPE_TAG = /\{param\.([^}.]+)/g;
282
+ /**
283
+ * Reject a `{param.X}` tag whose `X` is not one of this route's own path
284
+ * parameters.
285
+ *
286
+ * Unlike `{query.*}`/`{data.*}` — which depend on what a given *request*
287
+ * happens to send, and so can only fail per-request — a route's path pattern
288
+ * is fixed the moment it is registered. Whether `{param.id}` can ever resolve
289
+ * is therefore knowable right now: if the route's path has no `:id` segment,
290
+ * it never will, on any request. Left unchecked, this fails the exact same
291
+ * way as `{body.*}` — `renderTags` throws mid-`purge(...)` call, after the
292
+ * mutation already committed. Catching it here turns that into a boot-time
293
+ * error instead.
294
+ *
295
+ * No-ops when `context.routeParams` is `undefined` — the caller didn't supply
296
+ * the route's path, so there is nothing to validate against.
297
+ */
298
+ function assertParamTagsResolvable(tags, decorator, context) {
299
+ if (!tags?.length || context.routeParams === void 0) return;
300
+ const routeParams = context.routeParams;
301
+ for (const tag of tags) for (const match of tag.matchAll(PARAM_SCOPE_TAG)) {
302
+ const param = match[1];
303
+ if (routeParams.includes(param)) continue;
304
+ const available = routeParams.length > 0 ? routeParams.join(", ") : "(none)";
305
+ throw new ResponseCacheConfigError(`${context.controller}.${context.method}: ${decorator} tag "${tag}" references \`{param.${param}}\`, but this route's path has no ":${param}" segment, so it can never resolve — available params: ${available}. Use one of those, or a \`{query.*}\`/\`{data.*}\` tag instead.`);
306
+ }
307
+ }
308
+ /** Matches a `{partition.X}` cache tag placeholder, capturing the partition name `X`. */
309
+ const PARTITION_SCOPE_TAG = /\{partition\.([^}.]+)/g;
310
+ /**
311
+ * Reject a `{partition.X}` tag whose `X` is not one of this route's own
312
+ * declared partitions.
313
+ *
314
+ * Knowable at boot for the same reason `{param.X}` is: `partitionBy` is fixed
315
+ * when the route registers. A partition the route does not declare is not in
316
+ * the cache key, so a tag naming it would describe an entry by something that
317
+ * did not key it — and on `@PurgesCache` it throws mid-purge, after the
318
+ * mutation committed.
319
+ *
320
+ * Only checked on `@Cacheable`, which is where `partitionBy` is declared. A
321
+ * `@PurgesCache` route runs inline in the gateway, which resolves no
322
+ * partitions of its own, so `{partition.*}` can never render there.
323
+ */
324
+ function assertPartitionTagsResolvable(tags, partitionBy, decorator, context) {
325
+ if (!tags?.length) return;
326
+ for (const tag of tags) for (const match of tag.matchAll(PARTITION_SCOPE_TAG)) {
327
+ const partition = match[1];
328
+ if (decorator === "@Cacheable" && partitionBy.includes(partition)) continue;
329
+ const available = partitionBy.length > 0 ? partitionBy.join(", ") : "(none)";
330
+ throw new ResponseCacheConfigError(decorator === "@PurgesCache" ? `${context.controller}.${context.method}: @PurgesCache tag "${tag}" references \`{partition.${partition}}\`, but a mutation runs inline in the gateway, which resolves no partitions — so it can never resolve. Purge by a \`{param.*}\`/\`{query.*}\`/\`{data.*}\` tag the cached route also carries.` : `${context.controller}.${context.method}: @Cacheable tag "${tag}" references \`{partition.${partition}}\`, but this route does not declare that partition, so it is not in the cache key and can never resolve — declared: ${available}. Add it to \`partitionBy\`, or use one of those.`);
331
+ }
332
+ }
333
+ /**
334
+ * Reject a `pathPrefixes` entry that looks like it expects interpolation.
335
+ *
336
+ * `ResponseCacheService.buildPurgeSpec` copies `pathPrefixes` through
337
+ * verbatim — only `tags` go through `renderTags`. So
338
+ * `pathPrefixes: ['/blog/{param.slug}']` is sent to `ctx.cache.purge()` as
339
+ * that literal string, which matches no request path: the purge "succeeds",
340
+ * nothing is invalidated, and the author is never told. Same class of silent
341
+ * miss as a `{body.*}` tag, so it gets the same boot-time treatment.
342
+ */
343
+ function assertNoTemplatedPathPrefixes(pathPrefixes, context) {
344
+ const offending = pathPrefixes?.find((prefix) => prefix.includes("{") || prefix.includes("}"));
345
+ if (offending === void 0) return;
346
+ throw new ResponseCacheConfigError(`${context.controller}.${context.method}: @PurgesCache \`pathPrefixes\` entry "${offending}" contains \`{\`/\`}\`, but path prefixes are not interpolated — only \`tags\` are. It would be purged as that literal string and match nothing. Use a static prefix (\`/blog\`), or move the dynamic part into a \`{param.*}\`/\`{query.*}\`/\`{data.*}\` cache tag.`);
347
+ }
348
+ /**
349
+ * Fold a route's decorators and the module defaults into the config the request
350
+ * path uses. Runs once per route at registration, never per request.
351
+ *
352
+ * Returns `undefined` when the route declares neither decorator, so the hot path
353
+ * can skip all cache work with a single truthiness check.
354
+ *
355
+ * @throws {ResponseCacheConfigError} On a non-empty `partitionBy` when no
356
+ * `gateway.entrypoint` is configured. Placing a partition in the cache key
357
+ * requires the gateway to forward the read to a cached entrypoint over
358
+ * `ctx.exports`. Accepting the option and ignoring it would give every
359
+ * visitor a single shared entry for a route the author explicitly marked
360
+ * per-user.
361
+ * @throws {ResponseCacheConfigError} On a `{body.*}` tag in either decorator's
362
+ * `tags` — see `assertNoBodyScopeTags`.
363
+ * @throws {ResponseCacheConfigError} On a `{param.X}` tag whose `X` is not a
364
+ * `:param` in this route's own path (when `context.routeParams` is
365
+ * supplied) — see `assertParamTagsResolvable`.
366
+ * @throws {ResponseCacheConfigError} On a `{`/`}` in `@PurgesCache`'s
367
+ * `pathPrefixes` — see `assertNoTemplatedPathPrefixes`.
368
+ */
369
+ function bindRouteCache(cacheable, purges, defaults, context) {
370
+ if (!cacheable && !purges) return void 0;
371
+ assertNoBodyScopeTags(cacheable?.tags, "@Cacheable", context);
372
+ assertNoBodyScopeTags(purges?.tags, "@PurgesCache", context);
373
+ assertParamTagsResolvable(cacheable?.tags, "@Cacheable", context);
374
+ assertParamTagsResolvable(purges?.tags, "@PurgesCache", context);
375
+ assertPartitionTagsResolvable(purges?.tags, [], "@PurgesCache", context);
376
+ assertNoTemplatedPathPrefixes(purges?.pathPrefixes, context);
377
+ const binding = {};
378
+ if (purges) binding.purges = purges;
379
+ if (cacheable) {
380
+ const declared = cacheable.partitionBy ?? defaults.partitionBy ?? [];
381
+ assertPartitionTagsResolvable(cacheable.tags, declared, "@Cacheable", context);
382
+ if (declared.length > 0 && !context.gatewayConfigured) throw new ResponseCacheConfigError(`${context.controller}.${context.method}: \`partitionBy\` requires a gateway entrypoint. Placing a partition in the cache key means forwarding the read to a cached entrypoint over \`ctx.exports\`, so the option cannot be honored without one — and a route marked per-caller that quietly shared a single cache entry would serve one visitor's response to everyone. Configure \`ResponseCacheModule.forRoot({ gateway: { entrypoint: 'Cached' }, partitions: { … } })\` and \`export const Cached = cachedEntrypoint(stratal)\`, or remove \`partitionBy\`.`);
383
+ binding.cacheable = resolveCacheable(cacheable, defaults, context);
384
+ }
385
+ return binding;
386
+ }
387
+ //#endregion
388
+ export { PurgesCache as a, getCacheable as c, resolveCachedEntrypoint as d, cachingUnavailableReason as i, RESPONSE_CACHE_TOKENS as l, assertNoGatewayOptions as n, getPurgesCache as o, assertValidGatewayEntrypoint as r, Cacheable as s, bindRouteCache as t, createLoopbackPurgeTarget as u };
389
+
390
+ //# sourceMappingURL=route-cache-binding-CrLh8tB6.mjs.map