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.
- package/README.md +1 -1
- package/dist/{base-email.provider-CfQCA08m.mjs → base-email.provider-BWZHIjt8.mjs} +1 -1
- package/dist/{base-email.provider-CfQCA08m.mjs.map → base-email.provider-BWZHIjt8.mjs.map} +1 -1
- package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
- package/dist/bin/quarry.mjs +46 -109
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +6 -46
- package/dist/cache/index.d.mts.map +1 -1
- package/dist/cache/index.mjs +22 -67
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{cache.service-DsnKuNyO.d.mts → cache.service-e34gV6tz.d.mts} +8 -8
- package/dist/{cache.service-DsnKuNyO.d.mts.map → cache.service-e34gV6tz.d.mts.map} +1 -1
- package/dist/{cache.tokens-B7Rw1C9Q.mjs → cache.tokens-ovi_c52J.mjs} +1 -1
- package/dist/{cache.tokens-B7Rw1C9Q.mjs.map → cache.tokens-ovi_c52J.mjs.map} +1 -1
- package/dist/{colors-DJaRDXoS.mjs → colors-axmupKdp.mjs} +1 -1
- package/dist/{colors-DJaRDXoS.mjs.map → colors-axmupKdp.mjs.map} +1 -1
- package/dist/{command-BgSlsS4M.mjs → command-BU4ApTo5.mjs} +2 -3
- package/dist/command-BU4ApTo5.mjs.map +1 -0
- package/dist/{command-Bu-PjJrX.d.mts → command-wXfvHbBZ.d.mts} +3 -2
- package/dist/command-wXfvHbBZ.d.mts.map +1 -0
- package/dist/config/index.d.mts +24 -11
- package/dist/config/index.d.mts.map +1 -1
- package/dist/config/index.mjs +33 -57
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-B7yUNh0q.d.mts → consumer-registry-DHQtypr1.d.mts} +1 -1
- package/dist/{consumer-registry-B7yUNh0q.d.mts.map → consumer-registry-DHQtypr1.d.mts.map} +1 -1
- package/dist/container-storage-GpNNz79X.mjs +52 -0
- package/dist/container-storage-GpNNz79X.mjs.map +1 -0
- package/dist/{controller.decorator-DQzenvSN.mjs → controller.decorator-DIUazNU7.mjs} +8 -8
- package/dist/controller.decorator-DIUazNU7.mjs.map +1 -0
- package/dist/cron/index.d.mts +26 -5
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +1 -1
- package/dist/{cron-manager-7Symz_TE.mjs → cron-manager-9bpN9bu4.mjs} +42 -16
- package/dist/cron-manager-9bpN9bu4.mjs.map +1 -0
- package/dist/{cron-manager-BEsH1mjW.d.mts → cron-manager-CSTIBPcM.d.mts} +6 -13
- package/dist/cron-manager-CSTIBPcM.d.mts.map +1 -0
- package/dist/decorate-HgTKAYK8.mjs +16 -0
- package/dist/deep-merge-C8NgcXw4.mjs +18 -0
- package/dist/deep-merge-C8NgcXw4.mjs.map +1 -0
- package/dist/di/index.d.mts +2 -2
- package/dist/di/index.mjs +4 -3
- package/dist/di-BO1QIb5H.mjs +415 -0
- package/dist/di-BO1QIb5H.mjs.map +1 -0
- package/dist/email/index.d.mts +14 -89
- package/dist/email/index.d.mts.map +1 -1
- package/dist/email/index.mjs +30 -125
- package/dist/email/index.mjs.map +1 -1
- package/dist/en-BPP6h6y5.mjs +202 -0
- package/dist/en-BPP6h6y5.mjs.map +1 -0
- package/dist/{env-D1rcZ8_r.d.mts → env-DKSbuBi5.d.mts} +1 -1
- package/dist/env-DKSbuBi5.d.mts.map +1 -0
- package/dist/errors/index.d.mts +2 -2
- package/dist/errors/index.mjs +4 -2
- package/dist/errors-BBZTnjdq.mjs +333 -0
- package/dist/errors-BBZTnjdq.mjs.map +1 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.d.mts.map +1 -1
- package/dist/events/index.mjs +1 -1
- package/dist/{events-COKixqnG.mjs → events-D1KdDaiP.mjs} +13 -11
- package/dist/events-D1KdDaiP.mjs.map +1 -0
- package/dist/exception-context-B4kM-M53.mjs +429 -0
- package/dist/exception-context-B4kM-M53.mjs.map +1 -0
- package/dist/{gateway-context-CdJjpUCW.mjs → gateway-context-CFe6a9gz.mjs} +20 -31
- package/dist/gateway-context-CFe6a9gz.mjs.map +1 -0
- package/dist/guards/index.d.mts +3 -3
- package/dist/guards/index.d.mts.map +1 -1
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-DUk_Kzst.mjs → guards-Ced-uNIF.mjs} +7 -5
- package/dist/guards-Ced-uNIF.mjs.map +1 -0
- package/dist/{http-method.decorator-DXwxAfb_.mjs → http-method.decorator-CdjKFJZZ.mjs} +7 -6
- package/dist/http-method.decorator-CdjKFJZZ.mjs.map +1 -0
- package/dist/i18n/index.d.mts +238 -3
- package/dist/i18n/index.d.mts.map +1 -0
- package/dist/i18n/index.mjs +39 -3
- package/dist/i18n/index.mjs.map +1 -0
- package/dist/i18n/messages/en/index.d.mts +2 -2
- package/dist/i18n/messages/en/index.mjs +2 -2
- package/dist/i18n/utils/index.d.mts +4 -26
- package/dist/i18n/utils/index.d.mts.map +1 -1
- package/dist/i18n/utils/index.mjs +2 -2
- package/dist/i18n/validation/index.d.mts +3 -2
- package/dist/i18n/validation/index.mjs +4 -2
- package/dist/i18n.module-BlXrtAlV.mjs +219 -0
- package/dist/i18n.module-BlXrtAlV.mjs.map +1 -0
- package/dist/i18n.tokens-hwRpmjRq.mjs +19 -0
- package/dist/i18n.tokens-hwRpmjRq.mjs.map +1 -0
- package/dist/{index-7-hU3GTV.d.mts → index-B4UBK-2T.d.mts} +1 -1
- package/dist/{index-7-hU3GTV.d.mts.map → index-B4UBK-2T.d.mts.map} +1 -1
- package/dist/index-BtlE9RuO.d.mts +124 -0
- package/dist/index-BtlE9RuO.d.mts.map +1 -0
- package/dist/{index-CjaQ6_tZ.d.mts → index-CW1YHSft.d.mts} +71 -167
- package/dist/index-CW1YHSft.d.mts.map +1 -0
- package/dist/{index-D0US0X14.d.mts → index-DEncMcC6.d.mts} +559 -2239
- package/dist/index-DEncMcC6.d.mts.map +1 -0
- package/dist/index-Dj5IMwtr.d.mts +44 -0
- package/dist/index-Dj5IMwtr.d.mts.map +1 -0
- package/dist/{index-C1KvMncZ.d.mts → index-KMgSCSM7.d.mts} +3 -108
- package/dist/index-KMgSCSM7.d.mts.map +1 -0
- package/dist/index.d.mts +5 -43
- package/dist/index.mjs +1 -1
- package/dist/{is-command-C6a7WTPw.mjs → is-command-CX5rAfZW.mjs} +2 -2
- package/dist/{is-command-C6a7WTPw.mjs.map → is-command-CX5rAfZW.mjs.map} +1 -1
- package/dist/{is-seeder-CebjZCDn.mjs → is-seeder-CYCtELlm.mjs} +1 -1
- package/dist/{is-seeder-CebjZCDn.mjs.map → is-seeder-CYCtELlm.mjs.map} +1 -1
- package/dist/logger/index.d.mts +2 -2
- package/dist/logger/index.mjs +170 -2
- package/dist/logger/index.mjs.map +1 -0
- package/dist/macroable/index.d.mts +1 -1
- package/dist/macroable/index.mjs +1 -1
- package/dist/{macroable-BmufBshB.mjs → macroable-DzlfzT50.mjs} +1 -1
- package/dist/{macroable-BmufBshB.mjs.map → macroable-DzlfzT50.mjs.map} +1 -1
- package/dist/metadata-BVkc4aUu.mjs +39 -0
- package/dist/metadata-BVkc4aUu.mjs.map +1 -0
- package/dist/module/index.d.mts +6 -24
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +2 -2
- package/dist/module-xYoHba6B.mjs +422 -0
- package/dist/module-xYoHba6B.mjs.map +1 -0
- package/dist/openapi/index.d.mts +3 -3
- package/dist/openapi/index.d.mts.map +1 -1
- package/dist/openapi/index.mjs +1 -2
- package/dist/openapi-C6lm0RmV.mjs +483 -0
- package/dist/openapi-C6lm0RmV.mjs.map +1 -0
- package/dist/{openapi.service-BLgvn3hJ.d.mts → openapi.service-CrLlsXAd.d.mts} +3 -3
- package/dist/openapi.service-CrLlsXAd.d.mts.map +1 -0
- package/dist/quarry/index.d.mts +5 -163
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +5 -5
- package/dist/quarry/runner.d.mts +184 -0
- package/dist/quarry/runner.d.mts.map +1 -0
- package/dist/quarry/runner.mjs +775 -0
- package/dist/quarry/runner.mjs.map +1 -0
- package/dist/quarry-registry-D4hIGScf.d.mts +69 -0
- package/dist/quarry-registry-D4hIGScf.d.mts.map +1 -0
- package/dist/quarry-registry-DkraZNwn.mjs +311 -0
- package/dist/quarry-registry-DkraZNwn.mjs.map +1 -0
- package/dist/queue/index.d.mts +3 -3
- package/dist/queue/index.mjs +27 -28
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-BCdCiySt.mjs → queue.module-DeWJ0tQM.mjs} +67 -112
- package/dist/queue.module-DeWJ0tQM.mjs.map +1 -0
- package/dist/{r2-storage.provider-Co6F0ZYV.mjs → r2-storage.provider-Hfm6LdZQ.mjs} +8 -5
- package/dist/r2-storage.provider-Hfm6LdZQ.mjs.map +1 -0
- package/dist/{rate-limit.decorator--o6Q6p9w.mjs → rate-limit.decorator-D69zdZbp.mjs} +6 -11
- package/dist/rate-limit.decorator-D69zdZbp.mjs.map +1 -0
- package/dist/rate-limiter/index.d.mts +11 -50
- package/dist/rate-limiter/index.d.mts.map +1 -1
- package/dist/rate-limiter/index.mjs +25 -30
- package/dist/rate-limiter/index.mjs.map +1 -1
- package/dist/{resend.provider-M6qRLrcy.mjs → resend.provider-Ur6tU7fK.mjs} +8 -7
- package/dist/resend.provider-Ur6tU7fK.mjs.map +1 -0
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +8 -7
- package/dist/{i18n.module-BBlNNlcG.mjs → router-Cy6DjkvP.mjs} +215 -855
- package/dist/router-Cy6DjkvP.mjs.map +1 -0
- package/dist/seeder/index.d.mts +6 -11
- package/dist/seeder/index.d.mts.map +1 -1
- package/dist/seeder/index.mjs +3 -3
- package/dist/{seeder-CJAOHEIo.mjs → seeder-BADTig4n.mjs} +17 -22
- package/dist/seeder-BADTig4n.mjs.map +1 -0
- package/dist/{signed-url-BQPbv2In.mjs → signed-url-BqUqt5dF.mjs} +1 -1
- package/dist/{signed-url-BQPbv2In.mjs.map → signed-url-BqUqt5dF.mjs.map} +1 -1
- package/dist/{smtp.provider-w0Ve52Xg.mjs → smtp.provider-C129sNBT.mjs} +7 -6
- package/dist/smtp.provider-C129sNBT.mjs.map +1 -0
- package/dist/storage/index.d.mts +15 -39
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +3 -3
- package/dist/storage/providers/index.d.mts +2 -2
- package/dist/storage/providers/index.mjs +1 -1
- package/dist/{storage-1zw-6Yiz.mjs → storage-BA3ppVYM.mjs} +70 -59
- package/dist/storage-BA3ppVYM.mjs.map +1 -0
- package/dist/{storage-provider.interface-Bd6vA4ak.d.mts → storage-provider.interface-DQMtT42e.d.mts} +2 -3
- package/dist/storage-provider.interface-DQMtT42e.d.mts.map +1 -0
- package/dist/storage.error-C6FY037a.mjs +8 -0
- package/dist/storage.error-C6FY037a.mjs.map +1 -0
- package/dist/{stratal-DeEcGgdq.mjs → stratal-Bdq4IdB3.mjs} +31 -183
- package/dist/stratal-Bdq4IdB3.mjs.map +1 -0
- package/dist/stratal-BsKmvP6J.d.mts +43 -0
- package/dist/stratal-BsKmvP6J.d.mts.map +1 -0
- package/dist/{types-cySNS_lp.d.mts → types-BaeHi67f.d.mts} +1 -1
- package/dist/types-BaeHi67f.d.mts.map +1 -0
- package/dist/{usage-generator-BUdlhnCK.mjs → usage-generator-DTqaUMR9.mjs} +6 -3
- package/dist/usage-generator-DTqaUMR9.mjs.map +1 -0
- package/dist/validation-DUzcjb8Q.mjs +49 -0
- package/dist/validation-DUzcjb8Q.mjs.map +1 -0
- package/dist/validation.context-XTysWJ3b.mjs +117 -0
- package/dist/validation.context-XTysWJ3b.mjs.map +1 -0
- package/dist/websocket/index.d.mts +7 -14
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +2 -2
- package/dist/workers/index.d.mts +2 -2
- package/dist/workers/index.mjs +3 -2
- package/dist/workers/index.mjs.map +1 -1
- package/dist/{index-Bnpfq6uk.d.mts → zod-DvWTfRpI.d.mts} +58 -133
- package/dist/zod-DvWTfRpI.d.mts.map +1 -0
- package/dist/zod-hMa3rSHV.mjs +72 -0
- package/dist/zod-hMa3rSHV.mjs.map +1 -0
- package/package.json +20 -20
- package/dist/command-BgSlsS4M.mjs.map +0 -1
- package/dist/command-Bu-PjJrX.d.mts.map +0 -1
- package/dist/controller.decorator-DQzenvSN.mjs.map +0 -1
- package/dist/cron-manager-7Symz_TE.mjs.map +0 -1
- package/dist/cron-manager-BEsH1mjW.d.mts.map +0 -1
- package/dist/en-DSH_bhh6.mjs +0 -308
- package/dist/en-DSH_bhh6.mjs.map +0 -1
- package/dist/env-D1rcZ8_r.d.mts.map +0 -1
- package/dist/errors-BdyV5PnY.mjs +0 -1725
- package/dist/errors-BdyV5PnY.mjs.map +0 -1
- package/dist/errors-Da3Pz2X7.mjs +0 -74
- package/dist/errors-Da3Pz2X7.mjs.map +0 -1
- package/dist/events-COKixqnG.mjs.map +0 -1
- package/dist/gateway-context-CdJjpUCW.mjs.map +0 -1
- package/dist/guards-DUk_Kzst.mjs.map +0 -1
- package/dist/http-method.decorator-DXwxAfb_.mjs.map +0 -1
- package/dist/i18n.module-BBlNNlcG.mjs.map +0 -1
- package/dist/index-Bnpfq6uk.d.mts.map +0 -1
- package/dist/index-C1KvMncZ.d.mts.map +0 -1
- package/dist/index-CjaQ6_tZ.d.mts.map +0 -1
- package/dist/index-D0US0X14.d.mts.map +0 -1
- package/dist/index-DBd_2wv8.d.mts +0 -263
- package/dist/index-DBd_2wv8.d.mts.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/logger-V6Ms3QnQ.mjs +0 -436
- package/dist/logger-V6Ms3QnQ.mjs.map +0 -1
- package/dist/module-Dk2qTa77.mjs +0 -860
- package/dist/module-Dk2qTa77.mjs.map +0 -1
- package/dist/openapi-tools.service-Zs-Ewv7F.mjs +0 -200
- package/dist/openapi-tools.service-Zs-Ewv7F.mjs.map +0 -1
- package/dist/openapi.service-BLgvn3hJ.d.mts.map +0 -1
- package/dist/quarry-registry-DNEej-Db.mjs +0 -688
- package/dist/quarry-registry-DNEej-Db.mjs.map +0 -1
- package/dist/queue.module-BCdCiySt.mjs.map +0 -1
- package/dist/r2-storage.provider-Co6F0ZYV.mjs.map +0 -1
- package/dist/rate-limit.decorator--o6Q6p9w.mjs.map +0 -1
- package/dist/resend.provider-M6qRLrcy.mjs.map +0 -1
- package/dist/seeder-CJAOHEIo.mjs.map +0 -1
- package/dist/setup-CefZKV_e.mjs +0 -37
- package/dist/setup-CefZKV_e.mjs.map +0 -1
- package/dist/smtp.provider-w0Ve52Xg.mjs.map +0 -1
- package/dist/storage-1zw-6Yiz.mjs.map +0 -1
- package/dist/storage-provider.interface-Bd6vA4ak.d.mts.map +0 -1
- package/dist/stratal-DeEcGgdq.mjs.map +0 -1
- package/dist/types-cySNS_lp.d.mts.map +0 -1
- package/dist/usage-generator-BUdlhnCK.mjs.map +0 -1
- package/dist/validation-DtJwAv7O.mjs +0 -248
- package/dist/validation-DtJwAv7O.mjs.map +0 -1
package/dist/queue/index.mjs
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
44
|
-
*
|
|
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
|
|
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('
|
|
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
|
|
65
|
-
* For module-internal
|
|
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(
|
|
69
|
-
return inject(
|
|
67
|
+
function InjectQueue(binding) {
|
|
68
|
+
return inject(binding);
|
|
70
69
|
}
|
|
71
70
|
//#endregion
|
|
72
|
-
export { CloudflareQueueProvider, ConsumerRegistry, InjectQueue, QUEUE_TOKENS,
|
|
71
|
+
export { CloudflareQueueProvider, ConsumerRegistry, InjectQueue, QUEUE_TOKENS, QueueError, QueueManager, QueueModule, QueueProviderFactory, QueueRegistry, QueueSender, SyncQueueProvider };
|
|
73
72
|
|
|
74
73
|
//# sourceMappingURL=index.mjs.map
|
package/dist/queue/index.mjs.map
CHANGED
|
@@ -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 '
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
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([
|
|
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
|
|
76
|
-
* Created by QueueRegistry for each registered
|
|
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('
|
|
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
|
-
|
|
96
|
-
|
|
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.
|
|
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
|
|
143
|
+
* Get or create a QueueSender for the specified binding.
|
|
138
144
|
*
|
|
139
|
-
* Senders are cached per
|
|
145
|
+
* Senders are cached per binding within the request scope.
|
|
140
146
|
*
|
|
141
|
-
* @param
|
|
142
|
-
* @returns QueueSender bound to the specified
|
|
147
|
+
* @param binding - The queue binding to get a sender for
|
|
148
|
+
* @returns QueueSender bound to the specified binding
|
|
143
149
|
*/
|
|
144
|
-
getQueue(
|
|
145
|
-
let sender = this.senders.get(
|
|
150
|
+
getQueue(binding) {
|
|
151
|
+
let sender = this.senders.get(binding);
|
|
146
152
|
if (!sender) {
|
|
147
|
-
sender = new QueueSender(
|
|
148
|
-
this.senders.set(
|
|
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
|
-
|
|
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/
|
|
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
|
|
177
|
+
* @param binding - Queue binding identifier (e.g., 'NOTIFICATIONS_QUEUE')
|
|
201
178
|
* @param message - Complete message with id, timestamp, and payload
|
|
202
|
-
* @throws {
|
|
179
|
+
* @throws {QueueError} If the binding is not configured on env
|
|
203
180
|
*/
|
|
204
|
-
async send(
|
|
205
|
-
|
|
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
|
|
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(
|
|
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 {
|
|
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
|
|
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
|
|
310
|
-
* QueueModule.registerQueue('
|
|
311
|
-
* QueueModule.registerQueue('
|
|
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('
|
|
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
|
|
356
|
-
*
|
|
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
|
|
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('
|
|
321
|
+
* QueueModule.registerQueue('NOTIFICATIONS_QUEUE')
|
|
365
322
|
*
|
|
366
|
-
* // Then inject using the
|
|
367
|
-
* constructor(@InjectQueue('
|
|
323
|
+
* // Then inject using the binding name
|
|
324
|
+
* constructor(@InjectQueue('NOTIFICATIONS_QUEUE') private queue: IQueueSender) {}
|
|
368
325
|
* ```
|
|
369
326
|
*/
|
|
370
|
-
static registerQueue(
|
|
327
|
+
static registerQueue(binding) {
|
|
371
328
|
return {
|
|
372
329
|
module: _QueueModule,
|
|
373
330
|
providers: [{
|
|
374
|
-
provide:
|
|
375
|
-
useFactory: (registry) => registry.getQueue(
|
|
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 {
|
|
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-
|
|
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-
|
|
3
|
-
import {
|
|
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
|
|
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
|
|
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-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
50
|
+
//# sourceMappingURL=rate-limit.decorator-D69zdZbp.mjs.map
|