@vercel/queue 0.3.0 → 0.3.1
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/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +100 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +99 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -120,6 +120,15 @@ var MessageLockedError = class extends Error {
|
|
|
120
120
|
this.retryAfter = retryAfter;
|
|
121
121
|
}
|
|
122
122
|
};
|
|
123
|
+
var TooManyRequestsError = class extends Error {
|
|
124
|
+
/** Suggested retry delay in seconds, from the Retry-After header, if sent. */
|
|
125
|
+
retryAfter;
|
|
126
|
+
constructor(message = "Too many requests", retryAfter) {
|
|
127
|
+
super(message);
|
|
128
|
+
this.name = "TooManyRequestsError";
|
|
129
|
+
this.retryAfter = retryAfter;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
123
132
|
var UnauthorizedError = class extends Error {
|
|
124
133
|
constructor(message = "Missing or invalid authentication token") {
|
|
125
134
|
super(message);
|
|
@@ -185,6 +194,8 @@ var MIN_VISIBILITY_TIMEOUT_SECONDS = 30;
|
|
|
185
194
|
var MAX_RENEWAL_INTERVAL_SECONDS = 60;
|
|
186
195
|
var MIN_RENEWAL_INTERVAL_SECONDS = 10;
|
|
187
196
|
var RETRY_INTERVAL_MS = 3e3;
|
|
197
|
+
var DIRECTIVE_CALL_ATTEMPTS = 3;
|
|
198
|
+
var DIRECTIVE_CALL_RETRY_DELAY_MS = 250;
|
|
188
199
|
function calculateRenewalInterval(visibilityTimeoutSeconds) {
|
|
189
200
|
return Math.min(
|
|
190
201
|
MAX_RENEWAL_INTERVAL_SECONDS,
|
|
@@ -228,6 +239,54 @@ var ConsumerGroup = class {
|
|
|
228
239
|
error instanceof UnauthorizedError || // 401 - auth failed
|
|
229
240
|
error instanceof ForbiddenError;
|
|
230
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Network-level failures (DNS, connection reset, socket close) surface
|
|
244
|
+
* from fetch as TypeError with the cause attached; any response that
|
|
245
|
+
* actually reached the server — whatever its HTTP status — does not.
|
|
246
|
+
*/
|
|
247
|
+
isNetworkError(error) {
|
|
248
|
+
return error instanceof TypeError;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Run a directive call (acknowledge / changeVisibility) with bounded
|
|
252
|
+
* retries. Only failures that are worth re-attempting in process are
|
|
253
|
+
* retried:
|
|
254
|
+
* - network-level failures (the request may never have reached the
|
|
255
|
+
* server), with jittered linear backoff;
|
|
256
|
+
* - 429 responses that carry a Retry-After header, waiting the
|
|
257
|
+
* indicated delay.
|
|
258
|
+
* Everything else — other 4xx, 5xx, 429 without Retry-After — is
|
|
259
|
+
* thrown immediately.
|
|
260
|
+
*/
|
|
261
|
+
async directiveCallWithRetries(fn) {
|
|
262
|
+
let lastError;
|
|
263
|
+
for (let attempt = 1; attempt <= DIRECTIVE_CALL_ATTEMPTS; attempt++) {
|
|
264
|
+
try {
|
|
265
|
+
return await fn();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
lastError = error;
|
|
268
|
+
if (attempt === DIRECTIVE_CALL_ATTEMPTS) {
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
if (error instanceof TooManyRequestsError) {
|
|
272
|
+
if (error.retryAfter === void 0) {
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
await new Promise(
|
|
276
|
+
(resolve2) => setTimeout(resolve2, error.retryAfter * 1e3)
|
|
277
|
+
);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (!this.isNetworkError(error)) {
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
const baseDelayMs = DIRECTIVE_CALL_RETRY_DELAY_MS * attempt;
|
|
284
|
+
const delayMs = baseDelayMs / 2 + Math.random() * (baseDelayMs / 2);
|
|
285
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
throw lastError;
|
|
289
|
+
}
|
|
231
290
|
/**
|
|
232
291
|
* Starts a background loop that periodically extends the visibility timeout for a message.
|
|
233
292
|
*
|
|
@@ -372,11 +431,13 @@ var ConsumerGroup = class {
|
|
|
372
431
|
if (directive) {
|
|
373
432
|
if ("acknowledge" in directive && directive.acknowledge) {
|
|
374
433
|
try {
|
|
375
|
-
await this.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
434
|
+
await this.directiveCallWithRetries(
|
|
435
|
+
() => this.client.acknowledgeMessage({
|
|
436
|
+
queueName: this.topicName,
|
|
437
|
+
consumerGroup: this.consumerGroupName,
|
|
438
|
+
receiptHandle: message.receiptHandle
|
|
439
|
+
})
|
|
440
|
+
);
|
|
380
441
|
} catch (ackError) {
|
|
381
442
|
console.warn("Failed to acknowledge message:", ackError);
|
|
382
443
|
}
|
|
@@ -385,12 +446,14 @@ var ConsumerGroup = class {
|
|
|
385
446
|
}
|
|
386
447
|
if ("afterSeconds" in directive && typeof directive.afterSeconds === "number") {
|
|
387
448
|
try {
|
|
388
|
-
await this.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
449
|
+
await this.directiveCallWithRetries(
|
|
450
|
+
() => this.client.changeVisibility({
|
|
451
|
+
queueName: this.topicName,
|
|
452
|
+
consumerGroup: this.consumerGroupName,
|
|
453
|
+
receiptHandle: message.receiptHandle,
|
|
454
|
+
visibilityTimeoutSeconds: directive.afterSeconds
|
|
455
|
+
})
|
|
456
|
+
);
|
|
394
457
|
} catch (changeError) {
|
|
395
458
|
console.warn(
|
|
396
459
|
"Failed to reschedule message for retry:",
|
|
@@ -1452,10 +1515,28 @@ async function consumeStream(stream) {
|
|
|
1452
1515
|
reader.releaseLock();
|
|
1453
1516
|
}
|
|
1454
1517
|
}
|
|
1455
|
-
function
|
|
1518
|
+
function parseRetryAfterSeconds(value) {
|
|
1519
|
+
if (!value) return void 0;
|
|
1520
|
+
const seconds = Number(value);
|
|
1521
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
1522
|
+
return seconds;
|
|
1523
|
+
}
|
|
1524
|
+
const dateMs = Date.parse(value);
|
|
1525
|
+
if (!Number.isNaN(dateMs)) {
|
|
1526
|
+
return Math.max(0, (dateMs - Date.now()) / 1e3);
|
|
1527
|
+
}
|
|
1528
|
+
return void 0;
|
|
1529
|
+
}
|
|
1530
|
+
function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters", retryAfterHeader) {
|
|
1456
1531
|
if (status === 400) {
|
|
1457
1532
|
throw new BadRequestError(errorText || badRequestDefault);
|
|
1458
1533
|
}
|
|
1534
|
+
if (status === 429) {
|
|
1535
|
+
throw new TooManyRequestsError(
|
|
1536
|
+
errorText || `Too many requests: ${operation}`,
|
|
1537
|
+
parseRetryAfterSeconds(retryAfterHeader)
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1459
1540
|
if (status === 401) {
|
|
1460
1541
|
throw new UnauthorizedError(errorText || void 0);
|
|
1461
1542
|
}
|
|
@@ -1632,7 +1713,7 @@ Cause: ${cause}`
|
|
|
1632
1713
|
}
|
|
1633
1714
|
console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
|
|
1634
1715
|
}
|
|
1635
|
-
init.headers.set("User-Agent", `@vercel/queue/${"0.3.
|
|
1716
|
+
init.headers.set("User-Agent", `@vercel/queue/${"0.3.1"}`);
|
|
1636
1717
|
init.headers.set("Vqs-Client-Ts", (/* @__PURE__ */ new Date()).toISOString());
|
|
1637
1718
|
const fetchInit = this.dispatcher ? { ...init, dispatcher: this.dispatcher } : init;
|
|
1638
1719
|
const response = await fetch(url, fetchInit);
|
|
@@ -1911,7 +1992,8 @@ Cause: ${cause}`
|
|
|
1911
1992
|
response.statusText,
|
|
1912
1993
|
errorText,
|
|
1913
1994
|
"acknowledge message",
|
|
1914
|
-
"Missing or invalid receipt handle"
|
|
1995
|
+
"Missing or invalid receipt handle",
|
|
1996
|
+
response.headers?.get("Retry-After") ?? null
|
|
1915
1997
|
);
|
|
1916
1998
|
}
|
|
1917
1999
|
await response.text();
|
|
@@ -1963,7 +2045,8 @@ Cause: ${cause}`
|
|
|
1963
2045
|
response.statusText,
|
|
1964
2046
|
errorText,
|
|
1965
2047
|
"change visibility",
|
|
1966
|
-
"Missing receipt handle or invalid visibility timeout"
|
|
2048
|
+
"Missing receipt handle or invalid visibility timeout",
|
|
2049
|
+
response.headers?.get("Retry-After") ?? null
|
|
1967
2050
|
);
|
|
1968
2051
|
}
|
|
1969
2052
|
await response.text();
|
|
@@ -2321,6 +2404,7 @@ export {
|
|
|
2321
2404
|
QueueClient,
|
|
2322
2405
|
QueueEmptyError,
|
|
2323
2406
|
StreamTransport,
|
|
2407
|
+
TooManyRequestsError,
|
|
2324
2408
|
UnauthorizedError,
|
|
2325
2409
|
handleCallback2 as handleCallback,
|
|
2326
2410
|
parseCallback,
|