stratal 0.0.20 → 0.0.22

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 (247) hide show
  1. package/README.md +1 -1
  2. package/dist/{base-email.provider-CfQCA08m.mjs → base-email.provider-BWZHIjt8.mjs} +1 -1
  3. package/dist/{base-email.provider-CfQCA08m.mjs.map → base-email.provider-BWZHIjt8.mjs.map} +1 -1
  4. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  5. package/dist/bin/quarry.mjs +46 -109
  6. package/dist/bin/quarry.mjs.map +1 -1
  7. package/dist/cache/index.d.mts +6 -46
  8. package/dist/cache/index.d.mts.map +1 -1
  9. package/dist/cache/index.mjs +22 -67
  10. package/dist/cache/index.mjs.map +1 -1
  11. package/dist/{cache.service-DsnKuNyO.d.mts → cache.service-e34gV6tz.d.mts} +8 -8
  12. package/dist/{cache.service-DsnKuNyO.d.mts.map → cache.service-e34gV6tz.d.mts.map} +1 -1
  13. package/dist/{cache.tokens-B7Rw1C9Q.mjs → cache.tokens-ovi_c52J.mjs} +1 -1
  14. package/dist/{cache.tokens-B7Rw1C9Q.mjs.map → cache.tokens-ovi_c52J.mjs.map} +1 -1
  15. package/dist/{colors-DJaRDXoS.mjs → colors-axmupKdp.mjs} +1 -1
  16. package/dist/{colors-DJaRDXoS.mjs.map → colors-axmupKdp.mjs.map} +1 -1
  17. package/dist/{command-BgSlsS4M.mjs → command-BU4ApTo5.mjs} +2 -3
  18. package/dist/command-BU4ApTo5.mjs.map +1 -0
  19. package/dist/{command-Bu-PjJrX.d.mts → command-wXfvHbBZ.d.mts} +3 -2
  20. package/dist/command-wXfvHbBZ.d.mts.map +1 -0
  21. package/dist/config/index.d.mts +24 -11
  22. package/dist/config/index.d.mts.map +1 -1
  23. package/dist/config/index.mjs +33 -57
  24. package/dist/config/index.mjs.map +1 -1
  25. package/dist/{consumer-registry-B7yUNh0q.d.mts → consumer-registry-DHQtypr1.d.mts} +1 -1
  26. package/dist/{consumer-registry-B7yUNh0q.d.mts.map → consumer-registry-DHQtypr1.d.mts.map} +1 -1
  27. package/dist/container-storage-GpNNz79X.mjs +52 -0
  28. package/dist/container-storage-GpNNz79X.mjs.map +1 -0
  29. package/dist/{controller.decorator-DQzenvSN.mjs → controller.decorator-DIUazNU7.mjs} +8 -8
  30. package/dist/controller.decorator-DIUazNU7.mjs.map +1 -0
  31. package/dist/cron/index.d.mts +26 -5
  32. package/dist/cron/index.d.mts.map +1 -1
  33. package/dist/cron/index.mjs +1 -1
  34. package/dist/{cron-manager-7Symz_TE.mjs → cron-manager-9bpN9bu4.mjs} +42 -16
  35. package/dist/cron-manager-9bpN9bu4.mjs.map +1 -0
  36. package/dist/{cron-manager-BEsH1mjW.d.mts → cron-manager-CSTIBPcM.d.mts} +6 -13
  37. package/dist/cron-manager-CSTIBPcM.d.mts.map +1 -0
  38. package/dist/decorate-HgTKAYK8.mjs +16 -0
  39. package/dist/deep-merge-C8NgcXw4.mjs +18 -0
  40. package/dist/deep-merge-C8NgcXw4.mjs.map +1 -0
  41. package/dist/di/index.d.mts +2 -2
  42. package/dist/di/index.mjs +4 -3
  43. package/dist/di-BO1QIb5H.mjs +415 -0
  44. package/dist/di-BO1QIb5H.mjs.map +1 -0
  45. package/dist/email/index.d.mts +14 -89
  46. package/dist/email/index.d.mts.map +1 -1
  47. package/dist/email/index.mjs +30 -125
  48. package/dist/email/index.mjs.map +1 -1
  49. package/dist/en-BPP6h6y5.mjs +202 -0
  50. package/dist/en-BPP6h6y5.mjs.map +1 -0
  51. package/dist/{env-D1rcZ8_r.d.mts → env-DKSbuBi5.d.mts} +1 -1
  52. package/dist/env-DKSbuBi5.d.mts.map +1 -0
  53. package/dist/errors/index.d.mts +2 -2
  54. package/dist/errors/index.mjs +4 -2
  55. package/dist/errors-BBZTnjdq.mjs +333 -0
  56. package/dist/errors-BBZTnjdq.mjs.map +1 -0
  57. package/dist/events/index.d.mts +2 -2
  58. package/dist/events/index.d.mts.map +1 -1
  59. package/dist/events/index.mjs +1 -1
  60. package/dist/{events-COKixqnG.mjs → events-D1KdDaiP.mjs} +13 -11
  61. package/dist/events-D1KdDaiP.mjs.map +1 -0
  62. package/dist/exception-context-B4kM-M53.mjs +429 -0
  63. package/dist/exception-context-B4kM-M53.mjs.map +1 -0
  64. package/dist/{gateway-context-CdJjpUCW.mjs → gateway-context-CFe6a9gz.mjs} +20 -31
  65. package/dist/gateway-context-CFe6a9gz.mjs.map +1 -0
  66. package/dist/guards/index.d.mts +3 -3
  67. package/dist/guards/index.d.mts.map +1 -1
  68. package/dist/guards/index.mjs +1 -1
  69. package/dist/{guards-DUk_Kzst.mjs → guards-Ced-uNIF.mjs} +7 -5
  70. package/dist/guards-Ced-uNIF.mjs.map +1 -0
  71. package/dist/{http-method.decorator-DXwxAfb_.mjs → http-method.decorator-CdjKFJZZ.mjs} +7 -6
  72. package/dist/http-method.decorator-CdjKFJZZ.mjs.map +1 -0
  73. package/dist/i18n/index.d.mts +238 -3
  74. package/dist/i18n/index.d.mts.map +1 -0
  75. package/dist/i18n/index.mjs +39 -3
  76. package/dist/i18n/index.mjs.map +1 -0
  77. package/dist/i18n/messages/en/index.d.mts +2 -2
  78. package/dist/i18n/messages/en/index.mjs +2 -2
  79. package/dist/i18n/utils/index.d.mts +4 -26
  80. package/dist/i18n/utils/index.d.mts.map +1 -1
  81. package/dist/i18n/utils/index.mjs +2 -2
  82. package/dist/i18n/validation/index.d.mts +3 -2
  83. package/dist/i18n/validation/index.mjs +4 -2
  84. package/dist/i18n.module-BlXrtAlV.mjs +219 -0
  85. package/dist/i18n.module-BlXrtAlV.mjs.map +1 -0
  86. package/dist/i18n.tokens-hwRpmjRq.mjs +19 -0
  87. package/dist/i18n.tokens-hwRpmjRq.mjs.map +1 -0
  88. package/dist/{index-7-hU3GTV.d.mts → index-B4UBK-2T.d.mts} +1 -1
  89. package/dist/{index-7-hU3GTV.d.mts.map → index-B4UBK-2T.d.mts.map} +1 -1
  90. package/dist/index-BtlE9RuO.d.mts +124 -0
  91. package/dist/index-BtlE9RuO.d.mts.map +1 -0
  92. package/dist/{index-CjaQ6_tZ.d.mts → index-CW1YHSft.d.mts} +71 -167
  93. package/dist/index-CW1YHSft.d.mts.map +1 -0
  94. package/dist/{index-D0US0X14.d.mts → index-DEncMcC6.d.mts} +559 -2239
  95. package/dist/index-DEncMcC6.d.mts.map +1 -0
  96. package/dist/index-Dj5IMwtr.d.mts +44 -0
  97. package/dist/index-Dj5IMwtr.d.mts.map +1 -0
  98. package/dist/{index-C1KvMncZ.d.mts → index-KMgSCSM7.d.mts} +3 -108
  99. package/dist/index-KMgSCSM7.d.mts.map +1 -0
  100. package/dist/index.d.mts +5 -43
  101. package/dist/index.mjs +1 -1
  102. package/dist/{is-command-C6a7WTPw.mjs → is-command-CX5rAfZW.mjs} +2 -2
  103. package/dist/{is-command-C6a7WTPw.mjs.map → is-command-CX5rAfZW.mjs.map} +1 -1
  104. package/dist/{is-seeder-CebjZCDn.mjs → is-seeder-CYCtELlm.mjs} +1 -1
  105. package/dist/{is-seeder-CebjZCDn.mjs.map → is-seeder-CYCtELlm.mjs.map} +1 -1
  106. package/dist/logger/index.d.mts +2 -2
  107. package/dist/logger/index.mjs +170 -2
  108. package/dist/logger/index.mjs.map +1 -0
  109. package/dist/macroable/index.d.mts +1 -1
  110. package/dist/macroable/index.mjs +1 -1
  111. package/dist/{macroable-BmufBshB.mjs → macroable-DzlfzT50.mjs} +1 -1
  112. package/dist/{macroable-BmufBshB.mjs.map → macroable-DzlfzT50.mjs.map} +1 -1
  113. package/dist/metadata-BVkc4aUu.mjs +39 -0
  114. package/dist/metadata-BVkc4aUu.mjs.map +1 -0
  115. package/dist/module/index.d.mts +6 -24
  116. package/dist/module/index.d.mts.map +1 -1
  117. package/dist/module/index.mjs +2 -2
  118. package/dist/module-xYoHba6B.mjs +422 -0
  119. package/dist/module-xYoHba6B.mjs.map +1 -0
  120. package/dist/openapi/index.d.mts +3 -3
  121. package/dist/openapi/index.d.mts.map +1 -1
  122. package/dist/openapi/index.mjs +1 -2
  123. package/dist/openapi-C6lm0RmV.mjs +483 -0
  124. package/dist/openapi-C6lm0RmV.mjs.map +1 -0
  125. package/dist/{openapi.service-BLgvn3hJ.d.mts → openapi.service-CrLlsXAd.d.mts} +3 -3
  126. package/dist/openapi.service-CrLlsXAd.d.mts.map +1 -0
  127. package/dist/quarry/index.d.mts +5 -163
  128. package/dist/quarry/index.d.mts.map +1 -1
  129. package/dist/quarry/index.mjs +5 -5
  130. package/dist/quarry/runner.d.mts +184 -0
  131. package/dist/quarry/runner.d.mts.map +1 -0
  132. package/dist/quarry/runner.mjs +775 -0
  133. package/dist/quarry/runner.mjs.map +1 -0
  134. package/dist/quarry-registry-D4hIGScf.d.mts +69 -0
  135. package/dist/quarry-registry-D4hIGScf.d.mts.map +1 -0
  136. package/dist/quarry-registry-DkraZNwn.mjs +311 -0
  137. package/dist/quarry-registry-DkraZNwn.mjs.map +1 -0
  138. package/dist/queue/index.d.mts +3 -3
  139. package/dist/queue/index.mjs +27 -28
  140. package/dist/queue/index.mjs.map +1 -1
  141. package/dist/{queue.module-BCdCiySt.mjs → queue.module-DeWJ0tQM.mjs} +67 -112
  142. package/dist/queue.module-DeWJ0tQM.mjs.map +1 -0
  143. package/dist/{r2-storage.provider-Co6F0ZYV.mjs → r2-storage.provider-Hfm6LdZQ.mjs} +8 -5
  144. package/dist/r2-storage.provider-Hfm6LdZQ.mjs.map +1 -0
  145. package/dist/{rate-limit.decorator--o6Q6p9w.mjs → rate-limit.decorator-D69zdZbp.mjs} +6 -11
  146. package/dist/rate-limit.decorator-D69zdZbp.mjs.map +1 -0
  147. package/dist/rate-limiter/index.d.mts +11 -50
  148. package/dist/rate-limiter/index.d.mts.map +1 -1
  149. package/dist/rate-limiter/index.mjs +25 -30
  150. package/dist/rate-limiter/index.mjs.map +1 -1
  151. package/dist/{resend.provider-M6qRLrcy.mjs → resend.provider-Ur6tU7fK.mjs} +8 -7
  152. package/dist/resend.provider-Ur6tU7fK.mjs.map +1 -0
  153. package/dist/router/index.d.mts +2 -2
  154. package/dist/router/index.mjs +8 -7
  155. package/dist/{i18n.module-BBlNNlcG.mjs → router-Cy6DjkvP.mjs} +215 -855
  156. package/dist/router-Cy6DjkvP.mjs.map +1 -0
  157. package/dist/seeder/index.d.mts +6 -11
  158. package/dist/seeder/index.d.mts.map +1 -1
  159. package/dist/seeder/index.mjs +3 -3
  160. package/dist/{seeder-CJAOHEIo.mjs → seeder-BADTig4n.mjs} +17 -22
  161. package/dist/seeder-BADTig4n.mjs.map +1 -0
  162. package/dist/{signed-url-BQPbv2In.mjs → signed-url-BqUqt5dF.mjs} +1 -1
  163. package/dist/{signed-url-BQPbv2In.mjs.map → signed-url-BqUqt5dF.mjs.map} +1 -1
  164. package/dist/{smtp.provider-w0Ve52Xg.mjs → smtp.provider-C129sNBT.mjs} +7 -6
  165. package/dist/smtp.provider-C129sNBT.mjs.map +1 -0
  166. package/dist/storage/index.d.mts +15 -39
  167. package/dist/storage/index.d.mts.map +1 -1
  168. package/dist/storage/index.mjs +3 -3
  169. package/dist/storage/providers/index.d.mts +2 -2
  170. package/dist/storage/providers/index.mjs +1 -1
  171. package/dist/{storage-1zw-6Yiz.mjs → storage-BA3ppVYM.mjs} +70 -59
  172. package/dist/storage-BA3ppVYM.mjs.map +1 -0
  173. package/dist/{storage-provider.interface-Bd6vA4ak.d.mts → storage-provider.interface-DQMtT42e.d.mts} +2 -3
  174. package/dist/storage-provider.interface-DQMtT42e.d.mts.map +1 -0
  175. package/dist/storage.error-C6FY037a.mjs +8 -0
  176. package/dist/storage.error-C6FY037a.mjs.map +1 -0
  177. package/dist/{stratal-DeEcGgdq.mjs → stratal-Bdq4IdB3.mjs} +31 -183
  178. package/dist/stratal-Bdq4IdB3.mjs.map +1 -0
  179. package/dist/stratal-BsKmvP6J.d.mts +43 -0
  180. package/dist/stratal-BsKmvP6J.d.mts.map +1 -0
  181. package/dist/{types-cySNS_lp.d.mts → types-BaeHi67f.d.mts} +1 -1
  182. package/dist/types-BaeHi67f.d.mts.map +1 -0
  183. package/dist/{usage-generator-BUdlhnCK.mjs → usage-generator-DTqaUMR9.mjs} +6 -3
  184. package/dist/usage-generator-DTqaUMR9.mjs.map +1 -0
  185. package/dist/validation-DUzcjb8Q.mjs +49 -0
  186. package/dist/validation-DUzcjb8Q.mjs.map +1 -0
  187. package/dist/validation.context-XTysWJ3b.mjs +117 -0
  188. package/dist/validation.context-XTysWJ3b.mjs.map +1 -0
  189. package/dist/websocket/index.d.mts +7 -14
  190. package/dist/websocket/index.d.mts.map +1 -1
  191. package/dist/websocket/index.mjs +2 -2
  192. package/dist/workers/index.d.mts +2 -2
  193. package/dist/workers/index.mjs +3 -2
  194. package/dist/workers/index.mjs.map +1 -1
  195. package/dist/{index-Bnpfq6uk.d.mts → zod-DvWTfRpI.d.mts} +58 -133
  196. package/dist/zod-DvWTfRpI.d.mts.map +1 -0
  197. package/dist/zod-hMa3rSHV.mjs +72 -0
  198. package/dist/zod-hMa3rSHV.mjs.map +1 -0
  199. package/package.json +20 -20
  200. package/dist/command-BgSlsS4M.mjs.map +0 -1
  201. package/dist/command-Bu-PjJrX.d.mts.map +0 -1
  202. package/dist/controller.decorator-DQzenvSN.mjs.map +0 -1
  203. package/dist/cron-manager-7Symz_TE.mjs.map +0 -1
  204. package/dist/cron-manager-BEsH1mjW.d.mts.map +0 -1
  205. package/dist/en-DSH_bhh6.mjs +0 -308
  206. package/dist/en-DSH_bhh6.mjs.map +0 -1
  207. package/dist/env-D1rcZ8_r.d.mts.map +0 -1
  208. package/dist/errors-BdyV5PnY.mjs +0 -1725
  209. package/dist/errors-BdyV5PnY.mjs.map +0 -1
  210. package/dist/errors-Da3Pz2X7.mjs +0 -74
  211. package/dist/errors-Da3Pz2X7.mjs.map +0 -1
  212. package/dist/events-COKixqnG.mjs.map +0 -1
  213. package/dist/gateway-context-CdJjpUCW.mjs.map +0 -1
  214. package/dist/guards-DUk_Kzst.mjs.map +0 -1
  215. package/dist/http-method.decorator-DXwxAfb_.mjs.map +0 -1
  216. package/dist/i18n.module-BBlNNlcG.mjs.map +0 -1
  217. package/dist/index-Bnpfq6uk.d.mts.map +0 -1
  218. package/dist/index-C1KvMncZ.d.mts.map +0 -1
  219. package/dist/index-CjaQ6_tZ.d.mts.map +0 -1
  220. package/dist/index-D0US0X14.d.mts.map +0 -1
  221. package/dist/index-DBd_2wv8.d.mts +0 -263
  222. package/dist/index-DBd_2wv8.d.mts.map +0 -1
  223. package/dist/index.d.mts.map +0 -1
  224. package/dist/logger-V6Ms3QnQ.mjs +0 -436
  225. package/dist/logger-V6Ms3QnQ.mjs.map +0 -1
  226. package/dist/module-Dk2qTa77.mjs +0 -860
  227. package/dist/module-Dk2qTa77.mjs.map +0 -1
  228. package/dist/openapi-tools.service-Zs-Ewv7F.mjs +0 -200
  229. package/dist/openapi-tools.service-Zs-Ewv7F.mjs.map +0 -1
  230. package/dist/openapi.service-BLgvn3hJ.d.mts.map +0 -1
  231. package/dist/quarry-registry-DNEej-Db.mjs +0 -688
  232. package/dist/quarry-registry-DNEej-Db.mjs.map +0 -1
  233. package/dist/queue.module-BCdCiySt.mjs.map +0 -1
  234. package/dist/r2-storage.provider-Co6F0ZYV.mjs.map +0 -1
  235. package/dist/rate-limit.decorator--o6Q6p9w.mjs.map +0 -1
  236. package/dist/resend.provider-M6qRLrcy.mjs.map +0 -1
  237. package/dist/seeder-CJAOHEIo.mjs.map +0 -1
  238. package/dist/setup-CefZKV_e.mjs +0 -37
  239. package/dist/setup-CefZKV_e.mjs.map +0 -1
  240. package/dist/smtp.provider-w0Ve52Xg.mjs.map +0 -1
  241. package/dist/storage-1zw-6Yiz.mjs.map +0 -1
  242. package/dist/storage-provider.interface-Bd6vA4ak.d.mts.map +0 -1
  243. package/dist/stratal-DeEcGgdq.mjs.map +0 -1
  244. package/dist/types-cySNS_lp.d.mts.map +0 -1
  245. package/dist/usage-generator-BUdlhnCK.mjs.map +0 -1
  246. package/dist/validation-DtJwAv7O.mjs +0 -248
  247. package/dist/validation-DtJwAv7O.mjs.map +0 -1
