@spinajs/queue-sqs-transport 2.0.482 → 2.0.484

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=connection-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-factory.d.ts","sourceRoot":"","sources":["../../src/connection-factory.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const di_1 = require("@spinajs/di");
4
+ const connection_js_1 = require("./connection.js");
5
+ const configuration_1 = require("@spinajs/configuration");
6
+ /**
7
+ * Create factory func that sets client-id to connection by app name.
8
+ * We cannot have two connections with the same ID, so by default we take app-name
9
+ * with NODE_ENV and then name from options.
10
+ *
11
+ * This way we can share the same config/connections across multiple apps.
12
+ */
13
+ di_1.DI.register(async (container, options) => {
14
+ const cfg = container.get(configuration_1.Configuration);
15
+ const appName = cfg.get('app.name', 'no-app');
16
+ const env = cfg.get('process.env.APP_ENV', 'development');
17
+ const c = new connection_js_1.SqsQueueClient({
18
+ ...options,
19
+ clientId: `${appName}-${env}-${options.name}`,
20
+ });
21
+ await c.resolve();
22
+ return c;
23
+ }).as(connection_js_1.SqsQueueClient);
24
+ //# sourceMappingURL=connection-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-factory.js","sourceRoot":"","sources":["../../src/connection-factory.ts"],"names":[],"mappings":";;AAAA,oCAAiC;AAEjC,mDAAiD;AACjD,0DAAuD;AAEvD;;;;;;GAMG;AACH,OAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,OAAgC,EAAE,EAAE;IAChE,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,6BAAa,CAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAS,UAAU,EAAE,QAAQ,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAS,qBAAqB,EAAE,aAAa,CAAC,CAAC;IAElE,MAAM,CAAC,GAAG,IAAI,8BAAc,CAAC;QAC3B,GAAG,OAAO;QACV,QAAQ,EAAE,GAAG,OAAO,IAAI,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE;KAC9C,CAAC,CAAC;IACH,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;IAElB,OAAO,CAAC,CAAC;AACX,CAAC,CAAC,CAAC,EAAE,CAAC,8BAAc,CAAC,CAAC"}
@@ -0,0 +1,99 @@
1
+ import { IQueueMessage, IQueueConnectionOptions, QueueClient, QueueMessage } from '@spinajs/queue';
2
+ import { Constructor } from '@spinajs/di';
3
+ import { SQSClient } from '@aws-sdk/client-sqs';
4
+ /**
5
+ * SQS specific connection options that can be passed through the generic
6
+ * {@link IQueueConnectionOptions.options} bag.
7
+ */
8
+ export interface ISqsConnectionOptions {
9
+ region?: string;
10
+ queueUrl?: string;
11
+ endpoint?: string;
12
+ waitTimeSeconds?: number;
13
+ visibilityTimeout?: number;
14
+ maxMessages?: number;
15
+ credentials?: {
16
+ accessKeyId: string;
17
+ secretAccessKey: string;
18
+ };
19
+ /**
20
+ * Base delay ( ms ) the poll loop sleeps after a failed ReceiveMessage before
21
+ * retrying. Escalates ( doubles ) on consecutive failures up to
22
+ * {@link receiveErrorBackoffMaxMs} and resets after a successful receive.
23
+ * Prevents a persistent fault ( deleted queue, revoked credentials ) from
24
+ * hot-looping the receive-error branch. Defaults to 500ms.
25
+ */
26
+ receiveErrorBackoffMs?: number;
27
+ /**
28
+ * Cap ( ms ) for the escalating receive-error backoff. Defaults to 10000ms.
29
+ */
30
+ receiveErrorBackoffMaxMs?: number;
31
+ }
32
+ /**
33
+ * Tracks a live subscription. Unlike STOMP there is no broker-side subscription
34
+ * object - a subscription here is a detached long-poll loop over one queue URL.
35
+ * The descriptor is the loop's control block: `running` gates the loop and the
36
+ * `AbortController` interrupts an in-flight ( up to WaitTimeSeconds ) ReceiveMessage
37
+ * so unsubscribe / dispose return promptly instead of blocking on the poll.
38
+ */
39
+ interface ISqsSubscriptionDescriptor {
40
+ url: string;
41
+ callback: (e: IQueueMessage) => Promise<void>;
42
+ running: boolean;
43
+ controller: AbortController;
44
+ /**
45
+ * Set to true by {@link pollLoop} right before it returns, regardless of how it
46
+ * exits ( normal stop, abort or a swallowed error ). Lets callers / tests
47
+ * observe that the detached loop has actually wound down.
48
+ */
49
+ exited: boolean;
50
+ }
51
+ export declare class SqsQueueClient extends QueueClient {
52
+ /**
53
+ * Underlying AWS SQS client. SQS is a plain HTTP service so there is no
54
+ * long-lived connection to keep alive - the client is created in {@link resolve}
55
+ * and reused for every {@link emit}.
56
+ */
57
+ protected Sqs: SQSClient;
58
+ /**
59
+ * Active subscriptions keyed by queue URL. Each entry owns a detached poll loop.
60
+ */
61
+ protected Subscriptions: Map<string, ISqsSubscriptionDescriptor>;
62
+ constructor(options: IQueueConnectionOptions);
63
+ resolve(): Promise<void>;
64
+ emit(message: IQueueMessage): Promise<void>;
65
+ subscribe(channelOrMessage: string | Constructor<QueueMessage>, callback: (e: IQueueMessage) => Promise<void>, _subscriptionId?: string, _durable?: boolean): Promise<void>;
66
+ /**
67
+ * Long-poll loop for a single subscription. Runs until `desc.running` is cleared
68
+ * ( by unsubscribe / dispose ). On success the message is acked by DeleteMessage;
69
+ * on handler failure the message is left untouched so the SQS visibility timeout
70
+ * redelivers it and the redrive policy dead-letters after maxReceiveCount.
71
+ */
72
+ protected pollLoop(desc: ISqsSubscriptionDescriptor): Promise<void>;
73
+ /**
74
+ * Processes a single received SQS message: parse -> rehydrate -> handler -> ack.
75
+ * Guards against poison / non-object payloads so a bad message is skipped rather
76
+ * than crashing the loop. Keeps the ack/nack contract: delete on handler success,
77
+ * leave ( for redelivery / DLQ ) on handler failure.
78
+ */
79
+ protected handleMessage(desc: ISqsSubscriptionDescriptor, m: {
80
+ Body?: string;
81
+ ReceiptHandle?: string;
82
+ }): Promise<void>;
83
+ unsubscribe(channelOrMessage: string | Constructor<QueueMessage>, _removeDurable?: boolean): void;
84
+ dispose(): Promise<void>;
85
+ /**
86
+ * Abortable delay used by the receive-error backoff. Resolves after `ms`, or
87
+ * immediately if the descriptor's AbortController fires ( unsubscribe / dispose )
88
+ * so a disposed subscription never sits parked in a long backoff sleep. The
89
+ * loop re-checks `desc.running` right after, so an abort mid-sleep exits promptly.
90
+ */
91
+ protected backoffSleep(ms: number, signal: AbortSignal): Promise<void>;
92
+ /**
93
+ * Stops a subscription's poll loop: clears the running flag and aborts any
94
+ * in-flight ReceiveMessage. The loop observes both and exits.
95
+ */
96
+ protected stop(desc: ISqsSubscriptionDescriptor): void;
97
+ }
98
+ export {};
99
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnG,OAAO,EAAE,WAAW,EAAgC,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,SAAS,EAAmE,MAAM,qBAAqB,CAAC;AAIjH;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;GAMG;AACH,UAAU,0BAA0B;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,eAAe,CAAC;IAC5B;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,qBAEa,cAAe,SAAQ,WAAW;IAC7C;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC;IAEzB;;OAEG;IACH,SAAS,CAAC,aAAa,0CAAiD;gBAE5D,OAAO,EAAE,uBAAuB;IAI/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBxB,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3C,SAAS,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BxL;;;;;OAKG;cACa,QAAQ,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IA2EzE;;;;;OAKG;cACa,aAAa,CAAC,IAAI,EAAE,0BAA0B,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDrH,WAAW,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,EAAE,cAAc,CAAC,EAAE,OAAO,GAAG,IAAI;IAiB3F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAcrC;;;;;OAKG;IACH,SAAS,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBtE;;;OAGG;IACH,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,0BAA0B,GAAG,IAAI;CAIvD"}
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SqsQueueClient = void 0;
16
+ const queue_1 = require("@spinajs/queue");
17
+ const di_1 = require("@spinajs/di");
18
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
19
+ const luxon_1 = require("luxon");
20
+ const lodash_1 = __importDefault(require("lodash"));
21
+ let SqsQueueClient = class SqsQueueClient extends queue_1.QueueClient {
22
+ constructor(options) {
23
+ super(options);
24
+ /**
25
+ * Active subscriptions keyed by queue URL. Each entry owns a detached poll loop.
26
+ */
27
+ this.Subscriptions = new Map();
28
+ }
29
+ async resolve() {
30
+ const o = (this.Options.options ?? {});
31
+ // SQS is HTTP based - there is no eager connection to establish and no
32
+ // connection-level destination to validate here. Destinations are resolved
33
+ // per-message at emit time via getChannelForMessage, which consults the
34
+ // global queue.routing table first and only then falls back to the
35
+ // connection defaults - so a routing-only connection ( no defaultQueueChannel /
36
+ // defaultTopicChannel ) is perfectly valid. resolve()'s only job is to build
37
+ // the SQSClient; region/endpoint/credentials are all optional for the SDK.
38
+ this.Sqs = new client_sqs_1.SQSClient({
39
+ region: o.region,
40
+ endpoint: o.endpoint,
41
+ credentials: o.credentials,
42
+ });
43
+ this.Log.info(`SQS queue client ${this.Options.name} resolved ( region: ${o.region ?? '<default>'}, endpoint: ${o.endpoint ?? '<default>'} )`);
44
+ }
45
+ async emit(message) {
46
+ // routing[message.Name] || defaultQueueChannel|defaultTopicChannel - for SQS
47
+ // these entries are queue URLs. `Name`/`Type`/`JobId` ride the JSON body so
48
+ // the core consumer can rehydrate the message on the receiving side.
49
+ const urls = this.getChannelForMessage(message);
50
+ const body = JSON.stringify(message);
51
+ await Promise.all(urls.map((QueueUrl) => {
52
+ this.Log.trace(`Publishing ${message.Type} Name: ${message.Name} to SQS queue ${QueueUrl} ( ${this.Options.name} )`);
53
+ return this.Sqs.send(new client_sqs_1.SendMessageCommand({ QueueUrl, MessageBody: body }));
54
+ }));
55
+ }
56
+ async subscribe(channelOrMessage, callback, _subscriptionId, _durable) {
57
+ // mirror STOMP's overload handling: a raw string is the queue URL itself,
58
+ // a message class is resolved through the routing table.
59
+ const urls = lodash_1.default.isString(channelOrMessage) ? [channelOrMessage] : this.getChannelForMessage(channelOrMessage);
60
+ urls.forEach((url) => {
61
+ if (this.Subscriptions.has(url)) {
62
+ this.Log.warn(`SQS queue ${url} already subscribed !`);
63
+ return;
64
+ }
65
+ const desc = {
66
+ url,
67
+ callback,
68
+ running: true,
69
+ controller: new AbortController(),
70
+ exited: false,
71
+ };
72
+ this.Subscriptions.set(url, desc);
73
+ // detached loop - subscribe returns as soon as the poll loop is started,
74
+ // it is NOT awaited. Errors inside the loop must never escape ( there is no
75
+ // caller to catch them ), so pollLoop swallows/logs everything itself.
76
+ void this.pollLoop(desc);
77
+ this.Log.success(`SQS queue ${url} subscribed and polling for messages !`);
78
+ });
79
+ }
80
+ /**
81
+ * Long-poll loop for a single subscription. Runs until `desc.running` is cleared
82
+ * ( by unsubscribe / dispose ). On success the message is acked by DeleteMessage;
83
+ * on handler failure the message is left untouched so the SQS visibility timeout
84
+ * redelivers it and the redrive policy dead-letters after maxReceiveCount.
85
+ */
86
+ async pollLoop(desc) {
87
+ const o = (this.Options.options ?? {});
88
+ // Escalating backoff for the receive-error branch. Starts at the base delay,
89
+ // doubles on each consecutive failure up to the cap, and is reset to the base
90
+ // after any successful ReceiveMessage. Without this a persistent fault
91
+ // ( deleted queue, revoked credentials, permanently dead endpoint ) makes the
92
+ // SDK send throw immediately and the loop spins as a tight infinite loop,
93
+ // spamming logs and hammering the SQS API.
94
+ const baseBackoff = Math.max(0, o.receiveErrorBackoffMs ?? 500);
95
+ const maxBackoff = Math.max(baseBackoff, o.receiveErrorBackoffMaxMs ?? 10000);
96
+ let backoff = baseBackoff;
97
+ try {
98
+ while (desc.running) {
99
+ let res;
100
+ try {
101
+ res = await this.Sqs.send(new client_sqs_1.ReceiveMessageCommand({
102
+ QueueUrl: desc.url,
103
+ WaitTimeSeconds: o.waitTimeSeconds ?? 20,
104
+ MaxNumberOfMessages: o.maxMessages ?? 1,
105
+ VisibilityTimeout: o.visibilityTimeout,
106
+ }), { abortSignal: desc.controller.signal });
107
+ // a successful receive clears the fault - drop back to the base delay so a
108
+ // transient blip doesn't leave the loop permanently slow.
109
+ backoff = baseBackoff;
110
+ }
111
+ catch (err) {
112
+ // aborted by unsubscribe / dispose - the in-flight long poll was cut short
113
+ if (!desc.running) {
114
+ break;
115
+ }
116
+ // receive error ( throttling, network blip, or a persistent fault ) - log
117
+ // and keep the loop alive, otherwise a single hiccup would silently stop
118
+ // consumption. Back off before retrying so a *persistent* fault can't
119
+ // hot-loop the receive; the sleep is abortable so unsubscribe / dispose
120
+ // still tears the loop down promptly instead of waiting out the delay.
121
+ this.Log.warn(`SQS receive error on ${desc.url} ( ${this.Options.name} ), backing off ${backoff}ms: ${err}`);
122
+ await this.backoffSleep(backoff, desc.controller.signal);
123
+ backoff = Math.min(backoff * 2, maxBackoff);
124
+ continue;
125
+ }
126
+ for (const m of res?.Messages ?? []) {
127
+ // a concurrent unsubscribe / dispose may have flipped this mid-batch
128
+ if (!desc.running) {
129
+ break;
130
+ }
131
+ // Defense-in-depth: NOTHING a single message does may escape this loop as
132
+ // an unhandled rejection ( the loop runs detached - there is no caller to
133
+ // catch it, and an escaped throw would crash the worker / Node process ).
134
+ // Any per-message failure is logged and the loop moves on.
135
+ try {
136
+ await this.handleMessage(desc, m);
137
+ }
138
+ catch (err) {
139
+ this.Log.error(`Unexpected error handling SQS message on ${desc.url} ( ${this.Options.name} ), skipping: ${err}`);
140
+ }
141
+ }
142
+ }
143
+ }
144
+ catch (err) {
145
+ // last-resort guard: even the loop scaffolding ( e.g. an unexpected throw from
146
+ // the receive-error branch ) must not reject the detached promise.
147
+ this.Log.error(`SQS poll loop for ${desc.url} ( ${this.Options.name} ) crashed unexpectedly: ${err}`);
148
+ }
149
+ finally {
150
+ desc.exited = true;
151
+ this.Log.trace(`SQS poll loop for ${desc.url} ( ${this.Options.name} ) stopped`);
152
+ }
153
+ }
154
+ /**
155
+ * Processes a single received SQS message: parse -> rehydrate -> handler -> ack.
156
+ * Guards against poison / non-object payloads so a bad message is skipped rather
157
+ * than crashing the loop. Keeps the ack/nack contract: delete on handler success,
158
+ * leave ( for redelivery / DLQ ) on handler failure.
159
+ */
160
+ async handleMessage(desc, m) {
161
+ let parsed;
162
+ try {
163
+ parsed = JSON.parse(m.Body);
164
+ }
165
+ catch (e) {
166
+ // poison message - we can't tell its Type so we can't route it. Leave it:
167
+ // SQS redelivery + the redrive policy will dead-letter it after maxReceiveCount,
168
+ // which is preferable to deleting evidence of a producer bug.
169
+ this.Log.error(`Unparseable SQS message on ${desc.url} ( ${this.Options.name} ), leaving for redelivery / DLQ: ${e}`);
170
+ return;
171
+ }
172
+ // JSON.parse happily yields non-object values ( null, numbers, strings,
173
+ // booleans ) that would blow up the CreatedAt / callback access below with a
174
+ // TypeError. Treat them as poison and skip so the loop stays alive.
175
+ if (!parsed || typeof parsed !== 'object') {
176
+ this.Log.error(`Non-object SQS message body on ${desc.url} ( ${this.Options.name} ), skipping`);
177
+ return;
178
+ }
179
+ const message = parsed;
180
+ // luxon DateTime serializes to an ISO string over the wire - rehydrate it
181
+ if (typeof message.CreatedAt === 'string') {
182
+ message.CreatedAt = luxon_1.DateTime.fromISO(message.CreatedAt);
183
+ }
184
+ try {
185
+ await desc.callback(message);
186
+ }
187
+ catch (err) {
188
+ // NACK: do nothing. The message stays invisible only for the visibility
189
+ // timeout, after which SQS redelivers; the redrive policy dead-letters it
190
+ // once maxReceiveCount is hit. No manual retry / DLQ logic here.
191
+ this.Log.error(`Handler failed for ${message.Name} on ${desc.url}, leaving message for redelivery: ${err}`);
192
+ return;
193
+ }
194
+ // ACK: only delete once the handler has fully succeeded. A delete failure is
195
+ // non-fatal ( at-least-once: SQS will simply redeliver ) and is logged
196
+ // distinctly so it is never mistaken for a handler failure.
197
+ try {
198
+ await this.Sqs.send(new client_sqs_1.DeleteMessageCommand({ QueueUrl: desc.url, ReceiptHandle: m.ReceiptHandle }));
199
+ this.Log.trace(`Processed and acked ${message.Type} Name: ${message.Name} from ${desc.url} ( ${this.Options.name} )`);
200
+ }
201
+ catch (err) {
202
+ this.Log.error(`ack/delete failed for message ${message.Name} on ${desc.url} ( ${this.Options.name} ), it will redeliver: ${err}`);
203
+ }
204
+ }
205
+ unsubscribe(channelOrMessage, _removeDurable) {
206
+ const urls = lodash_1.default.isString(channelOrMessage) ? [channelOrMessage] : this.getChannelForMessage(channelOrMessage);
207
+ urls.forEach((url) => {
208
+ const desc = this.Subscriptions.get(url);
209
+ if (!desc) {
210
+ return;
211
+ }
212
+ this.stop(desc);
213
+ this.Subscriptions.delete(url);
214
+ this.Log.info(`Unsubscribed from SQS queue ${url} ( ${this.Options.name} )`);
215
+ });
216
+ }
217
+ async dispose() {
218
+ // stop every poll loop and interrupt any in-flight long poll so this returns
219
+ // promptly instead of waiting up to WaitTimeSeconds for the last receive.
220
+ for (const desc of this.Subscriptions.values()) {
221
+ this.stop(desc);
222
+ }
223
+ this.Subscriptions.clear();
224
+ this.Sqs?.destroy();
225
+ this.Log.info(`SQS queue client ${this.Options.name} disposed`);
226
+ }
227
+ /**
228
+ * Abortable delay used by the receive-error backoff. Resolves after `ms`, or
229
+ * immediately if the descriptor's AbortController fires ( unsubscribe / dispose )
230
+ * so a disposed subscription never sits parked in a long backoff sleep. The
231
+ * loop re-checks `desc.running` right after, so an abort mid-sleep exits promptly.
232
+ */
233
+ backoffSleep(ms, signal) {
234
+ if (ms <= 0 || signal.aborted) {
235
+ return Promise.resolve();
236
+ }
237
+ return new Promise((resolve) => {
238
+ const onAbort = () => {
239
+ clearTimeout(timer);
240
+ resolve();
241
+ };
242
+ const timer = setTimeout(() => {
243
+ signal.removeEventListener('abort', onAbort);
244
+ resolve();
245
+ }, ms);
246
+ signal.addEventListener('abort', onAbort, { once: true });
247
+ });
248
+ }
249
+ /**
250
+ * Stops a subscription's poll loop: clears the running flag and aborts any
251
+ * in-flight ReceiveMessage. The loop observes both and exits.
252
+ */
253
+ stop(desc) {
254
+ desc.running = false;
255
+ desc.controller.abort();
256
+ }
257
+ };
258
+ exports.SqsQueueClient = SqsQueueClient;
259
+ exports.SqsQueueClient = SqsQueueClient = __decorate([
260
+ (0, di_1.PerInstanceCheck)(),
261
+ (0, di_1.Injectable)(queue_1.QueueClient),
262
+ __metadata("design:paramtypes", [Object])
263
+ ], SqsQueueClient);
264
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from './connection-factory.js';
2
+ export * from './connection.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./connection-factory.js"), exports);
18
+ __exportStar(require("./connection.js"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,kDAAgC"}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=connection-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-factory.d.ts","sourceRoot":"","sources":["../../src/connection-factory.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import { DI } from '@spinajs/di';
2
+ import { SqsQueueClient } from './connection.js';
3
+ import { Configuration } from '@spinajs/configuration';
4
+ /**
5
+ * Create factory func that sets client-id to connection by app name.
6
+ * We cannot have two connections with the same ID, so by default we take app-name
7
+ * with NODE_ENV and then name from options.
8
+ *
9
+ * This way we can share the same config/connections across multiple apps.
10
+ */
11
+ DI.register(async (container, options) => {
12
+ const cfg = container.get(Configuration);
13
+ const appName = cfg.get('app.name', 'no-app');
14
+ const env = cfg.get('process.env.APP_ENV', 'development');
15
+ const c = new SqsQueueClient({
16
+ ...options,
17
+ clientId: `${appName}-${env}-${options.name}`,
18
+ });
19
+ await c.resolve();
20
+ return c;
21
+ }).as(SqsQueueClient);
22
+ //# sourceMappingURL=connection-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-factory.js","sourceRoot":"","sources":["../../src/connection-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;;;;GAMG;AACH,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,OAAgC,EAAE,EAAE;IAChE,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,aAAa,CAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAS,UAAU,EAAE,QAAQ,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAS,qBAAqB,EAAE,aAAa,CAAC,CAAC;IAElE,MAAM,CAAC,GAAG,IAAI,cAAc,CAAC;QAC3B,GAAG,OAAO;QACV,QAAQ,EAAE,GAAG,OAAO,IAAI,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE;KAC9C,CAAC,CAAC;IACH,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;IAElB,OAAO,CAAC,CAAC;AACX,CAAC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC"}
@@ -0,0 +1,99 @@
1
+ import { IQueueMessage, IQueueConnectionOptions, QueueClient, QueueMessage } from '@spinajs/queue';
2
+ import { Constructor } from '@spinajs/di';
3
+ import { SQSClient } from '@aws-sdk/client-sqs';
4
+ /**
5
+ * SQS specific connection options that can be passed through the generic
6
+ * {@link IQueueConnectionOptions.options} bag.
7
+ */
8
+ export interface ISqsConnectionOptions {
9
+ region?: string;
10
+ queueUrl?: string;
11
+ endpoint?: string;
12
+ waitTimeSeconds?: number;
13
+ visibilityTimeout?: number;
14
+ maxMessages?: number;
15
+ credentials?: {
16
+ accessKeyId: string;
17
+ secretAccessKey: string;
18
+ };
19
+ /**
20
+ * Base delay ( ms ) the poll loop sleeps after a failed ReceiveMessage before
21
+ * retrying. Escalates ( doubles ) on consecutive failures up to
22
+ * {@link receiveErrorBackoffMaxMs} and resets after a successful receive.
23
+ * Prevents a persistent fault ( deleted queue, revoked credentials ) from
24
+ * hot-looping the receive-error branch. Defaults to 500ms.
25
+ */
26
+ receiveErrorBackoffMs?: number;
27
+ /**
28
+ * Cap ( ms ) for the escalating receive-error backoff. Defaults to 10000ms.
29
+ */
30
+ receiveErrorBackoffMaxMs?: number;
31
+ }
32
+ /**
33
+ * Tracks a live subscription. Unlike STOMP there is no broker-side subscription
34
+ * object - a subscription here is a detached long-poll loop over one queue URL.
35
+ * The descriptor is the loop's control block: `running` gates the loop and the
36
+ * `AbortController` interrupts an in-flight ( up to WaitTimeSeconds ) ReceiveMessage
37
+ * so unsubscribe / dispose return promptly instead of blocking on the poll.
38
+ */
39
+ interface ISqsSubscriptionDescriptor {
40
+ url: string;
41
+ callback: (e: IQueueMessage) => Promise<void>;
42
+ running: boolean;
43
+ controller: AbortController;
44
+ /**
45
+ * Set to true by {@link pollLoop} right before it returns, regardless of how it
46
+ * exits ( normal stop, abort or a swallowed error ). Lets callers / tests
47
+ * observe that the detached loop has actually wound down.
48
+ */
49
+ exited: boolean;
50
+ }
51
+ export declare class SqsQueueClient extends QueueClient {
52
+ /**
53
+ * Underlying AWS SQS client. SQS is a plain HTTP service so there is no
54
+ * long-lived connection to keep alive - the client is created in {@link resolve}
55
+ * and reused for every {@link emit}.
56
+ */
57
+ protected Sqs: SQSClient;
58
+ /**
59
+ * Active subscriptions keyed by queue URL. Each entry owns a detached poll loop.
60
+ */
61
+ protected Subscriptions: Map<string, ISqsSubscriptionDescriptor>;
62
+ constructor(options: IQueueConnectionOptions);
63
+ resolve(): Promise<void>;
64
+ emit(message: IQueueMessage): Promise<void>;
65
+ subscribe(channelOrMessage: string | Constructor<QueueMessage>, callback: (e: IQueueMessage) => Promise<void>, _subscriptionId?: string, _durable?: boolean): Promise<void>;
66
+ /**
67
+ * Long-poll loop for a single subscription. Runs until `desc.running` is cleared
68
+ * ( by unsubscribe / dispose ). On success the message is acked by DeleteMessage;
69
+ * on handler failure the message is left untouched so the SQS visibility timeout
70
+ * redelivers it and the redrive policy dead-letters after maxReceiveCount.
71
+ */
72
+ protected pollLoop(desc: ISqsSubscriptionDescriptor): Promise<void>;
73
+ /**
74
+ * Processes a single received SQS message: parse -> rehydrate -> handler -> ack.
75
+ * Guards against poison / non-object payloads so a bad message is skipped rather
76
+ * than crashing the loop. Keeps the ack/nack contract: delete on handler success,
77
+ * leave ( for redelivery / DLQ ) on handler failure.
78
+ */
79
+ protected handleMessage(desc: ISqsSubscriptionDescriptor, m: {
80
+ Body?: string;
81
+ ReceiptHandle?: string;
82
+ }): Promise<void>;
83
+ unsubscribe(channelOrMessage: string | Constructor<QueueMessage>, _removeDurable?: boolean): void;
84
+ dispose(): Promise<void>;
85
+ /**
86
+ * Abortable delay used by the receive-error backoff. Resolves after `ms`, or
87
+ * immediately if the descriptor's AbortController fires ( unsubscribe / dispose )
88
+ * so a disposed subscription never sits parked in a long backoff sleep. The
89
+ * loop re-checks `desc.running` right after, so an abort mid-sleep exits promptly.
90
+ */
91
+ protected backoffSleep(ms: number, signal: AbortSignal): Promise<void>;
92
+ /**
93
+ * Stops a subscription's poll loop: clears the running flag and aborts any
94
+ * in-flight ReceiveMessage. The loop observes both and exits.
95
+ */
96
+ protected stop(desc: ISqsSubscriptionDescriptor): void;
97
+ }
98
+ export {};
99
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnG,OAAO,EAAE,WAAW,EAAgC,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,SAAS,EAAmE,MAAM,qBAAqB,CAAC;AAIjH;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;GAMG;AACH,UAAU,0BAA0B;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,eAAe,CAAC;IAC5B;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,qBAEa,cAAe,SAAQ,WAAW;IAC7C;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC;IAEzB;;OAEG;IACH,SAAS,CAAC,aAAa,0CAAiD;gBAE5D,OAAO,EAAE,uBAAuB;IAI/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBxB,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3C,SAAS,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BxL;;;;;OAKG;cACa,QAAQ,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IA2EzE;;;;;OAKG;cACa,aAAa,CAAC,IAAI,EAAE,0BAA0B,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDrH,WAAW,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,EAAE,cAAc,CAAC,EAAE,OAAO,GAAG,IAAI;IAiB3F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAcrC;;;;;OAKG;IACH,SAAS,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBtE;;;OAGG;IACH,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,0BAA0B,GAAG,IAAI;CAIvD"}