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

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.
@@ -87,7 +87,7 @@ interface QueueClientOptions {
87
87
  /**
88
88
  * Base path for API endpoints
89
89
  * Can also be set via VERCEL_QUEUE_BASE_PATH environment variable
90
- * @default "/api/v2/messages"
90
+ * @default "/api/v3/topic"
91
91
  */
92
92
  basePath?: string;
93
93
  /**
@@ -99,6 +99,21 @@ interface QueueClientOptions {
99
99
  * Custom headers to include in all requests
100
100
  */
101
101
  headers?: Record<string, string>;
102
+ /**
103
+ * Default deployment ID to include in requests
104
+ * If not provided, defaults to VERCEL_DEPLOYMENT_ID environment variable
105
+ */
106
+ deploymentId?: string;
107
+ /**
108
+ * Whether to pin messages to the current deployment when publishing.
109
+ * When true, sends VERCEL_DEPLOYMENT_ID from environment, ensuring
110
+ * messages are routed to consumers on the same deployment.
111
+ * Only affects send/publish operations - consume operations always
112
+ * send deployment ID in production to identify the processing deployment.
113
+ * Ignored in development mode (deployment ID is never sent locally).
114
+ * @default true
115
+ */
116
+ pinToDeployment?: boolean;
102
117
  }
103
118
  /**
104
119
  * Shared options for publishing messages
@@ -117,10 +132,11 @@ interface PublishOptions {
117
132
  */
118
133
  retentionSeconds?: number;
119
134
  /**
120
- * Explicit deployment identifier to include in the `Vqs-Deployment-Id` header
121
- * If provided, this takes precedence over any value from the environment
135
+ * Delay delivery of the message by this many seconds
136
+ * @min 0
137
+ * @max retentionSeconds
122
138
  */
123
- deploymentId?: string;
139
+ delaySeconds?: number;
124
140
  }
125
141
  interface SendMessageOptions<T = unknown> extends PublishOptions {
126
142
  /**
@@ -162,9 +178,9 @@ interface Message<T = unknown> {
162
178
  */
163
179
  contentType: string;
164
180
  /**
165
- * Unique ticket for this message delivery (required for delete/patch operations)
181
+ * Receipt handle for this message delivery (required for delete/patch operations)
166
182
  */
167
- ticket: string;
183
+ receiptHandle: string;
168
184
  }
169
185
  interface ReceiveMessagesOptions<T = unknown> {
170
186
  /**
@@ -177,15 +193,19 @@ interface ReceiveMessagesOptions<T = unknown> {
177
193
  consumerGroup: string;
178
194
  /**
179
195
  * Time in seconds that messages will be invisible to other consumers
180
- * @default 900 (15 minutes)
196
+ * @default 30
181
197
  */
182
198
  visibilityTimeoutSeconds?: number;
183
199
  /**
184
200
  * Maximum number of messages to retrieve
185
- * @default 10
201
+ * @default 1
186
202
  * @max 10
187
203
  */
188
204
  limit?: number;
205
+ /**
206
+ * Maximum concurrent in-flight messages
207
+ */
208
+ maxConcurrency?: number;
189
209
  }
190
210
  interface DeleteMessageOptions {
191
211
  /**
@@ -197,13 +217,9 @@ interface DeleteMessageOptions {
197
217
  */
198
218
  consumerGroup: string;
199
219
  /**
200
- * The message ID to delete
220
+ * Receipt handle received from the message
201
221
  */
202
- messageId: string;
203
- /**
204
- * Ticket received from the message
205
- */
206
- ticket: string;
222
+ receiptHandle: string;
207
223
  }
