@aws-sdk/lib-sqs 0.0.0-stage → 3.1158.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,666 @@
1
+ const { SQSClient, ReceiveMessageCommand, DeleteMessageBatchCommand, ChangeMessageVisibilityCommand } = require("@aws-sdk/client-sqs");
2
+
3
+ class Message {
4
+ raw;
5
+ body;
6
+ messageId;
7
+ receiptHandle;
8
+ attributes;
9
+ messageAttributes;
10
+ receivedAt;
11
+ _callbacks;
12
+ _handled = false;
13
+ constructor(raw, callbacks) {
14
+ this.raw = raw;
15
+ this.body = raw.Body;
16
+ this.messageId = raw.MessageId ?? "";
17
+ this.receiptHandle = raw.ReceiptHandle ?? "";
18
+ this.attributes = raw.Attributes ?? {};
19
+ this.messageAttributes = raw.MessageAttributes ?? {};
20
+ this.receivedAt = new Date();
21
+ this._callbacks = callbacks;
22
+ }
23
+ get handled() {
24
+ return this._handled;
25
+ }
26
+ enqueueDelete() {
27
+ if (this._handled)
28
+ return;
29
+ this._handled = true;
30
+ this._callbacks.enqueueDelete(this);
31
+ }
32
+ keep() {
33
+ this._handled = true;
34
+ this._callbacks.keep(this.receiptHandle);
35
+ }
36
+ async release() {
37
+ this._handled = true;
38
+ await this._callbacks.release(this.receiptHandle);
39
+ }
40
+ async changeVisibility(timeoutMs) {
41
+ await this._callbacks.changeVisibility(this.receiptHandle, timeoutMs);
42
+ }
43
+ }
44
+
45
+ class PollerError extends Error {
46
+ name = "PollerError";
47
+ constructor(message, options) {
48
+ super(message, options);
49
+ }
50
+ }
51
+ class TimeoutError extends PollerError {
52
+ name = "TimeoutError";
53
+ timeoutMs;
54
+ constructor(message, timeoutMs, options) {
55
+ super(message, options);
56
+ this.timeoutMs = timeoutMs;
57
+ }
58
+ }
59
+ class DeleteError extends PollerError {
60
+ name = "DeleteError";
61
+ failures;
62
+ messageIds;
63
+ constructor(message, failures, messageIds, options) {
64
+ super(message, options);
65
+ this.failures = failures;
66
+ this.messageIds = messageIds ?? [];
67
+ }
68
+ }
69
+ class VisibilityExtensionError extends PollerError {
70
+ name = "VisibilityExtensionError";
71
+ messageId;
72
+ constructor(message, messageId, options) {
73
+ super(message, options);
74
+ this.messageId = messageId;
75
+ }
76
+ }
77
+
78
+ const DEFAULTS = {
79
+ receiveWaitTimeMs: 20_000,
80
+ receiveBatchSize: 10,
81
+ activePollIntervalMs: 0,
82
+ idlePollIntervalMs: 0,
83
+ maxConcurrency: 10,
84
+ deleteMessages: true,
85
+ deleteBatchSize: 10,
86
+ deleteWaitMs: 2_000,
87
+ backoffMs: 1_000,
88
+ maxBackoffMs: 30_000,
89
+ shutdownTimeoutMs: 30_000,
90
+ extendTimeout: false,
91
+ extendTimeoutAdvanceMs: 5_000,
92
+ maxTimeoutExtensionMs: 43_200_000,
93
+ };
94
+ const MIN_POLL_INTERVAL_MS = 100;
95
+ function createRun(previous) {
96
+ let resolveDone = () => { };
97
+ const done = new Promise((resolve) => {
98
+ resolveDone = resolve;
99
+ });
100
+ return {
101
+ ac: new AbortController(),
102
+ hard: new AbortController(),
103
+ stopping: false,
104
+ graceful: true,
105
+ finished: false,
106
+ consecutiveErrors: 0,
107
+ inFlight: 0,
108
+ drainResolvers: [],
109
+ receiptHandles: new Set(),
110
+ done,
111
+ resolveDone,
112
+ previous: previous?.done,
113
+ };
114
+ }
115
+ const toError = (err) => (err instanceof Error ? err : new PollerError(String(err)));
116
+ class SQSPoller {
117
+ _client;
118
+ _config;
119
+ _stats = {
120
+ requestCount: 0,
121
+ receivedMessageCount: 0,
122
+ deletedMessageCount: 0,
123
+ errorCount: 0,
124
+ lastMessageReceivedAt: null,
125
+ pollingStartedAt: null,
126
+ pollingStoppedAt: null,
127
+ };
128
+ _run = null;
129
+ _externalSignal;
130
+ _deleteBatch = [];
131
+ _deleteTimer = null;
132
+ _pendingFlushes = [];
133
+ _heartbeatTimers = new Map();
134
+ _heartbeatsInFlight = new Map();
135
+ _listeners = {
136
+ message: [],
137
+ empty: [],
138
+ idle: [],
139
+ stopped: [],
140
+ error: [],
141
+ timeoutExtended: [],
142
+ heartbeatExpired: [],
143
+ };
144
+ constructor(config) {
145
+ if (!config.queueUrl)
146
+ throw new PollerError("queueUrl is required");
147
+ this._client = config.client ?? new SQSClient(config.clientConfig ?? {});
148
+ const batchSize = config.receiveBatchSize ?? DEFAULTS.receiveBatchSize;
149
+ if (batchSize < 1 || batchSize > 10 || !Number.isInteger(batchSize))
150
+ throw new PollerError("receiveBatchSize must be an integer between 1 and 10");
151
+ const deleteBatchSize = config.deleteBatchSize ?? DEFAULTS.deleteBatchSize;
152
+ if (deleteBatchSize < 1 || deleteBatchSize > 10 || !Number.isInteger(deleteBatchSize))
153
+ throw new PollerError("deleteBatchSize must be an integer between 1 and 10");
154
+ const receiveWaitTimeMs = config.receiveWaitTimeMs ?? DEFAULTS.receiveWaitTimeMs;
155
+ if (receiveWaitTimeMs < 0 || receiveWaitTimeMs > 20_000)
156
+ throw new PollerError("receiveWaitTimeMs must be between 0 and 20000");
157
+ const maxConcurrency = config.maxConcurrency ?? DEFAULTS.maxConcurrency;
158
+ if (!Number.isInteger(maxConcurrency) || maxConcurrency < 1)
159
+ throw new PollerError("maxConcurrency must be a positive integer");
160
+ if (config.extendTimeout) {
161
+ if (config.visibilityTimeoutMs == null)
162
+ throw new PollerError("visibilityTimeoutMs is required when extendTimeout is true");
163
+ if ((config.extendTimeoutAdvanceMs ?? DEFAULTS.extendTimeoutAdvanceMs) >= config.visibilityTimeoutMs)
164
+ throw new PollerError("extendTimeoutAdvanceMs must be less than visibilityTimeoutMs");
165
+ }
166
+ this._config = {
167
+ queueUrl: config.queueUrl,
168
+ receiveWaitTimeMs,
169
+ receiveBatchSize: batchSize,
170
+ activePollIntervalMs: config.activePollIntervalMs ?? DEFAULTS.activePollIntervalMs,
171
+ idlePollIntervalMs: config.idlePollIntervalMs ?? DEFAULTS.idlePollIntervalMs,
172
+ maxConcurrency,
173
+ deleteMessages: config.deleteMessages ?? DEFAULTS.deleteMessages,
174
+ deleteBatchSize,
175
+ deleteWaitMs: config.deleteWaitMs ?? DEFAULTS.deleteWaitMs,
176
+ backoffMs: config.backoffMs ?? DEFAULTS.backoffMs,
177
+ maxBackoffMs: config.maxBackoffMs ?? DEFAULTS.maxBackoffMs,
178
+ shutdownTimeoutMs: config.shutdownTimeoutMs ?? DEFAULTS.shutdownTimeoutMs,
179
+ extendTimeout: config.extendTimeout ?? DEFAULTS.extendTimeout,
180
+ extendTimeoutAdvanceMs: config.extendTimeoutAdvanceMs ?? DEFAULTS.extendTimeoutAdvanceMs,
181
+ maxTimeoutExtensionMs: config.maxTimeoutExtensionMs ?? DEFAULTS.maxTimeoutExtensionMs,
182
+ visibilityTimeoutMs: config.visibilityTimeoutMs,
183
+ idleTimeoutMs: config.idleTimeoutMs,
184
+ beforeRequest: config.beforeRequest,
185
+ systemAttributeNames: config.systemAttributeNames,
186
+ messageAttributeNames: config.messageAttributeNames,
187
+ };
188
+ this._externalSignal = config.abortSignal;
189
+ if (this._externalSignal) {
190
+ this._externalSignal.addEventListener("abort", () => {
191
+ void this.stop();
192
+ }, { once: true });
193
+ }
194
+ }
195
+ get running() {
196
+ return this._run !== null && !this._run.stopping;
197
+ }
198
+ get inFlight() {
199
+ return this._run?.inFlight ?? 0;
200
+ }
201
+ getStats() {
202
+ return { ...this._stats };
203
+ }
204
+ toJSON() {
205
+ const now = new Date();
206
+ const s = this._stats.pollingStartedAt;
207
+ return {
208
+ requestCount: this._stats.requestCount,
209
+ receivedMessageCount: this._stats.receivedMessageCount,
210
+ deletedMessageCount: this._stats.deletedMessageCount,
211
+ errorCount: this._stats.errorCount,
212
+ lastMessageReceivedAt: this._stats.lastMessageReceivedAt?.toISOString() ?? null,
213
+ pollingStartedAt: s?.toISOString() ?? null,
214
+ pollingStoppedAt: this._stats.pollingStoppedAt?.toISOString() ?? null,
215
+ pollingDurationMs: s ? (this._stats.pollingStoppedAt ?? now).getTime() - s.getTime() : 0,
216
+ };
217
+ }
218
+ toString() {
219
+ const s = this._stats;
220
+ return `SQSPoller(${this.running ? "running" : "stopped"}, requests=${s.requestCount}, received=${s.receivedMessageCount}, deleted=${s.deletedMessageCount}, errors=${s.errorCount}, inFlight=${this.inFlight})`;
221
+ }
222
+ on(event, listener) {
223
+ this._listeners[event].push(listener);
224
+ return () => {
225
+ this.off(event, listener);
226
+ };
227
+ }
228
+ off(event, listener) {
229
+ if (!listener) {
230
+ this._listeners[event] = [];
231
+ return this;
232
+ }
233
+ const arr = this._listeners[event];
234
+ const idx = arr.indexOf(listener);
235
+ if (idx !== -1)
236
+ arr.splice(idx, 1);
237
+ return this;
238
+ }
239
+ start() {
240
+ this._assertCanStart("Poller is already running");
241
+ const run = createRun(this._run);
242
+ this._run = run;
243
+ void this._pollLoop(run);
244
+ }
245
+ async stop(graceful = true) {
246
+ const run = this._run;
247
+ if (!run)
248
+ return { ...this._stats };
249
+ run.stopping = true;
250
+ run.ac.abort();
251
+ if (!graceful) {
252
+ run.graceful = false;
253
+ run.hard.abort();
254
+ }
255
+ if (run.iterator) {
256
+ const outcome = run.graceful ? await this._waitFor(run.done, run) : "hard";
257
+ if (outcome === "timeout")
258
+ this._emitShutdownTimeout("the async iterator was not advanced");
259
+ if (outcome !== "done")
260
+ await run.iterator.return(undefined);
261
+ }
262
+ await run.done;
263
+ return { ...this._stats };
264
+ }
265
+ [Symbol.asyncIterator]() {
266
+ const ref = {};
267
+ ref.gen = this._iterate(ref);
268
+ return ref.gen;
269
+ }
270
+ async *_iterate(ref) {
271
+ this._assertCanStart("Poller is already running or an open iterator exists");
272
+ const run = createRun(this._run);
273
+ run.iterator = ref.gen;
274
+ this._run = run;
275
+ try {
276
+ await this._beginRun(run);
277
+ while (!run.stopping && !this._isAborted(run)) {
278
+ let batch = [];
279
+ try {
280
+ if (!(await this._runBeforeRequest(run)))
281
+ break;
282
+ const messages = await this._receiveMessages(run);
283
+ run.consecutiveErrors = 0;
284
+ if (messages.length === 0) {
285
+ if (await this._handleEmpty(run))
286
+ break;
287
+ continue;
288
+ }
289
+ this._recordReceived(messages.length);
290
+ batch = messages.map((raw) => this._wrapMessage(raw, run));
291
+ for (const msg of batch)
292
+ this._startHeartbeat(msg, run);
293
+ }
294
+ catch (err) {
295
+ if (this._isAborted(run))
296
+ break;
297
+ await this._handlePollError(err, run);
298
+ continue;
299
+ }
300
+ for (const msg of batch) {
301
+ if (run.stopping || this._isAborted(run))
302
+ break;
303
+ yield msg;
304
+ void this._stopHeartbeat(msg.receiptHandle, run);
305
+ if (this._config.deleteMessages && !msg.handled)
306
+ msg.enqueueDelete();
307
+ }
308
+ if (this._config.activePollIntervalMs > 0)
309
+ await this._delay(this._config.activePollIntervalMs, run);
310
+ }
311
+ }
312
+ finally {
313
+ await this._teardown(run);
314
+ }
315
+ }
316
+ _assertCanStart(activeMessage) {
317
+ if (this._run && !this._run.stopping)
318
+ throw new PollerError(activeMessage);
319
+ if (this._externalSignal?.aborted)
320
+ throw new PollerError("abortSignal is already aborted");
321
+ }
322
+ async _beginRun(run) {
323
+ if (run.previous)
324
+ await run.previous;
325
+ this._stats.pollingStartedAt = new Date();
326
+ this._stats.pollingStoppedAt = null;
327
+ }
328
+ async _teardown(run) {
329
+ await this._stopHeartbeats(run);
330
+ do {
331
+ await this._flushDeleteBatch();
332
+ await this._awaitPendingFlushes();
333
+ } while (this._deleteBatch.length > 0);
334
+ run.stopping = true;
335
+ run.finished = true;
336
+ this._stats.pollingStoppedAt = new Date();
337
+ if (this._run === run)
338
+ this._run = null;
339
+ run.resolveDone();
340
+ this._emit("stopped", { ...this._stats });
341
+ }
342
+ _waitFor(work, run) {
343
+ return new Promise((resolve) => {
344
+ if (run.hard.signal.aborted) {
345
+ resolve("hard");
346
+ return;
347
+ }
348
+ let timer;
349
+ function finish(outcome) {
350
+ clearTimeout(timer);
351
+ run.hard.signal.removeEventListener("abort", onHard);
352
+ resolve(outcome);
353
+ }
354
+ function onHard() {
355
+ finish("hard");
356
+ }
357
+ timer = setTimeout(() => finish("timeout"), this._config.shutdownTimeoutMs);
358
+ run.hard.signal.addEventListener("abort", onHard, { once: true });
359
+ void work.then(() => finish("done"));
360
+ });
361
+ }
362
+ async _waitForDrain(run) {
363
+ if (run.inFlight === 0)
364
+ return;
365
+ const drained = new Promise((resolve) => run.drainResolvers.push(resolve));
366
+ if ((await this._waitFor(drained, run)) === "timeout") {
367
+ this._emitShutdownTimeout(`${run.inFlight} message(s) were still in flight`);
368
+ }
369
+ }
370
+ _emitShutdownTimeout(reason) {
371
+ this._stats.errorCount++;
372
+ this._emit("error", new TimeoutError(`Shutdown timed out because ${reason}. If a handler or loop body awaits stop(), use \`void poller.stop()\` instead.`, this._config.shutdownTimeoutMs));
373
+ }
374
+ async _pollLoop(run) {
375
+ await this._beginRun(run);
376
+ while (!run.stopping && !this._isAborted(run)) {
377
+ try {
378
+ while (!run.stopping && run.inFlight >= this._config.maxConcurrency)
379
+ await this._delay(50, run);
380
+ if (run.stopping || this._isAborted(run))
381
+ break;
382
+ if (!(await this._runBeforeRequest(run)))
383
+ break;
384
+ const messages = await this._receiveMessages(run, Math.min(this._config.receiveBatchSize, this._config.maxConcurrency - run.inFlight));
385
+ run.consecutiveErrors = 0;
386
+ if (messages.length === 0) {
387
+ if (await this._handleEmpty(run))
388
+ break;
389
+ continue;
390
+ }
391
+ this._recordReceived(messages.length);
392
+ for (const raw of messages) {
393
+ const msg = this._wrapMessage(raw, run);
394
+ run.inFlight++;
395
+ void this._processMessage(msg, run).finally(() => {
396
+ run.inFlight--;
397
+ if (run.inFlight === 0) {
398
+ for (const resolve of run.drainResolvers)
399
+ resolve();
400
+ run.drainResolvers = [];
401
+ }
402
+ });
403
+ }
404
+ if (this._config.activePollIntervalMs > 0)
405
+ await this._delay(this._config.activePollIntervalMs, run);
406
+ }
407
+ catch (err) {
408
+ if (this._isAborted(run))
409
+ break;
410
+ await this._handlePollError(err, run);
411
+ }
412
+ }
413
+ if (run.graceful)
414
+ await this._waitForDrain(run);
415
+ await this._teardown(run);
416
+ }
417
+ async _processMessage(msg, run) {
418
+ this._startHeartbeat(msg, run);
419
+ try {
420
+ const hasListeners = this._listeners.message.length > 0;
421
+ if (hasListeners)
422
+ await this._emitAsync("message", msg);
423
+ if (this._config.deleteMessages && !msg.handled && hasListeners)
424
+ msg.enqueueDelete();
425
+ }
426
+ catch (err) {
427
+ this._stats.errorCount++;
428
+ this._emit("error", toError(err));
429
+ }
430
+ finally {
431
+ void this._stopHeartbeat(msg.receiptHandle, run);
432
+ }
433
+ }
434
+ async _runBeforeRequest(run) {
435
+ if (!this._config.beforeRequest)
436
+ return true;
437
+ try {
438
+ await this._config.beforeRequest(this.getStats());
439
+ return true;
440
+ }
441
+ catch (err) {
442
+ run.stopping = true;
443
+ this._emit("error", toError(err));
444
+ return false;
445
+ }
446
+ }
447
+ async _handleEmpty(run) {
448
+ this._emit("empty");
449
+ if (this._checkIdleTimeout()) {
450
+ run.stopping = true;
451
+ this._emit("idle");
452
+ return true;
453
+ }
454
+ const delayMs = this._effectiveIdlePollInterval();
455
+ if (delayMs > 0)
456
+ await this._delay(delayMs, run);
457
+ return false;
458
+ }
459
+ async _handlePollError(err, run) {
460
+ run.consecutiveErrors++;
461
+ this._stats.errorCount++;
462
+ this._emit("error", toError(err));
463
+ await this._backoff(run);
464
+ }
465
+ _recordReceived(count) {
466
+ this._stats.receivedMessageCount += count;
467
+ this._stats.lastMessageReceivedAt = new Date();
468
+ }
469
+ async _receiveMessages(run, maxMessages) {
470
+ this._stats.requestCount++;
471
+ const result = await this._client.send(new ReceiveMessageCommand({
472
+ QueueUrl: this._config.queueUrl,
473
+ WaitTimeSeconds: Math.floor(this._config.receiveWaitTimeMs / 1000),
474
+ MaxNumberOfMessages: maxMessages ?? this._config.receiveBatchSize,
475
+ VisibilityTimeout: this._config.visibilityTimeoutMs != null ? Math.ceil(this._config.visibilityTimeoutMs / 1000) : undefined,
476
+ MessageSystemAttributeNames: this._config.systemAttributeNames,
477
+ MessageAttributeNames: this._config.messageAttributeNames,
478
+ }), { abortSignal: run.ac.signal });
479
+ return result.Messages ?? [];
480
+ }
481
+ _enqueueDelete(msg, run) {
482
+ this._deleteBatch.push(msg);
483
+ if (run.finished || this._deleteBatch.length >= this._config.deleteBatchSize) {
484
+ this._trackFlush(this._flushDeleteBatch());
485
+ }
486
+ else if (!this._deleteTimer) {
487
+ this._deleteTimer = setTimeout(() => {
488
+ this._deleteTimer = null;
489
+ this._trackFlush(this._flushDeleteBatch());
490
+ }, this._config.deleteWaitMs);
491
+ }
492
+ }
493
+ async _flushDeleteBatch() {
494
+ if (this._deleteTimer) {
495
+ clearTimeout(this._deleteTimer);
496
+ this._deleteTimer = null;
497
+ }
498
+ if (this._deleteBatch.length === 0)
499
+ return;
500
+ const batch = this._deleteBatch.splice(0, this._config.deleteBatchSize);
501
+ const idMap = new Map();
502
+ const entries = batch.map((m, i) => {
503
+ const id = String(i);
504
+ idMap.set(id, m.messageId);
505
+ return { Id: id, ReceiptHandle: m.receiptHandle };
506
+ });
507
+ try {
508
+ const result = await this._client.send(new DeleteMessageBatchCommand({ QueueUrl: this._config.queueUrl, Entries: entries }));
509
+ this._stats.deletedMessageCount += result.Successful?.length ?? 0;
510
+ if (result.Failed?.length) {
511
+ this._stats.errorCount += result.Failed.length;
512
+ const mapped = result.Failed.map((f) => ({ ...f, MessageId: idMap.get(f.Id) ?? f.Id }));
513
+ this._emit("error", new DeleteError(`Failed to delete ${result.Failed.length} message(s): ${mapped.map((f) => f.MessageId).join(", ")}`, result.Failed, mapped.map((f) => f.MessageId)));
514
+ }
515
+ }
516
+ catch (err) {
517
+ this._stats.errorCount++;
518
+ this._emit("error", err instanceof Error ? err : new PollerError(String(err)));
519
+ }
520
+ if (this._deleteBatch.length > 0)
521
+ await this._flushDeleteBatch();
522
+ }
523
+ _trackFlush(p) {
524
+ this._pendingFlushes.push(p);
525
+ p.finally(() => {
526
+ const i = this._pendingFlushes.indexOf(p);
527
+ if (i !== -1)
528
+ this._pendingFlushes.splice(i, 1);
529
+ });
530
+ }
531
+ async _awaitPendingFlushes() {
532
+ while (this._pendingFlushes.length > 0)
533
+ await Promise.all([...this._pendingFlushes]);
534
+ }
535
+ _startHeartbeat(msg, run) {
536
+ if (!this._config.extendTimeout || this._config.visibilityTimeoutMs == null)
537
+ return;
538
+ const intervalMs = this._config.visibilityTimeoutMs - this._config.extendTimeoutAdvanceMs;
539
+ if (intervalMs <= 0)
540
+ return;
541
+ run.receiptHandles.add(msg.receiptHandle);
542
+ const startTime = Date.now();
543
+ const scheduleNext = () => {
544
+ const timer = setTimeout(async () => {
545
+ if (Date.now() - startTime > this._config.maxTimeoutExtensionMs) {
546
+ this._heartbeatTimers.delete(msg.receiptHandle);
547
+ run.receiptHandles.delete(msg.receiptHandle);
548
+ this._emit("heartbeatExpired", msg);
549
+ return;
550
+ }
551
+ try {
552
+ const extension = this._client.send(new ChangeMessageVisibilityCommand({
553
+ QueueUrl: this._config.queueUrl,
554
+ ReceiptHandle: msg.receiptHandle,
555
+ VisibilityTimeout: Math.ceil(this._config.visibilityTimeoutMs / 1000),
556
+ }));
557
+ this._heartbeatsInFlight.set(msg.receiptHandle, extension);
558
+ await extension;
559
+ this._emit("timeoutExtended", msg);
560
+ }
561
+ catch (err) {
562
+ this._stats.errorCount++;
563
+ this._emit("error", new VisibilityExtensionError(`Failed to extend visibility for message ${msg.messageId}`, msg.messageId, {
564
+ cause: err instanceof Error ? err : undefined,
565
+ }));
566
+ }
567
+ finally {
568
+ this._heartbeatsInFlight.delete(msg.receiptHandle);
569
+ }
570
+ if (this._heartbeatTimers.has(msg.receiptHandle))
571
+ scheduleNext();
572
+ }, intervalMs);
573
+ this._heartbeatTimers.set(msg.receiptHandle, timer);
574
+ };
575
+ scheduleNext();
576
+ }
577
+ async _stopHeartbeat(receiptHandle, run) {
578
+ const t = this._heartbeatTimers.get(receiptHandle);
579
+ if (t) {
580
+ clearTimeout(t);
581
+ this._heartbeatTimers.delete(receiptHandle);
582
+ }
583
+ await this._heartbeatsInFlight.get(receiptHandle)?.catch(() => { });
584
+ run.receiptHandles.delete(receiptHandle);
585
+ }
586
+ async _stopHeartbeats(run) {
587
+ await Promise.all([...run.receiptHandles].map((rh) => this._stopHeartbeat(rh, run)));
588
+ }
589
+ _wrapMessage(raw, run) {
590
+ const callbacks = {
591
+ enqueueDelete: (m) => this._trackFlush(this._stopHeartbeat(m.receiptHandle, run).then(() => this._enqueueDelete(m, run))),
592
+ keep: (rh) => void this._stopHeartbeat(rh, run),
593
+ release: (rh) => this._stopHeartbeat(rh, run)
594
+ .then(() => this._client.send(new ChangeMessageVisibilityCommand({
595
+ QueueUrl: this._config.queueUrl,
596
+ ReceiptHandle: rh,
597
+ VisibilityTimeout: 0,
598
+ })))
599
+ .then(() => { }),
600
+ changeVisibility: (rh, ms) => this._stopHeartbeat(rh, run)
601
+ .then(() => this._client.send(new ChangeMessageVisibilityCommand({
602
+ QueueUrl: this._config.queueUrl,
603
+ ReceiptHandle: rh,
604
+ VisibilityTimeout: Math.ceil(ms / 1000),
605
+ })))
606
+ .then(() => { }),
607
+ };
608
+ return new Message(raw, callbacks);
609
+ }
610
+ _isAborted(run) {
611
+ return run.ac.signal.aborted || this._externalSignal?.aborted === true;
612
+ }
613
+ _checkIdleTimeout() {
614
+ if (this._config.idleTimeoutMs == null)
615
+ return false;
616
+ const started = this._stats.pollingStartedAt?.getTime() ?? 0;
617
+ const lastMessage = this._stats.lastMessageReceivedAt?.getTime() ?? 0;
618
+ const since = Math.max(started, lastMessage);
619
+ return since > 0 && Date.now() - since > this._config.idleTimeoutMs;
620
+ }
621
+ _effectiveIdlePollInterval() {
622
+ if (this._config.idlePollIntervalMs > 0)
623
+ return this._config.idlePollIntervalMs;
624
+ return this._config.receiveWaitTimeMs === 0 ? MIN_POLL_INTERVAL_MS : 0;
625
+ }
626
+ async _backoff(run) {
627
+ await this._delay(Math.min(this._config.backoffMs * 2 ** (run.consecutiveErrors - 1), this._config.maxBackoffMs), run);
628
+ }
629
+ _delay(ms, run) {
630
+ return new Promise((r) => {
631
+ const signal = run.ac.signal;
632
+ if (signal.aborted) {
633
+ r();
634
+ return;
635
+ }
636
+ const onAbort = () => {
637
+ clearTimeout(timer);
638
+ r();
639
+ };
640
+ const timer = setTimeout(() => {
641
+ signal.removeEventListener("abort", onAbort);
642
+ r();
643
+ }, ms);
644
+ signal.addEventListener("abort", onAbort, { once: true });
645
+ });
646
+ }
647
+ _emit(event, ...args) {
648
+ for (const fn of this._listeners[event]) {
649
+ try {
650
+ fn(...args);
651
+ }
652
+ catch { }
653
+ }
654
+ }
655
+ async _emitAsync(event, ...args) {
656
+ for (const fn of this._listeners[event])
657
+ await fn(...args);
658
+ }
659
+ }
660
+
661
+ exports.DeleteError = DeleteError;
662
+ exports.Message = Message;
663
+ exports.PollerError = PollerError;
664
+ exports.SQSPoller = SQSPoller;
665
+ exports.TimeoutError = TimeoutError;
666
+ exports.VisibilityExtensionError = VisibilityExtensionError;
@@ -0,0 +1,41 @@
1
+ export class Message {
2
+ raw;
3
+ body;
4
+ messageId;
5
+ receiptHandle;
6
+ attributes;
7
+ messageAttributes;
8
+ receivedAt;
9
+ _callbacks;
10
+ _handled = false;
11
+ constructor(raw, callbacks) {
12
+ this.raw = raw;
13
+ this.body = raw.Body;
14
+ this.messageId = raw.MessageId ?? "";
15
+ this.receiptHandle = raw.ReceiptHandle ?? "";
16
+ this.attributes = raw.Attributes ?? {};
17
+ this.messageAttributes = raw.MessageAttributes ?? {};
18
+ this.receivedAt = new Date();
19
+ this._callbacks = callbacks;
20
+ }
21
+ get handled() {
22
+ return this._handled;
23
+ }
24
+ enqueueDelete() {
25
+ if (this._handled)
26
+ return;
27
+ this._handled = true;
28
+ this._callbacks.enqueueDelete(this);
29
+ }
30
+ keep() {
31
+ this._handled = true;
32
+ this._callbacks.keep(this.receiptHandle);
33
+ }
34
+ async release() {
35
+ this._handled = true;
36
+ await this._callbacks.release(this.receiptHandle);
37
+ }
38
+ async changeVisibility(timeoutMs) {
39
+ await this._callbacks.changeVisibility(this.receiptHandle, timeoutMs);
40
+ }
41
+ }