@vercel/queue 0.0.0-alpha.33 → 0.0.0-alpha.35

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.
@@ -1,196 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Standalone CLI script to discover queue handlers in development mode
5
- * This script is self-contained and doesn't import from the main package
6
- */
7
-
8
- const fs = require("fs");
9
- const path = require("path");
10
-
11
- function showHelp() {
12
- console.log(`
13
- @vercel/queue local-init - Initialize queue handlers in local development
14
-
15
- USAGE:
16
- npx vercel-queue-local-init [options]
17
-
18
- OPTIONS:
19
- --port <number> Port number for Next.js dev server (default: 3000)
20
- --config <path> Path to vercel.json file (default: ./vercel.json)
21
- --help, -h Show this help message
22
-
23
- EXAMPLES:
24
- npx vercel-queue-local-init
25
- npx vercel-queue-local-init --port 3001
26
- npx vercel-queue-local-init --config ./my-vercel.json
27
- `);
28
- }
29
-
30
- /**
31
- * Read vercel.json and extract function endpoints with queue triggers
32
- */
33
- function readVercelConfig(configPath = "./vercel.json") {
34
- try {
35
- const fullPath = path.resolve(configPath);
36
- const configContent = fs.readFileSync(fullPath, "utf-8");
37
- const config = JSON.parse(configContent);
38
-
39
- const routes = [];
40
-
41
- // Extract routes from functions with queue triggers
42
- if (config.functions) {
43
- for (const [functionPath, functionConfig] of Object.entries(
44
- config.functions,
45
- )) {
46
- if (
47
- functionConfig &&
48
- typeof functionConfig === "object" &&
49
- "experimentalTriggers" in functionConfig
50
- ) {
51
- const triggers = functionConfig.experimentalTriggers;
52
- if (Array.isArray(triggers)) {
53
- // Check if any trigger is a queue trigger
54
- const hasQueueTrigger = triggers.some(
55
- (trigger) =>
56
- trigger &&
57
- typeof trigger === "object" &&
58
- trigger.type === "queue/v1beta",
59
- );
60
-
61
- if (hasQueueTrigger) {
62
- // Convert file path to API route
63
- // Examples:
64
- // app/api/vm/queue/route.ts -> /api/vm/queue
65
- // app/(dashboard)/api/queue/route.ts -> /api/queue
66
- // app/(auth)/login/route.ts -> /login
67
- // src/app/api/queue/route.ts -> /api/queue
68
- // src/app/(admin)/settings/route.ts -> /settings
69
- // pages/api/queue.ts -> /api/queue
70
- // src/pages/api/queue.ts -> /api/queue
71
- // app/queue/route.ts -> /queue
72
- // src/app/webhooks/route.ts -> /webhooks
73
- let apiPath = functionPath;
74
-
75
- // Handle src/ prefix
76
- if (apiPath.startsWith("src/")) {
77
- apiPath = apiPath.replace("src/", "");
78
- }
79
-
80
- // Convert Next.js file paths to HTTP routes
81
- if (apiPath.startsWith("app/")) {
82
- // App Router: app/api/queue/route.ts -> /api/queue
83
- // App Router: app/queue/route.ts -> /queue
84
- apiPath = apiPath.replace("app/", "/");
85
- } else if (apiPath.startsWith("pages/api/")) {
86
- // Pages Router: pages/api/queue.ts -> /api/queue
87
- apiPath = apiPath.replace("pages/api/", "/api/");
88
- } else if (apiPath.startsWith("pages/")) {
89
- // Pages Router: pages/queue.ts -> /queue
90
- apiPath = apiPath.replace("pages/", "/");
91
- } else {
92
- // Fallback: assume it's a root-level route
93
- apiPath = "/" + apiPath;
94
- }
95
-
96
- // Remove Next.js route groups (folders wrapped in parentheses)
97
- // Route groups like (dashboard), (auth), (admin) are used for organization
98
- // but don't affect the URL structure
99
- apiPath = apiPath.replace(/\/\([^)]+\)/g, "");
100
-
101
- // Remove file extensions and Next.js-specific suffixes
102
- apiPath = apiPath.replace(
103
- /\/(route|index)\.(ts|js|tsx|jsx)$/,
104
- "",
105
- );
106
- apiPath = apiPath.replace(/\.(ts|js|tsx|jsx)$/, "");
107
-
108
- routes.push(apiPath);
109
- }
110
- }
111
- }
112
- }
113
- }
114
-
115
- return routes;
116
- } catch (error) {
117
- // vercel.json might not exist or be malformed
118
- return [];
119
- }
120
- }
121
-
122
- /**
123
- * Make OPTIONS requests to trigger Next.js lazy loading
124
- */
125
- async function discoverQueueHandlers(options = {}) {
126
- const baseUrl = options.baseUrl || "http://localhost:3000";
127
- const configPath = options.configPath || "./vercel.json";
128
-
129
- let routes = options.routes || [];
130
-
131
- // Auto-discover from vercel.json
132
- if (routes.length === 0) {
133
- const configRoutes = readVercelConfig(configPath);
134
- if (configRoutes.length > 0) {
135
- routes = configRoutes;
136
- console.log(
137
- `[Dev Mode] Found ${configRoutes.length} queue endpoints in vercel.json`,
138
- );
139
- } else {
140
- console.error("[Dev Mode] No queue endpoints found in vercel.json");
141
- process.exit(1);
142
- }
143
- }
144
-
145
- console.log(
146
- "[Dev Mode] Making OPTIONS requests to trigger module loading...",
147
- );
148
-
149
- for (const route of routes) {
150
- try {
151
- // Make an OPTIONS request to trigger module loading without processing
152
- const response = await fetch(`${baseUrl}${route}`, {
153
- method: "OPTIONS",
154
- // Add a header to identify this as a discovery request
155
- headers: { "x-queue-discovery": "true" },
156
- });
157
-
158
- // Any response means the route exists and module was loaded
159
- console.log(`[Dev Mode] ✓ ${route} (${response.status})`);
160
- } catch (error) {
161
- // Ignore errors - route might not exist or server not running
162
- console.log(`[Dev Mode] ✗ ${route} (server may not be running)`);
163
- }
164
- }
165
-
166
- console.log("[Dev Mode] Module loading complete.");
167
- }
168
-
169
- async function main() {
170
- const args = process.argv.slice(2);
171
- const options = {};
172
-
173
- // Parse simple command line arguments
174
- for (let i = 0; i < args.length; i++) {
175
- const arg = args[i];
176
- if (arg === "--help" || arg === "-h") {
177
- showHelp();
178
- return;
179
- } else if (arg === "--port" && args[i + 1]) {
180
- options.baseUrl = `http://localhost:${args[i + 1]}`;
181
- i++;
182
- } else if (arg === "--config" && args[i + 1]) {
183
- options.configPath = args[i + 1];
184
- i++;
185
- }
186
- }
187
-
188
- try {
189
- await discoverQueueHandlers(options);
190
- } catch (error) {
191
- console.error("Failed to discover queue handlers:", error.message);
192
- process.exit(1);
193
- }
194
- }
195
-
196
- main();
@@ -1,380 +0,0 @@
1
- /**
2
- * Serializer/Deserializer interface for message payloads
3
- */
4
- interface Transport<T = unknown> {
5
- /**
6
- * Serialize a value to a buffer or stream for transmission
7
- */
8
- serialize(value: T): Buffer | ReadableStream<Uint8Array>;
9
- /**
10
- * Deserialize a readable stream back to the original value
11
- */
12
- deserialize(stream: ReadableStream<Uint8Array>): Promise<T>;
13
- /**
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
16
- * @param payload The deserialized payload to clean up
17
- */
18
- finalize?(payload: T): Promise<void>;
19
- /**
20
- * MIME type for this serialization format
21
- */
22
- contentType: string;
23
- }
24
- /**
25
- * Built-in JSON serializer/deserializer
26
- * This implementation reads the entire stream into memory for JSON parsing
27
- */
28
- declare class JsonTransport<T = unknown> implements Transport<T> {
29
- readonly contentType = "application/json";
30
- readonly replacer?: Parameters<typeof JSON.parse>[1];
31
- readonly reviver?: Parameters<typeof JSON.parse>[1];
32
- constructor(options?: {
33
- replacer?: Parameters<typeof JSON.parse>[1];
34
- reviver?: Parameters<typeof JSON.parse>[1];
35
- });
36
- serialize(value: T): Buffer;
37
- deserialize(stream: ReadableStream<Uint8Array>): Promise<T>;
38
- }
39
- /**
40
- * Built-in Buffer serializer/deserializer (reads entire stream into a Buffer)
41
- */
42
- declare class BufferTransport implements Transport<Buffer> {
43
- readonly contentType = "application/octet-stream";
44
- serialize(value: Buffer): Buffer;
45
- deserialize(stream: ReadableStream<Uint8Array>): Promise<Buffer>;
46
- }
47
- /**
48
- * Stream serializer/deserializer (pass-through for streaming binary data)
49
- * This is ideal for large payloads that don't need to be buffered in memory
50
- *
51
- * IMPORTANT: When using StreamTransport, you must call finalize(payload) when done
52
- * processing the message to prevent resource leaks, especially in error cases.
53
- * ConsumerGroup handles this automatically, but direct Client usage requires manual cleanup.
54
- *
55
- * Example usage:
56
- * ```typescript
57
- * const transport = new StreamTransport();
58
- * try {
59
- * for await (const message of client.receiveMessages(options, transport)) {
60
- * // Process the stream...
61
- * const reader = message.payload.getReader();
62
- * // ... handle stream data
63
- * }
64
- * } catch (error) {
65
- * // Cleanup is handled automatically by ConsumerGroup
66
- * // or manually: await transport.finalize(message.payload);
67
- * }
68
- */
69
- declare class StreamTransport implements Transport<ReadableStream<Uint8Array>> {
70
- readonly contentType = "application/octet-stream";
71
- serialize(value: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
72
- deserialize(stream: ReadableStream<Uint8Array>): Promise<ReadableStream<Uint8Array>>;
73
- finalize(payload: ReadableStream<Uint8Array>): Promise<void>;
74
- }
75
-
76
- /**
77
- * Vercel Queue Service client types
78
- */
79
-
80
- interface QueueClientOptions {
81
- /**
82
- * Base URL for the Vercel Queue Service API
83
- * Can also be set via VERCEL_QUEUE_BASE_URL environment variable
84
- * @default "https://vercel-queue.com"
85
- */
86
- baseUrl?: string;
87
- /**
88
- * Base path for API endpoints
89
- * Can also be set via VERCEL_QUEUE_BASE_PATH environment variable
90
- * @default "/api/v2/messages"
91
- */
92
- basePath?: string;
93
- /**
94
- * Authentication token for the Vercel Queue Service API
95
- * If not provided, the client will attempt to get a token via OIDC
96
- */
97
- token?: string;
98
- /**
99
- * Custom headers to include in all requests
100
- */
101
- headers?: Record<string, string>;
102
- }
103
- /**
104
- * Shared options for publishing messages
105
- */
106
- interface PublishOptions {
107
- /**
108
- * Unique key to prevent duplicate message submissions
109
- * @default random UUID
110
- */
111
- idempotencyKey?: string;
112
- /**
113
- * Message retention time in seconds
114
- * @default 86400 (24 hours)
115
- * @min 60
116
- * @max 86400
117
- */
118
- retentionSeconds?: number;
119
- /**
120
- * Explicit deployment identifier to include in the `Vqs-Deployment-Id` header
121
- * If provided, this takes precedence over any value from the environment
122
- */
123
- deploymentId?: string;
124
- }
125
- interface SendMessageOptions<T = unknown> extends PublishOptions {
126
- /**
127
- * The queue name to send the message to
128
- */
129
- queueName: string;
130
- /**
131
- * The message payload
132
- */
133
- payload: T;
134
- }
135
- interface SendMessageResponse {
136
- /**
137
- * The generated message ID
138
- */
139
- messageId: string;
140
- }
141
- interface Message<T = unknown> {
142
- /**
143
- * The message ID
144
- */
145
- messageId: string;
146
- /**
147
- * The deserialized message payload
148
- * Note: If using streaming transports, ensure proper cleanup by calling transport.finalize(payload)
149
- * when done processing, especially in error cases
150
- */
151
- payload: T;
152
- /**
153
- * Number of times this message has been delivered
154
- */
155
- deliveryCount: number;
156
- /**
157
- * When the message was created
158
- */
159
- createdAt: Date;
160
- /**
161
- * MIME type of the message content
162
- */
163
- contentType: string;
164
- /**
165
- * Unique ticket for this message delivery (required for delete/patch operations)
166
- */
167
- ticket: string;
168
- }
169
- interface ReceiveMessagesOptions<T = unknown> {
170
- /**
171
- * The queue name to receive messages from
172
- */
173
- queueName: string;
174
- /**
175
- * Consumer group name
176
- */
177
- consumerGroup: string;
178
- /**
179
- * Time in seconds that messages will be invisible to other consumers
180
- * @default 900 (15 minutes)
181
- */
182
- visibilityTimeoutSeconds?: number;
183
- /**
184
- * Maximum number of messages to retrieve
185
- * @default 10
186
- * @max 10
187
- */
188
- limit?: number;
189
- }
190
- interface DeleteMessageOptions {
191
- /**
192
- * The queue name the message belongs to
193
- */
194
- queueName: string;
195
- /**
196
- * Consumer group name
197
- */
198
- consumerGroup: string;
199
- /**
200
- * The message ID to delete
201
- */
202
- messageId: string;
203
- /**
204
- * Ticket received from the message
205
- */
206
- ticket: string;
207
- }
208
- interface DeleteMessageResponse {
209
- /**
210
- * Whether the message was successfully deleted
211
- */
212
- deleted: boolean;
213
- }
214
- interface ChangeVisibilityOptions {
215
- /**
216
- * The queue name the message belongs to
217
- */
218
- queueName: string;
219
- /**
220
- * Consumer group name
221
- */
222
- consumerGroup: string;
223
- /**
224
- * The message ID to update
225
- */
226
- messageId: string;
227
- /**
228
- * Ticket received from the message
229
- */
230
- ticket: string;
231
- /**
232
- * New visibility timeout in seconds
233
- */
234
- visibilityTimeoutSeconds: number;
235
- }
236
- interface ChangeVisibilityResponse {
237
- /**
238
- * Whether the visibility was successfully updated
239
- */
240
- updated: boolean;
241
- }
242
- /**
243
- * Result indicating the message should be timed out for retry later
244
- */
245
- interface MessageTimeoutResult {
246
- /**
247
- * Time in seconds before the message becomes visible again
248
- */
249
- timeoutSeconds: number;
250
- }
251
- /**
252
- * Result returned by message handlers
253
- */
254
- type MessageHandlerResult = void | MessageTimeoutResult;
255
- /**
256
- * Message metadata provided to handlers
257
- */
258
- interface MessageMetadata {
259
- messageId: string;
260
- deliveryCount: number;
261
- createdAt: Date;
262
- topicName: string;
263
- consumerGroup: string;
264
- }
265
- /**
266
- * Message handler function type
267
- */
268
- type MessageHandler<T = unknown> = (message: T, metadata: MessageMetadata) => Promise<MessageHandlerResult> | MessageHandlerResult;
269
- /**
270
- * Options for creating a ConsumerGroup instance
271
- */
272
- interface ConsumerGroupOptions<T = unknown> {
273
- /**
274
- * Serializer/deserializer for the payload
275
- * @default JsonTransport instance
276
- */
277
- transport?: Transport<T>;
278
- /**
279
- * Time in seconds that messages will be invisible to other consumers
280
- * @default 30
281
- */
282
- visibilityTimeoutSeconds?: number;
283
- /**
284
- * How often to refresh the visibility timeout during processing (in seconds)
285
- * @default 10
286
- */
287
- refreshInterval?: number;
288
- }
289
- interface ReceiveMessageByIdOptions<T = unknown> {
290
- /**
291
- * The queue name to receive the message from
292
- */
293
- queueName: string;
294
- /**
295
- * Consumer group name
296
- */
297
- consumerGroup: string;
298
- /**
299
- * The message ID to retrieve
300
- */
301
- messageId: string;
302
- /**
303
- * Time in seconds that the message will be invisible to other consumers
304
- * @default 900 (15 minutes)
305
- */
306
- visibilityTimeoutSeconds?: number;
307
- /**
308
- * Skip payload content and only return message metadata
309
- * When true, the server returns a 204 status with headers containing message metadata
310
- * @default false
311
- */
312
- skipPayload?: boolean;
313
- }
314
- interface ReceiveMessageByIdResponse<T = unknown, TSkipPayload extends boolean = false> {
315
- message: TSkipPayload extends true ? Message<void> : Message<T>;
316
- }
317
- /**
318
- * Error thrown when a message is not found (404)
319
- */
320
- declare class MessageNotFoundError extends Error {
321
- constructor(messageId: string);
322
- }
323
- /**
324
- * Error thrown when a message is not available for processing (409)
325
- * This can happen when the message is in the wrong state, already claimed, etc.
326
- */
327
- declare class MessageNotAvailableError extends Error {
328
- constructor(messageId: string, reason?: string);
329
- }
330
- /**
331
- * Error thrown when message data is corrupted or can't be parsed
332
- */
333
- declare class MessageCorruptedError extends Error {
334
- constructor(messageId: string, reason: string);
335
- }
336
- /**
337
- * Error thrown when there are no messages available in the queue (204)
338
- */
339
- declare class QueueEmptyError extends Error {
340
- constructor(queueName: string, consumerGroup: string);
341
- }
342
- /**
343
- * Error thrown when a message is temporarily locked (423)
344
- */
345
- declare class MessageLockedError extends Error {
346
- readonly retryAfter?: number;
347
- constructor(messageId: string, retryAfter?: number);
348
- }
349
- /**
350
- * Error thrown when authentication fails (401)
351
- */
352
- declare class UnauthorizedError extends Error {
353
- constructor(message?: string);
354
- }
355
- /**
356
- * Error thrown when access is forbidden (403)
357
- */
358
- declare class ForbiddenError extends Error {
359
- constructor(message?: string);
360
- }
361
- /**
362
- * Error thrown for bad requests (400)
363
- */
364
- declare class BadRequestError extends Error {
365
- constructor(message: string);
366
- }
367
- /**
368
- * Error thrown for internal server errors (500)
369
- */
370
- declare class InternalServerError extends Error {
371
- constructor(message?: string);
372
- }
373
- /**
374
- * Error thrown when batch limit parameter is invalid
375
- */
376
- declare class InvalidLimitError extends Error {
377
- constructor(limit: number, min?: number, max?: number);
378
- }
379
-
380
- export { BufferTransport as B, type ChangeVisibilityOptions as C, type DeleteMessageOptions as D, ForbiddenError as F, InternalServerError as I, JsonTransport as J, type Message as M, type PublishOptions as P, type QueueClientOptions as Q, type ReceiveMessagesOptions as R, type SendMessageOptions as S, type Transport as T, UnauthorizedError as U, type SendMessageResponse as a, type ReceiveMessageByIdOptions as b, type ReceiveMessageByIdResponse as c, type DeleteMessageResponse as d, type ChangeVisibilityResponse as e, type MessageHandler as f, type ConsumerGroupOptions as g, StreamTransport as h, BadRequestError as i, InvalidLimitError as j, MessageCorruptedError as k, MessageLockedError as l, MessageNotAvailableError as m, MessageNotFoundError as n, QueueEmptyError as o, type MessageHandlerResult as p, type MessageMetadata as q, type MessageTimeoutResult as r };