@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.d.mts
CHANGED
|
@@ -477,6 +477,14 @@ declare class MessageLockedError extends Error {
|
|
|
477
477
|
readonly retryAfter?: number;
|
|
478
478
|
constructor(messageId: string, retryAfter?: number);
|
|
479
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Error thrown when the server rate-limits a request (HTTP 429).
|
|
482
|
+
*/
|
|
483
|
+
declare class TooManyRequestsError extends Error {
|
|
484
|
+
/** Suggested retry delay in seconds, from the Retry-After header, if sent. */
|
|
485
|
+
readonly retryAfter?: number;
|
|
486
|
+
constructor(message?: string, retryAfter?: number);
|
|
487
|
+
}
|
|
480
488
|
/**
|
|
481
489
|
* Error thrown when authentication fails.
|
|
482
490
|
* This typically means the token is missing, invalid, or expired.
|
|
@@ -895,4 +903,4 @@ declare function parseRawCallback(body: unknown, headers: Record<string, string
|
|
|
895
903
|
*/
|
|
896
904
|
declare function parseCallback(request: Request): Promise<ParsedCallbackRequest>;
|
|
897
905
|
|
|
898
|
-
export { BadRequestError, type BaseUrlResolver, BufferTransport, CLOUD_EVENT_TYPE_V1BETA, CLOUD_EVENT_TYPE_V2BETA, ConsumerDiscoveryError, ConsumerRegistryNotConfiguredError, DuplicateMessageError, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageAlreadyProcessedError, MessageCorruptedError, type MessageHandler, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type ParsedCallbackRequest, type ParsedCallbackV1, type ParsedCallbackV2, PollingQueueClient, type PollingQueueClientOptions, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveBatchOptions, type ReceiveByIdOptions, type ReceiveOptions, type ReceiveResult, type RegisterDevConsumerOptions, type RetryDirective, type RetryHandler, type SendOptions, type SendResult, StreamTransport, type Transport, UnauthorizedError, type VercelRegion, handleCallback, parseCallback, parseRawCallback, registerDevConsumer, send };
|
|
906
|
+
export { BadRequestError, type BaseUrlResolver, BufferTransport, CLOUD_EVENT_TYPE_V1BETA, CLOUD_EVENT_TYPE_V2BETA, ConsumerDiscoveryError, ConsumerRegistryNotConfiguredError, DuplicateMessageError, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageAlreadyProcessedError, MessageCorruptedError, type MessageHandler, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type ParsedCallbackRequest, type ParsedCallbackV1, type ParsedCallbackV2, PollingQueueClient, type PollingQueueClientOptions, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveBatchOptions, type ReceiveByIdOptions, type ReceiveOptions, type ReceiveResult, type RegisterDevConsumerOptions, type RetryDirective, type RetryHandler, type SendOptions, type SendResult, StreamTransport, TooManyRequestsError, type Transport, UnauthorizedError, type VercelRegion, handleCallback, parseCallback, parseRawCallback, registerDevConsumer, send };
|
package/dist/index.d.ts
CHANGED
|
@@ -477,6 +477,14 @@ declare class MessageLockedError extends Error {
|
|
|
477
477
|
readonly retryAfter?: number;
|
|
478
478
|
constructor(messageId: string, retryAfter?: number);
|
|
479
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Error thrown when the server rate-limits a request (HTTP 429).
|
|
482
|
+
*/
|
|
483
|
+
declare class TooManyRequestsError extends Error {
|
|
484
|
+
/** Suggested retry delay in seconds, from the Retry-After header, if sent. */
|
|
485
|
+
readonly retryAfter?: number;
|
|
486
|
+
constructor(message?: string, retryAfter?: number);
|
|
487
|
+
}
|
|
480
488
|
/**
|
|
481
489
|
* Error thrown when authentication fails.
|
|
482
490
|
* This typically means the token is missing, invalid, or expired.
|
|
@@ -895,4 +903,4 @@ declare function parseRawCallback(body: unknown, headers: Record<string, string
|
|
|
895
903
|
*/
|
|
896
904
|
declare function parseCallback(request: Request): Promise<ParsedCallbackRequest>;
|
|
897
905
|
|
|
898
|
-
export { BadRequestError, type BaseUrlResolver, BufferTransport, CLOUD_EVENT_TYPE_V1BETA, CLOUD_EVENT_TYPE_V2BETA, ConsumerDiscoveryError, ConsumerRegistryNotConfiguredError, DuplicateMessageError, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageAlreadyProcessedError, MessageCorruptedError, type MessageHandler, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type ParsedCallbackRequest, type ParsedCallbackV1, type ParsedCallbackV2, PollingQueueClient, type PollingQueueClientOptions, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveBatchOptions, type ReceiveByIdOptions, type ReceiveOptions, type ReceiveResult, type RegisterDevConsumerOptions, type RetryDirective, type RetryHandler, type SendOptions, type SendResult, StreamTransport, type Transport, UnauthorizedError, type VercelRegion, handleCallback, parseCallback, parseRawCallback, registerDevConsumer, send };
|
|
906
|
+
export { BadRequestError, type BaseUrlResolver, BufferTransport, CLOUD_EVENT_TYPE_V1BETA, CLOUD_EVENT_TYPE_V2BETA, ConsumerDiscoveryError, ConsumerRegistryNotConfiguredError, DuplicateMessageError, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageAlreadyProcessedError, MessageCorruptedError, type MessageHandler, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type ParsedCallbackRequest, type ParsedCallbackV1, type ParsedCallbackV2, PollingQueueClient, type PollingQueueClientOptions, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveBatchOptions, type ReceiveByIdOptions, type ReceiveOptions, type ReceiveResult, type RegisterDevConsumerOptions, type RetryDirective, type RetryHandler, type SendOptions, type SendResult, StreamTransport, TooManyRequestsError, type Transport, UnauthorizedError, type VercelRegion, handleCallback, parseCallback, parseRawCallback, registerDevConsumer, send };
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ __export(index_exports, {
|
|
|
50
50
|
QueueClient: () => QueueClient,
|
|
51
51
|
QueueEmptyError: () => QueueEmptyError,
|
|
52
52
|
StreamTransport: () => StreamTransport,
|
|
53
|
+
TooManyRequestsError: () => TooManyRequestsError,
|
|
53
54
|
UnauthorizedError: () => UnauthorizedError,
|
|
54
55
|
handleCallback: () => handleCallback2,
|
|
55
56
|
parseCallback: () => parseCallback,
|
|
@@ -181,6 +182,15 @@ var MessageLockedError = class extends Error {
|
|
|
181
182
|
this.retryAfter = retryAfter;
|
|
182
183
|
}
|
|
183
184
|
};
|
|
185
|
+
var TooManyRequestsError = class extends Error {
|
|
186
|
+
/** Suggested retry delay in seconds, from the Retry-After header, if sent. */
|
|
187
|
+
retryAfter;
|
|
188
|
+
constructor(message = "Too many requests", retryAfter) {
|
|
189
|
+
super(message);
|
|
190
|
+
this.name = "TooManyRequestsError";
|
|
191
|
+
this.retryAfter = retryAfter;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
184
194
|
var UnauthorizedError = class extends Error {
|
|
185
195
|
constructor(message = "Missing or invalid authentication token") {
|
|
186
196
|
super(message);
|
|
@@ -246,6 +256,8 @@ var MIN_VISIBILITY_TIMEOUT_SECONDS = 30;
|
|
|
246
256
|
var MAX_RENEWAL_INTERVAL_SECONDS = 60;
|
|
247
257
|
var MIN_RENEWAL_INTERVAL_SECONDS = 10;
|
|
248
258
|
var RETRY_INTERVAL_MS = 3e3;
|
|
259
|
+
var DIRECTIVE_CALL_ATTEMPTS = 3;
|
|
260
|
+
var DIRECTIVE_CALL_RETRY_DELAY_MS = 250;
|
|
249
261
|
function calculateRenewalInterval(visibilityTimeoutSeconds) {
|
|
250
262
|
return Math.min(
|
|
251
263
|
MAX_RENEWAL_INTERVAL_SECONDS,
|
|
@@ -289,6 +301,54 @@ var ConsumerGroup = class {
|
|
|
289
301
|
error instanceof UnauthorizedError || // 401 - auth failed
|
|
290
302
|
error instanceof ForbiddenError;
|
|
291
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Network-level failures (DNS, connection reset, socket close) surface
|
|
306
|
+
* from fetch as TypeError with the cause attached; any response that
|
|
307
|
+
* actually reached the server — whatever its HTTP status — does not.
|
|
308
|
+
*/
|
|
309
|
+
isNetworkError(error) {
|
|
310
|
+
return error instanceof TypeError;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Run a directive call (acknowledge / changeVisibility) with bounded
|
|
314
|
+
* retries. Only failures that are worth re-attempting in process are
|
|
315
|
+
* retried:
|
|
316
|
+
* - network-level failures (the request may never have reached the
|
|
317
|
+
* server), with jittered linear backoff;
|
|
318
|
+
* - 429 responses that carry a Retry-After header, waiting the
|
|
319
|
+
* indicated delay.
|
|
320
|
+
* Everything else — other 4xx, 5xx, 429 without Retry-After — is
|
|
321
|
+
* thrown immediately.
|
|
322
|
+
*/
|
|
323
|
+
async directiveCallWithRetries(fn) {
|
|
324
|
+
let lastError;
|
|
325
|
+
for (let attempt = 1; attempt <= DIRECTIVE_CALL_ATTEMPTS; attempt++) {
|
|
326
|
+
try {
|
|
327
|
+
return await fn();
|
|
328
|
+
} catch (error) {
|
|
329
|
+
lastError = error;
|
|
330
|
+
if (attempt === DIRECTIVE_CALL_ATTEMPTS) {
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
if (error instanceof TooManyRequestsError) {
|
|
334
|
+
if (error.retryAfter === void 0) {
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
await new Promise(
|
|
338
|
+
(resolve2) => setTimeout(resolve2, error.retryAfter * 1e3)
|
|
339
|
+
);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (!this.isNetworkError(error)) {
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
const baseDelayMs = DIRECTIVE_CALL_RETRY_DELAY_MS * attempt;
|
|
346
|
+
const delayMs = baseDelayMs / 2 + Math.random() * (baseDelayMs / 2);
|
|
347
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
throw lastError;
|
|
351
|
+
}
|
|
292
352
|
/**
|
|
293
353
|
* Starts a background loop that periodically extends the visibility timeout for a message.
|
|
294
354
|
*
|
|
@@ -433,11 +493,13 @@ var ConsumerGroup = class {
|
|
|
433
493
|
if (directive) {
|
|
434
494
|
if ("acknowledge" in directive && directive.acknowledge) {
|
|
435
495
|
try {
|
|
436
|
-
await this.
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
496
|
+
await this.directiveCallWithRetries(
|
|
497
|
+
() => this.client.acknowledgeMessage({
|
|
498
|
+
queueName: this.topicName,
|
|
499
|
+
consumerGroup: this.consumerGroupName,
|
|
500
|
+
receiptHandle: message.receiptHandle
|
|
501
|
+
})
|
|
502
|
+
);
|
|
441
503
|
} catch (ackError) {
|
|
442
504
|
console.warn("Failed to acknowledge message:", ackError);
|
|
443
505
|
}
|
|
@@ -446,12 +508,14 @@ var ConsumerGroup = class {
|
|
|
446
508
|
}
|
|
447
509
|
if ("afterSeconds" in directive && typeof directive.afterSeconds === "number") {
|
|
448
510
|
try {
|
|
449
|
-
await this.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
511
|
+
await this.directiveCallWithRetries(
|
|
512
|
+
() => this.client.changeVisibility({
|
|
513
|
+
queueName: this.topicName,
|
|
514
|
+
consumerGroup: this.consumerGroupName,
|
|
515
|
+
receiptHandle: message.receiptHandle,
|
|
516
|
+
visibilityTimeoutSeconds: directive.afterSeconds
|
|
517
|
+
})
|
|
518
|
+
);
|
|
455
519
|
} catch (changeError) {
|
|
456
520
|
console.warn(
|
|
457
521
|
"Failed to reschedule message for retry:",
|
|
@@ -1514,10 +1578,28 @@ async function consumeStream(stream) {
|
|
|
1514
1578
|
reader.releaseLock();
|
|
1515
1579
|
}
|
|
1516
1580
|
}
|
|
1517
|
-
function
|
|
1581
|
+
function parseRetryAfterSeconds(value) {
|
|
1582
|
+
if (!value) return void 0;
|
|
1583
|
+
const seconds = Number(value);
|
|
1584
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
1585
|
+
return seconds;
|
|
1586
|
+
}
|
|
1587
|
+
const dateMs = Date.parse(value);
|
|
1588
|
+
if (!Number.isNaN(dateMs)) {
|
|
1589
|
+
return Math.max(0, (dateMs - Date.now()) / 1e3);
|
|
1590
|
+
}
|
|
1591
|
+
return void 0;
|
|
1592
|
+
}
|
|
1593
|
+
function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters", retryAfterHeader) {
|
|
1518
1594
|
if (status === 400) {
|
|
1519
1595
|
throw new BadRequestError(errorText || badRequestDefault);
|
|
1520
1596
|
}
|
|
1597
|
+
if (status === 429) {
|
|
1598
|
+
throw new TooManyRequestsError(
|
|
1599
|
+
errorText || `Too many requests: ${operation}`,
|
|
1600
|
+
parseRetryAfterSeconds(retryAfterHeader)
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1521
1603
|
if (status === 401) {
|
|
1522
1604
|
throw new UnauthorizedError(errorText || void 0);
|
|
1523
1605
|
}
|
|
@@ -1694,7 +1776,7 @@ Cause: ${cause}`
|
|
|
1694
1776
|
}
|
|
1695
1777
|
console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
|
|
1696
1778
|
}
|
|
1697
|
-
init.headers.set("User-Agent", `@vercel/queue/${"0.3.
|
|
1779
|
+
init.headers.set("User-Agent", `@vercel/queue/${"0.3.1"}`);
|
|
1698
1780
|
init.headers.set("Vqs-Client-Ts", (/* @__PURE__ */ new Date()).toISOString());
|
|
1699
1781
|
const fetchInit = this.dispatcher ? { ...init, dispatcher: this.dispatcher } : init;
|
|
1700
1782
|
const response = await fetch(url, fetchInit);
|
|
@@ -1973,7 +2055,8 @@ Cause: ${cause}`
|
|
|
1973
2055
|
response.statusText,
|
|
1974
2056
|
errorText,
|
|
1975
2057
|
"acknowledge message",
|
|
1976
|
-
"Missing or invalid receipt handle"
|
|
2058
|
+
"Missing or invalid receipt handle",
|
|
2059
|
+
response.headers?.get("Retry-After") ?? null
|
|
1977
2060
|
);
|
|
1978
2061
|
}
|
|
1979
2062
|
await response.text();
|
|
@@ -2025,7 +2108,8 @@ Cause: ${cause}`
|
|
|
2025
2108
|
response.statusText,
|
|
2026
2109
|
errorText,
|
|
2027
2110
|
"change visibility",
|
|
2028
|
-
"Missing receipt handle or invalid visibility timeout"
|
|
2111
|
+
"Missing receipt handle or invalid visibility timeout",
|
|
2112
|
+
response.headers?.get("Retry-After") ?? null
|
|
2029
2113
|
);
|
|
2030
2114
|
}
|
|
2031
2115
|
await response.text();
|
|
@@ -2384,6 +2468,7 @@ function handleCallback2(handler, options) {
|
|
|
2384
2468
|
QueueClient,
|
|
2385
2469
|
QueueEmptyError,
|
|
2386
2470
|
StreamTransport,
|
|
2471
|
+
TooManyRequestsError,
|
|
2387
2472
|
UnauthorizedError,
|
|
2388
2473
|
handleCallback,
|
|
2389
2474
|
parseCallback,
|