@@ -1,21 +1,16 @@
1
- import { a as __decorate, f as DI_TOKENS, o as __decorateParam, p as Transient, s as __decorateMetadata } from "../logger-V6Ms3QnQ.mjs";
2
- import { a as QueueProviderNotSupportedError, c as QUEUE_TOKENS, i as CloudflareQueueProvider, l as QueueSender, n as QueueProviderFactory, o as QueueBindingNotFoundError, r as SyncQueueProvider, s as QueueRegistry, t as QueueModule, u as ConsumerRegistry } from "../queue.module-BCdCiySt.mjs";
3
- import { inject } from "tsyringe";
1
+ import { c as DI_TOKENS, d as Transient, m as inject } from "../di-BO1QIb5H.mjs";
2
+ import { n as __decorateParam, t as __decorate } from "../decorate-HgTKAYK8.mjs";
3
+ import { LOGGER_TOKENS } from "../logger/index.mjs";
4
+ import { a as QueueError, c as QueueSender, i as CloudflareQueueProvider, l as ConsumerRegistry, n as QueueProviderFactory, o as QueueRegistry, r as SyncQueueProvider, s as QUEUE_TOKENS, t as QueueModule } from "../queue.module-DeWJ0tQM.mjs";
4
5
  //#region src/queue/queue-manager.ts