208
224
  interface DeleteMessageResponse {
209
225
  /**
@@ -221,13 +237,9 @@ interface ChangeVisibilityOptions {
221
237
  */
222
238
  consumerGroup: string;
223
239
  /**
224
- * The message ID to update
225
- */
226
- messageId: string;
227
- /**
228
- * Ticket received from the message
240
+ * Receipt handle received from the message
229
241
  */
230
- ticket: string;
242
+ receiptHandle: string;
231
243
  /**
232
244
  * New visibility timeout in seconds
233
245
  */
@@ -237,21 +249,8 @@ interface ChangeVisibilityResponse {
237
249
  /**
238
250
  * Whether the visibility was successfully updated
239
251
  */
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;
252
+ success: boolean;
250
253
  }
251
- /**
252
- * Result returned by message handlers
253
- */
254
- type MessageHandlerResult = void | MessageTimeoutResult;
255
254
  /**
256
255
  * Message metadata provided to handlers
257
256
  */
@@ -265,7 +264,7 @@ interface MessageMetadata {
265
264
  /**
266
265
  * Message handler function type
267
266
  */
268
- type MessageHandler<T = unknown> = (message: T, metadata: MessageMetadata) => Promise<MessageHandlerResult> | MessageHandlerResult;
267
+ type MessageHandler<T = unknown> = (message: T, metadata: MessageMetadata) => Promise<void> | void;
269
268
  /**
270
269
  * Options for creating a ConsumerGroup instance
271
270
  */
@@ -301,18 +300,16 @@ interface ReceiveMessageByIdOptions<T = unknown> {
301
300
  messageId: string;
302
301
  /**
303
302
  * Time in seconds that the message will be invisible to other consumers
304
- * @default 900 (15 minutes)
303
+ * @default 30
305
304
  */
306
305
  visibilityTimeoutSeconds?: number;
307
306
  /**
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
307
+ * Maximum concurrent in-flight messages
311
308
  */
312
- skipPayload?: boolean;
309
+ maxConcurrency?: number;
313
310
  }
314
- interface ReceiveMessageByIdResponse<T = unknown, TSkipPayload extends boolean = false> {
315
- message: TSkipPayload extends true ? Message<void> : Message<T>;
311
+ interface ReceiveMessageByIdResponse<T = unknown> {
312
+ message: Message<T>;
316
313
  }
317
314
  /**
318
315
  * Error thrown when a message is not found (404)
@@ -376,5 +373,39 @@ declare class InternalServerError extends Error {
376
373
  declare class InvalidLimitError extends Error {
377
374
  constructor(limit: number, min?: number, max?: number);
378
375
  }
376
+ /**
377
+ * Error thrown when a message has already been processed (410)
378
+ */
379
+ declare class MessageAlreadyProcessedError extends Error {
380
+ constructor(messageId: string);
381
+ }
382
+ /**
383
+ * Error thrown when concurrency limit is exceeded (429)
384
+ */
385
+ declare class ConcurrencyLimitError extends Error {
386
+ readonly currentInflight?: number;
387
+ readonly maxConcurrency?: number;
388
+ constructor(message?: string, currentInflight?: number, maxConcurrency?: number);
389
+ }
390
+ /**
391
+ * Error thrown when a duplicate idempotency key is detected (409)
392
+ */
393
+ declare class DuplicateMessageError extends Error {
394
+ readonly idempotencyKey?: string;
395
+ constructor(message: string, idempotencyKey?: string);
396
+ }
397
+ /**
398
+ * Error thrown when consumer discovery fails (502)
399
+ */
400
+ declare class ConsumerDiscoveryError extends Error {
401
+ readonly deploymentId?: string;
402
+ constructor(message: string, deploymentId?: string);
403
+ }
404
+ /**
405
+ * Error thrown when consumer registry is not configured (503)
406
+ */
407
+ declare class ConsumerRegistryNotConfiguredError extends Error {
408
+ constructor(message?: string);
409
+ }
379
410
 
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 };
411
+ 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, ConcurrencyLimitError as j, ConsumerDiscoveryError as k, ConsumerRegistryNotConfiguredError as l, DuplicateMessageError as m, InvalidLimitError as n, MessageAlreadyProcessedError as o, MessageCorruptedError as p, MessageLockedError as q, MessageNotAvailableError as r, MessageNotFoundError as s, QueueEmptyError as t, type MessageMetadata as u };
@@ -87,7 +87,7 @@ interface QueueClientOptions {
87
87
  /**
88
88
  * Base path for API endpoints
89
89
  * Can also be set via VERCEL_QUEUE_BASE_PATH environment variable
90
- * @default "/api/v2/messages"
90
+ * @default "/api/v3/topic"
91
91
  */
92
92
  basePath?: string;
93
93
  /**
@@ -99,6 +99,21 @@ interface QueueClientOptions {
99
99
  * Custom headers to include in all requests
100
100
  */
101
101
  headers?: Record<string, string>;
102
+ /**
103
+ * Default deployment ID to include in requests
104
+ * If not provided, defaults to VERCEL_DEPLOYMENT_ID environment variable
105
+ */
106
+ deploymentId?: string;
107
+ /**
108
+ * Whether to pin messages to the current deployment when publishing.
109
+ * When true, sends VERCEL_DEPLOYMENT_ID from environment, ensuring
110
+ * messages are routed to consumers on the same deployment.
111
+ * Only affects send/publish operations - consume operations always
112
+ * send deployment ID in production to identify the processing deployment.
113
+ * Ignored in development mode (deployment ID is never sent locally).
114
+ * @default true
115
+ */
116
+ pinToDeployment?: boolean;
102
117
  }
103
118
  /**
104
119
  * Shared options for publishing messages
@@ -117,10 +132,11 @@ interface PublishOptions {
117
132
  */
118
133
  retentionSeconds?: number;
119
134
  /**
120
- * Explicit deployment identifier to include in the `Vqs-Deployment-Id` header
121
- * If provided, this takes precedence over any value from the environment
135
+ * Delay delivery of the message by this many seconds
136
+ * @min 0
137
+ * @max retentionSeconds
122
138
  */
123
- deploymentId?: string;
139
+ delaySeconds?: number;
124
140
  }
