@vercel/queue 0.0.0-alpha.15 → 0.0.0-alpha.17
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/README.md +52 -9
- package/bin/local-discover.js +179 -0
- package/dist/index.js +219 -201
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +219 -201
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -18,6 +18,11 @@ A TypeScript client library for interacting with the Vercel Queue Service API, d
|
|
|
18
18
|
npm install @vercel/queue
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
The package includes:
|
|
22
|
+
|
|
23
|
+
- **Main Library**: Queue client and utilities for production and development
|
|
24
|
+
- **CLI Tool**: `npx @vercel/queue/local-discover` for local development handler discovery
|
|
25
|
+
|
|
21
26
|
## Quick Start
|
|
22
27
|
|
|
23
28
|
For local development, you'll need to pull your Vercel environment variables:
|
|
@@ -30,15 +35,53 @@ npm i -g vercel
|
|
|
30
35
|
vc env pull
|
|
31
36
|
```
|
|
32
37
|
|
|
38
|
+
## Local Development
|
|
39
|
+
|
|
40
|
+
**Queues just work locally.** When you `send()` messages in development mode, they automatically trigger your handlers locally - no external queue infrastructure needed.
|
|
41
|
+
|
|
42
|
+
### Next.js Lazy Loading
|
|
43
|
+
|
|
44
|
+
For Next.js API routes (or others that are lazy-loaded), run this simple command to ensure handlers are discovered:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx @vercel/queue/local-discover
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
That's it! The script reads your `vercel.json`, finds your queue handlers, and triggers Next.js to load them.
|
|
51
|
+
|
|
52
|
+
### Example Workflow
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Start your dev server
|
|
56
|
+
npm run dev
|
|
57
|
+
|
|
58
|
+
# Discover handlers (only needed for Next.js lazy loading)
|
|
59
|
+
npx @vercel/queue/local-discover
|
|
60
|
+
|
|
61
|
+
# Send messages - they process locally automatically!
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### CLI Options
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Custom port
|
|
68
|
+
npx @vercel/queue/local-discover --port 3001
|
|
69
|
+
|
|
70
|
+
# Different config file
|
|
71
|
+
npx @vercel/queue/local-discover --config ./my-vercel.json
|
|
72
|
+
|
|
73
|
+
# Skip vercel.json, use defaults
|
|
74
|
+
npx @vercel/queue/local-discover --no-vercel-config
|
|
75
|
+
```
|
|
76
|
+
|
|
33
77
|
### TypeScript Configuration
|
|
34
78
|
|
|
35
79
|
Update your `tsconfig.json` to use `"bundler"` module resolution for proper package export resolution:
|
|
36
80
|
|
|
37
|
-
```
|
|
81
|
+
```json
|
|
38
82
|
{
|
|
39
83
|
"compilerOptions": {
|
|
40
84
|
"moduleResolution": "bundler"
|
|
41
|
-
// ... other options
|
|
42
85
|
}
|
|
43
86
|
}
|
|
44
87
|
```
|
|
@@ -140,7 +183,7 @@ While you can split handlers into separate routes if needed (e.g., for code orga
|
|
|
140
183
|
|
|
141
184
|
Configure which topics and consumers your API route handles:
|
|
142
185
|
|
|
143
|
-
```
|
|
186
|
+
```json
|
|
144
187
|
{
|
|
145
188
|
"functions": {
|
|
146
189
|
"app/api/queue/route.ts": {
|
|
@@ -149,9 +192,9 @@ Configure which topics and consumers your API route handles:
|
|
|
149
192
|
"type": "queue/v1beta",
|
|
150
193
|
"topic": "my-topic",
|
|
151
194
|
"consumer": "my-consumer",
|
|
152
|
-
"maxAttempts": 3,
|
|
153
|
-
"retryAfterSeconds": 60,
|
|
154
|
-
"initialDelaySeconds": 0
|
|
195
|
+
"maxAttempts": 3,
|
|
196
|
+
"retryAfterSeconds": 60,
|
|
197
|
+
"initialDelaySeconds": 0
|
|
155
198
|
},
|
|
156
199
|
{
|
|
157
200
|
"type": "queue/v1beta",
|
|
@@ -162,8 +205,8 @@ Configure which topics and consumers your API route handles:
|
|
|
162
205
|
"type": "queue/v1beta",
|
|
163
206
|
"topic": "order-events",
|
|
164
207
|
"consumer": "analytics",
|
|
165
|
-
"maxAttempts": 5,
|
|
166
|
-
"retryAfterSeconds": 300
|
|
208
|
+
"maxAttempts": 5,
|
|
209
|
+
"retryAfterSeconds": 300
|
|
167
210
|
}
|
|
168
211
|
]
|
|
169
212
|
}
|
|
@@ -305,7 +348,7 @@ interface MessageTimeoutResult {
|
|
|
305
348
|
|
|
306
349
|
If you need more than 1,000 messages per second, you can create multiple topics (e.g., user-specific or shard-based topics) and handle them with a single consumer using wildcards in your `vercel.json`:
|
|
307
350
|
|
|
308
|
-
```
|
|
351
|
+
```json
|
|
309
352
|
{
|
|
310
353
|
"functions": {
|
|
311
354
|
"app/api/queue/route.ts": {
|
|
@@ -0,0 +1,179 @@
|
|
|
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-discover - Discover queue handlers in development mode
|
|
14
|
+
|
|
15
|
+
USAGE:
|
|
16
|
+
npx @vercel/queue/local-discover [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
|
+
--no-vercel-config Skip reading vercel.json, use default routes
|
|
22
|
+
--help, -h Show this help message
|
|
23
|
+
|
|
24
|
+
EXAMPLES:
|
|
25
|
+
npx @vercel/queue/local-discover
|
|
26
|
+
npx @vercel/queue/local-discover --port 3001
|
|
27
|
+
npx @vercel/queue/local-discover --config ./my-vercel.json
|
|
28
|
+
npx @vercel/queue/local-discover --no-vercel-config
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read vercel.json and extract function endpoints with queue triggers
|
|
34
|
+
*/
|
|
35
|
+
function readVercelConfig(configPath = "./vercel.json") {
|
|
36
|
+
try {
|
|
37
|
+
const fullPath = path.resolve(configPath);
|
|
38
|
+
const configContent = fs.readFileSync(fullPath, "utf-8");
|
|
39
|
+
const config = JSON.parse(configContent);
|
|
40
|
+
|
|
41
|
+
const routes = [];
|
|
42
|
+
|
|
43
|
+
// Extract routes from functions with queue triggers
|
|
44
|
+
if (config.functions) {
|
|
45
|
+
for (const [functionPath, functionConfig] of Object.entries(
|
|
46
|
+
config.functions,
|
|
47
|
+
)) {
|
|
48
|
+
if (
|
|
49
|
+
functionConfig &&
|
|
50
|
+
typeof functionConfig === "object" &&
|
|
51
|
+
"experimentalTriggers" in functionConfig
|
|
52
|
+
) {
|
|
53
|
+
const triggers = functionConfig.experimentalTriggers;
|
|
54
|
+
if (Array.isArray(triggers)) {
|
|
55
|
+
// Check if any trigger is a queue trigger
|
|
56
|
+
const hasQueueTrigger = triggers.some(
|
|
57
|
+
(trigger) =>
|
|
58
|
+
trigger &&
|
|
59
|
+
typeof trigger === "object" &&
|
|
60
|
+
trigger.type === "queue/v1beta",
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (hasQueueTrigger) {
|
|
64
|
+
// Convert file path to API route
|
|
65
|
+
// app/api/vm/queue/route.ts -> /api/vm/queue
|
|
66
|
+
// pages/api/queue.ts -> /api/queue
|
|
67
|
+
let apiPath = functionPath;
|
|
68
|
+
|
|
69
|
+
if (apiPath.startsWith("app/api/")) {
|
|
70
|
+
apiPath = apiPath.replace("app/api/", "/api/");
|
|
71
|
+
} else if (apiPath.startsWith("pages/api/")) {
|
|
72
|
+
apiPath = apiPath.replace("pages/api/", "/api/");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Remove file extensions and /route suffix
|
|
76
|
+
apiPath = apiPath.replace(
|
|
77
|
+
/\/(route|index)\.(ts|js|tsx|jsx)$/,
|
|
78
|
+
"",
|
|
79
|
+
);
|
|
80
|
+
apiPath = apiPath.replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
81
|
+
|
|
82
|
+
routes.push(apiPath);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return routes;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// vercel.json might not exist or be malformed
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Make OPTIONS requests to trigger Next.js lazy loading
|
|
98
|
+
*/
|
|
99
|
+
async function discoverQueueHandlers(options = {}) {
|
|
100
|
+
if (process.env.NODE_ENV !== "development") {
|
|
101
|
+
console.log("[Dev Mode] Not in development mode, skipping discovery");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const baseUrl = options.baseUrl || "http://localhost:3000";
|
|
106
|
+
const fromVercelConfig = options.fromVercelConfig ?? true;
|
|
107
|
+
const configPath = options.configPath || "./vercel.json";
|
|
108
|
+
|
|
109
|
+
let routes = options.routes || [];
|
|
110
|
+
|
|
111
|
+
// Auto-discover from vercel.json if enabled
|
|
112
|
+
if (fromVercelConfig && routes.length === 0) {
|
|
113
|
+
const configRoutes = readVercelConfig(configPath);
|
|
114
|
+
if (configRoutes.length > 0) {
|
|
115
|
+
routes = configRoutes;
|
|
116
|
+
console.log(
|
|
117
|
+
`[Dev Mode] Found ${configRoutes.length} queue endpoints in vercel.json`,
|
|
118
|
+
);
|
|
119
|
+
} else {
|
|
120
|
+
// Fallback to common routes
|
|
121
|
+
routes = ["/api/queue/callback", "/api/queue", "/api/queues/callback"];
|
|
122
|
+
console.log("[Dev Mode] No vercel.json found, using default routes");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.log(
|
|
127
|
+
"[Dev Mode] Making OPTIONS requests to trigger module loading...",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
for (const route of routes) {
|
|
131
|
+
try {
|
|
132
|
+
// Make an OPTIONS request to trigger module loading without processing
|
|
133
|
+
const response = await fetch(`${baseUrl}${route}`, {
|
|
134
|
+
method: "OPTIONS",
|
|
135
|
+
// Add a header to identify this as a discovery request
|
|
136
|
+
headers: { "x-queue-discovery": "true" },
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Any response means the route exists and module was loaded
|
|
140
|
+
console.log(`[Dev Mode] ✓ ${route} (${response.status})`);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
// Ignore errors - route might not exist or server not running
|
|
143
|
+
console.log(`[Dev Mode] ✗ ${route} (server may not be running)`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log("[Dev Mode] Module loading complete.");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function main() {
|
|
151
|
+
const args = process.argv.slice(2);
|
|
152
|
+
const options = {};
|
|
153
|
+
|
|
154
|
+
// Parse simple command line arguments
|
|
155
|
+
for (let i = 0; i < args.length; i++) {
|
|
156
|
+
const arg = args[i];
|
|
157
|
+
if (arg === "--help" || arg === "-h") {
|
|
158
|
+
showHelp();
|
|
159
|
+
return;
|
|
160
|
+
} else if (arg === "--port" && args[i + 1]) {
|
|
161
|
+
options.baseUrl = `http://localhost:${args[i + 1]}`;
|
|
162
|
+
i++;
|
|
163
|
+
} else if (arg === "--config" && args[i + 1]) {
|
|
164
|
+
options.configPath = args[i + 1];
|
|
165
|
+
i++;
|
|
166
|
+
} else if (arg === "--no-vercel-config") {
|
|
167
|
+
options.fromVercelConfig = false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
await discoverQueueHandlers(options);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error("Failed to discover queue handlers:", error.message);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
main();
|