5
6
  let QueueManager = class QueueManager {
6
- constructor(registry) {
7
+ registry;
8
+ logger;
9
+ constructor(registry, logger) {
7
10
  this.registry = registry;
11
+ this.logger = logger;
8
12
  }
9
- /**
10
- * Process a batch of queue messages
11
- *
12
- * Routes messages to registered consumers based on message type.
13
- * Uses ConsumerRegistry to find matching consumers.
14
- *
15
- * @param _queueName - Name of the queue (for logging, not used for routing)
16
- * @param batch - Batch of messages from Cloudflare Queue
17
- */
18
- async processBatch(_queueName, batch) {
13
+ async processBatch(queueName, batch) {
19
14
  for (const message of batch.messages) {
20
15
  const queueMessage = message.body;
21
16
  const consumers = this.registry.getConsumers(queueMessage.type);
@@ -24,6 +19,10 @@ let QueueManager = class QueueManager {
24
19
  message.ack();
25
20
  } catch (error) {
26
21
  const errorInstance = error instanceof Error ? error : new Error(String(error));
22
+ this.logger.error("Queue message processing failed", errorInstance, {
23
+ type: queueMessage.type,
24
+ queue: queueName
25
+ });
27
26
  if (consumer.onError) await consumer.onError(errorInstance, queueMessage);
28
27
  message.retry();
29
28
  }
@@ -33,27 +32,27 @@ let QueueManager = class QueueManager {
33
32
  QueueManager = __decorate([
34
33
  Transient(DI_TOKENS.Queue),
35
34
  __decorateParam(0, inject(DI_TOKENS.ConsumerRegistry)),
36
- __decorateMetadata("design:paramtypes", [Object])
35
+ __decorateParam(1, inject(LOGGER_TOKENS.LoggerService))
37
36
  ], QueueManager);
38
37
  //#endregion
39
38
  //#region src/queue/decorators/inject-queue.decorator.ts
40
39
  /**
41
- * Inject a queue sender by name.
40
+ * Inject a queue sender by binding name.
42
41
  *
43
- * Queue names are typed via module augmentation of QueueNames interface.
44
- * The queue name serves as both the identifier and the DI injection token.
42
+ * The binding name matches the `binding` field declared under `queues.producers`
43
+ * in `wrangler.jsonc` (e.g. `BACKGROUND_QUEUE`). Stratal looks the binding up
44
+ * directly on the worker's `env`; the underlying Cloudflare queue can be any
45
+ * env-specific name (e.g. `background-queue-dev`) without affecting code.
45
46
  *
46
- * @param name - Queue name (typed with autocomplete if QueueNames is augmented)
47
+ * @param binding - Queue binding identifier (typed against `StratalEnv`).
47
48
  * @returns Parameter decorator for constructor injection
48
49
  *
49
50
  * @example
50
51
  * ```typescript
51
- * // Direct injection by queue name
52
52
  * constructor(
53
- * @InjectQueue('notifications-queue') private queue: IQueueSender
53
+ * @InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender
54
54
  * ) {}
55
55
  *
56
- * // Usage
57
56
  * await this.queue.dispatch({
58
57
  * type: 'email.send',
59
58
  * payload: { to: 'user@example.com', subject: 'Hello' }
@@ -61,14 +60,14 @@ QueueManager = __decorate([
61
60
  * ```
62
61
  *
63
62
  * @remarks
64
- * The queue must be registered via `QueueModule.registerQueue(name)` before injection.
65
- * For module-internal queue bindings (e.g., EmailModule), use `@inject(TOKEN)` with
66
- * `useExisting` provider binding instead.
63
+ * The binding must be registered via `QueueModule.registerQueue(binding)`
64
+ * before injection. For module-internal bindings (e.g. EmailModule),
65
+ * use `@inject(TOKEN)` with `useExisting` provider binding instead.
67
66
  */
68
- function InjectQueue(name) {
69
- return inject(name);
67
+ function InjectQueue(binding) {
68
+ return inject(binding);
70
69
  }
71
70
  //#endregion
72
- export { CloudflareQueueProvider, ConsumerRegistry, InjectQueue, QUEUE_TOKENS, QueueBindingNotFoundError, QueueManager, QueueModule, QueueProviderFactory, QueueProviderNotSupportedError, QueueRegistry, QueueSender, SyncQueueProvider };
71
+ export { CloudflareQueueProvider, ConsumerRegistry, InjectQueue, QUEUE_TOKENS, QueueError, QueueManager, QueueModule, QueueProviderFactory, QueueRegistry, QueueSender, SyncQueueProvider };
73
72
 
74
73
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/queue/queue-manager.ts","../../src/queue/decorators/inject-queue.decorator.ts"],"sourcesContent":["import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport { type ConsumerRegistry } from './consumer-registry'\nimport type { QueueMessage } from './queue-consumer'\n\n/**\n * Queue Manager\n *\n * Singleton service for processing queue message batches.\n * Routes messages to consumers based on message type.\n *\n * **Message Routing:**\n * - Consumers declare message types they handle (e.g., ['email.send'])\n * - When a message arrives, consumers matching the message type are invoked\n * - A consumer can handle messages from ANY queue (routing is by type, not queue)\n *\n * **Note:** For sending messages to queues, use IQueueSender instances\n * obtained via @InjectQueue('queue-name') or module bindings.\n *\n * @example Processing a queue batch\n * ```typescript\n * // In Cloudflare Worker queue handler\n * await queueManager.processBatch('notifications-queue', batch)\n * ```\n */\n@Transient(DI_TOKENS.Queue)\nexport class QueueManager {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry\n ) {}\n\n /**\n * Process a batch of queue messages\n *\n * Routes messages to registered consumers based on message type.\n * Uses ConsumerRegistry to find matching consumers.\n *\n * @param _queueName - Name of the queue (for logging, not used for routing)\n * @param batch - Batch of messages from Cloudflare Queue\n */\n async processBatch(_queueName: string, batch: MessageBatch): Promise<void> {\n for (const message of batch.messages) {\n const queueMessage = message.body as QueueMessage\n\n // Find consumers by message type (not by queue name)\n const consumers = this.registry.getConsumers(queueMessage.type)\n\n for (const consumer of consumers) {\n try {\n await consumer.handle(queueMessage)\n message.ack()\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n if (consumer.onError) {\n await consumer.onError(errorInstance, queueMessage)\n }\n message.retry()\n }\n }\n }\n }\n}\n","import { inject } from 'tsyringe'\nimport type { QueueName } from '../queue-name'\n\n/**\n * Inject a queue sender by name.\n *\n * Queue names are typed via module augmentation of QueueNames interface.\n * The queue name serves as both the identifier and the DI injection token.\n *\n * @param name - Queue name (typed with autocomplete if QueueNames is augmented)\n * @returns Parameter decorator for constructor injection\n *\n * @example\n * ```typescript\n * // Direct injection by queue name\n * constructor(\n * @InjectQueue('notifications-queue') private queue: IQueueSender\n * ) {}\n *\n * // Usage\n * await this.queue.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n *\n * @remarks\n * The queue must be registered via `QueueModule.registerQueue(name)` before injection.\n * For module-internal queue bindings (e.g., EmailModule), use `@inject(TOKEN)` with\n * `useExisting` provider binding instead.\n */\nexport function InjectQueue(name: QueueName): ParameterDecorator {\n return inject(name)\n}\n"],"mappings":";;;;AA2BO,IAAA,eAAA,MAAM,aAAa;CACxB,YACE,UACA;AADqD,OAAA,WAAA;;;;;;;;;;;CAYvD,MAAM,aAAa,YAAoB,OAAoC;AACzE,OAAK,MAAM,WAAW,MAAM,UAAU;GACpC,MAAM,eAAe,QAAQ;GAG7B,MAAM,YAAY,KAAK,SAAS,aAAa,aAAa,KAAK;AAE/D,QAAK,MAAM,YAAY,UACrB,KAAI;AACF,UAAM,SAAS,OAAO,aAAa;AACnC,YAAQ,KAAK;YACN,OAAO;IACd,MAAM,gBAAgB,iBAAiB,QACnC,QACA,IAAI,MAAM,OAAO,MAAM,CAAC;AAE5B,QAAI,SAAS,QACX,OAAM,SAAS,QAAQ,eAAe,aAAa;AAErD,YAAQ,OAAO;;;;;;CAlCxB,UAAU,UAAU,MAAM;oBAGtB,OAAO,UAAU,iBAAiB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACEvC,SAAgB,YAAY,MAAqC;AAC/D,QAAO,OAAO,KAAK"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/queue/queue-manager.ts","../../src/queue/decorators/inject-queue.decorator.ts"],"sourcesContent":["import { inject } from '../di';\nimport { Transient } from '../di/decorators';\nimport { DI_TOKENS } from '../di/tokens';\nimport { LOGGER_TOKENS, type LoggerService } from '../logger';\nimport { type ConsumerRegistry } from './consumer-registry';\nimport type { QueueMessage } from './queue-consumer';\n\n@Transient(DI_TOKENS.Queue)\nexport class QueueManager {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry,\n @inject(LOGGER_TOKENS.LoggerService) private readonly logger: LoggerService,\n ) {}\n\n async processBatch(queueName: string, batch: MessageBatch): Promise<void> {\n for (const message of batch.messages) {\n const queueMessage = message.body as QueueMessage\n\n const consumers = this.registry.getConsumers(queueMessage.type)\n\n for (const consumer of consumers) {\n try {\n await consumer.handle(queueMessage)\n message.ack()\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n this.logger.error('Queue message processing failed', errorInstance, {\n type: queueMessage.type,\n queue: queueName,\n })\n\n if (consumer.onError) {\n await consumer.onError(errorInstance, queueMessage)\n }\n message.retry()\n }\n }\n }\n }\n}\n","import { inject } from '../../di'\nimport type { QueueBinding } from '../queue-binding'\n\n/**\n * Inject a queue sender by binding name.\n *\n * The binding name matches the `binding` field declared under `queues.producers`\n * in `wrangler.jsonc` (e.g. `BACKGROUND_QUEUE`). Stratal looks the binding up\n * directly on the worker's `env`; the underlying Cloudflare queue can be any\n * env-specific name (e.g. `background-queue-dev`) without affecting code.\n *\n * @param binding - Queue binding identifier (typed against `StratalEnv`).\n * @returns Parameter decorator for constructor injection\n *\n * @example\n * ```typescript\n * constructor(\n * @InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender\n * ) {}\n *\n * await this.queue.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n *\n * @remarks\n * The binding must be registered via `QueueModule.registerQueue(binding)`\n * before injection. For module-internal bindings (e.g. EmailModule),\n * use `@inject(TOKEN)` with `useExisting` provider binding instead.\n */\nexport function InjectQueue(binding: QueueBinding): ParameterDecorator {\n return inject(binding)\n}\n"],"mappings":";;;;;AAQO,IAAA,eAAA,MAAM,aAAa;CAE+B;CACC;CAFxD,YACE,UACA,QACA;EAFqD,KAAA,WAAA;EACC,KAAA,SAAA;;CAGxD,MAAM,aAAa,WAAmB,OAAoC;EACxE,KAAK,MAAM,WAAW,MAAM,UAAU;GACpC,MAAM,eAAe,QAAQ;GAE7B,MAAM,YAAY,KAAK,SAAS,aAAa,aAAa,KAAK;GAE/D,KAAK,MAAM,YAAY,WACrB,IAAI;IACF,MAAM,SAAS,OAAO,aAAa;IACnC,QAAQ,KAAK;YACN,OAAO;IACd,MAAM,gBAAgB,iBAAiB,QACnC,QACA,IAAI,MAAM,OAAO,MAAM,CAAC;IAE5B,KAAK,OAAO,MAAM,mCAAmC,eAAe;KAClE,MAAM,aAAa;KACnB,OAAO;KACR,CAAC;IAEF,IAAI,SAAS,SACX,MAAM,SAAS,QAAQ,eAAe,aAAa;IAErD,QAAQ,OAAO;;;;;;CA9BxB,UAAU,UAAU,MAAM;oBAGtB,OAAO,UAAU,iBAAiB,CAAA;oBAClC,OAAO,cAAc,cAAc,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACoBxC,SAAgB,YAAY,SAA2C;CACrE,OAAO,OAAO,QAAQ"}
@@ -1,7 +1,9 @@
1
- import { A as Scope, H as ApplicationError, k as ERROR_CODES, w as I18N_TOKENS } from "./errors-BdyV5PnY.mjs";
2
- import { a as __decorate, f as DI_TOKENS, o as __decorateParam, p as Transient, s as __decorateMetadata } from "./logger-V6Ms3QnQ.mjs";
3
- import { k as Module } from "./module-Dk2qTa77.mjs";
4
- import { inject } from "tsyringe";
1
+ import { a as ApplicationError } from "./container-storage-GpNNz79X.mjs";
2
+ import { c as DI_TOKENS, d as Transient, l as Request, m as inject, u as Singleton } from "./di-BO1QIb5H.mjs";
3
+ import { n as __decorateParam, t as __decorate } from "./decorate-HgTKAYK8.mjs";
4
+ import "./errors-BBZTnjdq.mjs";
5
+ import { f as Module } from "./module-xYoHba6B.mjs";
6
+ import { t as I18N_TOKENS } from "./i18n.tokens-hwRpmjRq.mjs";
5
7
  //#region src/queue/consumer-registry.ts
6
8
  let ConsumerRegistry = class ConsumerRegistry {
7
9
  /** Map from message type to consumers handling that type */
@@ -66,14 +68,14 @@ let ConsumerRegistry = class ConsumerRegistry {
66
68
  return Array.from(this.allConsumers);
67
69
  }
68
70
  };
69
- ConsumerRegistry = __decorate([Transient(DI_TOKENS.ConsumerRegistry)], ConsumerRegistry);
71
+ ConsumerRegistry = __decorate([Singleton(DI_TOKENS.ConsumerRegistry)], ConsumerRegistry);
70
72
  //#endregion
71
73
  //#region src/queue/queue-sender.ts
72
74
  /**
73
75
  * Queue Sender
74
76
  *
75
- * Implementation of IQueueSender bound to a specific queue name.
76
- * Created by QueueRegistry for each registered queue.
77
+ * Implementation of IQueueSender bound to a specific queue binding.
78
+ * Created by QueueRegistry for each registered binding.
77
79
  *
78
80
  * Automatically enriches messages with:
79
81
  * - `id`: UUID generated via crypto.randomUUID()
@@ -83,7 +85,7 @@ ConsumerRegistry = __decorate([Transient(DI_TOKENS.ConsumerRegistry)], ConsumerR
83
85
  * @example
84
86
  * ```typescript
85
87
  * // Created by QueueRegistry, not directly instantiated
86
- * const sender = registry.getQueue('notifications-queue')
88
+ * const sender = registry.getQueue('NOTIFICATIONS_QUEUE')
87
89
  *
88
90
  * await sender.dispatch({
89
91
  * type: 'email.send',
@@ -92,8 +94,11 @@ ConsumerRegistry = __decorate([Transient(DI_TOKENS.ConsumerRegistry)], ConsumerR
92
94
  * ```
93
95
  */
94
96
  var QueueSender = class {
95
- constructor(queueName, provider, i18n) {
96
- this.queueName = queueName;
97
+ binding;
98
+ provider;
99
+ i18n;
100
+ constructor(binding, provider, i18n) {
101
+ this.binding = binding;
97
102
  this.provider = provider;
98
103
  this.i18n = i18n;
99
104
  }
@@ -114,7 +119,7 @@ var QueueSender = class {
114
119
  ...message,
115
120
  metadata: Object.keys(metadata).length > 0 ? metadata : void 0
116
121
  };
117
- await this.provider.send(this.queueName, fullMessage);
122
+ await this.provider.send(this.binding, fullMessage);
118
123
  }
119
124
  };
120
125
  //#endregion
@@ -127,6 +132,7 @@ const QUEUE_TOKENS = {
127
132
  //#endregion
128
133
  //#region src/queue/queue-registry.ts
129
134
  let QueueRegistry = class QueueRegistry {
135
+ i18n;
130
136
  provider;
131
137
  senders = /* @__PURE__ */ new Map();
132
138
  constructor(providerFactory, i18n) {
@@ -134,100 +140,55 @@ let QueueRegistry = class QueueRegistry {
134
140
  this.provider = providerFactory.create();
135
141
  }
136
142
  /**
137
- * Get or create a QueueSender for the specified queue name.
143
+ * Get or create a QueueSender for the specified binding.
138
144
  *
139
- * Senders are cached per queue name within the request scope.
145
+ * Senders are cached per binding within the request scope.
140
146
  *
141
- * @param queueName - The queue name to get a sender for
142
- * @returns QueueSender bound to the specified queue
147
+ * @param binding - The queue binding to get a sender for
148
+ * @returns QueueSender bound to the specified binding
143
149
  */
144
- getQueue(queueName) {
145
- let sender = this.senders.get(queueName);
150
+ getQueue(binding) {
151
+ let sender = this.senders.get(binding);
146
152
  if (!sender) {
147
- sender = new QueueSender(queueName, this.provider, this.i18n);
148
- this.senders.set(queueName, sender);
153
+ sender = new QueueSender(binding, this.provider, this.i18n);
154
+ this.senders.set(binding, sender);
149
155
  }
150
156
  return sender;
151
157
  }
152
158
  };
153
159
  QueueRegistry = __decorate([
154
- Transient(QUEUE_TOKENS.QueueRegistry),
160
+ Request(QUEUE_TOKENS.QueueRegistry),
155
161
  __decorateParam(0, inject(QUEUE_TOKENS.QueueProviderFactory)),
156
- __decorateParam(1, inject(I18N_TOKENS.I18nService)),
157
- __decorateMetadata("design:paramtypes", [Object, Object])
162
+ __decorateParam(1, inject(I18N_TOKENS.I18nService))
158
163
  ], QueueRegistry);
159
164
  //#endregion
160
- //#region src/queue/errors/queue-binding-not-found.error.ts
161
- /**
162
- * QueueBindingNotFoundError
163
- *
164
- * Thrown when attempting to access a Cloudflare Queue binding that hasn't been configured.
165
- * This typically indicates that the queue binding is missing from wrangler.jsonc
166
- * or the environment variables are not properly set.
167
- */
168
- var QueueBindingNotFoundError = class extends ApplicationError {
169
- constructor(queueName, bindingName) {
170
- super("errors.queueBindingNotFound", ERROR_CODES.SYSTEM.QUEUE_BINDING_NOT_FOUND, {
171
- queueName,
172
- bindingName
173
- });
174
- }
175
- };
176
- //#endregion
177
- //#region src/queue/errors/queue-provider-not-supported.error.ts
178
- /**
179
- * QueueProviderNotSupportedError
180
- *
181
- * Thrown when attempting to use a queue provider that is not supported.
182
- * Valid providers are: 'cloudflare', 'sync'
183
- *
184
- * This typically indicates an invalid QUEUE_PROVIDER environment variable.
185
- */
186
- var QueueProviderNotSupportedError = class extends ApplicationError {
187
- constructor(provider) {
188
- super("errors.queueProviderNotSupported", ERROR_CODES.SYSTEM.QUEUE_PROVIDER_NOT_SUPPORTED, { provider });
189
- }
190
- };
165
+ //#region src/queue/queue.error.ts
166
+ var QueueError = class extends ApplicationError {};
191
167
  //#endregion
192
168
  //#region src/queue/providers/cloudflare-queue.provider.ts
193
169
  let CloudflareQueueProvider = class CloudflareQueueProvider {
170
+ env;
194
171
  constructor(env) {
195
172
  this.env = env;
196
173
  }
197
174
  /**
198
175
  * Send a message to a Cloudflare Queue
199
176
  *
200
- * @param queueName - Queue name (e.g., 'notifications-queue')
177
+ * @param binding - Queue binding identifier (e.g., 'NOTIFICATIONS_QUEUE')
201
178
  * @param message - Complete message with id, timestamp, and payload
202
- * @throws {QueueBindingNotFoundError} If queue binding is not configured
179
+ * @throws {QueueError} If the binding is not configured on env
203
180
  */
204
- async send(queueName, message) {
205
- await this.getQueue(queueName).send(message);
206
- }
207
- /**
208
- * Resolve queue binding from Cloudflare environment
209
- *
210
- * Converts kebab-case queue name to UPPER_SNAKE_CASE binding name.
211
- *
212
- * @param queueName - Queue name (e.g., 'notifications-queue')
213
- * @returns Cloudflare Queue binding
214
- * @throws {QueueBindingNotFoundError} If binding not found in env
215
- */
216
- getQueue(queueName) {
217
- const bindingName = queueName.toUpperCase().replace(/-/g, "_");
218
- const queue = this.env[bindingName];
219
- if (!queue) throw new QueueBindingNotFoundError(queueName, bindingName);
220
- return queue;
181
+ async send(binding, message) {
182
+ const queue = this.env[binding];
183
+ if (!queue) throw new QueueError(`Queue binding "${binding}" was not found in the environment`);
184
+ await queue.send(message);
221
185
  }
222
186
  };
223
- CloudflareQueueProvider = __decorate([
224
- Transient(),
225
- __decorateParam(0, inject(DI_TOKENS.CloudflareEnv)),
226
- __decorateMetadata("design:paramtypes", [Object])
227
- ], CloudflareQueueProvider);
187
+ CloudflareQueueProvider = __decorate([Transient(), __decorateParam(0, inject(DI_TOKENS.CloudflareEnv))], CloudflareQueueProvider);
228
188
  //#endregion
229
189
  //#region src/queue/providers/sync-queue.provider.ts
230
190
  let SyncQueueProvider = class SyncQueueProvider {
191
+ registry;
231
192
  constructor(registry) {
232
193
  this.registry = registry;
233
194
  }
@@ -237,11 +198,11 @@ let SyncQueueProvider = class SyncQueueProvider {
237
198
  * Finds all matching consumers by message type and calls their handle() method.
238
199
  * If any consumer throws, onError() is called and the error is re-thrown.
239
200
  *
240
- * @param _queueName - Queue name (not used for routing, consumers match by message type)
201
+ * @param _binding - Queue binding (not used for routing, consumers match by message type)
241
202
  * @param message - Complete message with id, timestamp, and payload
242
203
  * @throws Re-throws any error from consumer.handle() after calling onError()
243
204
  */
244
- async send(_queueName, message) {
205
+ async send(_binding, message) {
245
206
  const consumers = this.registry.getConsumers(message.type);
246
207
  for (const consumer of consumers) try {
247
208
  await consumer.handle(message);
@@ -252,14 +213,13 @@ let SyncQueueProvider = class SyncQueueProvider {
252
213
  }
253
214
  }
254
215
  };
255
- SyncQueueProvider = __decorate([
256
- Transient(),
257
- __decorateParam(0, inject(DI_TOKENS.ConsumerRegistry)),
258
- __decorateMetadata("design:paramtypes", [Object])
259
- ], SyncQueueProvider);
216
+ SyncQueueProvider = __decorate([Transient(), __decorateParam(0, inject(DI_TOKENS.ConsumerRegistry))], SyncQueueProvider);
260
217
  //#endregion
261
218
  //#region src/queue/services/queue-provider-factory.ts
262
219
  let QueueProviderFactory = class QueueProviderFactory {
220
+ env;
221
+ registry;
222
+ options;
263
223
  constructor(env, registry, options) {
264
224
  this.env = env;
265
225
  this.registry = registry;
@@ -269,14 +229,14 @@ let QueueProviderFactory = class QueueProviderFactory {
269
229
  * Create a queue provider based on module configuration
270
230
  *
271
231
  * @returns Queue provider instance
272
- * @throws {QueueProviderNotSupportedError} If provider type is not supported
232
+ * @throws {QueueError} If provider type is not supported
273
233
  */
274
234
  create() {
275
235
  const providerType = this.options?.provider ?? "cloudflare";
276
236
  switch (providerType) {
277
237
  case "cloudflare": return new CloudflareQueueProvider(this.env);
278
238
  case "sync": return new SyncQueueProvider(this.registry);
279
- default: throw new QueueProviderNotSupportedError(providerType);
239
+ default: throw new QueueError(`Queue provider "${String(providerType)}" is not supported`);
280
240
  }
281
241
  }
282
242
  };
@@ -284,12 +244,7 @@ QueueProviderFactory = __decorate([
284
244
  Transient(QUEUE_TOKENS.QueueProviderFactory),
285
245
  __decorateParam(0, inject(DI_TOKENS.CloudflareEnv)),
286
246
  __decorateParam(1, inject(DI_TOKENS.ConsumerRegistry)),
287
- __decorateParam(2, inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true })),
288
- __decorateMetadata("design:paramtypes", [
289
- Object,
290
- Object,
291
- Object
292
- ])
247
+ __decorateParam(2, inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true }))
293
248
  ], QueueProviderFactory);
294
249
  //#endregion
295
250
  //#region src/queue/queue.module.ts
@@ -306,12 +261,12 @@ QueueProviderFactory = __decorate([
306
261
  * useFactory: (config) => ({ provider: config.get('queue').provider })
307
262
  * })
308
263
  *
309
- * // 2. Register queues (queue name IS the injection token)
310
- * QueueModule.registerQueue('notifications-queue')
311
- * QueueModule.registerQueue('batch-notifications-queue')
264
+ * // 2. Register queue bindings (the binding IS the injection token)
265
+ * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')
266
+ * QueueModule.registerQueue('BACKGROUND_QUEUE')
312
267
  *
313
268
  * // 3. Inject and use
314
- * constructor(@InjectQueue('notifications-queue') private queue: IQueueSender) {}
269
+ * constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}
315
270
  * await this.queue.dispatch({ type: 'email.send', payload: {...} })
316
271
  * ```
317
272
  *
@@ -350,29 +305,31 @@ let QueueModule = _QueueModule = class QueueModule {
350
305
  };
351
306
  }
352
307
  /**
353
- * Register a queue for injection.
308
+ * Register a queue binding for injection.
354
309
  *
355
- * The queue name serves as both the identifier and the DI injection token.
356
- * Queue names are typed via module augmentation of QueueNames interface.
310
+ * The binding name doubles as the DI injection token and the
311
+ * `env`-lookup key. Binding names are typed against `StratalEnv`
312
+ * (autocomplete works once an app augments `StratalEnv` with its
313
+ * Cloudflare bindings).
357
314
  *
358
- * @param name - Queue name (typed with autocomplete if QueueNames is augmented)
315
+ * @param binding - Queue binding identifier (e.g. `NOTIFICATIONS_QUEUE`).
359
316
  * @returns Dynamic module that provides the queue sender
360
317
  *
361
318
  * @example
362
319
  * ```typescript
363
320
  * // In AppModule imports
364
- * QueueModule.registerQueue('notifications-queue')
321
+ * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')
365
322
  *
366
- * // Then inject using the queue name
367
- * constructor(@InjectQueue('notifications-queue') private queue: IQueueSender) {}
323
+ * // Then inject using the binding name
324
+ * constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}
368
325
  * ```
369
326
  */
370
- static registerQueue(name) {
327
+ static registerQueue(binding) {
371
328
  return {
372
329
  module: _QueueModule,
373
330
  providers: [{
374
- provide: name,
375
- useFactory: (registry) => registry.getQueue(name),
331
+ provide: binding,
332
+ useFactory: (registry) => registry.getQueue(binding),
376
333
  inject: [QUEUE_TOKENS.QueueRegistry]
377
334
  }]
378
335
  };
@@ -381,13 +338,11 @@ let QueueModule = _QueueModule = class QueueModule {
381
338
  QueueModule = _QueueModule = __decorate([Module({ providers: [
382
339
  {
383
340
  provide: DI_TOKENS.ConsumerRegistry,
384
- useClass: ConsumerRegistry,
385
- scope: Scope.Singleton
341
+ useClass: ConsumerRegistry
386
342
  },
387
343
  {
388
344
  provide: QUEUE_TOKENS.QueueProviderFactory,
389
- useClass: QueueProviderFactory,
390
- scope: Scope.Singleton
345
+ useClass: QueueProviderFactory
391
346
  },
392
347
  {
393
348
  provide: QUEUE_TOKENS.QueueRegistry,
@@ -395,6 +350,6 @@ QueueModule = _QueueModule = __decorate([Module({ providers: [
395
350
  }
396
351
  ] })], QueueModule);
397
352
  //#endregion
398
- export { QueueProviderNotSupportedError as a, QUEUE_TOKENS as c, CloudflareQueueProvider as i, QueueSender as l, QueueProviderFactory as n, QueueBindingNotFoundError as o, SyncQueueProvider as r, QueueRegistry as s, QueueModule as t, ConsumerRegistry as u };
353
+ export { QueueError as a, QueueSender as c, CloudflareQueueProvider as i, ConsumerRegistry as l, QueueProviderFactory as n, QueueRegistry as o, SyncQueueProvider as r, QUEUE_TOKENS as s, QueueModule as t };
399
354
 
400
- //# sourceMappingURL=queue.module-BCdCiySt.mjs.map
355
+ //# sourceMappingURL=queue.module-DeWJ0tQM.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.module-DeWJ0tQM.mjs","names":[],"sources":["../src/queue/consumer-registry.ts","../src/queue/queue-sender.ts","../src/queue/queue.tokens.ts","../src/queue/queue-registry.ts","../src/queue/queue.error.ts","../src/queue/providers/cloudflare-queue.provider.ts","../src/queue/providers/sync-queue.provider.ts","../src/queue/services/queue-provider-factory.ts","../src/queue/queue.module.ts"],"sourcesContent":["import { Singleton } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { IQueueConsumer } from './queue-consumer'\n\n/**\n * Consumer Registry\n *\n * Singleton service that holds all registered queue consumers indexed by message type.\n * Consumers declare the message types they handle, and this registry routes messages\n * to the appropriate consumers based on their types.\n *\n * **Message-Type Routing:**\n * - Consumers declare `messageTypes` array (e.g., `['email.send', 'email.batch.send']`)\n * - When a message arrives, consumers matching the message type are invoked\n * - A consumer can handle messages from ANY queue (routing is by type, not queue)\n * - Use `'*'` as a wildcard to handle all message types\n *\n * @example Consumer registration\n * ```typescript\n * // In consumer.ts\n * @Transient()\n * export class EmailConsumer implements IQueueConsumer {\n * readonly messageTypes = ['email.send', 'email.batch.send']\n * // ...\n * }\n *\n * // In module.ts\n * @Module({\n * consumers: [EmailConsumer]\n * })\n *\n * // Application auto-registers via ConsumerRegistry\n * this.consumerRegistry.register(consumer)\n * ```\n */\n@Singleton(DI_TOKENS.ConsumerRegistry)\nexport class ConsumerRegistry {\n /** Map from message type to consumers handling that type */\n private consumersByType = new Map<string, IQueueConsumer[]>()\n\n /** Set of all registered consumers (for iteration) */\n private allConsumers = new Set<IQueueConsumer>()\n\n /**\n * Register a queue consumer\n *\n * Indexes the consumer by each of its declared message types.\n *\n * @param consumer - Queue consumer to register\n */\n register(consumer: IQueueConsumer): void {\n if (this.allConsumers.has(consumer)) {\n return // Already registered\n }\n\n this.allConsumers.add(consumer)\n\n for (const messageType of consumer.messageTypes) {\n const existing = this.consumersByType.get(messageType) ?? []\n existing.push(consumer)\n this.consumersByType.set(messageType, existing)\n }\n }\n\n /**\n * Get all consumers that can handle a specific message type\n *\n * Returns consumers that either:\n * - Explicitly declare the message type\n * - Use '*' wildcard to handle all types\n *\n * @param messageType - The message type to find consumers for\n * @returns Array of consumers that can handle this message type\n */\n getConsumers(messageType: string): IQueueConsumer[] {\n const exactMatch = this.consumersByType.get(messageType) ?? []\n const wildcardMatch = this.consumersByType.get('*') ?? []\n\n // Combine and dedupe\n const combined = new Set([...exactMatch, ...wildcardMatch])\n return Array.from(combined)\n }\n\n /**\n * Check if any consumers can handle a message type\n *\n * @param messageType - The message type to check\n * @returns true if at least one consumer can handle this type\n */\n hasConsumers(messageType: string): boolean {\n return this.getConsumers(messageType).length > 0\n }\n\n /**\n * Get all registered message types\n *\n * @returns Array of message types with registered consumers\n */\n getMessageTypes(): string[] {\n return Array.from(this.consumersByType.keys())\n }\n\n /**\n * Get all registered consumers\n *\n * @returns Array of all registered consumers\n */\n getAllConsumers(): IQueueConsumer[] {\n return Array.from(this.allConsumers)\n }\n}\n","import type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { QueueMessage } from './queue-consumer'\nimport type { DispatchMessage, IQueueSender } from './queue-sender.interface'\n\n/**\n * Queue Sender\n *\n * Implementation of IQueueSender bound to a specific queue binding.\n * Created by QueueRegistry for each registered binding.\n *\n * Automatically enriches messages with:\n * - `id`: UUID generated via crypto.randomUUID()\n * - `timestamp`: Current time in milliseconds\n * - `metadata.locale`: Current locale from I18n context\n *\n * @example\n * ```typescript\n * // Created by QueueRegistry, not directly instantiated\n * const sender = registry.getQueue('NOTIFICATIONS_QUEUE')\n *\n * await sender.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n */\nexport class QueueSender implements IQueueSender {\n constructor(\n private readonly binding: string,\n private readonly provider: IQueueProvider,\n private readonly i18n: II18nService\n ) {}\n\n /**\n * Dispatch a message to this queue.\n *\n * @param message - Message to dispatch (without id/timestamp)\n */\n async dispatch<T>(message: DispatchMessage<T>): Promise<void> {\n const metadata = { ...message.metadata }\n\n if (!metadata.locale) {\n const locale = this.i18n.getLocale()\n if (locale) {\n metadata.locale = locale\n }\n }\n\n const fullMessage: QueueMessage<T> = {\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n ...message,\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined\n }\n\n await this.provider.send(this.binding, fullMessage)\n }\n}\n","export const QUEUE_TOKENS = {\n QueueProviderFactory: Symbol.for('stratal:queue:provider:factory'),\n QueueRegistry: Symbol.for('stratal:queue:registry'),\n QueueModuleOptions: Symbol.for('stratal:queue:options'),\n} as const\n\nexport type QueueToken = (typeof QUEUE_TOKENS)[keyof typeof QUEUE_TOKENS]\n","import { inject } from '../di'\nimport { Request } from '../di/decorators'\nimport { I18N_TOKENS } from '../i18n/i18n.tokens'\nimport type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QueueSender } from './queue-sender'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { type QueueProviderFactory } from './services'\n\n/**\n * Queue Registry\n *\n * Request-scoped factory service for creating QueueSender instances.\n * Caches senders per binding within the request scope.\n *\n * This service is used internally by QueueModule.registerQueue() to provide\n * IQueueSender instances for each registered binding.\n *\n * **Why request-scoped?**\n * - Needs access to I18nService for locale-aware message metadata\n * - Provider is created once per request for consistency\n * - Queue senders are cached per request to avoid recreating them\n *\n * @example\n * ```typescript\n * // Used internally by QueueModule.registerQueue()\n * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')\n *\n * // The module creates a factory provider:\n * {\n * provide: 'NOTIFICATIONS_QUEUE',\n * useFactory: (registry: QueueRegistry) => registry.getQueue('NOTIFICATIONS_QUEUE'),\n * inject: [QUEUE_TOKENS.QueueRegistry],\n * }\n * ```\n */\n@Request(QUEUE_TOKENS.QueueRegistry)\nexport class QueueRegistry {\n private readonly provider: IQueueProvider\n private readonly senders = new Map<string, IQueueSender>()\n\n constructor(\n @inject(QUEUE_TOKENS.QueueProviderFactory) providerFactory: QueueProviderFactory,\n @inject(I18N_TOKENS.I18nService) private readonly i18n: II18nService\n ) {\n this.provider = providerFactory.create()\n }\n\n /**\n * Get or create a QueueSender for the specified binding.\n *\n * Senders are cached per binding within the request scope.\n *\n * @param binding - The queue binding to get a sender for\n * @returns QueueSender bound to the specified binding\n */\n getQueue(binding: string): IQueueSender {\n let sender = this.senders.get(binding)\n\n if (!sender) {\n sender = new QueueSender(binding, this.provider, this.i18n)\n this.senders.set(binding, sender)\n }\n\n return sender\n }\n}\n","import { ApplicationError } from '../errors'\n\nexport class QueueError extends ApplicationError {}\n","import { inject } from '../../di'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { QueueError } from '../queue.error'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Cloudflare Queue Provider\n *\n * Sends messages to Cloudflare Queues by resolving the binding directly on\n * the worker's `env`. Used in production environments where Cloudflare Workers\n * handle queue processing.\n *\n * @example\n * ```typescript\n * const provider = new CloudflareQueueProvider(env)\n * await provider.send('NOTIFICATIONS_QUEUE', message)\n * ```\n */\n@Transient()\nexport class CloudflareQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv\n ) { }\n\n /**\n * Send a message to a Cloudflare Queue\n *\n * @param binding - Queue binding identifier (e.g., 'NOTIFICATIONS_QUEUE')\n * @param message - Complete message with id, timestamp, and payload\n * @throws {QueueError} If the binding is not configured on env\n */\n async send<T>(binding: string, message: QueueMessage<T>): Promise<void> {\n const queue = (this.env as unknown as Record<string, unknown>)[binding] as Queue | undefined\n\n if (!queue) {\n throw new QueueError(`Queue binding \"${binding}\" was not found in the environment`)\n }\n\n await queue.send(message)\n }\n}\n","import { inject } from '../../di'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type ConsumerRegistry } from '../consumer-registry'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Sync Queue Provider\n *\n * Processes messages immediately by finding matching consumers and calling\n * their handle() method directly. Used for testing and development where\n * real queue infrastructure is not available.\n *\n * **Behavior:**\n * - Messages are processed synchronously when send() is called\n * - Matching consumers are found via ConsumerRegistry by message type\n * - All matching consumers are called sequentially\n * - Errors are re-thrown after onError() is called (fail-fast for testing)\n *\n * **Consumer Matching:**\n * - Consumers are matched by message type, not queue name\n * - Wildcard ('*') matches all message types\n *\n * @example Testing with sync provider\n * ```typescript\n * const provider = new SyncQueueProvider(registry)\n * await provider.send('NOTIFICATIONS_QUEUE', {\n * id: '123',\n * timestamp: Date.now(),\n * type: 'email.send',\n * payload: { to: 'test@example.com' }\n * })\n * // Consumer's handle() is called immediately!\n * ```\n */\n@Transient()\nexport class SyncQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry\n ) {}\n\n /**\n * Process a message synchronously\n *\n * Finds all matching consumers by message type and calls their handle() method.\n * If any consumer throws, onError() is called and the error is re-thrown.\n *\n * @param _binding - Queue binding (not used for routing, consumers match by message type)\n * @param message - Complete message with id, timestamp, and payload\n * @throws Re-throws any error from consumer.handle() after calling onError()\n */\n async send<T>(_binding: string, message: QueueMessage<T>): Promise<void> {\n // Consumers are matched by message type, not queue name\n const consumers = this.registry.getConsumers(message.type)\n\n // Process synchronously - call each matching consumer\n for (const consumer of consumers) {\n try {\n await consumer.handle(message)\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n // Call onError hook if defined\n if (consumer.onError) {\n await consumer.onError(errorInstance, message)\n }\n\n // Re-throw for fail-fast behavior in tests\n throw errorInstance\n }\n }\n }\n}\n","import { inject } from '../../di'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type ConsumerRegistry } from '../consumer-registry'\nimport { QueueError } from '../queue.error'\nimport { CloudflareQueueProvider, SyncQueueProvider, type IQueueProvider } from '../providers'\nimport type { QueueModuleOptions } from '../queue.module'\nimport { QUEUE_TOKENS } from '../queue.tokens'\n\n/**\n * Queue Provider Factory\n *\n * Creates the appropriate queue provider based on configuration provided\n * via QueueModule.forRootAsync().\n *\n * **Provider Selection:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n *\n * @example\n * ```typescript\n * // Configuration via QueueModule.forRootAsync()\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // Factory usage (internal)\n * const factory = container.resolve(QueueProviderFactory)\n * const provider = factory.create()\n * ```\n */\n@Transient(QUEUE_TOKENS.QueueProviderFactory)\nexport class QueueProviderFactory {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry,\n @inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true }) private readonly options?: QueueModuleOptions,\n ) { }\n\n /**\n * Create a queue provider based on module configuration\n *\n * @returns Queue provider instance\n * @throws {QueueError} If provider type is not supported\n */\n create(): IQueueProvider {\n const providerType = this.options?.provider ?? 'cloudflare'\n\n switch (providerType) {\n case 'cloudflare':\n return new CloudflareQueueProvider(this.env)\n\n case 'sync':\n return new SyncQueueProvider(this.registry)\n\n default:\n throw new QueueError(`Queue provider \"${String(providerType)}\" is not supported`)\n }\n }\n}\n","/**\n * Queue Module\n *\n * Provides declarative queue infrastructure with provider abstraction.\n *\n * **Usage:**\n * ```typescript\n * // 1. Configure provider (once, in app root)\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // 2. Register queue bindings (the binding IS the injection token)\n * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')\n * QueueModule.registerQueue('BACKGROUND_QUEUE')\n *\n * // 3. Inject and use\n * constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}\n * await this.queue.dispatch({ type: 'email.send', payload: {...} })\n * ```\n *\n * **Providers:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n */\n\nimport { DI_TOKENS } from '../di/tokens'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule, InjectionToken } from '../module/types'\nimport { ConsumerRegistry } from './consumer-registry'\nimport type { QueueBinding } from './queue-binding'\nimport { QueueRegistry } from './queue-registry'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { QueueProviderFactory } from './services'\n\n/**\n * Queue module configuration options\n */\nexport interface QueueModuleOptions {\n /**\n * Queue provider type\n * - 'cloudflare': Production provider using Cloudflare Queue bindings\n * - 'sync': Testing provider that processes messages immediately\n */\n provider: 'cloudflare' | 'sync'\n}\n\n@Module({\n providers: [\n { provide: DI_TOKENS.ConsumerRegistry, useClass: ConsumerRegistry },\n { provide: QUEUE_TOKENS.QueueProviderFactory, useClass: QueueProviderFactory },\n { provide: QUEUE_TOKENS.QueueRegistry, useClass: QueueRegistry },\n ],\n})\nexport class QueueModule {\n /**\n * Configure queue infrastructure with async factory.\n *\n * Use when provider configuration depends on other services like ConfigService.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with queue infrastructure\n *\n * @example\n * ```typescript\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config: IConfigService) => ({\n * provider: config.get('queue').provider\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<QueueModuleOptions>): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: QUEUE_TOKENS.QueueModuleOptions,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n /**\n * Register a queue binding for injection.\n *\n * The binding name doubles as the DI injection token and the\n * `env`-lookup key. Binding names are typed against `StratalEnv`\n * (autocomplete works once an app augments `StratalEnv` with its\n * Cloudflare bindings).\n *\n * @param binding - Queue binding identifier (e.g. `NOTIFICATIONS_QUEUE`).\n * @returns Dynamic module that provides the queue sender\n *\n * @example\n * ```typescript\n * // In AppModule imports\n * QueueModule.registerQueue('NOTIFICATIONS_QUEUE')\n *\n * // Then inject using the binding name\n * constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}\n * ```\n */\n static registerQueue(binding: QueueBinding): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: binding as InjectionToken<IQueueSender>,\n useFactory: (registry: QueueRegistry) => registry.getQueue(binding),\n inject: [QUEUE_TOKENS.QueueRegistry],\n },\n ],\n }\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-D1SwGrFN.mjs";
2
- import { t as signUrl } from "./signed-url-BQPbv2In.mjs";
3
- import { n as R2PresignedUrlSecretMissingError, t as StorageResponseBodyMissingError } from "./errors-Da3Pz2X7.mjs";
2
+ import { t as signUrl } from "./signed-url-BqUqt5dF.mjs";
3
+ import { t as StorageError } from "./storage.error-C6FY037a.mjs";
4
4
  //#region src/storage/providers/r2-storage.provider.ts
5
5
  var r2_storage_provider_exports = /* @__PURE__ */ __exportAll({ R2StorageProvider: () => R2StorageProvider });
6
6
  const MIN_PART_SIZE = 5 * 1024 * 1024;
@@ -15,6 +15,9 @@ const PARTS_PREFIX = "__parts/";
15
15
  * listParts/listMultipartUploads work in all environments (local, test, prod).
16
16
  */
17
17
  var R2StorageProvider = class {
18
+ config;
19
+ bucket;
20
+ env;
18
21
  routeBasePath;
19
22
  constructor(config, bucket, env, routeConfig) {
20
23
  this.config = config;
@@ -40,7 +43,7 @@ var R2StorageProvider = class {
40
43
  }
41
44
  async download(path) {
42
45
  const obj = await this.bucket.get(path);
43
- if (!obj) throw new StorageResponseBodyMissingError(path);
46
+ if (!obj) throw new StorageError(`Storage object not found at path "${path}"`);
44
47
  const objBody = obj;
45
48
  return {
46
49
  toStream: () => objBody.body,
@@ -59,7 +62,7 @@ var R2StorageProvider = class {
59
62
  }
60
63
  async getPresignedUrl(path, method, expiresIn) {
61
64
  const secret = this.env.APP_SECRET;
62
- if (!secret) throw new R2PresignedUrlSecretMissingError();
65
+ if (!secret) throw new StorageError("APP_SECRET is required for generating presigned URLs");
63
66
  return {
64
67
  url: await signUrl(`${`${this.routeBasePath}/${this.config.disk}/${path}`}?method=${method}`, secret, { expiresIn }),
65
68
  expiresIn,
@@ -241,4 +244,4 @@ var R2StorageProvider = class {
241
244
  //#endregion
242
245
  export { r2_storage_provider_exports as n, R2StorageProvider as t };
243
246
 
244
- //# sourceMappingURL=r2-storage.provider-Co6F0ZYV.mjs.map
247
+ //# sourceMappingURL=r2-storage.provider-Hfm6LdZQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-storage.provider-Hfm6LdZQ.mjs","names":[],"sources":["../src/storage/providers/r2-storage.provider.ts"],"sourcesContent":["import { type StratalEnv } from '../../env'\nimport { signUrl } from '../../router/signed-url'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport { StorageError } from '../storage.error'\nimport type { StorageEntry, StorageRouteConfig } from '../types'\nimport type {\n CompletedPart,\n CompleteMultipartResult,\n CreateMultipartOptions,\n CreateMultipartResult,\n DeleteObjectsResult,\n HeadObjectResult,\n IMultipartProvider,\n ListMultipartUploadsResult,\n ListPartsResult,\n UploadPartResult,\n} from './multipart-provider.interface'\nimport type { StreamingBlobPayloadInputTypes } from './storage-provider.interface'\n\nconst MIN_PART_SIZE = 5 * 1024 * 1024 // 5MB\nconst MULTIPART_PREFIX = '__multipart/'\nconst PARTS_PREFIX = '__parts/'\n\n/**\n * R2 Storage Provider\n * Implements storage operations using native Cloudflare R2 bindings\n *\n * Implements IMultipartProvider for multipart upload support needed by TUS.\n * Tracks multipart upload metadata using companion R2 objects so that\n * listParts/listMultipartUploads work in all environments (local, test, prod).\n */\nexport class R2StorageProvider implements IMultipartProvider {\n private readonly routeBasePath: string\n\n constructor(\n private readonly config: StorageEntry,\n private readonly bucket: R2Bucket,\n private readonly env: StratalEnv,\n routeConfig?: StorageRouteConfig\n ) {\n this.routeBasePath = routeConfig?.basePath ?? '/storage'\n }\n\n async upload(\n body: StreamingBlobPayloadInputTypes,\n path: string,\n options: UploadOptions\n ): Promise<UploadResult> {\n const customMetadata: Record<string, string> = { ...options.metadata }\n if (options.tagging) {\n customMetadata['x-tags'] = options.tagging\n }\n\n await this.bucket.put(path, body as ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob | null, {\n httpMetadata: {\n contentType: options.mimeType,\n },\n customMetadata,\n })\n\n return {\n path,\n disk: this.config.disk,\n fullPath: path,\n size: options.size,\n mimeType: options.mimeType ?? 'application/octet-stream',\n uploadedAt: new Date(),\n }\n }\n\n async download(path: string): Promise<DownloadResult> {\n const obj = await this.bucket.get(path)\n\n if (!obj) {\n throw new StorageError(`Storage object not found at path \"${path}\"`)\n }\n\n // R2ObjectBody has body, text(), arrayBuffer(), etc.\n const objBody = obj\n\n return {\n toStream: () => objBody.body as ReadableStream<Uint8Array>,\n contentType: obj.httpMetadata?.contentType ?? 'application/octet-stream',\n size: obj.size,\n metadata: obj.customMetadata,\n toString: () => objBody.text(),\n toArrayBuffer: () => objBody.bytes(),\n }\n }\n\n async delete(path: string): Promise<void> {\n await this.bucket.delete(path)\n }\n\n async exists(path: string): Promise<boolean> {\n const head = await this.bucket.head(path)\n return head !== null\n }\n\n async getPresignedUrl(\n path: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn: number\n ): Promise<PresignedUrlResult> {\n const secret = this.env.APP_SECRET;\n if (!secret) {\n throw new StorageError('APP_SECRET is required for generating presigned URLs')\n }\n\n // Build a relative URL pointing to the StorageController route\n const routePath = `${this.routeBasePath}/${this.config.disk}/${path}`\n const urlWithMethod = `${routePath}?method=${method}`\n const url = await signUrl(urlWithMethod, secret, { expiresIn })\n\n return {\n url,\n expiresIn,\n expiresAt: new Date(Date.now() + expiresIn * 1000),\n method,\n }\n }\n\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n path: string,\n options: Omit<UploadOptions, 'size'> & { size?: number }\n ): Promise<UploadResult> {\n const multipartUpload = await this.bucket.createMultipartUpload(path, {\n httpMetadata: {\n contentType: options.mimeType,\n },\n customMetadata: options.metadata,\n })\n\n const parts: R2UploadedPart[] = []\n\n try {\n if (body instanceof ReadableStream) {\n const reader = (body as ReadableStream<Uint8Array>).getReader()\n let buffer = new Uint8Array(0)\n let partNumber = 1\n\n while (true) {\n const { done, value } = await reader.read()\n\n if (value) {\n const newBuffer = new Uint8Array(buffer.length + value.length)\n newBuffer.set(buffer, 0)\n newBuffer.set(value, buffer.length)\n buffer = newBuffer\n }\n\n while (buffer.length >= MIN_PART_SIZE) {\n const partData = buffer.slice(0, MIN_PART_SIZE)\n buffer = buffer.slice(MIN_PART_SIZE)\n const part = await multipartUpload.uploadPart(partNumber, partData)\n parts.push(part)\n partNumber++\n }\n\n if (done) {\n // Upload remaining buffer as final part\n if (buffer.length > 0) {\n const part = await multipartUpload.uploadPart(partNumber, buffer)\n parts.push(part)\n }\n break\n }\n }\n } else if (body !== null && body !== undefined) {\n // Non-stream body: upload as single part\n const part = await multipartUpload.uploadPart(1, body as ArrayBuffer | ArrayBufferView | string | Blob)\n parts.push(part)\n }\n\n await multipartUpload.complete(parts)\n } catch (error) {\n await multipartUpload.abort()\n throw error\n }\n\n // Get actual size via head\n const headResult = await this.bucket.head(path)\n\n return {\n path,\n disk: this.config.disk,\n fullPath: path,\n size: headResult?.size ?? options.size ?? 0,\n mimeType: options.mimeType ?? 'application/octet-stream',\n uploadedAt: new Date(),\n }\n }\n\n // ============================================\n // IMultipartProvider - Multipart upload methods\n // ============================================\n\n getBucket(): string {\n return this.config.binding\n }\n\n async headObject(key: string): Promise<HeadObjectResult | null> {\n const obj = await this.bucket.head(key)\n if (!obj) {\n return null\n }\n return {\n size: obj.size,\n contentType: obj.httpMetadata?.contentType,\n metadata: obj.customMetadata,\n }\n }\n\n async deleteObjects(keys: string[]): Promise<DeleteObjectsResult> {\n if (keys.length === 0) {\n return { deleted: 0, errors: [] }\n }\n\n await this.bucket.delete(keys)\n\n return {\n deleted: keys.length,\n errors: [],\n }\n }\n\n async createMultipartUpload(\n key: string,\n options?: CreateMultipartOptions\n ): Promise<CreateMultipartResult> {\n const upload = await this.bucket.createMultipartUpload(key, {\n httpMetadata: {\n contentType: options?.contentType,\n cacheControl: options?.cacheControl,\n },\n customMetadata: options?.metadata,\n })\n\n // Track upload for listMultipartUploads\n await this.bucket.put(\n `${MULTIPART_PREFIX}${upload.uploadId}.json`,\n JSON.stringify({ key: upload.key, uploadId: upload.uploadId, initiated: new Date().toISOString() }),\n { httpMetadata: { contentType: 'application/json' } }\n )\n\n return {\n uploadId: upload.uploadId,\n key: upload.key,\n }\n }\n\n async uploadPart(\n key: string,\n uploadId: string,\n partNumber: number,\n body: Uint8Array\n ): Promise<UploadPartResult> {\n const upload = this.bucket.resumeMultipartUpload(key, uploadId)\n const part = await upload.uploadPart(partNumber, body)\n\n // Track part for listParts\n await this.bucket.put(\n `${PARTS_PREFIX}${uploadId}/${partNumber}`,\n JSON.stringify({ partNumber: part.partNumber, etag: part.etag, size: body.length }),\n { httpMetadata: { contentType: 'application/json' } }\n )\n\n return {\n etag: part.etag,\n partNumber: part.partNumber,\n }\n }\n\n async completeMultipartUpload(\n key: string,\n uploadId: string,\n parts: CompletedPart[]\n ): Promise<CompleteMultipartResult> {\n const upload = this.bucket.resumeMultipartUpload(key, uploadId)\n const result = await upload.complete(\n parts.map((p) => ({\n partNumber: p.partNumber,\n etag: p.etag,\n }))\n )\n\n await this.cleanupTrackingObjects(uploadId)\n\n return {\n key: result.key,\n }\n }\n\n async abortMultipartUpload(key: string, uploadId: string): Promise<void> {\n const upload = this.bucket.resumeMultipartUpload(key, uploadId)\n await upload.abort()\n\n await this.cleanupTrackingObjects(uploadId)\n }\n\n async listParts(\n _key: string,\n uploadId: string,\n partNumberMarker?: string\n ): Promise<ListPartsResult> {\n const prefix = `${PARTS_PREFIX}${uploadId}/`\n const listed = await this.bucket.list({\n prefix,\n cursor: partNumberMarker,\n })\n\n const parts = await Promise.all(\n listed.objects.map(async (obj) => {\n const data = await this.bucket.get(obj.key)\n if (!data) {\n return null\n }\n return JSON.parse(await data.text()) as { partNumber: number; etag: string; size: number }\n })\n )\n\n const validParts = parts\n .filter((p): p is NonNullable<typeof p> => p !== null)\n .sort((a, b) => a.partNumber - b.partNumber)\n\n return {\n parts: validParts,\n isTruncated: listed.truncated,\n nextPartNumberMarker: listed.truncated ? listed.cursor : undefined,\n }\n }\n\n async listMultipartUploads(\n keyMarker?: string,\n _uploadIdMarker?: string\n ): Promise<ListMultipartUploadsResult> {\n const listed = await this.bucket.list({\n prefix: MULTIPART_PREFIX,\n cursor: keyMarker,\n })\n\n const uploads = await Promise.all(\n listed.objects.map(async (obj) => {\n const data = await this.bucket.get(obj.key)\n if (!data) {\n return null\n }\n const parsed = JSON.parse(await data.text()) as { key: string; uploadId: string; initiated: string }\n return {\n key: parsed.key,\n uploadId: parsed.uploadId,\n initiated: new Date(parsed.initiated),\n }\n })\n )\n\n return {\n uploads: uploads.filter((u): u is NonNullable<typeof u> => u !== null),\n isTruncated: listed.truncated,\n nextKeyMarker: listed.truncated ? listed.cursor : undefined,\n nextUploadIdMarker: undefined,\n }\n }\n\n // ============================================\n // Private helpers\n // ============================================\n\n private async cleanupTrackingObjects(uploadId: string): Promise<void> {\n // Delete part tracking objects\n const partsPrefix = `${PARTS_PREFIX}${uploadId}/`\n let cursor: string | undefined\n const keysToDelete: string[] = []\n\n do {\n const listed = await this.bucket.list({ prefix: partsPrefix, cursor })\n for (const obj of listed.objects) {\n keysToDelete.push(obj.key)\n }\n cursor = listed.truncated ? listed.cursor : undefined\n } while (cursor)\n\n // Delete multipart tracking object\n keysToDelete.push(`${MULTIPART_PREFIX}${uploadId}.json`)\n\n if (keysToDelete.length > 0) {\n await this.bucket.delete(keysToDelete)\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { f as ROUTE_METADATA_KEYS } from "./errors-BdyV5PnY.mjs";
1
+ import { n as getMetadata, t as defineMetadata } from "./metadata-BVkc4aUu.mjs";
2
+ import { u as ROUTE_METADATA_KEYS } from "./exception-context-B4kM-M53.mjs";
2
3
  //#region src/rate-limiter/decorators/rate-limit.decorator.ts
3
4
  const KEY = ROUTE_METADATA_KEYS.RATE_LIMIT;
4
5
  /**
@@ -29,13 +30,8 @@ const KEY = ROUTE_METADATA_KEYS.RATE_LIMIT;
29
30
  */
30
31
  function RateLimit(name) {
31
32
  return (target, propertyKey) => {
32
- if (propertyKey === void 0) {
33
- const existing = Reflect.getOwnMetadata(KEY, target) ?? [];
34
- Reflect.defineMetadata(KEY, [...existing, name], target);
35
- } else {
36
- const existing = Reflect.getOwnMetadata(KEY, target, propertyKey) ?? [];
37
- Reflect.defineMetadata(KEY, [...existing, name], target, propertyKey);
38
- }
33
+ if (propertyKey === void 0) defineMetadata(KEY, [...getMetadata(KEY, target) ?? [], name], target);
34
+ else defineMetadata(KEY, [...getMetadata(KEY, target, propertyKey) ?? [], name], target, propertyKey);
39
35
  };
40
36
  }
41
37
  /**
@@ -46,10 +42,9 @@ function RateLimit(name) {
46
42
  * For method metadata, pass `Controller.prototype` and the method name.
47
43
  */
48
44
  function getRateLimits(target, propertyKey) {
49
- const meta = propertyKey === void 0 ? Reflect.getMetadata(KEY, target) : Reflect.getMetadata(KEY, target, propertyKey);
50
- return Array.isArray(meta) ? meta : [];
45
+ return (propertyKey === void 0 ? getMetadata(KEY, target) : getMetadata(KEY, target, propertyKey)) ?? [];
51
46
  }
52
47
  //#endregion
53
48
  export { getRateLimits as n, RateLimit as t };
54
49
 
55
- //# sourceMappingURL=rate-limit.decorator--o6Q6p9w.mjs.map
50
+ //# sourceMappingURL=rate-limit.decorator-D69zdZbp.mjs.map