125
141
  interface SendMessageOptions<T = unknown> extends PublishOptions {
126
142
  /**
@@ -162,9 +178,9 @@ interface Message<T = unknown> {
162
178
  */
163
179
  contentType: string;
164
180
  /**
165
- * Unique ticket for this message delivery (required for delete/patch operations)
181
+ * Receipt handle for this message delivery (required for delete/patch operations)
166
182
  */
167
- ticket: string;
183
+ receiptHandle: string;
168
184
  }
169
185
  interface ReceiveMessagesOptions<T = unknown> {
170
186
  /**
@@ -177,15 +193,19 @@ interface ReceiveMessagesOptions<T = unknown> {
177
193
  consumerGroup: string;
178
194
  /**
179
195
  * Time in seconds that messages will be invisible to other consumers
180
- * @default 900 (15 minutes)
196
+ * @default 30
181
197
  */
182
198
  visibilityTimeoutSeconds?: number;
183
199
  /**
184
200
  * Maximum number of messages to retrieve
185
- * @default 10
201
+ * @default 1
186
202
  * @max 10
187
203
  */
188
204
  limit?: number;
205
+ /**
206
+ * Maximum concurrent in-flight messages
207
+ */
208
+ maxConcurrency?: number;
189
209
  }
190
210
  interface DeleteMessageOptions {
191
211
  /**
@@ -197,13 +217,9 @@ interface DeleteMessageOptions {
197
217
  */
198
218
  consumerGroup: string;
199
219
  /**
200
- * The message ID to delete
220
+ * Receipt handle received from the message
201
221
  */
202
- messageId: string;
203
- /**
204
- * Ticket received from the message
205
- */
206
- ticket: string;
222
+ receiptHandle: string;
207
223
  }
