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

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.
@@ -74,6 +74,9 @@ var InvalidLimitError = class extends Error {
74
74
  };
75
75
 
76
76
  // src/client.ts
77
+ function isDebugEnabled() {
78
+ return process.env.VERCEL_QUEUE_DEBUG === "1" || process.env.VERCEL_QUEUE_DEBUG === "true";
79
+ }
77
80
  async function consumeStream(stream) {
78
81
  const reader = stream.getReader();
79
82
  try {
@@ -85,6 +88,31 @@ async function consumeStream(stream) {
85
88
  reader.releaseLock();
86
89
  }
87
90
  }
91
+ function parseRetryAfter(headers) {
92
+ const retryAfterHeader = headers.get("Retry-After");
93
+ if (retryAfterHeader) {
94
+ const parsed = parseInt(retryAfterHeader, 10);
95
+ return Number.isNaN(parsed) ? void 0 : parsed;
96
+ }
97
+ return void 0;
98
+ }
99
+ function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters") {
100
+ if (status === 400) {
101
+ throw new BadRequestError(errorText || badRequestDefault);
102
+ }
103
+ if (status === 401) {
104
+ throw new UnauthorizedError(errorText || void 0);
105
+ }
106
+ if (status === 403) {
107
+ throw new ForbiddenError(errorText || void 0);
108
+ }
109
+ if (status >= 500) {
110
+ throw new InternalServerError(
111
+ errorText || `Server error: ${status} ${statusText}`
112
+ );
113
+ }
114
+ throw new Error(`Failed to ${operation}: ${status} ${statusText}`);
115
+ }
88
116
  function parseQueueHeaders(headers) {
89
117
  const messageId = headers.get("Vqs-Message-Id");
90
118
  const deliveryCountStr = headers.get("Vqs-Delivery-Count") || "0";
@@ -95,7 +123,7 @@ function parseQueueHeaders(headers) {
95
123
  return null;
96
124
  }
97
125
  const deliveryCount = parseInt(deliveryCountStr, 10);
98
- if (isNaN(deliveryCount)) {
126
+ if (Number.isNaN(deliveryCount)) {
99
127
  return null;
100
128
  }
101
129
  return {
@@ -109,24 +137,22 @@ function parseQueueHeaders(headers) {
109
137
  var QueueClient = class {
110
138
  baseUrl;
111
139
  basePath;
112
- customHeaders = {};
140
+ customHeaders;
141
+ providedToken;
113
142
  /**
114
143
  * Create a new Vercel Queue Service client
115
- * @param options Client configuration options
144
+ * @param options QueueClient configuration options
116
145
  */
117
146
  constructor(options = {}) {
118
147
  this.baseUrl = options.baseUrl || process.env.VERCEL_QUEUE_BASE_URL || "https://vercel-queue.com";
119
148
  this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v2/messages";
120
- const VERCEL_QUEUE_HEADER_PREFIX = "VERCEL_QUEUE_HEADER_";
121
- this.customHeaders = Object.fromEntries(
122
- Object.entries(process.env).filter(([key]) => key.startsWith(VERCEL_QUEUE_HEADER_PREFIX)).map(([key, value]) => [
123
- // This allows headers to use dashes independent of shell used
124
- key.replace(VERCEL_QUEUE_HEADER_PREFIX, "").replaceAll("__", "-"),
125
- value || ""
126
- ])
127
- );
149
+ this.customHeaders = options.headers || {};
150
+ this.providedToken = options.token;
128
151
  }
129
152
  async getToken() {
153
+ if (this.providedToken) {
154
+ return this.providedToken;
155
+ }
130
156
  const token = await getVercelOidcToken();
131
157
  if (!token) {
132
158
  throw new Error(
@@ -135,6 +161,45 @@ var QueueClient = class {
135
161
  }
136
162
  return token;
137
163
  }
164
+ /**
165
+ * Internal fetch wrapper that automatically handles debug logging
166
+ * when VERCEL_QUEUE_DEBUG is enabled
167
+ */
168
+ async fetch(url, init) {
169
+ const method = init.method || "GET";
170
+ if (isDebugEnabled()) {
171
+ const logData = {
172
+ method,
173
+ url,
174
+ headers: init.headers
175
+ };
176
+ const body = init.body;
177
+ if (body !== void 0 && body !== null) {
178
+ if (body instanceof ArrayBuffer) {
179
+ logData.bodySize = body.byteLength;
180
+ } else if (body instanceof Uint8Array) {
181
+ logData.bodySize = body.byteLength;
182
+ } else if (typeof body === "string") {
183
+ logData.bodySize = body.length;
184
+ } else {
185
+ logData.bodyType = typeof body;
186
+ }
187
+ }
188
+ console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
189
+ }
190
+ const response = await fetch(url, init);
191
+ if (isDebugEnabled()) {
192
+ const logData = {
193
+ method,
194
+ url,
195
+ status: response.status,
196
+ statusText: response.statusText,
197
+ headers: response.headers
198
+ };
199
+ console.debug("[VQS Debug] Response:", JSON.stringify(logData, null, 2));
200
+ }
201
+ return response;
202
+ }
138
203
  /**
139
204
  * Send a message to a queue
140
205
  * @param options Send message options
@@ -164,32 +229,21 @@ var QueueClient = class {
164
229
  headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
165
230
  }
166
231
  const body = transport.serialize(payload);
167
- const response = await fetch(`${this.baseUrl}${this.basePath}`, {
232
+ const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
168
233
  method: "POST",
169
234
  body,
170
235
  headers
171
236
  });
172
237
  if (!response.ok) {
173
- if (response.status === 400) {
174
- const errorText = await response.text();
175
- throw new BadRequestError(errorText || "Invalid parameters");
176
- }
177
- if (response.status === 401) {
178
- throw new UnauthorizedError();
179
- }
180
- if (response.status === 403) {
181
- throw new ForbiddenError();
182
- }
238
+ const errorText = await response.text();
183
239
  if (response.status === 409) {
184
240
  throw new Error("Duplicate idempotency key detected");
185
241
  }
186
- if (response.status >= 500) {
187
- throw new InternalServerError(
188
- `Server error: ${response.status} ${response.statusText}`
189
- );
190
- }
191
- throw new Error(
192
- `Failed to send message: ${response.status} ${response.statusText}`
242
+ throwCommonHttpError(
243
+ response.status,
244
+ response.statusText,
245
+ errorText,
246
+ "send message"
193
247
  );
194
248
  }
195
249
  const responseData = await response.json();
@@ -229,7 +283,7 @@ var QueueClient = class {
229
283
  if (limit !== void 0) {
230
284
  headers.set("Vqs-Limit", limit.toString());
231
285
  }
232
- const response = await fetch(`${this.baseUrl}${this.basePath}`, {
286
+ const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
233
287
  method: "GET",
234
288
  headers
235
289
  });
@@ -237,32 +291,18 @@ var QueueClient = class {
237
291
  throw new QueueEmptyError(queueName, consumerGroup);
238
292
  }
239
293
  if (!response.ok) {
240
- if (response.status === 400) {
241
- const errorText = await response.text();
242
- throw new BadRequestError(errorText || "Invalid parameters");
243
- }
244
- if (response.status === 401) {
245
- throw new UnauthorizedError();
246
- }
247
- if (response.status === 403) {
248
- throw new ForbiddenError();
249
- }
294
+ const errorText = await response.text();
250
295
  if (response.status === 423) {
251
- const retryAfterHeader = response.headers.get("Retry-After");
252
- let retryAfter;
253
- if (retryAfterHeader) {
254
- const parsed = parseInt(retryAfterHeader, 10);
255
- retryAfter = isNaN(parsed) ? void 0 : parsed;
256
- }
257
- throw new MessageLockedError("next message", retryAfter);
258
- }
259
- if (response.status >= 500) {
260
- throw new InternalServerError(
261
- `Server error: ${response.status} ${response.statusText}`
296
+ throw new MessageLockedError(
297
+ "next message",
298
+ parseRetryAfter(response.headers)
262
299
  );
263
300
  }
264
- throw new Error(
265
- `Failed to receive messages: ${response.status} ${response.statusText}`
301
+ throwCommonHttpError(
302
+ response.status,
303
+ response.statusText,
304
+ errorText,
305
+ "receive messages"
266
306
  );
267
307
  }
268
308
  for await (const multipartMessage of parseMultipartStream(response)) {
@@ -311,7 +351,7 @@ var QueueClient = class {
311
351
  if (skipPayload) {
312
352
  headers.set("Vqs-Skip-Payload", "1");
313
353
  }
314
- const response = await fetch(
354
+ const response = await this.fetch(
315
355
  `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
316
356
  {
317
357
  method: "GET",
@@ -319,38 +359,24 @@ var QueueClient = class {
319
359
  }
320
360
  );
321
361
  if (!response.ok) {
322
- if (response.status === 400) {
323
- const errorText = await response.text();
324
- throw new BadRequestError(errorText || "Invalid parameters");
325
- }
326
- if (response.status === 401) {
327
- throw new UnauthorizedError();
328
- }
329
- if (response.status === 403) {
330
- throw new ForbiddenError();
331
- }
362
+ const errorText = await response.text();
332
363
  if (response.status === 404) {
333
364
  throw new MessageNotFoundError(messageId);
334
365
  }
335
- if (response.status === 423) {
336
- const retryAfterHeader = response.headers.get("Retry-After");
337
- let retryAfter;
338
- if (retryAfterHeader) {
339
- const parsed = parseInt(retryAfterHeader, 10);
340
- retryAfter = isNaN(parsed) ? void 0 : parsed;
341
- }
342
- throw new MessageLockedError(messageId, retryAfter);
343
- }
344
366
  if (response.status === 409) {
345
367
  throw new MessageNotAvailableError(messageId);
346
368
  }
347
- if (response.status >= 500) {
348
- throw new InternalServerError(
349
- `Server error: ${response.status} ${response.statusText}`
369
+ if (response.status === 423) {
370
+ throw new MessageLockedError(
371
+ messageId,
372
+ parseRetryAfter(response.headers)
350
373
  );
351
374
  }
352
- throw new Error(
353
- `Failed to receive message by ID: ${response.status} ${response.statusText}`
375
+ throwCommonHttpError(
376
+ response.status,
377
+ response.statusText,
378
+ errorText,
379
+ "receive message by ID"
354
380
  );
355
381
  }
356
382
  if (skipPayload && response.status === 204) {
@@ -420,7 +446,7 @@ var QueueClient = class {
420
446
  */
421
447
  async deleteMessage(options) {
422
448
  const { queueName, consumerGroup, messageId, ticket } = options;
423
- const response = await fetch(
449
+ const response = await this.fetch(
424
450
  `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
425
451
  {
426
452
  method: "DELETE",
@@ -434,31 +460,22 @@ var QueueClient = class {
434
460
  }
435
461
  );
436
462
  if (!response.ok) {
437
- if (response.status === 400) {
438
- throw new BadRequestError("Missing or invalid ticket");
439
- }
440
- if (response.status === 401) {
441
- throw new UnauthorizedError();
442
- }
443
- if (response.status === 403) {
444
- throw new ForbiddenError();
445
- }
463
+ const errorText = await response.text();
446
464
  if (response.status === 404) {
447
465
  throw new MessageNotFoundError(messageId);
448
466
  }
449
467
  if (response.status === 409) {
450
468
  throw new MessageNotAvailableError(
451
469
  messageId,
452
- "Invalid ticket, message not in correct state, or already processed"
470
+ errorText || "Invalid ticket, message not in correct state, or already processed"
453
471
  );
454
472
  }
455
- if (response.status >= 500) {
456
- throw new InternalServerError(
457
- `Server error: ${response.status} ${response.statusText}`
458
- );
459
- }
460
- throw new Error(
461
- `Failed to delete message: ${response.status} ${response.statusText}`
473
+ throwCommonHttpError(
474
+ response.status,
475
+ response.statusText,
476
+ errorText,
477
+ "delete message",
478
+ "Missing or invalid ticket"
462
479
  );
463
480
  }
464
481
  return { deleted: true };
@@ -482,7 +499,7 @@ var QueueClient = class {
482
499
  ticket,
483
500
  visibilityTimeoutSeconds
484
501
  } = options;
485
- const response = await fetch(
502
+ const response = await this.fetch(
486
503
  `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
487
504
  {
488
505
  method: "PATCH",
@@ -497,33 +514,22 @@ var QueueClient = class {
497
514
  }
498
515
  );
499
516
  if (!response.ok) {
500
- if (response.status === 400) {
501
- throw new BadRequestError(
502
- "Missing ticket or invalid visibility timeout"
503
- );
504
- }
505
- if (response.status === 401) {
506
- throw new UnauthorizedError();
507
- }
508
- if (response.status === 403) {
509
- throw new ForbiddenError();
510
- }
517
+ const errorText = await response.text();
511
518
  if (response.status === 404) {
512
519
  throw new MessageNotFoundError(messageId);
513
520
  }
514
521
  if (response.status === 409) {
515
522
  throw new MessageNotAvailableError(
516
523
  messageId,
517
- "Invalid ticket, message not in correct state, or already processed"
524
+ errorText || "Invalid ticket, message not in correct state, or already processed"
518
525
  );
519
526
  }
520
- if (response.status >= 500) {
521
- throw new InternalServerError(
522
- `Server error: ${response.status} ${response.statusText}`
523
- );
524
- }
525
- throw new Error(
526
- `Failed to change visibility: ${response.status} ${response.statusText}`
527
+ throwCommonHttpError(
528
+ response.status,
529
+ response.statusText,
530
+ errorText,
531
+ "change visibility",
532
+ "Missing ticket or invalid visibility timeout"
527
533
  );
528
534
  }
529
535
  return { updated: true };
@@ -565,8 +571,18 @@ var JsonTransport = class {
565
571
  };
566
572
 
567
573
  // src/dev.ts
568
- var devRouteHandlers = /* @__PURE__ */ new Map();
569
- var wildcardRouteHandlers = /* @__PURE__ */ new Map();
574
+ var GLOBAL_KEY = Symbol.for("@vercel/queue.devHandlers");
575
+ function getDevHandlerState() {
576
+ const g = globalThis;
577
+ if (!g[GLOBAL_KEY]) {
578
+ g[GLOBAL_KEY] = {
579
+ devRouteHandlers: /* @__PURE__ */ new Map(),
580
+ wildcardRouteHandlers: /* @__PURE__ */ new Map()
581
+ };
582
+ }
583
+ return g[GLOBAL_KEY];
584
+ }
585
+ var { devRouteHandlers, wildcardRouteHandlers } = getDevHandlerState();
570
586
  function cleanupDeadRefs(key, refs) {
571
587
  const aliveRefs = refs.filter((ref) => ref.deref() !== void 0);
572
588
  if (aliveRefs.length === 0) {
@@ -1087,7 +1103,7 @@ async function parseCallback(request) {
1087
1103
  messageId
1088
1104
  };
1089
1105
  }
1090
- function handleCallback(handlers) {
1106
+ function createCallbackHandler(handlers, client) {
1091
1107
  for (const topicPattern in handlers) {
1092
1108
  if (topicPattern.includes("*")) {
1093
1109
  if (!validateWildcardPattern(topicPattern)) {
@@ -1122,7 +1138,6 @@ function handleCallback(handlers) {
1122
1138
  { status: 404 }
1123
1139
  );
1124
1140
  }
1125
- const client = new QueueClient();
1126
1141
  const topic = new Topic(client, queueName);
1127
1142
  const cg = topic.consumerGroup(consumerGroup);
1128
1143
  await cg.consume(consumerGroupHandler, { messageId });
@@ -1143,6 +1158,9 @@ function handleCallback(handlers) {
1143
1158
  }
1144
1159
  return routeHandler;
1145
1160
  }
1161
+ function handleCallback(handlers, client) {
1162
+ return createCallbackHandler(handlers, client || new QueueClient());
1163
+ }
1146
1164
 
1147
1165
  // src/nextjs-pages.ts
1148
1166
  function getHeader(headers, name) {