@vercel/queue 0.0.0-alpha.4 → 0.0.0-alpha.40

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.ts CHANGED
@@ -1,37 +1,83 @@
1
1
  /**
2
- * Serializer/Deserializer interface for message payloads
2
+ * Serializer/Deserializer interface for message payloads.
3
+ *
4
+ * Built-in implementations:
5
+ * - `JsonTransport` - JSON serialization (default, buffers entire payload)
6
+ * - `BufferTransport` - Binary data (buffers entire payload)
7
+ * - `StreamTransport` - Pass-through streaming (no buffering, ideal for large payloads)
3
8
  */
4
9
  interface Transport<T = unknown> {
5
10
  /**
6
- * Serialize a value to a buffer or stream for transmission
11
+ * Serialize a value to a buffer or stream for transmission.
12
+ * The result is sent as the request body with the `contentType` header.
7
13
  */
8
14
  serialize(value: T): Buffer | ReadableStream<Uint8Array>;
9
15
  /**
10
- * Deserialize a readable stream back to the original value
16
+ * Deserialize a readable stream back to the original value.
17
+ * Called when receiving messages from the queue.
11
18
  */
12
19
  deserialize(stream: ReadableStream<Uint8Array>): Promise<T>;
13
20
  /**
14
- * Optional cleanup method for deserialized payloads that may contain resources
15
- * Should be called when the payload is no longer needed, especially in error cases
21
+ * Optional cleanup method for deserialized payloads that may contain resources.
22
+ * Called automatically by ConsumerGroup on error; manual cleanup required for direct client usage.
16
23
  * @param payload The deserialized payload to clean up
17
24
  */
18
25
  finalize?(payload: T): Promise<void>;
19
26
  /**
20
- * MIME type for this serialization format
27
+ * MIME type for this serialization format.
28
+ * Sent as the Content-Type header when publishing messages.
21
29
  */
22
30
  contentType: string;
23
31
  }
24
32
  /**
25
- * Built-in JSON serializer/deserializer
26
- * This implementation reads the entire stream into memory for JSON parsing
33
+ * JSON serializer/deserializer (default transport).
34
+ *
35
+ * Ideal for structured data. Buffers the entire payload in memory for parsing.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // Default (JsonTransport is used automatically)
40
+ * const queue = new QueueClient({ region: process.env.QUEUE_REGION! });
41
+ * await queue.send("topic", { data: "example" });
42
+ *
43
+ * // With custom serialization
44
+ * const queue = new QueueClient({
45
+ * region: process.env.QUEUE_REGION!,
46
+ * transport: new JsonTransport({
47
+ * replacer: (key, value) => key === "password" ? undefined : value,
48
+ * reviver: (key, value) => key === "date" ? new Date(value) : value,
49
+ * }),
50
+ * });
51
+ * await queue.send("topic", { data: "example" });
52
+ * ```
27
53
  */
28
54
  declare class JsonTransport<T = unknown> implements Transport<T> {
29
55
  readonly contentType = "application/json";
56
+ readonly replacer?: Parameters<typeof JSON.stringify>[1];
57
+ readonly reviver?: Parameters<typeof JSON.parse>[1];
58
+ /**
59
+ * Create a new JsonTransport.
60
+ * @param options - Optional JSON serialization options
61
+ * @param options.replacer - Custom replacer for JSON.stringify
62
+ * @param options.reviver - Custom reviver for JSON.parse
63
+ */
64
+ constructor(options?: {
65
+ replacer?: Parameters<typeof JSON.stringify>[1];
66
+ reviver?: Parameters<typeof JSON.parse>[1];
67
+ });
30
68
  serialize(value: T): Buffer;
31
69
  deserialize(stream: ReadableStream<Uint8Array>): Promise<T>;
32
70
  }
33
71
  /**
34
- * Built-in Buffer serializer/deserializer (reads entire stream into a Buffer)
72
+ * Binary data serializer/deserializer.
73
+ *
74
+ * Ideal for binary data that fits in memory. Buffers the entire payload.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const queue = new QueueClient({ region: "iad1", transport: new BufferTransport() });
79
+ * await queue.send("binary-topic", myBuffer);
80
+ * ```
35
81
  */