208
224
  interface DeleteMessageResponse {
209
225
  /**
@@ -221,13 +237,9 @@ interface ChangeVisibilityOptions {
221
237
  */
222
238
  consumerGroup: string;
223
239
  /**
224
- * The message ID to update
225
- */
226
- messageId: string;
227
- /**
228
- * Ticket received from the message
240
+ * Receipt handle received from the message
229
241
  */
230
- ticket: string;
242
+ receiptHandle: string;
231
243
  /**
232
244
  * New visibility timeout in seconds
233
245
  */
@@ -237,21 +249,8 @@ interface ChangeVisibilityResponse {
237
249
  /**
238
250
  * Whether the visibility was successfully updated
239
251
  */
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;
252
+ success: boolean;
250
253
  }
251
- /**
252
- * Result returned by message handlers
253
- */
254
- type MessageHandlerResult = void | MessageTimeoutResult;
255
254
  /**
256
255
  * Message metadata provided to handlers
257
256
  */
@@ -265,7 +264,7 @@ interface MessageMetadata {
265
264
  /**
266
265
  * Message handler function type
267
266
  */
268
- type MessageHandler<T = unknown> = (message: T, metadata: MessageMetadata) => Promise<MessageHandlerResult> | MessageHandlerResult;
267
+ type MessageHandler<T = unknown> = (message: T, metadata: MessageMetadata) => Promise<void> | void;
269
268
  /**
270
269
  * Options for creating a ConsumerGroup instance
271
270
  */
@@ -301,18 +300,16 @@ interface ReceiveMessageByIdOptions<T = unknown> {
301
300
  messageId: string;
302
301
  /**
303
302
  * Time in seconds that the message will be invisible to other consumers
304
- * @default 900 (15 minutes)
303
+ * @default 30
305
304
  */
306
305
  visibilityTimeoutSeconds?: number;
307
306
  /**
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
307
+ * Maximum concurrent in-flight messages
311
308
  */
312
- skipPayload?: boolean;
309
+ maxConcurrency?: number;
313
310
  }
314
- interface ReceiveMessageByIdResponse<T = unknown, TSkipPayload extends boolean = false> {
315
- message: TSkipPayload extends true ? Message<void> : Message<T>;
311
+ interface ReceiveMessageByIdResponse<T = unknown> {
312
+ message: Message<T>;
316
313
  }
317
314
  /**
318
315
  * Error thrown when a message is not found (404)
@@ -376,5 +373,39 @@ declare class InternalServerError extends Error {
376
373
  declare class InvalidLimitError extends Error {
377
374
  constructor(limit: number, min?: number, max?: number);
378
375
  }
376
+ /**
377
+ * Error thrown when a message has already been processed (410)
378
+ */
379
+ declare class MessageAlreadyProcessedError extends Error {
380
+ constructor(messageId: string);
381
+ }
382
+ /**
383
+ * Error thrown when concurrency limit is exceeded (429)
384
+ */
385
+ declare class ConcurrencyLimitError extends Error {
386
+ readonly currentInflight?: number;
387
+ readonly maxConcurrency?: number;
388
+ constructor(message?: string, currentInflight?: number, maxConcurrency?: number);
389
+ }
390
+ /**
391
+ * Error thrown when a duplicate idempotency key is detected (409)
392
+ */
393
+ declare class DuplicateMessageError extends Error {
394
+ readonly idempotencyKey?: string;
395
+ constructor(message: string, idempotencyKey?: string);
396
+ }
397
+ /**
398
+ * Error thrown when consumer discovery fails (502)
399
+ */
400
+ declare class ConsumerDiscoveryError extends Error {
401
+ readonly deploymentId?: string;
402
+ constructor(message: string, deploymentId?: string);
403
+ }
404
+ /**
405
+ * Error thrown when consumer registry is not configured (503)
406
+ */
407
+ declare class ConsumerRegistryNotConfiguredError extends Error {
408
+ constructor(message?: string);
409
+ }
379
410
 
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 };
411
+ 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, ConcurrencyLimitError as j, ConsumerDiscoveryError as k, ConsumerRegistryNotConfiguredError as l, DuplicateMessageError as m, InvalidLimitError as n, MessageAlreadyProcessedError as o, MessageCorruptedError as p, MessageLockedError as q, MessageNotAvailableError as r, MessageNotFoundError as s, QueueEmptyError as t, type MessageMetadata as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/queue",
3
- "version": "0.0.0-alpha.33",
3
+ "version": "0.0.0-alpha.34",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -8,9 +8,6 @@
8
8
  "main": "dist/index.js",
9
9
  "module": "dist/index.mjs",
10
10
  "types": "dist/index.d.ts",
11
- "bin": {
12
- "vercel-queue-local-init": "./bin/local-discover.js"
13
- },
14
11
  "exports": {
15
12
  ".": {
16
13
  "import": {
@@ -34,8 +31,7 @@
34
31
  }
35
32
  },
36
33
  "files": [
37
- "dist",
38
- "bin"
34
+ "dist"
39
35
  ],
40
36
  "engines": {
41
37
  "node": ">=20.0.0"
@@ -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();