@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.
@@ -100,6 +100,9 @@ var InvalidLimitError = class extends Error {
100
100
  };
101
101
 
102
102
  // src/client.ts
103
+ function isDebugEnabled() {
104
+ return process.env.VERCEL_QUEUE_DEBUG === "1" || process.env.VERCEL_QUEUE_DEBUG === "true";
105
+ }
103
106
  async function consumeStream(stream) {
104
107
  const reader = stream.getReader();
105
108
  try {
@@ -111,6 +114,31 @@ async function consumeStream(stream) {
111
114
  reader.releaseLock();
112
115
  }
113
116
  }
117
+ function parseRetryAfter(headers) {
118
+ const retryAfterHeader = headers.get("Retry-After");
119
+ if (retryAfterHeader) {
120
+ const parsed = parseInt(retryAfterHeader, 10);
121
+ return Number.isNaN(parsed) ? void 0 : parsed;
122
+ }
123
+ return void 0;
124
+ }
125
+ function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters") {
126
+ if (status === 400) {
127
+ throw new BadRequestError(errorText || badRequestDefault);
128
+ }
129
+ if (status === 401) {
130
+ throw new UnauthorizedError(errorText || void 0);
131
+ }
132
+ if (status === 403) {
133
+ throw new ForbiddenError(errorText || void 0);
134
+ }
135
+ if (status >= 500) {
136
+ throw new InternalServerError(
137
+ errorText || `Server error: ${status} ${statusText}`
138
+ );
139
+ }
140
+ throw new Error(`Failed to ${operation}: ${status} ${statusText}`);
141
+ }
114
142
  function parseQueueHeaders(headers) {
115
143
  const messageId = headers.get("Vqs-Message-Id");
116
144
  const deliveryCountStr = headers.get("Vqs-Delivery-Count") || "0";
@@ -121,7 +149,7 @@ function parseQueueHeaders(headers) {
121
149
  return null;
122
150
  }
123
151
  const deliveryCount = parseInt(deliveryCountStr, 10);
124
- if (isNaN(deliveryCount)) {
152
+ if (Number.isNaN(deliveryCount)) {
125
153
  return null;
126
154
  }
127
155
  return {
@@ -135,24 +163,22 @@ function parseQueueHeaders(headers) {
135
163
  var QueueClient = class {
136
164
  baseUrl;
137
165
  basePath;
138
- customHeaders = {};
166
+ customHeaders;
167
+ providedToken;
139
168
  /**
140
169
  * Create a new Vercel Queue Service client
141
- * @param options Client configuration options
170
+ * @param options QueueClient configuration options
142
171
  */
143
172
  constructor(options = {}) {
144
173
  this.baseUrl = options.baseUrl || process.env.VERCEL_QUEUE_BASE_URL || "https://vercel-queue.com";
145
174
  this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v2/messages";
146
- const VERCEL_QUEUE_HEADER_PREFIX = "VERCEL_QUEUE_HEADER_";
147
- this.customHeaders = Object.fromEntries(
148
- Object.entries(process.env).filter(([key]) => key.startsWith(VERCEL_QUEUE_HEADER_PREFIX)).map(([key, value]) => [
149
- // This allows headers to use dashes independent of shell used
150
- key.replace(VERCEL_QUEUE_HEADER_PREFIX, "").replaceAll("__", "-"),
151
- value || ""
152
- ])
153
- );
175
+ this.customHeaders = options.headers || {};
176
+ this.providedToken = options.token;
154
177
  }
155
178
  async getToken() {
179
+ if (this.providedToken) {
180
+ return this.providedToken;
181
+ }
156
182
  const token = await (0, import_oidc.getVercelOidcToken)();
157
183
  if (!token) {
158
184
  throw new Error(
@@ -161,6 +187,45 @@ var QueueClient = class {
161
187
  }
162
188
  return token;
163
189
  }
190
+ /**
191
+ * Internal fetch wrapper that automatically handles debug logging
192
+ * when VERCEL_QUEUE_DEBUG is enabled
193
+ */
194
+ async fetch(url, init) {
195
+ const method = init.method || "GET";
196
+ if (isDebugEnabled()) {
197
+ const logData = {
198
+ method,
199
+ url,
200
+ headers: init.headers
201
+ };
202
+ const body = init.body;
203
+ if (body !== void 0 && body !== null) {
204
+ if (body instanceof ArrayBuffer) {
205
+ logData.bodySize = body.byteLength;
206
+ } else if (body instanceof Uint8Array) {
207
+ logData.bodySize = body.byteLength;
208
+ } else if (typeof body === "string") {
209
+ logData.bodySize = body.length;
210
+ } else {
211
+ logData.bodyType = typeof body;
212
+ }
213
+ }
214
+ console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
215
+ }
216
+ const response = await fetch(url, init);
217
+ if (isDebugEnabled()) {
218
+ const logData = {
219
+ method,
220
+ url,
221
+ status: response.status,
222
+ statusText: response.statusText,
223
+ headers: response.headers
224
+ };
225
+ console.debug("[VQS Debug] Response:", JSON.stringify(logData, null, 2));
226
+ }
227
+ return response;
228
+ }
164
229
  /**
165
230
  * Send a message to a queue
166
231
  * @param options Send message options
@@ -190,32 +255,21 @@ var QueueClient = class {
190
255
  headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
191
256
  }
192
257
  const body = transport.serialize(payload);
193
- const response = await fetch(`${this.baseUrl}${this.basePath}`, {
258
+ const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
194
259
  method: "POST",
195
260
  body,
196
261
  headers
197
262
  });
198
263
  if (!response.ok) {
199
- if (response.status === 400) {
200
- const errorText = await response.text();
201
- throw new BadRequestError(errorText || "Invalid parameters");
202
- }
203
- if (response.status === 401) {
204
- throw new UnauthorizedError();
205
- }
206
- if (response.status === 403) {
207
- throw new ForbiddenError();
208
- }
264
+ const errorText = await response.text();
209
265
  if (response.status === 409) {
210
266
  throw new Error("Duplicate idempotency key detected");
211
267
  }
212
- if (response.status >= 500) {
213
- throw new InternalServerError(
214
- `Server error: ${response.status} ${response.statusText}`
215
- );
216
- }
217
- throw new Error(
218
- `Failed to send message: ${response.status} ${response.statusText}`
268
+ throwCommonHttpError(
269
+ response.status,
270
+ response.statusText,
271
+ errorText,
272
+ "send message"
219
273
  );
220
274
  }
221
275
  const responseData = await response.json();
@@ -255,7 +309,7 @@ var QueueClient = class {
255
309
  if (limit !== void 0) {
256
310
  headers.set("Vqs-Limit", limit.toString());
257
311
  }
258
- const response = await fetch(`${this.baseUrl}${this.basePath}`, {
312
+ const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
259
313
  method: "GET",
260
314
  headers
261
315
  });
@@ -263,32 +317,18 @@ var QueueClient = class {
263
317
  throw new QueueEmptyError(queueName, consumerGroup);
264
318
  }
265
319
  if (!response.ok) {
266
- if (response.status === 400) {
267
- const errorText = await response.text();
268
- throw new BadRequestError(errorText || "Invalid parameters");
269
- }
270
- if (response.status === 401) {
271
- throw new UnauthorizedError();
272
- }
273
- if (response.status === 403) {
274
- throw new ForbiddenError();
275
- }
320
+ const errorText = await response.text();
276
321
  if (response.status === 423) {
277
- const retryAfterHeader = response.headers.get("Retry-After");
278
- let retryAfter;
279
- if (retryAfterHeader) {
280
- const parsed = parseInt(retryAfterHeader, 10);
281
- retryAfter = isNaN(parsed) ? void 0 : parsed;
282
- }
283
- throw new MessageLockedError("next message", retryAfter);
284
- }
285
- if (response.status >= 500) {
286
- throw new InternalServerError(
287
- `Server error: ${response.status} ${response.statusText}`
322
+ throw new MessageLockedError(
323
+ "next message",
324
+ parseRetryAfter(response.headers)
288
325
  );
289
326
  }
290
- throw new Error(
291
- `Failed to receive messages: ${response.status} ${response.statusText}`
327
+ throwCommonHttpError(
328
+ response.status,
329
+ response.statusText,
330
+ errorText,
331
+ "receive messages"
292
332
  );
293
333
  }
294
334
  for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
@@ -337,7 +377,7 @@ var QueueClient = class {
337
377
  if (skipPayload) {
338
378
  headers.set("Vqs-Skip-Payload", "1");
339
379
  }
340
- const response = await fetch(
380
+ const response = await this.fetch(
341
381
  `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
342
382
  {
343
383
  method: "GET",
@@ -345,38 +385,24 @@ var QueueClient = class {
345
385
  }
346
386
  );
347
387
  if (!response.ok) {
348
- if (response.status === 400) {
349
- const errorText = await response.text();
350
- throw new BadRequestError(errorText || "Invalid parameters");
351
- }
352
- if (response.status === 401) {
353
- throw new UnauthorizedError();
354
- }
355
- if (response.status === 403) {
356
- throw new ForbiddenError();
357
- }
388
+ const errorText = await response.text();
358
389
  if (response.status === 404) {
359
390
  throw new MessageNotFoundError(messageId);
360
391
  }
361
- if (response.status === 423) {
362
- const retryAfterHeader = response.headers.get("Retry-After");
363
- let retryAfter;
364
- if (retryAfterHeader) {
365
- const parsed = parseInt(retryAfterHeader, 10);
366
- retryAfter = isNaN(parsed) ? void 0 : parsed;
367
- }
368
- throw new MessageLockedError(messageId, retryAfter);
369
- }
370
392
  if (response.status === 409) {
371
393
  throw new MessageNotAvailableError(messageId);
372
394
  }
373
- if (response.status >= 500) {
374
- throw new InternalServerError(
375
- `Server error: ${response.status} ${response.statusText}`
395
+ if (response.status === 423) {
396
+ throw new MessageLockedError(
397
+ messageId,
398
+ parseRetryAfter(response.headers)
376
399
  );
377
400
  }
378
- throw new Error(
379
- `Failed to receive message by ID: ${response.status} ${response.statusText}`
401
+ throwCommonHttpError(
402
+ response.status,
403
+ response.statusText,
404
+ errorText,
405
+ "receive message by ID"
380
406
  );
381
407
  }
382
408
  if (skipPayload && response.status === 204) {
@@ -446,7 +472,7 @@ var QueueClient = class {
446
472
  */
447
473
  async deleteMessage(options) {
448
474
  const { queueName, consumerGroup, messageId, ticket } = options;
449
- const response = await fetch(
475
+ const response = await this.fetch(
450
476
  `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
451
477
  {
452
478
  method: "DELETE",
@@ -460,31 +486,22 @@ var QueueClient = class {
460
486
  }
461
487
  );
462
488
  if (!response.ok) {
463
- if (response.status === 400) {
464
- throw new BadRequestError("Missing or invalid ticket");
465
- }
466
- if (response.status === 401) {
467
- throw new UnauthorizedError();
468
- }
469
- if (response.status === 403) {
470
- throw new ForbiddenError();
471
- }
489
+ const errorText = await response.text();
472
490
  if (response.status === 404) {
473
491
  throw new MessageNotFoundError(messageId);
474
492
  }
475
493
  if (response.status === 409) {
476
494
  throw new MessageNotAvailableError(
477
495
  messageId,
478
- "Invalid ticket, message not in correct state, or already processed"
496
+ errorText || "Invalid ticket, message not in correct state, or already processed"
479
497
  );
480
498
  }
481
- if (response.status >= 500) {
482
- throw new InternalServerError(
483
- `Server error: ${response.status} ${response.statusText}`
484
- );
485
- }
486
- throw new Error(
487
- `Failed to delete message: ${response.status} ${response.statusText}`
499
+ throwCommonHttpError(
500
+ response.status,
501
+ response.statusText,
502
+ errorText,
503
+ "delete message",
504
+ "Missing or invalid ticket"
488
505
  );
489
506
  }
490
507
  return { deleted: true };
@@ -508,7 +525,7 @@ var QueueClient = class {
508
525
  ticket,
509
526
  visibilityTimeoutSeconds
510
527
  } = options;
511
- const response = await fetch(
528
+ const response = await this.fetch(
512
529
  `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
513
530
  {
514
531
  method: "PATCH",
@@ -523,33 +540,22 @@ var QueueClient = class {
523
540
  }
524
541
  );
525
542
  if (!response.ok) {
526
- if (response.status === 400) {
527
- throw new BadRequestError(
528
- "Missing ticket or invalid visibility timeout"
529
- );
530
- }
531
- if (response.status === 401) {
532
- throw new UnauthorizedError();
533
- }
534
- if (response.status === 403) {
535
- throw new ForbiddenError();
536
- }
543
+ const errorText = await response.text();
537
544
  if (response.status === 404) {
538
545
  throw new MessageNotFoundError(messageId);
539
546
  }
540
547
  if (response.status === 409) {
541
548
  throw new MessageNotAvailableError(
542
549
  messageId,
543
- "Invalid ticket, message not in correct state, or already processed"
550
+ errorText || "Invalid ticket, message not in correct state, or already processed"
544
551
  );
545
552
  }
546
- if (response.status >= 500) {
547
- throw new InternalServerError(
548
- `Server error: ${response.status} ${response.statusText}`
549
- );
550
- }
551
- throw new Error(
552
- `Failed to change visibility: ${response.status} ${response.statusText}`
553
+ throwCommonHttpError(
554
+ response.status,
555
+ response.statusText,
556
+ errorText,
557
+ "change visibility",
558
+ "Missing ticket or invalid visibility timeout"
553
559
  );
554
560
  }
555
561
  return { updated: true };
@@ -591,8 +597,18 @@ var JsonTransport = class {
591
597
  };
592
598
 
593
599
  // src/dev.ts
594
- var devRouteHandlers = /* @__PURE__ */ new Map();
595
- var wildcardRouteHandlers = /* @__PURE__ */ new Map();
600
+ var GLOBAL_KEY = Symbol.for("@vercel/queue.devHandlers");
601
+ function getDevHandlerState() {
602
+ const g = globalThis;
603
+ if (!g[GLOBAL_KEY]) {
604
+ g[GLOBAL_KEY] = {
605
+ devRouteHandlers: /* @__PURE__ */ new Map(),
606
+ wildcardRouteHandlers: /* @__PURE__ */ new Map()
607
+ };
608
+ }
609
+ return g[GLOBAL_KEY];
610
+ }
611
+ var { devRouteHandlers, wildcardRouteHandlers } = getDevHandlerState();
596
612
  function cleanupDeadRefs(key, refs) {
597
613
  const aliveRefs = refs.filter((ref) => ref.deref() !== void 0);
598
614
  if (aliveRefs.length === 0) {
@@ -1113,7 +1129,7 @@ async function parseCallback(request) {
1113
1129
  messageId
1114
1130
  };
1115
1131
  }
1116
- function handleCallback(handlers) {
1132
+ function createCallbackHandler(handlers, client) {
1117
1133
  for (const topicPattern in handlers) {
1118
1134
  if (topicPattern.includes("*")) {
1119
1135
  if (!validateWildcardPattern(topicPattern)) {
@@ -1148,7 +1164,6 @@ function handleCallback(handlers) {
1148
1164
  { status: 404 }
1149
1165
  );
1150
1166
  }
1151
- const client = new QueueClient();
1152
1167
  const topic = new Topic(client, queueName);
1153
1168
  const cg = topic.consumerGroup(consumerGroup);
1154
1169
  await cg.consume(consumerGroupHandler, { messageId });
@@ -1169,6 +1184,9 @@ function handleCallback(handlers) {
1169
1184
  }
1170
1185
  return routeHandler;
1171
1186
  }
1187
+ function handleCallback(handlers, client) {
1188
+ return createCallbackHandler(handlers, client || new QueueClient());
1189
+ }
1172
1190
 
1173
1191
  // src/nextjs-pages.ts
1174
1192
  function getHeader(headers, name) {