36
82
  declare class BufferTransport implements Transport<Buffer> {
37
83
  readonly contentType = "application/octet-stream";
@@ -39,31 +85,36 @@ declare class BufferTransport implements Transport<Buffer> {
39
85
  deserialize(stream: ReadableStream<Uint8Array>): Promise<Buffer>;
40
86
  }
41
87
  /**
42
- * Stream serializer/deserializer (pass-through for streaming binary data)
43
- * This is ideal for large payloads that don't need to be buffered in memory
88
+ * Pass-through stream serializer/deserializer.
89
+ *
90
+ * Ideal for large files and real-time streams. Does not buffer data in memory.
44
91
  *
45
- * IMPORTANT: When using StreamTransport, you must call finalize(payload) when done
46
- * processing the message to prevent resource leaks, especially in error cases.
47
- * ConsumerGroup handles this automatically, but direct QueueClient usage requires manual cleanup.
92
+ * **Important:** When using StreamTransport, you must call `finalize(payload)` when done
93
+ * processing to prevent resource leaks. ConsumerGroup handles this automatically;
94
+ * direct client usage requires manual cleanup.
48
95
  *
49
- * Example usage:
96
+ * @example
50
97
  * ```typescript
51
- * const transport = new StreamTransport();
52
- * try {
53
- * for await (const message of client.receiveMessages(options, transport)) {
54
- * // Process the stream...
55
- * const reader = message.payload.getReader();
56
- * // ... handle stream data
57
- * }
58
- * } catch (error) {
59
- * // Cleanup is handled automatically by ConsumerGroup
60
- * // or manually: await transport.finalize(message.payload);
61
- * }
98
+ * const queue = new QueueClient({ region: "iad1", transport: new StreamTransport() });
99
+ *
100
+ * // Sending a stream
101
+ * await queue.send("large-file", myReadableStream);
102
+ *
103
+ * // Receiving - cleanup handled automatically
104
+ * await queue.receive("large-file", "processor", async (stream, meta) => {
105
+ * const reader = stream.getReader();
106
+ * // Process chunks...
107
+ * });
108
+ * ```
62
109
  */
63
110
  declare class StreamTransport implements Transport<ReadableStream<Uint8Array>> {
64
111
  readonly contentType = "application/octet-stream";
65
112
  serialize(value: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
66
113
  deserialize(stream: ReadableStream<Uint8Array>): Promise<ReadableStream<Uint8Array>>;
114
+ /**
115
+ * Consume any remaining stream data to prevent resource leaks.
116
+ * Called automatically by ConsumerGroup; manual call required for direct client usage.
117
+ */
67
118
  finalize(payload: ReadableStream<Uint8Array>): Promise<void>;
68
119
  }
69
120
 
@@ -71,269 +122,276 @@ declare class StreamTransport implements Transport<ReadableStream<Uint8Array>> {
71
122
  * Vercel Queue Service client types
72
123
  */
73
124
 
125
+ /**
126
+ * Vercel region code. The 20 known codes match
127
+ * {@link https://dcs.vercel-infra.com/ | dcs.vercel-infra.com}.
128
+ * Arbitrary strings are also accepted for forward compatibility
129
+ * with regions added after this SDK version.
130
+ */
131
+ type VercelRegion = "arn1" | "bom1" | "cdg1" | "cle1" | "cpt1" | "dub1" | "dxb1" | "fra1" | "gru1" | "hkg1" | "hnd1" | "iad1" | "icn1" | "kix1" | "lhr1" | "pdx1" | "sfo1" | "sin1" | "syd1" | "yul1" | (string & {});
132
+ /**
133
+ * Resolves a region code to a base URL for the Vercel Queue Service API.
134
+ *
135
+ * Default: `` (region) => `https://${region}.vercel-queue.com` ``
136
+ */
137
+ type BaseUrlResolver = (region: string) => string;
74
138
  interface QueueClientOptions {
75
139
  /**
76
- * Base URL for the Vercel Queue Service API
77
- * @default "https://vqs.vercel.sh"
140
+ * Vercel region code for API routing.
141
+ *
142
+ * Requests are sent to the regional endpoint resolved by
143
+ * {@link BaseUrlResolver} (default: `https://${region}.vercel-queue.com`).
144
+ *
145
+ * Use a `QUEUE_REGION` env var set to `${VERCEL_REGION}` in production
146
+ * and a fixed region (e.g. `"iad1"`) in development.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * new QueueClient({ region: process.env.QUEUE_REGION! })
151
+ * ```
152
+ */
153
+ region: VercelRegion;
154
+ /**
155
+ * Custom resolver that maps a region code to a base URL.
156
+ * @default (region) => `https://${region}.vercel-queue.com`
78
157
  */
79
- baseUrl?: string;
158
+ resolveBaseUrl?: BaseUrlResolver;
80
159
  /**
81
- * Vercel function OIDC token
82
- * Can be obtained from x-vercel-oidc-token header in Vercel Serverless Functions
83
- * If not provided, will automatically attempt to retrieve from Vercel Function environment
160
+ * Authentication token for the Vercel Queue Service API.
161
+ * If not provided, the client will attempt to get a token via OIDC
162
+ * when running in a Vercel Function environment.
84
163
  */
85
164
  token?: string;
86
- }
87
- /**
88
- * Callback configuration for a consumer group
89
- */
90
- interface CallbackConfig {
91
165
  /**
92
- * Webhook URL to notify when messages are available
166
+ * Custom headers to include in all requests.
93
167
  */
94
- url: string;
168
+ headers?: Record<string, string>;
95
169
  /**
96
- * Delay in seconds before sending the first callback
170
+ * Deployment ID for message routing and lease validation.
171
+ *
172
+ * - `undefined` (default): auto-detect from `VERCEL_DEPLOYMENT_ID` env var;
173
+ * sent messages are pinned to this deployment.
174
+ * - `null`: explicitly unpinned — no deployment ID is sent on any request.
175
+ * - `"dpl_xxx"`: explicit value used for both send pinning and consume identification.
176
+ *
177
+ * Ignored in development mode (deployment ID is never sent locally).
97
178
  */
98
- delay?: number;
179
+ deploymentId?: string | null;
99
180
  /**
100
- * Delay in seconds between retry attempts when the callback fails
181
+ * Serializer/deserializer for message payloads.
182
+ * Used for all send and receive operations.
183
+ * @default JsonTransport
101
184
  */
102
- frequency?: number;
185
+ transport?: Transport;
103
186
  }
104
187
  /**
105
- * Shared options for publishing messages
188
+ * Options for sending messages.
106
189
  */
107
- interface PublishOptions {
190
+ interface SendOptions {
108
191
  /**
109
- * Unique key to prevent duplicate message submissions
110
- * @default random UUID
192
+ * Unique key to prevent duplicate message submissions.
193
+ * Deduplication window: `min(message retention, 24 hours)`.
111
194
  */
112
195
  idempotencyKey?: string;
113
196
  /**
114
- * Message retention time in seconds
197
+ * Message retention time in seconds. After this period, the message expires.
115
198
  * @default 86400 (24 hours)
116
- * @min 60
117
- * @max 86400
199
+ * @minimum 60 (1 minute)
200
+ * @maximum 86400 (24 hours)
118
201
  */
119
202
  retentionSeconds?: number;
120
203
  /**
121
- * Callback configuration
122
- * - If a single CallbackConfig is provided, it will use the "default" consumer group
123
- * - If an object is provided, keys are consumer group names with their respective callback configs
204
+ * Delay delivery of the message by this many seconds.
205
+ * The message will not be visible to consumers until the delay has passed.
206
+ * @default 0
207
+ * @minimum 0
208
+ * @maximum Value of retentionSeconds (delay cannot exceed retention)
124
209
  */
125
- callback?: Record<string, CallbackConfig> | CallbackConfig;
126
- }
127
- interface SendMessageOptions<T = unknown> extends PublishOptions {
210
+ delaySeconds?: number;
128
211
  /**
129
- * The queue name to send the message to
212
+ * Custom headers to include with this message.
213
+ * These headers are passed through to the VQS server.
130
214
  */
131
- queueName: string;
132
- /**
133
- * The message payload
134
- */
135
- payload: T;
215
+ headers?: Record<string, string>;
136
216
  }
137
- interface SendMessageResponse {
138
- /**
139
- * The generated message ID
140
- */
141
- messageId: string;
217
+ /**
218
+ * Result returned by `send()`.
219
+ *
220
+ * `messageId` is `null` when the server accepted the message for deferred
221
+ * processing (e.g. during a server-side outage) and no ID is available yet.
222
+ */
223
+ interface SendResult {
224
+ messageId: string | null;
142
225
  }
143
226
  interface Message<T = unknown> {
144
227
  /**
145
- * The message ID
228
+ * Unique message identifier. Used for receive-by-ID operations.
146
229
  */
147
230
  messageId: string;
148
231
  /**
149
- * The deserialized message payload
150
- * Note: If using streaming transports, ensure proper cleanup by calling transport.finalize(payload)
151
- * when done processing, especially in error cases
232
+ * The deserialized message payload.
233
+ * Note: If using StreamTransport, ensure proper cleanup by calling
234
+ * `transport.finalize(payload)` when done processing, especially in error cases.
152
235
  */
153
236
  payload: T;
154
237
  /**
155
- * Number of times this message has been delivered
238
+ * Number of times this message has been delivered.
239
+ * Starts at 1 for the first delivery, increments on each retry.
156
240
  */
157
241
  deliveryCount: number;
158
242
  /**
159
- * Timestamp when the message was created
160
- */
161
- timestamp: string;
162
- /**
163
- * MIME type of the message content
164
- */
165
- contentType: string;
166
- /**
167
- * Unique ticket for this message delivery (required for delete/patch operations)
168
- */
169
- ticket: string;
170
- }
171
- interface ReceiveMessagesOptions<T = unknown> {
172
- /**
173
- * The queue name to receive messages from
243
+ * Timestamp when the message was created/published.
174
244
  */
175
- queueName: string;
245
+ createdAt: Date;
176
246
  /**
177
- * Consumer group name
247
+ * Timestamp when the message expires.
248
+ * Only present for messages delivered via the v2beta binary callback path.
178
249
  */
179
- consumerGroup: string;
250
+ expiresAt?: Date;
180
251
  /**
181
- * Time in seconds that messages will be invisible to other consumers
182
- * @default 900 (15 minutes)
252
+ * MIME type of the message content.
253
+ * Set by the transport's `contentType` property during publish.
183
254
  */
184
- visibilityTimeoutSeconds?: number;
255
+ contentType: string;
185
256
  /**
186
- * Maximum number of messages to retrieve
187
- * @default 10
188
- * @max 10
189
- * @note FIFO queues must use limit=1
257
+ * Receipt handle (lease token) for this message delivery.
258
+ * Required for acknowledge and visibility extension operations.
259
+ * Valid only until the visibility timeout expires.
190
260
  */
191
- limit?: number;
261
+ receiptHandle: string;
192
262
  }
193
- interface DeleteMessageOptions {
194
- /**
195
- * The queue name the message belongs to
196
- */
197
- queueName: string;
198
- /**
199
- * Consumer group name
200
- */
201
- consumerGroup: string;
202
- /**
203
- * The message ID to delete
204
- */
263
+ /**
264
+ * Message metadata provided to handlers
265
+ */
266
+ interface MessageMetadata {
205
267
  messageId: string;
206
- /**
207
- * Ticket received from the message
208
- */
209
- ticket: string;
210
- }
211
- interface DeleteMessageResponse {
212
- /**
213
- * Whether the message was successfully deleted
214
- */
215
- deleted: boolean;
216
- }
217
- interface ChangeVisibilityOptions {
218
- /**
219
- * The queue name the message belongs to
220
- */
221
- queueName: string;
222
- /**
223
- * Consumer group name
224
- */
268
+ deliveryCount: number;
269
+ createdAt: Date;
270
+ expiresAt?: Date;
271
+ topicName: string;
225
272
  consumerGroup: string;
226
- /**
227
- * The message ID to update
228
- */
229
- messageId: string;
230
- /**
231
- * Ticket received from the message
232
- */
233
- ticket: string;
234
- /**
235
- * New visibility timeout in seconds
236
- */
237
- visibilityTimeoutSeconds: number;
238
- }
239
- interface ChangeVisibilityResponse {
240
- /**
241
- * Whether the visibility was successfully updated
242
- */
243
- updated: boolean;
273
+ /** Vercel region the client is targeting. */
274
+ region: string;
244
275
  }
245
276
  /**
246
- * Result indicating the message should be timed out for retry later
277
+ * Instruction returned by a {@link RetryHandler} to control what happens
278
+ * to a message when the handler throws.
279
+ *
280
+ * - `{ afterSeconds: N }` — reschedule the message for redelivery after N seconds
281
+ * - `{ acknowledge: true }` — acknowledge the message so it is never retried
247
282
  */
248
- interface MessageTimeoutResult {
249
- /**
250
- * Time in seconds before the message becomes visible again
251
- */
252
- timeoutSeconds: number;
253
- }
283
+ type RetryDirective = {
284
+ afterSeconds: number;
285
+ } | {
286
+ acknowledge: true;
287
+ };
254
288
  /**
255
- * Result returned by message handlers
289
+ * Called when the message handler throws an error.
290
+ * Return a {@link RetryDirective} to reschedule or acknowledge the message,
291
+ * or return `undefined` to let the error propagate normally.
256
292
  */
257
- type MessageHandlerResult = void | MessageTimeoutResult;
293
+ type RetryHandler = (error: unknown, metadata: MessageMetadata) => RetryDirective | void | undefined;
258
294
  /**
259
- * Message metadata provided to handlers
295
+ * Message handler function type.
296
+ *
297
+ * Called once per message with the deserialized payload and metadata.
298
+ * Not called when the queue is empty — check the return value of `receive()` instead.
260
299
  */
261
- interface MessageMetadata {
262
- messageId: string;
263
- deliveryCount: number;
264
- timestamp: string;
265
- }
300
+ type MessageHandler<T = unknown> = (message: T, metadata: MessageMetadata) => Promise<void> | void;
266
301
  /**
267
- * Message handler function type
302
+ * Result returned by `receive()`. Use `ok` to check if messages were processed.
303
+ *
304
+ * @example
305
+ * ```typescript
306
+ * const result = await receive("topic", "group", handler);
307
+ * if (result.ok) {
308
+ * // Messages were processed successfully
309
+ * } else if (result.reason === "empty") {
310
+ * // Queue had no messages available
311
+ * }
312
+ * ```
268
313
  */
269
- type MessageHandler<T = unknown> = (message: T, metadata: MessageMetadata) => Promise<MessageHandlerResult> | MessageHandlerResult;
314
+ type ReceiveResult = {
315
+ ok: true;
316
+ } | {
317
+ ok: false;
318
+ reason: "empty";
319
+ } | {
320
+ ok: false;
321
+ reason: "not_found";
322
+ messageId: string;
323
+ } | {
324
+ ok: false;
325
+ reason: "not_available";
326
+ messageId: string;
327
+ } | {
328
+ ok: false;
329
+ reason: "already_processed";
330
+ messageId: string;
331
+ };
270
332
  /**
271
- * Options for creating a ConsumerGroup instance
333
+ * Options for receiving a specific message by ID.
272
334
  */
273
- interface ConsumerGroupOptions<T = unknown> {
274
- /**
275
- * Serializer/deserializer for the payload
276
- * @default JsonTransport instance
277
- */
278
- transport?: Transport<T>;
335
+ interface ReceiveByIdOptions {
336
+ /** The specific message ID to consume */
337
+ messageId: string;
279
338
  /**
280
- * Time in seconds that messages will be invisible to other consumers
281
- * @default 30
339
+ * Message lock duration in seconds.
340
+ * @default 300 (5 minutes)
341
+ * @minimum 30
342
+ * @maximum 3600 (1 hour)
282
343
  */
283
344
  visibilityTimeoutSeconds?: number;
284
345
  /**
285
- * How often to refresh the visibility timeout during processing (in seconds)
286
- * @default 10
346
+ * Called when the handler throws. Return `{ afterSeconds: N }` to reschedule
347
+ * the message for redelivery after N seconds, or return `undefined`
348
+ * to let the error propagate normally.
287
349
  */
288
- refreshInterval?: number;
350
+ retry?: RetryHandler;
289
351
  }
290
- interface ReceiveMessageByIdOptions<T = unknown> {
291
- /**
292
- * The queue name to receive the message from
293
- */
294
- queueName: string;
295
- /**
296
- * Consumer group name
297
- */
298
- consumerGroup: string;
352
+ /**
353
+ * Options for receiving messages from the queue with an optional batch limit.
354
+ */
355
+ interface ReceiveBatchOptions {
299
356
  /**
300
- * The message ID to retrieve
357
+ * Maximum number of messages to retrieve in a single request.
358
+ * @default 1
359
+ * @minimum 1
360
+ * @maximum 10
301
361
  */
302
- messageId: string;
362
+ limit?: number;
303
363
  /**
304
- * Time in seconds that the message will be invisible to other consumers
305
- * @default 900 (15 minutes)
364
+ * Message lock duration in seconds.
365
+ * @default 300 (5 minutes)
366
+ * @minimum 30
367
+ * @maximum 3600 (1 hour)
306
368
  */
307
369
  visibilityTimeoutSeconds?: number;
308
370
  /**
309
- * Skip payload content and only return message metadata
310
- * When true, the server returns a 204 status with headers containing message metadata
311
- * @default false
371
+ * Called when the handler throws. Return `{ afterSeconds: N }` to reschedule
372
+ * the message for redelivery after N seconds, or return `undefined`
373
+ * to let the error propagate normally.
312
374
  */
313
- skipPayload?: boolean;
314
- }
315
- interface ReceiveMessageByIdResponse<T = unknown, TSkipPayload extends boolean = false> {
316
- message: TSkipPayload extends true ? Message<void> : Message<T>;
375
+ retry?: RetryHandler;
317
376
  }
318
377
  /**
319
- * Error thrown when a message is not found (404)
378
+ * Options for the receive method. Either receive by message ID or receive
379
+ * from the queue with an optional limit. These options are mutually exclusive.
380
+ */
381
+ type ReceiveOptions = ReceiveByIdOptions | ReceiveBatchOptions;
382
+ /**
383
+ * Error thrown when a message does not exist or has expired.
320
384
  */
321
385
  declare class MessageNotFoundError extends Error {
322
386
  constructor(messageId: string);
323
387
  }
324
388
  /**
325
- * Error thrown when a message is not available for processing (409)
326
- * This can happen when the message is in the wrong state, already claimed, etc.
389
+ * Error thrown when a message exists but cannot be processed.
390
+ * This can happen when the message is in the wrong state or already claimed by another consumer.
327
391
  */
328
392
  declare class MessageNotAvailableError extends Error {
329
393
  constructor(messageId: string, reason?: string);
330
394
  }
331
- /**
332
- * Error thrown when there's a FIFO ordering violation (409)
333
- */
334
- declare class FifoOrderingViolationError extends Error {
335
- constructor(messageId: string, reason: string);
336
- }
337
395
  /**
338
396
  * Error thrown when message data is corrupted or can't be parsed
339
397
  */
@@ -341,458 +399,252 @@ declare class MessageCorruptedError extends Error {
341
399
  constructor(messageId: string, reason: string);
342
400
  }
343
401
  /**
344
- * Error thrown when there are no messages available in the queue (204)
402
+ * Error thrown when there are no messages available in the queue.
345
403
  */
346
404
  declare class QueueEmptyError extends Error {
347
405
  constructor(queueName: string, consumerGroup: string);
348
406
  }
349
407
  /**
350
- * Error thrown when a message is temporarily locked in a FIFO queue (423)
408
+ * Error thrown when a message is temporarily locked by another consumer.
409
+ * The message is currently being processed and cannot be claimed.
351
410
  */
352
411
  declare class MessageLockedError extends Error {
412
+ /** Suggested retry delay in seconds, if provided by the server. */
353
413
  readonly retryAfter?: number;
354
414
  constructor(messageId: string, retryAfter?: number);
355
415
  }
356
416
  /**
357
- * Error thrown when authentication fails (401)
417
+ * Error thrown when authentication fails.
418
+ * This typically means the token is missing, invalid, or expired.
358
419
  */
359
420
  declare class UnauthorizedError extends Error {
360
421
  constructor(message?: string);
361
422
  }
362
423
  /**
363
- * Error thrown when access is forbidden (403)
424
+ * Error thrown when access is forbidden.
425
+ * This typically means the queue belongs to a different environment or project.
364
426
  */
365
427
  declare class ForbiddenError extends Error {
366
428
  constructor(message?: string);
367
429
  }
368
430
  /**
369
- * Error thrown for bad requests (400)
431
+ * Error thrown when request parameters are invalid.
370
432
  */
371
433
  declare class BadRequestError extends Error {
372
434
  constructor(message: string);
373
435
  }
374
436
  /**
375
- * Error thrown when there's a failed dependency (424) - FIFO ordering violation in receive by ID
376
- */
377
- declare class FailedDependencyError extends Error {
378
- constructor(messageId: string);
379
- }
380
- /**
381
- * Error thrown for internal server errors (500)
437
+ * Error thrown when the server encounters an unexpected error.
382
438
  */
383
439
  declare class InternalServerError extends Error {
384
440
  constructor(message?: string);
385
441
  }
386
442
  /**
387
- * Error thrown when batch limit parameter is invalid
443
+ * Error thrown when the batch limit parameter is outside the valid range.
444
+ * The limit must be between 1 and 10 (inclusive).
388
445
  */
389
446
  declare class InvalidLimitError extends Error {
390
447
  constructor(limit: number, min?: number, max?: number);
391
448
  }
392
449
  /**
393
- * Options extracted from a queue callback request
450
+ * Error thrown when attempting to process a message that has already been successfully processed.
394
451
  */
395
- interface CallbackMessageOptions {
396
- /**
397
- * The queue name extracted from Vqs-Queue-Name header
398
- */
399
- queueName: string;
400
- /**
401
- * The consumer group extracted from Vqs-Consumer-Group header
402
- */
403
- consumerGroup: string;
404
- /**
405
- * The message ID extracted from Vqs-Message-Id header
406
- */
407
- messageId: string;
452
+ declare class MessageAlreadyProcessedError extends Error {
453
+ constructor(messageId: string);
408
454
  }
409
455
  /**
410
- * Error thrown when queue callback headers are missing or invalid
456
+ * Error thrown when a duplicate idempotency key is detected.
457
+ * The message was already sent within the deduplication window.
411
458
  */
412
- declare class InvalidCallbackError extends Error {
413
- constructor(message: string);
459
+ declare class DuplicateMessageError extends Error {
460
+ readonly idempotencyKey?: string;
461
+ constructor(message: string, idempotencyKey?: string);
414
462
  }
415
-
416
463
  /**
417
- * Client for interacting with the Vercel Queue Service API
464
+ * Error thrown when consumer discovery fails.
465
+ * This typically means the deployment could not be reached or is not configured correctly.
418
466
  */
419
- declare class QueueClient {
420
- private baseUrl;
421
- private token;
422
- /**
423
- * Internal default instance for use by createTopic and other convenience functions
424
- * @internal
425
- */
426
- private static _defaultInstance;
427
- /**
428
- * Create a new Vercel Queue Service client
429
- * @param options Client configuration options (optional - will auto-detect Vercel Function environment)
430
- */
431
- constructor(options?: QueueClientOptions);
432
- /**
433
- * Get the default client instance for internal use by convenience functions
434
- * @internal
435
- */
436
- static _getDefaultInstance(): QueueClient;
437
- /**
438
- * Synchronously get OIDC token from environment
439
- * Used internally by constructor - mirrors the logic from getVercelOidcToken but synchronously
440
- */
441
- private getVercelOidcTokenSync;
442
- /**
443
- * Send a message to a queue
444
- * @param options Send message options
445
- * @param transport Serializer/deserializer for the payload
446
- * @returns Promise with the message ID
447
- * @throws {BadRequestError} When request parameters are invalid
448
- * @throws {UnauthorizedError} When authentication fails
449
- * @throws {ForbiddenError} When access is denied (environment mismatch)
450
- * @throws {InternalServerError} When server encounters an error
451
- */
452
- sendMessage<T = unknown>(options: SendMessageOptions<T>, transport: Transport<T>): Promise<SendMessageResponse>;
453
- /**
454
- * Receive messages from a queue
455
- * @param options Receive messages options
456
- * @param transport Serializer/deserializer for the payload
457
- * @returns AsyncGenerator that yields messages as they arrive
458
- * @throws {InvalidLimitError} When limit parameter is not between 1 and 10
459
- * @throws {QueueEmptyError} When no messages are available (204)
460
- * @throws {MessageLockedError} When FIFO queue has locked messages (423)
461
- * @throws {BadRequestError} When request parameters are invalid
462
- * @throws {UnauthorizedError} When authentication fails
463
- * @throws {ForbiddenError} When access is denied (environment mismatch)
464
- * @throws {InternalServerError} When server encounters an error
465
- */
466
- receiveMessages<T = unknown>(options: ReceiveMessagesOptions<T>, transport: Transport<T>): AsyncGenerator<Message<T>, void, unknown>;
467
- /**
468
- * Receive a specific message by its ID from a queue
469
- * @param options Receive message by ID options
470
- * @param transport Serializer/deserializer for the payload
471
- * @returns Promise with the message or null if not found/available
472
- * @throws {MessageNotFoundError} When the message doesn't exist (404)
473
- * @throws {MessageLockedError} When the message is temporarily locked (423)
474
- * @throws {FailedDependencyError} When FIFO ordering is violated (424)
475
- * @throws {MessageNotAvailableError} When message exists but isn't available (409)
476
- * @throws {MessageCorruptedError} When message data is corrupted
477
- * @throws {BadRequestError} When request parameters are invalid
478
- * @throws {UnauthorizedError} When authentication fails
479
- * @throws {ForbiddenError} When access is denied (environment mismatch)
480
- * @throws {InternalServerError} When server encounters an error
481
- */
482
- receiveMessageById<T = unknown>(options: ReceiveMessageByIdOptions<T> & {
483
- skipPayload: true;
484
- }, transport?: Transport<T>): Promise<ReceiveMessageByIdResponse<T, true>>;
485
- receiveMessageById<T = unknown>(options: ReceiveMessageByIdOptions<T> & {
486
- skipPayload?: false | undefined;
487
- }, transport: Transport<T>): Promise<ReceiveMessageByIdResponse<T, false>>;
488
- /**
489
- * Delete a message (acknowledge processing)
490
- * @param options Delete message options
491
- * @returns Promise with delete status
492
- * @throws {MessageNotFoundError} When the message doesn't exist (404)
493
- * @throws {MessageNotAvailableError} When message can't be deleted (409)
494
- * @throws {BadRequestError} When ticket is missing or invalid (400)
495
- * @throws {UnauthorizedError} When authentication fails
496
- * @throws {ForbiddenError} When access is denied (environment mismatch)
497
- * @throws {InternalServerError} When server encounters an error
498
- */
499
- deleteMessage(options: DeleteMessageOptions): Promise<DeleteMessageResponse>;
500
- /**
501
- * Change the visibility timeout of a message
502
- * @param options Change visibility options
503
- * @returns Promise with update status
504
- * @throws {MessageNotFoundError} When the message doesn't exist (404)
505
- * @throws {MessageNotAvailableError} When message can't be updated (409)
506
- * @throws {BadRequestError} When ticket is missing or visibility timeout invalid (400)
507
- * @throws {UnauthorizedError} When authentication fails
508
- * @throws {ForbiddenError} When access is denied (environment mismatch)
509
- * @throws {InternalServerError} When server encounters an error
510
- */
511
- changeVisibility(options: ChangeVisibilityOptions): Promise<ChangeVisibilityResponse>;
467
+ declare class ConsumerDiscoveryError extends Error {
468
+ readonly deploymentId?: string;
469
+ constructor(message: string, deploymentId?: string);
512
470
  }
513
-
514
471
  /**
515
- * Options for the consume method
472
+ * Error thrown when the consumer registry is not configured for the project.
516
473
  */
517
- interface ConsumeOptions {
518
- /** The specific message ID to consume (if not provided, consumes next available message) */
519
- messageId?: string;
520
- /** Whether to skip downloading the payload (only allowed when messageId is provided) */
521
- skipPayload?: boolean;
474
+ declare class ConsumerRegistryNotConfiguredError extends Error {
475
+ constructor(message?: string);
522
476
  }
523
- /**
524
- * A ConsumerGroup represents a named group of consumers that process messages from a topic
525
- */
526
- declare class ConsumerGroup<T = unknown> {
527
- private client;
528
- private topicName;
529
- private consumerGroupName;
530
- private visibilityTimeout;
531
- private refreshInterval;
532
- private transport;
477
+
478
+ declare class QueueClient {
479
+ constructor(options: QueueClientOptions);
533
480
  /**
534
- * Create a new ConsumerGroup instance
535
- * @param client QueueClient instance to use for API calls
536
- * @param topicName Name of the topic to consume from
537
- * @param consumerGroupName Name of the consumer group
538
- * @param options Optional configuration
481
+ * Send a message to a topic.
482
+ *
483
+ * This is an arrow function property so it can be destructured:
484
+ * ```typescript
485
+ * const { send } = new QueueClient({ region: process.env.QUEUE_REGION! });
486
+ * await send("my-topic", payload);
487
+ * ```
488
+ *
489
+ * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
490
+ * @param payload - The data to send (serialized via the configured transport)
491
+ * @param options - Optional send options (idempotencyKey, retentionSeconds, delaySeconds, headers)
492
+ * @returns `{ messageId }` — `messageId` is `null` when the server accepted
493
+ * the message for deferred processing (no ID available yet)
539
494
  */
540
- constructor(client: QueueClient, topicName: string, consumerGroupName: string, options?: ConsumerGroupOptions<T>);
495
+ send: <T = unknown>(topicName: string, payload: T, options?: SendOptions) => Promise<SendResult>;
541
496
  /**
542
- * Starts a background loop that periodically extends the visibility timeout for a message.
543
- * This prevents the message from becoming visible to other consumers while it's being processed.
497
+ * Receive and process messages from a topic.
544
498
  *
545
- * The extension loop runs every `refreshInterval` seconds and updates the message's
546
- * visibility timeout to `visibilityTimeout` seconds from the current time.
499
+ * Each message is automatically locked, kept alive via periodic visibility
500
+ * extensions during processing, and acknowledged upon successful handler completion.
501
+ * The handler is not called when the queue is empty — check `result.ok` instead.
547
502
  *
548
- * @param messageId - The unique identifier of the message to extend visibility for
549
- * @param ticket - The receipt ticket that proves ownership of the message
550
- * @returns A function that when called will stop the extension loop
503
+ * This is an arrow function property so it can be destructured:
504
+ * ```typescript
505
+ * const { receive } = new QueueClient({ region: process.env.QUEUE_REGION! });
506
+ * const result = await receive("my-topic", "my-group", handler);
507
+ * if (!result.ok) console.log(result.reason);
508
+ * ```
551
509
  *
552
- * @remarks
553
- * - The first extension attempt occurs after `refreshInterval` seconds, not immediately
554
- * - If an extension fails, the loop terminates with an error logged to console
555
- * - The returned stop function is idempotent - calling it multiple times is safe
556
- * - By default, the stop function returns immediately without waiting for in-flight
557
- * - Pass `true` to the stop function to wait for any in-flight extension to complete
558
- */
559
- private startVisibilityExtension;
560
- /**
561
- * Process a single message with the given handler
562
- * @param message The message to process
563
- * @param handler Function to process the message
564
- */
565
- private processMessage;
566
- /**
567
- * Consume the next available message from the queue
568
- * @param handler Function to process the message
569
- * @returns Promise that resolves when the message is processed
570
- * @throws All the same errors as the underlying client methods
571
- */
572
- consume(handler: MessageHandler<T>): Promise<void>;
573
- /**
574
- * Consume a specific message by its ID with full payload
575
- * @param handler Function to process the message
576
- * @param options Consume options with messageId specified
577
- * @returns Promise that resolves when the message is processed
578
- * @throws All the same errors as the underlying client methods
579
- */
580
- consume(handler: MessageHandler<T>, options: {
581
- messageId: string;
582
- skipPayload?: false | undefined;
583
- }): Promise<void>;
584
- /**
585
- * Consume a specific message by its ID without downloading the payload (metadata only)
586
- * @param handler Function to process the message metadata (payload will be void)
587
- * @param options Consume options with messageId and skipPayload specified
588
- * @returns Promise that resolves when the message is processed
589
- * @throws All the same errors as the underlying client methods
510
+ * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
511
+ * @param consumerGroup - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
512
+ * @param handler - Function to process each message payload and metadata.
513
+ * Not called when the queue is empty.
514
+ * @param options - Optional receive options (visibilityTimeoutSeconds, limit, or messageId)
515
+ * @returns Discriminated result: `{ ok: true }` on success, `{ ok: false, reason }` otherwise
590
516
  */
591
- consume(handler: MessageHandler<void>, options: {
592
- messageId: string;
593
- skipPayload: true;
594
- }): Promise<void>;
517
+ receive: <T = unknown>(topicName: string, consumerGroup: string, handler: MessageHandler<T>, options?: ReceiveOptions) => Promise<ReceiveResult>;
595
518
  /**
596
- * Get the consumer group name
597
- */
598
- get name(): string;
599
- /**
600
- * Get the topic name this consumer group is subscribed to
601
- */
602
- get topic(): string;
519
+ * Create a Web API route handler for processing queue callback messages.
520
+ *
521
+ * Parses incoming `Request` as a CloudEvent and invokes the handler.
522
+ * For use on Vercel — Vercel invokes this route when messages are available.
523
+ *
524
+ * This is an arrow function property so it can be destructured:
525
+ * ```typescript
526
+ * const { handleCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
527
+ * export const POST = handleCallback(handler);
528
+ * ```
529
+ *
530
+ * @param handler - Function to process the message payload and metadata
531
+ * @param options - Optional configuration
532
+ * @param options.visibilityTimeoutSeconds - Message lock duration (default: 300, max: 3600)
533
+ * @param options.retry - Called when the handler throws. Return `{ afterSeconds: N }` to
534
+ * reschedule the message for redelivery after N seconds.
535
+ * @returns A `(request: Request) => Promise<Response>` route handler
536
+ */
537
+ handleCallback: <T = unknown>(handler: MessageHandler<T>, options?: {
538
+ visibilityTimeoutSeconds?: number;
539
+ retry?: RetryHandler;
540
+ }) => ((request: Request) => Promise<Response>);
541
+ /**
542
+ * Create a Connect-style route handler for processing queue callback messages.
543
+ * For use on Vercel — Vercel invokes this route when messages are available.
544
+ *
545
+ * For frameworks using the `(req, res)` middleware pattern where `req.body`
546
+ * is pre-parsed (Next.js Pages Router, etc.).
547
+ *
548
+ * This is an arrow function property so it can be destructured:
549
+ * ```typescript
550
+ * const { handleNodeCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
551
+ * app.post("/api/queue", handleNodeCallback(handler));
552
+ * ```
553
+ *
554
+ * @param handler - Function to process the message payload and metadata
555
+ * @param options - Optional configuration
556
+ * @param options.visibilityTimeoutSeconds - Message lock duration (default: 300, max: 3600)
557
+ * @param options.retry - Called when the handler throws. Return `{ afterSeconds: N }` to
558
+ * reschedule the message for redelivery after N seconds.
559
+ * @returns A `(req, res) => Promise<void>` route handler
560
+ */
561
+ handleNodeCallback: <T = unknown>(handler: MessageHandler<T>, options?: {
562
+ visibilityTimeoutSeconds?: number;
563
+ retry?: RetryHandler;
564
+ }) => ((req: {
565
+ method?: string;
566
+ headers: Record<string, string | string[] | undefined>;
567
+ body?: unknown;
568
+ }, res: {
569
+ status(code: number): {
570
+ json(data: unknown): void;
571
+ end(): void;
572
+ };
573
+ end(): void;
574
+ }) => Promise<void>);
603
575
  }
604
576
 
605
577
  /**
606
- * A Topic represents a named channel for publishing messages in a pub/sub pattern
578
+ * Core queue callback utilities for handling incoming webhook payloads
579
+ * from Vercel triggers using the CloudEvent specification.
580
+ *
581
+ * This module provides the framework-agnostic core. For framework-specific
582
+ * wrappers, see `@vercel/queue/web` and `@vercel/queue/nextjs/pages`.
607
583
  */
608
- declare class Topic<T = unknown> {
609
- private client;
610
- private topicName;
611
- private transport;
612
- /**
613
- * Create a new Topic instance
614
- * @param client QueueClient instance to use for API calls
615
- * @param topicName Name of the topic to work with
616
- * @param transport Optional serializer/deserializer for the payload (defaults to JSON)
617
- */
618
- constructor(client: QueueClient, topicName: string, transport?: Transport<T>);
619
- /**
620
- * Publish a message to the topic
621
- * @param payload The data to publish
622
- * @param options Optional publish options
623
- * @returns An object containing the message ID
624
- * @throws {BadRequestError} When request parameters are invalid
625
- * @throws {UnauthorizedError} When authentication fails
626
- * @throws {ForbiddenError} When access is denied (environment mismatch)
627
- * @throws {InternalServerError} When server encounters an error
628
- */
629
- publish(payload: T, options?: PublishOptions): Promise<{
630
- messageId: string;
631
- }>;
632
- /**
633
- * Create a consumer group for this topic
634
- * @param consumerGroupName Name of the consumer group
635
- * @param options Optional configuration for the consumer group
636
- * @returns A ConsumerGroup instance
637
- */
638
- consumerGroup<U = T>(consumerGroupName: string, options?: ConsumerGroupOptions<U>): ConsumerGroup<U>;
639
- /**
640
- * Get the topic name
641
- */
642
- get name(): string;
643
- /**
644
- * Get the transport used by this topic
645
- */
646
- get serializer(): Transport<T>;
647
- }
648
584
 
585
+ declare const CLOUD_EVENT_TYPE_V1BETA = "com.vercel.queue.v1beta";
586
+ declare const CLOUD_EVENT_TYPE_V2BETA = "com.vercel.queue.v2beta";
649
587
  /**
650
- * Create a new Topic instance using the default QueueClient
651
- * For custom client configuration, use `new Topic(customClient, topicName, transport)` directly
652
- * @param topicName Name of the topic
653
- * @param transport Optional serializer/deserializer for the payload (defaults to JSON)
654
- * @returns A Topic instance
588
+ * Routing-only callback: the SDK must fetch the message by ID.
589
+ * Produced by v1beta structured mode and v2beta large-body mode.
655
590
  */
656
- declare function createTopic<T = unknown>(topicName: string, transport?: Transport<T>): Topic<T>;
657
- /**
658
- * Options for the send function
659
- */
660
- interface SendOptions<T = unknown> extends PublishOptions {
661
- /**
662
- * Serializer/deserializer for the payload
663
- * @default JsonTransport instance
664
- */
665
- transport?: Transport<T>;
666
- }
667
- /**
668
- * Send a message to a topic (shorthand for topic creation and publishing)
669
- * Uses the default QueueClient with automatic OIDC token detection
670
- * @param topicName Name of the topic to send to
671
- * @param payload The data to send
672
- * @param options Optional send options including transport and publish settings
673
- * @returns Promise with the message ID
674
- * @throws {BadRequestError} When request parameters are invalid
675
- * @throws {UnauthorizedError} When authentication fails
676
- * @throws {ForbiddenError} When access is denied (environment mismatch)
677
- * @throws {InternalServerError} When server encounters an error
678
- */
679
- declare function send<T = unknown>(topicName: string, payload: T, options?: SendOptions<T>): Promise<{
591
+ type ParsedCallbackV1 = {
592
+ queueName: string;
593
+ consumerGroup: string;
680
594
  messageId: string;
681
- }>;
595
+ region?: string;
596
+ };
682
597
  /**
683
- * Options for the receive function
598
+ * Full-message callback: payload and receipt handle are inlined,
599
+ * so the SDK can process directly without an extra fetch.
600
+ * Produced by v2beta small-body mode.
684
601
  */
685
- interface ReceiveOptions<T = unknown> extends ConsumerGroupOptions<T>, ConsumeOptions {
686
- }
687
- /**
688
- * Receive a message from a topic (shorthand for topic and consumer group creation)
689
- * Uses the default QueueClient with automatic OIDC token detection
690
- * @param topicName Name of the topic to receive from
691
- * @param consumerGroup Name of the consumer group
692
- * @param handler Function to process the message
693
- * @returns Promise that resolves when the message is processed
694
- * @throws All the same errors as the underlying client methods
695
- */
696
- declare function receive<T = unknown>(topicName: string, consumerGroup: string, handler: MessageHandler<T>, options?: ReceiveOptions<T>): Promise<void>;
697
- /**
698
- * Receive a specific message by its ID with full payload
699
- * @param topicName Name of the topic to receive from
700
- * @param consumerGroup Name of the consumer group
701
- * @param handler Function to process the message
702
- * @param options Receive options with messageId specified
703
- * @returns Promise that resolves when the message is processed
704
- * @throws All the same errors as the underlying client methods
705
- */
706
- declare function receive<T = unknown>(topicName: string, consumerGroup: string, handler: MessageHandler<T>, options: ReceiveOptions<T> & {
707
- messageId: string;
708
- skipPayload?: false | undefined;
709
- }): Promise<void>;
710
- /**
711
- * Receive a specific message by its ID without downloading the payload (metadata only)
712
- * @param topicName Name of the topic to receive from
713
- * @param consumerGroup Name of the consumer group
714
- * @param handler Function to process the message metadata (payload will be void)
715
- * @param options Receive options with messageId and skipPayload specified
716
- * @returns Promise that resolves when the message is processed
717
- * @throws All the same errors as the underlying client methods
718
- */
719
- declare function receive<T = unknown>(topicName: string, consumerGroup: string, handler: MessageHandler<void>, options: ReceiveOptions<T> & {
602
+ type ParsedCallbackV2 = {
603
+ queueName: string;
604
+ consumerGroup: string;
720
605
  messageId: string;
721
- skipPayload: true;
722
- }): Promise<void>;
723
-
724
- /**
725
- * Queue Callback utilities for handling incoming webhook payloads
726
- */
727
-
606
+ region?: string;
607
+ receiptHandle: string;
608
+ deliveryCount?: number;
609
+ createdAt?: string;
610
+ expiresAt?: string;
611
+ contentType?: string;
612
+ visibilityDeadline?: string;
613
+ rawBody?: ReadableStream<Uint8Array>;
614
+ parsedPayload?: unknown;
615
+ };
616
+ type ParsedCallbackRequest = ParsedCallbackV1 | ParsedCallbackV2;
728
617
  /**
729
- * Parse a queue callback request and extract the required information for receiveMessageById
618
+ * Parse a callback from a pre-parsed body and headers.
730
619
  *
731
- * @param request The incoming Request object from the queue callback
732
- * @returns CallbackMessageOptions that can be used with receiveMessageById
733
- * @throws {InvalidCallbackError} When required queue headers are missing or invalid
620
+ * For frameworks like Next.js Pages Router where the body has already been
621
+ * parsed, use this instead of {@link parseCallback}.
734
622
  *
735
- * @example
736
- * ```typescript
737
- * import { parseCallbackRequest } from '@vercel/queue';
623
+ * Detects the CloudEvent version from the `ce-type` header:
624
+ * - `com.vercel.queue.v2beta`: binary content mode (metadata in headers,
625
+ * payload in body). For small messages, the body is attached as `parsedPayload`.
626
+ * - Otherwise: structured content mode (v1beta, entire CloudEvent in JSON body)
738
627
  *
739
- * // In your webhook handler
740
- * export async function POST(request: Request) {
741
- * try {
742
- * const callbackOptions = parseCallbackRequest(request);
743
- *
744
- * // Use with receiveMessageById
745
- * const message = await client.receiveMessageById({
746
- * ...callbackOptions,
747
- * visibilityTimeoutSeconds: 30
748
- * }, transport);
749
- *
750
- * // Process the message...
751
- * } catch (error) {
752
- * if (error instanceof InvalidCallbackError) {
753
- * return new Response('Invalid callback', { status: 400 });
754
- * }
755
- * throw error;
756
- * }
757
- * }
758
- * ```
628
+ * @param body - The framework-parsed request body. Type depends on Content-Type
629
+ * and framework configuration:
630
+ * - v1beta: parsed CloudEvent JSON object
631
+ * - v2beta: the message payload as parsed by the framework (object, Buffer, or string)
632
+ * @param headers - HTTP headers
633
+ * @returns Parsed callback request with routing metadata and optional payload
759
634
  */
760
- declare function parseCallbackRequest(request: Request): CallbackMessageOptions;
761
- /**
762
- * Configuration object with handlers for different topics
763
- * Each topic can have either:
764
- * - A single handler function (uses 'default' consumer group)
765
- * - An object with handlers for specific consumer groups
766
- */
767
- type CallbackHandlers = {
768
- [topicName: string]: MessageHandler | {
769
- [consumerGroup: string]: MessageHandler;
770
- };
771
- };
635
+ declare function parseRawCallback(body: unknown, headers: Record<string, string | string[] | undefined>): ParsedCallbackRequest;
772
636
  /**
773
- * Simplified queue callback handler for NextJS route handlers
637
+ * Parse and validate a CloudEvent callback from a Web API `Request` object.
774
638
  *
775
- * @param handlers Object with topic-specific handlers
776
- * @returns A NextJS route handler function
639
+ * Detects the CloudEvent version from the `ce-type` header:
640
+ * - `com.vercel.queue.v2beta`: binary content mode (metadata in headers,
641
+ * payload in body). For v2beta, the body is attached as `rawBody` (a
642
+ * ReadableStream) rather than being parsed.
643
+ * - Otherwise: structured content mode (v1beta, entire CloudEvent in JSON body)
777
644
  *
778
- * @example
779
- * ```typescript
780
- * // Topic handler (uses 'default' consumer group)
781
- * export const POST = handleCallback({
782
- * "new-users": (message, metadata) => {
783
- * console.log(`New user event:`, message, metadata);
784
- * }
785
- * });
786
- *
787
- * // Consumer group specific handlers
788
- * export const POST = handleCallback({
789
- * "image-processing": {
790
- * "compress": (message, metadata) => console.log("Compressing image", message),
791
- * "resize": (message, metadata) => console.log("Resizing image", message),
792
- * }
793
- * });
794
- * ```
645
+ * For frameworks that pre-parse the body (e.g. Next.js Pages Router),
646
+ * use {@link parseRawCallback} instead.
795
647
  */
796
- declare function handleCallback(handlers: CallbackHandlers): (request: Request) => Promise<Response>;
648
+ declare function parseCallback(request: Request): Promise<ParsedCallbackRequest>;
797
649
 
798
- export { BadRequestError, BufferTransport, type CallbackConfig, type CallbackMessageOptions, type ChangeVisibilityOptions, type ChangeVisibilityResponse, type ConsumeOptions, ConsumerGroup, type ConsumerGroupOptions, type DeleteMessageOptions, type DeleteMessageResponse, FailedDependencyError, FifoOrderingViolationError, ForbiddenError, InternalServerError, InvalidCallbackError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, type PublishOptions, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveMessageByIdOptions, type ReceiveMessageByIdResponse, type ReceiveMessagesOptions, type ReceiveOptions, type SendMessageOptions, type SendMessageResponse, type SendOptions, StreamTransport, Topic, type Transport, UnauthorizedError, createTopic, handleCallback, parseCallbackRequest, receive, send };
650
+ 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, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveBatchOptions, type ReceiveByIdOptions, type ReceiveOptions, type ReceiveResult, type RetryDirective, type RetryHandler, type SendOptions, type SendResult, StreamTransport, type Transport, UnauthorizedError, type VercelRegion, parseCallback, parseRawCallback };