@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.
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -23,10 +33,15 @@ __export(index_exports, {
23
33
  BadRequestError: () => BadRequestError,
24
34
  BufferTransport: () => BufferTransport,
25
35
  Client: () => Client,
36
+ ConcurrencyLimitError: () => ConcurrencyLimitError,
37
+ ConsumerDiscoveryError: () => ConsumerDiscoveryError,
38
+ ConsumerRegistryNotConfiguredError: () => ConsumerRegistryNotConfiguredError,
39
+ DuplicateMessageError: () => DuplicateMessageError,
26
40
  ForbiddenError: () => ForbiddenError,
27
41
  InternalServerError: () => InternalServerError,
28
42
  InvalidLimitError: () => InvalidLimitError,
29
43
  JsonTransport: () => JsonTransport,
44
+ MessageAlreadyProcessedError: () => MessageAlreadyProcessedError,
30
45
  MessageCorruptedError: () => MessageCorruptedError,
31
46
  MessageLockedError: () => MessageLockedError,
32
47
  MessageNotAvailableError: () => MessageNotAvailableError,
@@ -107,8 +122,9 @@ var StreamTransport = class {
107
122
  // src/client.ts
108
123
  var import_mixpart = require("mixpart");
109
124
 
110
- // src/oidc.ts
111
- var import_oidc = require("@vercel/oidc");
125
+ // src/dev.ts
126
+ var fs = __toESM(require("fs"));
127
+ var path = __toESM(require("path"));
112
128
 
113
129
  // src/types.ts
114
130
  var MessageNotFoundError = class extends Error {
@@ -178,6 +194,263 @@ var InvalidLimitError = class extends Error {
178
194
  this.name = "InvalidLimitError";
179
195
  }
180
196
  };
197
+ var MessageAlreadyProcessedError = class extends Error {
198
+ constructor(messageId) {
199
+ super(`Message ${messageId} has already been processed`);
200
+ this.name = "MessageAlreadyProcessedError";
201
+ }
202
+ };
203
+ var ConcurrencyLimitError = class extends Error {
204
+ currentInflight;
205
+ maxConcurrency;
206
+ constructor(message = "Concurrency limit exceeded", currentInflight, maxConcurrency) {
207
+ super(message);
208
+ this.name = "ConcurrencyLimitError";
209
+ this.currentInflight = currentInflight;
210
+ this.maxConcurrency = maxConcurrency;
211
+ }
212
+ };
213
+ var DuplicateMessageError = class extends Error {
214
+ idempotencyKey;
215
+ constructor(message, idempotencyKey) {
216
+ super(message);
217
+ this.name = "DuplicateMessageError";
218
+ this.idempotencyKey = idempotencyKey;
219
+ }
220
+ };
221
+ var ConsumerDiscoveryError = class extends Error {
222
+ deploymentId;
223
+ constructor(message, deploymentId) {
224
+ super(message);
225
+ this.name = "ConsumerDiscoveryError";
226
+ this.deploymentId = deploymentId;
227
+ }
228
+ };
229
+ var ConsumerRegistryNotConfiguredError = class extends Error {
230
+ constructor(message = "Consumer registry not configured") {
231
+ super(message);
232
+ this.name = "ConsumerRegistryNotConfiguredError";
233
+ }
234
+ };
235
+
236
+ // src/dev.ts
237
+ var ROUTE_MAPPINGS_KEY = Symbol.for("@vercel/queue.devRouteMappings");
238
+ function filePathToUrlPath(filePath) {
239
+ let urlPath = filePath.replace(/^app\//, "/").replace(/^pages\//, "/").replace(/\/route\.(ts|js|tsx|jsx)$/, "").replace(/\.(ts|js|tsx|jsx)$/, "");
240
+ if (!urlPath.startsWith("/")) {
241
+ urlPath = "/" + urlPath;
242
+ }
243
+ return urlPath;
244
+ }
245
+ function getDevRouteMappings() {
246
+ const g = globalThis;
247
+ if (ROUTE_MAPPINGS_KEY in g) {
248
+ return g[ROUTE_MAPPINGS_KEY] ?? null;
249
+ }
250
+ try {
251
+ const vercelJsonPath = path.join(process.cwd(), "vercel.json");
252
+ if (!fs.existsSync(vercelJsonPath)) {
253
+ g[ROUTE_MAPPINGS_KEY] = null;
254
+ return null;
255
+ }
256
+ const vercelJson = JSON.parse(fs.readFileSync(vercelJsonPath, "utf-8"));
257
+ if (!vercelJson.functions) {
258
+ g[ROUTE_MAPPINGS_KEY] = null;
259
+ return null;
260
+ }
261
+ const mappings = [];
262
+ for (const [filePath, config] of Object.entries(vercelJson.functions)) {
263
+ if (!config.experimentalTriggers) continue;
264
+ for (const trigger of config.experimentalTriggers) {
265
+ if (trigger.type?.startsWith("queue/") && trigger.topic && trigger.consumer) {
266
+ mappings.push({
267
+ urlPath: filePathToUrlPath(filePath),
268
+ topic: trigger.topic,
269
+ consumer: trigger.consumer
270
+ });
271
+ }
272
+ }
273
+ }
274
+ g[ROUTE_MAPPINGS_KEY] = mappings.length > 0 ? mappings : null;
275
+ return g[ROUTE_MAPPINGS_KEY];
276
+ } catch (error) {
277
+ console.warn("[Dev Mode] Failed to read vercel.json:", error);
278
+ g[ROUTE_MAPPINGS_KEY] = null;
279
+ return null;
280
+ }
281
+ }
282
+ function findMatchingRoutes(topicName) {
283
+ const mappings = getDevRouteMappings();
284
+ if (!mappings) {
285
+ return [];
286
+ }
287
+ return mappings.filter((mapping) => {
288
+ if (mapping.topic.includes("*")) {
289
+ return matchesWildcardPattern(topicName, mapping.topic);
290
+ }
291
+ return mapping.topic === topicName;
292
+ });
293
+ }
294
+ function isDevMode() {
295
+ return process.env.NODE_ENV === "development";
296
+ }
297
+ var DEV_VISIBILITY_POLL_INTERVAL = 50;
298
+ var DEV_VISIBILITY_MAX_WAIT = 5e3;
299
+ var DEV_VISIBILITY_BACKOFF_MULTIPLIER = 2;
300
+ async function waitForMessageVisibility(topicName, consumerGroup, messageId) {
301
+ const client = new QueueClient();
302
+ const transport = new JsonTransport();
303
+ let elapsed = 0;
304
+ let interval = DEV_VISIBILITY_POLL_INTERVAL;
305
+ while (elapsed < DEV_VISIBILITY_MAX_WAIT) {
306
+ try {
307
+ await client.receiveMessageById(
308
+ {
309
+ queueName: topicName,
310
+ consumerGroup,
311
+ messageId,
312
+ visibilityTimeoutSeconds: 0
313
+ },
314
+ transport
315
+ );
316
+ return true;
317
+ } catch (error) {
318
+ if (error instanceof MessageNotFoundError) {
319
+ await new Promise((resolve) => setTimeout(resolve, interval));
320
+ elapsed += interval;
321
+ interval = Math.min(
322
+ interval * DEV_VISIBILITY_BACKOFF_MULTIPLIER,
323
+ DEV_VISIBILITY_MAX_WAIT - elapsed
324
+ );
325
+ continue;
326
+ }
327
+ if (error instanceof MessageAlreadyProcessedError) {
328
+ console.log(
329
+ `[Dev Mode] Message already processed: topic="${topicName}" messageId="${messageId}"`
330
+ );
331
+ return false;
332
+ }
333
+ console.error(
334
+ `[Dev Mode] Error polling for message visibility: topic="${topicName}" messageId="${messageId}"`,
335
+ error
336
+ );
337
+ return false;
338
+ }
339
+ }
340
+ console.warn(
341
+ `[Dev Mode] Message visibility timeout after ${DEV_VISIBILITY_MAX_WAIT}ms: topic="${topicName}" messageId="${messageId}"`
342
+ );
343
+ return false;
344
+ }
345
+ function triggerDevCallbacks(topicName, messageId, delaySeconds) {
346
+ if (delaySeconds && delaySeconds > 0) {
347
+ console.log(
348
+ `[Dev Mode] Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
349
+ );
350
+ setTimeout(() => {
351
+ triggerDevCallbacks(topicName, messageId);
352
+ }, delaySeconds * 1e3);
353
+ return;
354
+ }
355
+ console.log(
356
+ `[Dev Mode] Message sent: topic="${topicName}" messageId="${messageId}"`
357
+ );
358
+ const matchingRoutes = findMatchingRoutes(topicName);
359
+ if (matchingRoutes.length === 0) {
360
+ console.log(
361
+ `[Dev Mode] No matching routes in vercel.json for topic "${topicName}"`
362
+ );
363
+ return;
364
+ }
365
+ const consumerGroups = matchingRoutes.map((r) => r.consumer);
366
+ console.log(
367
+ `[Dev Mode] Scheduling callbacks for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
368
+ );
369
+ (async () => {
370
+ const firstRoute = matchingRoutes[0];
371
+ const isVisible = await waitForMessageVisibility(
372
+ topicName,
373
+ firstRoute.consumer,
374
+ messageId
375
+ );
376
+ if (!isVisible) {
377
+ console.warn(
378
+ `[Dev Mode] Skipping callbacks - message not visible: topic="${topicName}" messageId="${messageId}"`
379
+ );
380
+ return;
381
+ }
382
+ const port = process.env.PORT || 3e3;
383
+ const baseUrl = `http://localhost:${port}`;
384
+ for (const route of matchingRoutes) {
385
+ const url = `${baseUrl}${route.urlPath}`;
386
+ console.log(
387
+ `[Dev Mode] Invoking handler: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`
388
+ );
389
+ const cloudEvent = {
390
+ type: "com.vercel.queue.v1beta",
391
+ source: `/topic/${topicName}/consumer/${route.consumer}`,
392
+ id: messageId,
393
+ datacontenttype: "application/json",
394
+ data: {
395
+ messageId,
396
+ queueName: topicName,
397
+ consumerGroup: route.consumer
398
+ },
399
+ time: (/* @__PURE__ */ new Date()).toISOString(),
400
+ specversion: "1.0"
401
+ };
402
+ try {
403
+ const response = await fetch(url, {
404
+ method: "POST",
405
+ headers: {
406
+ "Content-Type": "application/cloudevents+json"
407
+ },
408
+ body: JSON.stringify(cloudEvent)
409
+ });
410
+ if (response.ok) {
411
+ try {
412
+ const responseData = await response.json();
413
+ if (responseData.status === "success") {
414
+ console.log(
415
+ `[Dev Mode] \u2713 Message processed successfully: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}"`
416
+ );
417
+ }
418
+ } catch {
419
+ console.warn(
420
+ `[Dev Mode] Handler returned OK but response was not JSON: topic="${topicName}" consumer="${route.consumer}"`
421
+ );
422
+ }
423
+ } else {
424
+ try {
425
+ const errorData = await response.json();
426
+ console.error(
427
+ `[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" error="${errorData.error || response.statusText}"`
428
+ );
429
+ } catch {
430
+ console.error(
431
+ `[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" status=${response.status}`
432
+ );
433
+ }
434
+ }
435
+ } catch (error) {
436
+ console.error(
437
+ `[Dev Mode] \u2717 HTTP request failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`,
438
+ error
439
+ );
440
+ }
441
+ }
442
+ })();
443
+ }
444
+ function clearDevRouteMappings() {
445
+ const g = globalThis;
446
+ delete g[ROUTE_MAPPINGS_KEY];
447
+ }
448
+ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
449
+ globalThis.__clearDevRouteMappings = clearDevRouteMappings;
450
+ }
451
+
452
+ // src/oidc.ts
453
+ var import_oidc = require("@vercel/oidc");
181
454
 
182
455
  // src/client.ts
183
456
  function isDebugEnabled() {
@@ -194,14 +467,6 @@ async function consumeStream(stream) {
194
467
  reader.releaseLock();
195
468
  }
196
469
  }
197
- function parseRetryAfter(headers) {
198
- const retryAfterHeader = headers.get("Retry-After");
199
- if (retryAfterHeader) {
200
- const parsed = parseInt(retryAfterHeader, 10);
201
- return Number.isNaN(parsed) ? void 0 : parsed;
202
- }
203
- return void 0;
204
- }
205
470
  function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters") {
206
471
  if (status === 400) {
207
472
  throw new BadRequestError(errorText || badRequestDefault);
@@ -224,8 +489,8 @@ function parseQueueHeaders(headers) {
224
489
  const deliveryCountStr = headers.get("Vqs-Delivery-Count") || "0";
225
490
  const timestamp = headers.get("Vqs-Timestamp");
226
491
  const contentType = headers.get("Content-Type") || "application/octet-stream";
227
- const ticket = headers.get("Vqs-Ticket");
228
- if (!messageId || !timestamp || !ticket) {
492
+ const receiptHandle = headers.get("Vqs-Receipt-Handle");
493
+ if (!messageId || !timestamp || !receiptHandle) {
229
494
  return null;
230
495
  }
231
496
  const deliveryCount = parseInt(deliveryCountStr, 10);
@@ -237,7 +502,7 @@ function parseQueueHeaders(headers) {
237
502
  deliveryCount,
238
503
  createdAt: new Date(timestamp),
239
504
  contentType,
240
- ticket
505
+ receiptHandle
241
506
  };
242
507
  }
243
508
  var QueueClient = class {
@@ -245,15 +510,30 @@ var QueueClient = class {
245
510
  basePath;
246
511
  customHeaders;
247
512
  providedToken;
248
- /**
249
- * Create a new Vercel Queue Service client
250
- * @param options QueueClient configuration options
251
- */
513
+ defaultDeploymentId;
514
+ pinToDeployment;
252
515
  constructor(options = {}) {
253
516
  this.baseUrl = options.baseUrl || process.env.VERCEL_QUEUE_BASE_URL || "https://vercel-queue.com";
254
- this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v2/messages";
517
+ this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v3/topic";
255
518
  this.customHeaders = options.headers || {};
256
519
  this.providedToken = options.token;
520
+ this.defaultDeploymentId = options.deploymentId || process.env.VERCEL_DEPLOYMENT_ID;
521
+ this.pinToDeployment = options.pinToDeployment ?? true;
522
+ }
523
+ getSendDeploymentId() {
524
+ if (isDevMode()) {
525
+ return void 0;
526
+ }
527
+ if (this.pinToDeployment) {
528
+ return this.defaultDeploymentId;
529
+ }
530
+ return void 0;
531
+ }
532
+ getConsumeDeploymentId() {
533
+ if (isDevMode()) {
534
+ return void 0;
535
+ }
536
+ return this.defaultDeploymentId;
257
537
  }
258
538
  async getToken() {
259
539
  if (this.providedToken) {
@@ -267,10 +547,12 @@ var QueueClient = class {
267
547
  }
268
548
  return token;
269
549
  }
270
- /**
271
- * Internal fetch wrapper that automatically handles debug logging
272
- * when VERCEL_QUEUE_DEBUG is enabled
273
- */
550
+ buildUrl(queueName, ...pathSegments) {
551
+ const encodedQueue = encodeURIComponent(queueName);
552
+ const segments = pathSegments.map((s) => encodeURIComponent(s));
553
+ const path2 = segments.length > 0 ? "/" + segments.join("/") : "";
554
+ return `${this.baseUrl}${this.basePath}/${encodedQueue}${path2}`;
555
+ }
274
556
  async fetch(url, init) {
275
557
  const method = init.method || "GET";
276
558
  if (isDebugEnabled()) {
@@ -306,25 +588,20 @@ var QueueClient = class {
306
588
  }
307
589
  return response;
308
590
  }
309
- /**
310
- * Send a message to a queue
311
- * @param options Send message options
312
- * @param transport Serializer/deserializer for the payload
313
- * @returns Promise with the message ID
314
- * @throws {BadRequestError} When request parameters are invalid
315
- * @throws {UnauthorizedError} When authentication fails
316
- * @throws {ForbiddenError} When access is denied (environment mismatch)
317
- * @throws {InternalServerError} When server encounters an error
318
- */
319
591
  async sendMessage(options, transport) {
320
- const { queueName, payload, idempotencyKey, retentionSeconds } = options;
592
+ const {
593
+ queueName,
594
+ payload,
595
+ idempotencyKey,
596
+ retentionSeconds,
597
+ delaySeconds
598
+ } = options;
321
599
  const headers = new Headers({
322
600
  Authorization: `Bearer ${await this.getToken()}`,
323
- "Vqs-Queue-Name": queueName,
324
601
  "Content-Type": transport.contentType,
325
602
  ...this.customHeaders
326
603
  });
327
- const deploymentId = options.deploymentId || process.env.VERCEL_DEPLOYMENT_ID;
604
+ const deploymentId = this.getSendDeploymentId();
328
605
  if (deploymentId) {
329
606
  headers.set("Vqs-Deployment-Id", deploymentId);
330
607
  }
@@ -334,8 +611,12 @@ var QueueClient = class {
334
611
  if (retentionSeconds !== void 0) {
335
612
  headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
336
613
  }
337
- const body = transport.serialize(payload);
338
- const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
614
+ if (delaySeconds !== void 0) {
615
+ headers.set("Vqs-Delay-Seconds", delaySeconds.toString());
616
+ }
617
+ const serialized = transport.serialize(payload);
618
+ const body = Buffer.isBuffer(serialized) ? new Uint8Array(serialized) : serialized;
619
+ const response = await this.fetch(this.buildUrl(queueName), {
339
620
  method: "POST",
340
621
  body,
341
622
  headers
@@ -343,7 +624,21 @@ var QueueClient = class {
343
624
  if (!response.ok) {
344
625
  const errorText = await response.text();
345
626
  if (response.status === 409) {
346
- throw new Error("Duplicate idempotency key detected");
627
+ throw new DuplicateMessageError(
628
+ errorText || "Duplicate idempotency key detected",
629
+ idempotencyKey
630
+ );
631
+ }
632
+ if (response.status === 502) {
633
+ throw new ConsumerDiscoveryError(
634
+ errorText || "Consumer discovery failed",
635
+ deploymentId
636
+ );
637
+ }
638
+ if (response.status === 503) {
639
+ throw new ConsumerRegistryNotConfiguredError(
640
+ errorText || "Consumer registry not configured"
641
+ );
347
642
  }
348
643
  throwCommonHttpError(
349
644
  response.status,
@@ -355,53 +650,60 @@ var QueueClient = class {
355
650
  const responseData = await response.json();
356
651
  return responseData;
357
652
  }
358
- /**
359
- * Receive messages from a queue
360
- * @param options Receive messages options
361
- * @param transport Serializer/deserializer for the payload
362
- * @returns AsyncGenerator that yields messages as they arrive
363
- * @throws {InvalidLimitError} When limit parameter is not between 1 and 10
364
- * @throws {QueueEmptyError} When no messages are available (204)
365
- * @throws {MessageLockedError} When messages are temporarily locked (423)
366
- * @throws {BadRequestError} When request parameters are invalid
367
- * @throws {UnauthorizedError} When authentication fails
368
- * @throws {ForbiddenError} When access is denied (environment mismatch)
369
- * @throws {InternalServerError} When server encounters an error
370
- */
371
653
  async *receiveMessages(options, transport) {
372
- const { queueName, consumerGroup, visibilityTimeoutSeconds, limit } = options;
654
+ const {
655
+ queueName,
656
+ consumerGroup,
657
+ visibilityTimeoutSeconds,
658
+ limit,
659
+ maxConcurrency
660
+ } = options;
373
661
  if (limit !== void 0 && (limit < 1 || limit > 10)) {
374
662
  throw new InvalidLimitError(limit);
375
663
  }
376
664
  const headers = new Headers({
377
665
  Authorization: `Bearer ${await this.getToken()}`,
378
- "Vqs-Queue-Name": queueName,
379
- "Vqs-Consumer-Group": consumerGroup,
380
666
  Accept: "multipart/mixed",
381
667
  ...this.customHeaders
382
668
  });
383
669
  if (visibilityTimeoutSeconds !== void 0) {
384
670
  headers.set(
385
- "Vqs-Visibility-Timeout",
671
+ "Vqs-Visibility-Timeout-Seconds",
386
672
  visibilityTimeoutSeconds.toString()
387
673
  );
388
674
  }
389
675
  if (limit !== void 0) {
390
- headers.set("Vqs-Limit", limit.toString());
676
+ headers.set("Vqs-Max-Messages", limit.toString());
391
677
  }
392
- const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
393
- method: "GET",
394
- headers
395
- });
678
+ if (maxConcurrency !== void 0) {
679
+ headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
680
+ }
681
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
682
+ if (effectiveDeploymentId) {
683
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
684
+ }
685
+ const response = await this.fetch(
686
+ this.buildUrl(queueName, "consumer", consumerGroup),
687
+ {
688
+ method: "POST",
689
+ headers
690
+ }
691
+ );
396
692
  if (response.status === 204) {
397
693
  throw new QueueEmptyError(queueName, consumerGroup);
398
694
  }
399
695
  if (!response.ok) {
400
696
  const errorText = await response.text();
401
- if (response.status === 423) {
402
- throw new MessageLockedError(
403
- "next message",
404
- parseRetryAfter(response.headers)
697
+ if (response.status === 429) {
698
+ let errorData = {};
699
+ try {
700
+ errorData = JSON.parse(errorText);
701
+ } catch {
702
+ }
703
+ throw new ConcurrencyLimitError(
704
+ errorData.error || "Concurrency limit exceeded or throttled",
705
+ errorData.currentInflight,
706
+ errorData.maxConcurrency
405
707
  );
406
708
  }
407
709
  throwCommonHttpError(
@@ -439,28 +741,30 @@ var QueueClient = class {
439
741
  consumerGroup,
440
742
  messageId,
441
743
  visibilityTimeoutSeconds,
442
- skipPayload
744
+ maxConcurrency
443
745
  } = options;
444
746
  const headers = new Headers({
445
747
  Authorization: `Bearer ${await this.getToken()}`,
446
- "Vqs-Queue-Name": queueName,
447
- "Vqs-Consumer-Group": consumerGroup,
448
748
  Accept: "multipart/mixed",
449
749
  ...this.customHeaders
450
750
  });
451
751
  if (visibilityTimeoutSeconds !== void 0) {
452
752
  headers.set(
453
- "Vqs-Visibility-Timeout",
753
+ "Vqs-Visibility-Timeout-Seconds",
454
754
  visibilityTimeoutSeconds.toString()
455
755
  );
456
756
  }
457
- if (skipPayload) {
458
- headers.set("Vqs-Skip-Payload", "1");
757
+ if (maxConcurrency !== void 0) {
758
+ headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
759
+ }
760
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
761
+ if (effectiveDeploymentId) {
762
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
459
763
  }
460
764
  const response = await this.fetch(
461
- `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
765
+ this.buildUrl(queueName, "consumer", consumerGroup, "id", messageId),
462
766
  {
463
- method: "GET",
767
+ method: "POST",
464
768
  headers
465
769
  }
466
770
  );
@@ -470,12 +774,32 @@ var QueueClient = class {
470
774
  throw new MessageNotFoundError(messageId);
471
775
  }
472
776
  if (response.status === 409) {
777
+ let errorData = {};
778
+ try {
779
+ errorData = JSON.parse(errorText);
780
+ } catch {
781
+ }
782
+ if (errorData.originalMessageId) {
783
+ throw new MessageNotAvailableError(
784
+ messageId,
785
+ `This message was a duplicate - use originalMessageId: ${errorData.originalMessageId}`
786
+ );
787
+ }
473
788
  throw new MessageNotAvailableError(messageId);
474
789
  }
475
- if (response.status === 423) {
476
- throw new MessageLockedError(
477
- messageId,
478
- parseRetryAfter(response.headers)
790
+ if (response.status === 410) {
791
+ throw new MessageAlreadyProcessedError(messageId);
792
+ }
793
+ if (response.status === 429) {
794
+ let errorData = {};
795
+ try {
796
+ errorData = JSON.parse(errorText);
797
+ } catch {
798
+ }
799
+ throw new ConcurrencyLimitError(
800
+ errorData.error || "Concurrency limit exceeded or throttled",
801
+ errorData.currentInflight,
802
+ errorData.maxConcurrency
479
803
  );
480
804
  }
481
805
  throwCommonHttpError(
@@ -485,95 +809,58 @@ var QueueClient = class {
485
809
  "receive message by ID"
486
810
  );
487
811
  }
488
- if (skipPayload && response.status === 204) {
489
- const parsedHeaders = parseQueueHeaders(response.headers);
812
+ for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
813
+ const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
490
814
  if (!parsedHeaders) {
815
+ await consumeStream(multipartMessage.payload);
491
816
  throw new MessageCorruptedError(
492
817
  messageId,
493
- "Missing required queue headers in 204 response"
818
+ "Missing required queue headers in response"
494
819
  );
495
820
  }
821
+ const deserializedPayload = await transport.deserialize(
822
+ multipartMessage.payload
823
+ );
496
824
  const message = {
497
825
  ...parsedHeaders,
498
- payload: void 0
826
+ payload: deserializedPayload
499
827
  };
500
828
  return { message };
501
829
  }
502
- if (!transport) {
503
- throw new Error("Transport is required when skipPayload is not true");
504
- }
505
- try {
506
- for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
507
- try {
508
- const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
509
- if (!parsedHeaders) {
510
- console.warn("Missing required queue headers in multipart part");
511
- await consumeStream(multipartMessage.payload);
512
- continue;
513
- }
514
- const deserializedPayload = await transport.deserialize(
515
- multipartMessage.payload
516
- );
517
- const message = {
518
- ...parsedHeaders,
519
- payload: deserializedPayload
520
- };
521
- return { message };
522
- } catch (error) {
523
- console.warn("Failed to deserialize message by ID:", error);
524
- await consumeStream(multipartMessage.payload);
525
- throw new MessageCorruptedError(
526
- messageId,
527
- `Failed to deserialize payload: ${error}`
528
- );
529
- }
530
- }
531
- } catch (error) {
532
- if (error instanceof MessageCorruptedError) {
533
- throw error;
534
- }
535
- throw new MessageCorruptedError(
536
- messageId,
537
- `Failed to parse multipart response: ${error}`
538
- );
539
- }
540
830
  throw new MessageNotFoundError(messageId);
541
831
  }
542
- /**
543
- * Delete a message (acknowledge processing)
544
- * @param options Delete message options
545
- * @returns Promise with delete status
546
- * @throws {MessageNotFoundError} When the message doesn't exist (404)
547
- * @throws {MessageNotAvailableError} When message can't be deleted (409)
548
- * @throws {BadRequestError} When ticket is missing or invalid (400)
549
- * @throws {UnauthorizedError} When authentication fails
550
- * @throws {ForbiddenError} When access is denied (environment mismatch)
551
- * @throws {InternalServerError} When server encounters an error
552
- */
553
832
  async deleteMessage(options) {
554
- const { queueName, consumerGroup, messageId, ticket } = options;
833
+ const { queueName, consumerGroup, receiptHandle } = options;
834
+ const headers = new Headers({
835
+ Authorization: `Bearer ${await this.getToken()}`,
836
+ ...this.customHeaders
837
+ });
838
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
839
+ if (effectiveDeploymentId) {
840
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
841
+ }
555
842
  const response = await this.fetch(
556
- `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
843
+ this.buildUrl(
844
+ queueName,
845
+ "consumer",
846
+ consumerGroup,
847
+ "lease",
848
+ receiptHandle
849
+ ),
557
850
  {
558
851
  method: "DELETE",
559
- headers: new Headers({
560
- Authorization: `Bearer ${await this.getToken()}`,
561
- "Vqs-Queue-Name": queueName,
562
- "Vqs-Consumer-Group": consumerGroup,
563
- "Vqs-Ticket": ticket,
564
- ...this.customHeaders
565
- })
852
+ headers
566
853
  }
567
854
  );
568
855
  if (!response.ok) {
569
856
  const errorText = await response.text();
570
857
  if (response.status === 404) {
571
- throw new MessageNotFoundError(messageId);
858
+ throw new MessageNotFoundError(receiptHandle);
572
859
  }
573
860
  if (response.status === 409) {
574
861
  throw new MessageNotAvailableError(
575
- messageId,
576
- errorText || "Invalid ticket, message not in correct state, or already processed"
862
+ receiptHandle,
863
+ errorText || "Invalid receipt handle, message not in correct state, or already processed"
577
864
  );
578
865
  }
579
866
  throwCommonHttpError(
@@ -581,53 +868,50 @@ var QueueClient = class {
581
868
  response.statusText,
582
869
  errorText,
583
870
  "delete message",
584
- "Missing or invalid ticket"
871
+ "Missing or invalid receipt handle"
585
872
  );
586
873
  }
587
874
  return { deleted: true };
588
875
  }
589
- /**
590
- * Change the visibility timeout of a message
591
- * @param options Change visibility options
592
- * @returns Promise with update status
593
- * @throws {MessageNotFoundError} When the message doesn't exist (404)
594
- * @throws {MessageNotAvailableError} When message can't be updated (409)
595
- * @throws {BadRequestError} When ticket is missing or visibility timeout invalid (400)
596
- * @throws {UnauthorizedError} When authentication fails
597
- * @throws {ForbiddenError} When access is denied (environment mismatch)
598
- * @throws {InternalServerError} When server encounters an error
599
- */
600
876
  async changeVisibility(options) {
601
877
  const {
602
878
  queueName,
603
879
  consumerGroup,
604
- messageId,
605
- ticket,
880
+ receiptHandle,
606
881
  visibilityTimeoutSeconds
607
882
  } = options;
883
+ const headers = new Headers({
884
+ Authorization: `Bearer ${await this.getToken()}`,
885
+ "Content-Type": "application/json",
886
+ ...this.customHeaders
887
+ });
888
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
889
+ if (effectiveDeploymentId) {
890
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
891
+ }
608
892
  const response = await this.fetch(
609
- `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
893
+ this.buildUrl(
894
+ queueName,
895
+ "consumer",
896
+ consumerGroup,
897
+ "lease",
898
+ receiptHandle
899
+ ),
610
900
  {
611
901
  method: "PATCH",
612
- headers: new Headers({
613
- Authorization: `Bearer ${await this.getToken()}`,
614
- "Vqs-Queue-Name": queueName,
615
- "Vqs-Consumer-Group": consumerGroup,
616
- "Vqs-Ticket": ticket,
617
- "Vqs-Visibility-Timeout": visibilityTimeoutSeconds.toString(),
618
- ...this.customHeaders
619
- })
902
+ headers,
903
+ body: JSON.stringify({ visibilityTimeoutSeconds })
620
904
  }
621
905
  );
622
906
  if (!response.ok) {
623
907
  const errorText = await response.text();
624
908
  if (response.status === 404) {
625
- throw new MessageNotFoundError(messageId);
909
+ throw new MessageNotFoundError(receiptHandle);
626
910
  }
627
911
  if (response.status === 409) {
628
912
  throw new MessageNotAvailableError(
629
- messageId,
630
- errorText || "Invalid ticket, message not in correct state, or already processed"
913
+ receiptHandle,
914
+ errorText || "Invalid receipt handle, message not in correct state, or already processed"
631
915
  );
632
916
  }
633
917
  throwCommonHttpError(
@@ -635,194 +919,72 @@ var QueueClient = class {
635
919
  response.statusText,
636
920
  errorText,
637
921
  "change visibility",
638
- "Missing ticket or invalid visibility timeout"
922
+ "Missing receipt handle or invalid visibility timeout"
639
923
  );
640
924
  }
641
- return { updated: true };
642
- }
643
- };
644
-
645
- // src/dev.ts
646
- var GLOBAL_KEY = Symbol.for("@vercel/queue.devHandlers");
647
- function getDevHandlerState() {
648
- const g = globalThis;
649
- if (!g[GLOBAL_KEY]) {
650
- g[GLOBAL_KEY] = {
651
- devRouteHandlers: /* @__PURE__ */ new Map(),
652
- wildcardRouteHandlers: /* @__PURE__ */ new Map()
653
- };
654
- }
655
- return g[GLOBAL_KEY];
656
- }
657
- var { devRouteHandlers, wildcardRouteHandlers } = getDevHandlerState();
658
- function cleanupDeadRefs(key, refs) {
659
- const aliveRefs = refs.filter((ref) => ref.deref() !== void 0);
660
- if (aliveRefs.length === 0) {
661
- wildcardRouteHandlers.delete(key);
662
- } else if (aliveRefs.length < refs.length) {
663
- wildcardRouteHandlers.set(key, aliveRefs);
925
+ return { success: true };
664
926
  }
665
- }
666
- function isDevMode() {
667
- return process.env.NODE_ENV === "development";
668
- }
669
- function registerDevRouteHandler(routeHandler, handlers) {
670
- for (const topicName in handlers) {
671
- for (const consumerGroup in handlers[topicName]) {
672
- const key = `${topicName}:${consumerGroup}`;
673
- if (topicName.includes("*")) {
674
- const existing = wildcardRouteHandlers.get(key) || [];
675
- cleanupDeadRefs(key, existing);
676
- const cleanedRefs = wildcardRouteHandlers.get(key) || [];
677
- const weakRef = new WeakRef(routeHandler);
678
- cleanedRefs.push(weakRef);
679
- wildcardRouteHandlers.set(key, cleanedRefs);
680
- } else {
681
- devRouteHandlers.set(key, {
682
- routeHandler,
683
- topicPattern: topicName
684
- });
685
- }
927
+ /**
928
+ * Alternative endpoint for changing message visibility timeout.
929
+ * Uses the /visibility path suffix and expects visibilityTimeoutSeconds in the body.
930
+ * Functionally equivalent to changeVisibility but follows an alternative API pattern.
931
+ *
932
+ * @param options - Options for changing visibility
933
+ * @returns Promise resolving to change visibility response
934
+ */
935
+ async changeVisibilityAlt(options) {
936
+ const {
937
+ queueName,
938
+ consumerGroup,
939
+ receiptHandle,
940
+ visibilityTimeoutSeconds
941
+ } = options;
942
+ const headers = new Headers({
943
+ Authorization: `Bearer ${await this.getToken()}`,
944
+ "Content-Type": "application/json",
945
+ ...this.customHeaders
946
+ });
947
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
948
+ if (effectiveDeploymentId) {
949
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
686
950
  }
687
- }
688
- }
689
- function findRouteHandlersForTopic(topicName) {
690
- const handlersMap = /* @__PURE__ */ new Map();
691
- for (const [
692
- key,
693
- { routeHandler, topicPattern }
694
- ] of devRouteHandlers.entries()) {
695
- const [_, consumerGroup] = key.split(":");
696
- if (topicPattern === topicName) {
697
- if (!handlersMap.has(routeHandler)) {
698
- handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
951
+ const response = await this.fetch(
952
+ this.buildUrl(
953
+ queueName,
954
+ "consumer",
955
+ consumerGroup,
956
+ "lease",
957
+ receiptHandle,
958
+ "visibility"
959
+ ),
960
+ {
961
+ method: "PATCH",
962
+ headers,
963
+ body: JSON.stringify({ visibilityTimeoutSeconds })
699
964
  }
700
- handlersMap.get(routeHandler).add(consumerGroup);
701
- }
702
- }
703
- for (const [key, refs] of wildcardRouteHandlers.entries()) {
704
- const [pattern, consumerGroup] = key.split(":");
705
- if (matchesWildcardPattern(topicName, pattern)) {
706
- cleanupDeadRefs(key, refs);
707
- const cleanedRefs = wildcardRouteHandlers.get(key) || [];
708
- for (const ref of cleanedRefs) {
709
- const routeHandler = ref.deref();
710
- if (routeHandler) {
711
- if (!handlersMap.has(routeHandler)) {
712
- handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
713
- }
714
- handlersMap.get(routeHandler).add(consumerGroup);
715
- }
965
+ );
966
+ if (!response.ok) {
967
+ const errorText = await response.text();
968
+ if (response.status === 404) {
969
+ throw new MessageNotFoundError(receiptHandle);
716
970
  }
717
- }
718
- }
719
- return handlersMap;
720
- }
721
- function createMockCloudEventRequest(topicName, consumerGroup, messageId) {
722
- const cloudEvent = {
723
- type: "com.vercel.queue.v1beta",
724
- source: `/topic/${topicName}/consumer/${consumerGroup}`,
725
- id: messageId,
726
- datacontenttype: "application/json",
727
- data: {
728
- messageId,
729
- queueName: topicName,
730
- consumerGroup
731
- },
732
- time: (/* @__PURE__ */ new Date()).toISOString(),
733
- specversion: "1.0"
734
- };
735
- return new Request("https://localhost/api/queue/callback", {
736
- method: "POST",
737
- headers: {
738
- "Content-Type": "application/cloudevents+json"
739
- },
740
- body: JSON.stringify(cloudEvent)
741
- });
742
- }
743
- var DEV_CALLBACK_DELAY = 1e3;
744
- function scheduleDevTimeout(topicName, messageId, timeoutSeconds) {
745
- console.log(
746
- `[Dev Mode] Message ${messageId} timed out for ${timeoutSeconds}s, will re-trigger`
747
- );
748
- setTimeout(
749
- () => {
750
- console.log(
751
- `[Dev Mode] Re-triggering callback for timed-out message ${messageId}`
752
- );
753
- triggerDevCallbacks(topicName, messageId);
754
- },
755
- timeoutSeconds * 1e3 + DEV_CALLBACK_DELAY
756
- );
757
- }
758
- function triggerDevCallbacks(topicName, messageId) {
759
- const handlersMap = findRouteHandlersForTopic(topicName);
760
- if (handlersMap.size === 0) {
761
- return;
762
- }
763
- const consumerGroups = Array.from(
764
- new Set(
765
- Array.from(handlersMap.values()).flatMap((groups) => Array.from(groups))
766
- )
767
- );
768
- console.log(
769
- `[Dev Mode] Triggering local callbacks for topic "${topicName}" \u2192 consumers: ${consumerGroups.join(", ")}`
770
- );
771
- setTimeout(async () => {
772
- for (const [routeHandler, consumerGroups2] of handlersMap.entries()) {
773
- for (const consumerGroup of consumerGroups2) {
774
- try {
775
- const request = createMockCloudEventRequest(
776
- topicName,
777
- consumerGroup,
778
- messageId
779
- );
780
- const response = await routeHandler(request);
781
- if (response.ok) {
782
- try {
783
- const responseData = await response.json();
784
- if (responseData.status === "success") {
785
- console.log(
786
- `[Dev Mode] Message processed for ${topicName}/${consumerGroup}`
787
- );
788
- }
789
- } catch (jsonError) {
790
- console.error(
791
- `[Dev Mode] Failed to parse success response for ${topicName}/${consumerGroup}:`,
792
- jsonError
793
- );
794
- }
795
- } else {
796
- try {
797
- const errorData = await response.json();
798
- console.error(
799
- `[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
800
- errorData.error || response.statusText
801
- );
802
- } catch (jsonError) {
803
- console.error(
804
- `[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
805
- response.statusText
806
- );
807
- }
808
- }
809
- } catch (error) {
810
- console.error(
811
- `[Dev Mode] Error triggering callback for ${topicName}/${consumerGroup}:`,
812
- error
813
- );
814
- }
971
+ if (response.status === 409) {
972
+ throw new MessageNotAvailableError(
973
+ receiptHandle,
974
+ errorText || "Invalid receipt handle, message not in correct state, or already processed"
975
+ );
815
976
  }
977
+ throwCommonHttpError(
978
+ response.status,
979
+ response.statusText,
980
+ errorText,
981
+ "change visibility (alt)",
982
+ "Missing receipt handle or invalid visibility timeout"
983
+ );
816
984
  }
817
- }, DEV_CALLBACK_DELAY);
818
- }
819
- function clearDevHandlers() {
820
- devRouteHandlers.clear();
821
- wildcardRouteHandlers.clear();
822
- }
823
- if (process.env.NODE_ENV === "test" || process.env.VITEST) {
824
- globalThis.__clearDevHandlers = clearDevHandlers;
825
- }
985
+ return { success: true };
986
+ }
987
+ };
826
988
 
827
989
  // src/consumer-group.ts
828
990
  var ConsumerGroup = class {
@@ -854,8 +1016,7 @@ var ConsumerGroup = class {
854
1016
  * The extension loop runs every `refreshInterval` seconds and updates the message's
855
1017
  * visibility timeout to `visibilityTimeout` seconds from the current time.
856
1018
  *
857
- * @param messageId - The unique identifier of the message to extend visibility for
858
- * @param ticket - The receipt ticket that proves ownership of the message
1019
+ * @param receiptHandle - The receipt handle that proves ownership of the message
859
1020
  * @returns A function that when called will stop the extension loop
860
1021
  *
861
1022
  * @remarks
@@ -865,37 +1026,43 @@ var ConsumerGroup = class {
865
1026
  * - By default, the stop function returns immediately without waiting for in-flight
866
1027
  * - Pass `true` to the stop function to wait for any in-flight extension to complete
867
1028
  */
868
- startVisibilityExtension(messageId, ticket) {
1029
+ startVisibilityExtension(receiptHandle) {
869
1030
  let isRunning = true;
1031
+ let isResolved = false;
870
1032
  let resolveLifecycle;
871
1033
  let timeoutId = null;
872
1034
  const lifecyclePromise = new Promise((resolve) => {
873
1035
  resolveLifecycle = resolve;
874
1036
  });
1037
+ const safeResolve = () => {
1038
+ if (!isResolved) {
1039
+ isResolved = true;
1040
+ resolveLifecycle();
1041
+ }
1042
+ };
875
1043
  const extend = async () => {
876
1044
  if (!isRunning) {
877
- resolveLifecycle();
1045
+ safeResolve();
878
1046
  return;
879
1047
  }
880
1048
  try {
881
1049
  await this.client.changeVisibility({
882
1050
  queueName: this.topicName,
883
1051
  consumerGroup: this.consumerGroupName,
884
- messageId,
885
- ticket,
1052
+ receiptHandle,
886
1053
  visibilityTimeoutSeconds: this.visibilityTimeout
887
1054
  });
888
1055
  if (isRunning) {
889
1056
  timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
890
1057
  } else {
891
- resolveLifecycle();
1058
+ safeResolve();
892
1059
  }
893
1060
  } catch (error) {
894
1061
  console.error(
895
- `Failed to extend visibility for message ${messageId}:`,
1062
+ `Failed to extend visibility for receipt handle ${receiptHandle}:`,
896
1063
  error
897
1064
  );
898
- resolveLifecycle();
1065
+ safeResolve();
899
1066
  }
900
1067
  };
901
1068
  timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
@@ -908,22 +1075,14 @@ var ConsumerGroup = class {
908
1075
  if (waitForCompletion) {
909
1076
  await lifecyclePromise;
910
1077
  } else {
911
- resolveLifecycle();
1078
+ safeResolve();
912
1079
  }
913
1080
  };
914
1081
  }
915
- /**
916
- * Process a single message with the given handler
917
- * @param message The message to process
918
- * @param handler Function to process the message
919
- */
920
1082
  async processMessage(message, handler) {
921
- const stopExtension = this.startVisibilityExtension(
922
- message.messageId,
923
- message.ticket
924
- );
1083
+ const stopExtension = this.startVisibilityExtension(message.receiptHandle);
925
1084
  try {
926
- const result = await handler(message.payload, {
1085
+ await handler(message.payload, {
927
1086
  messageId: message.messageId,
928
1087
  deliveryCount: message.deliveryCount,
929
1088
  createdAt: message.createdAt,
@@ -931,29 +1090,11 @@ var ConsumerGroup = class {
931
1090
  consumerGroup: this.consumerGroupName
932
1091
  });
933
1092
  await stopExtension();
934
- if (result && "timeoutSeconds" in result) {
935
- await this.client.changeVisibility({
936
- queueName: this.topicName,
937
- consumerGroup: this.consumerGroupName,
938
- messageId: message.messageId,
939
- ticket: message.ticket,
940
- visibilityTimeoutSeconds: result.timeoutSeconds
941
- });
942
- if (isDevMode()) {
943
- scheduleDevTimeout(
944
- this.topicName,
945
- message.messageId,
946
- result.timeoutSeconds
947
- );
948
- }
949
- } else {
950
- await this.client.deleteMessage({
951
- queueName: this.topicName,
952
- consumerGroup: this.consumerGroupName,
953
- messageId: message.messageId,
954
- ticket: message.ticket
955
- });
956
- }
1093
+ await this.client.deleteMessage({
1094
+ queueName: this.topicName,
1095
+ consumerGroup: this.consumerGroupName,
1096
+ receiptHandle: message.receiptHandle
1097
+ });
957
1098
  } catch (error) {
958
1099
  await stopExtension();
959
1100
  if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
@@ -968,36 +1109,16 @@ var ConsumerGroup = class {
968
1109
  }
969
1110
  async consume(handler, options) {
970
1111
  if (options?.messageId) {
971
- if (options.skipPayload) {
972
- const response = await this.client.receiveMessageById(
973
- {
974
- queueName: this.topicName,
975
- consumerGroup: this.consumerGroupName,
976
- messageId: options.messageId,
977
- visibilityTimeoutSeconds: this.visibilityTimeout,
978
- skipPayload: true
979
- },
980
- this.transport
981
- );
982
- await this.processMessage(
983
- response.message,
984
- handler
985
- );
986
- } else {
987
- const response = await this.client.receiveMessageById(
988
- {
989
- queueName: this.topicName,
990
- consumerGroup: this.consumerGroupName,
991
- messageId: options.messageId,
992
- visibilityTimeoutSeconds: this.visibilityTimeout
993
- },
994
- this.transport
995
- );
996
- await this.processMessage(
997
- response.message,
998
- handler
999
- );
1000
- }
1112
+ const response = await this.client.receiveMessageById(
1113
+ {
1114
+ queueName: this.topicName,
1115
+ consumerGroup: this.consumerGroupName,
1116
+ messageId: options.messageId,
1117
+ visibilityTimeoutSeconds: this.visibilityTimeout
1118
+ },
1119
+ this.transport
1120
+ );
1121
+ await this.processMessage(response.message, handler);
1001
1122
  } else {
1002
1123
  let messageFound = false;
1003
1124
  for await (const message of this.client.receiveMessages(
@@ -1065,7 +1186,7 @@ var Topic = class {
1065
1186
  payload,
1066
1187
  idempotencyKey: options?.idempotencyKey,
1067
1188
  retentionSeconds: options?.retentionSeconds,
1068
- deploymentId: options?.deploymentId
1189
+ delaySeconds: options?.delaySeconds
1069
1190
  },
1070
1191
  this.transport
1071
1192
  );
@@ -1225,9 +1346,6 @@ function createCallbackHandler(handlers, client) {
1225
1346
  );
1226
1347
  }
1227
1348
  };
1228
- if (isDevMode()) {
1229
- registerDevRouteHandler(routeHandler, handlers);
1230
- }
1231
1349
  return routeHandler;
1232
1350
  }
1233
1351
  function handleCallback(handlers, client) {
@@ -1244,12 +1362,12 @@ async function send(topicName, payload, options) {
1244
1362
  payload,
1245
1363
  idempotencyKey: options?.idempotencyKey,
1246
1364
  retentionSeconds: options?.retentionSeconds,
1247
- deploymentId: options?.deploymentId
1365
+ delaySeconds: options?.delaySeconds
1248
1366
  },
1249
1367
  transport
1250
1368
  );
1251
1369
  if (isDevMode()) {
1252
- triggerDevCallbacks(topicName, result.messageId);
1370
+ triggerDevCallbacks(topicName, result.messageId, options?.delaySeconds);
1253
1371
  }
1254
1372
  return { messageId: result.messageId };
1255
1373
  }
@@ -1257,22 +1375,10 @@ async function receive(topicName, consumerGroup, handler, options) {
1257
1375
  const transport = options?.transport || new JsonTransport();
1258
1376
  const client = options?.client || new QueueClient();
1259
1377
  const topic = new Topic(client, topicName, transport);
1260
- const {
1261
- messageId,
1262
- skipPayload,
1263
- client: _,
1264
- ...consumerGroupOptions
1265
- } = options || {};
1378
+ const { messageId, client: _, ...consumerGroupOptions } = options || {};
1266
1379
  const consumer = topic.consumerGroup(consumerGroup, consumerGroupOptions);
1267
1380
  if (messageId) {
1268
- if (skipPayload) {
1269
- return consumer.consume(handler, {
1270
- messageId,
1271
- skipPayload: true
1272
- });
1273
- } else {
1274
- return consumer.consume(handler, { messageId });
1275
- }
1381
+ return consumer.consume(handler, { messageId });
1276
1382
  } else {
1277
1383
  return consumer.consume(handler);
1278
1384
  }
@@ -1330,10 +1436,15 @@ var Client = class {
1330
1436
  BadRequestError,
1331
1437
  BufferTransport,
1332
1438
  Client,
1439
+ ConcurrencyLimitError,
1440
+ ConsumerDiscoveryError,
1441
+ ConsumerRegistryNotConfiguredError,
1442
+ DuplicateMessageError,
1333
1443
  ForbiddenError,
1334
1444
  InternalServerError,
1335
1445
  InvalidLimitError,
1336
1446
  JsonTransport,
1447
+ MessageAlreadyProcessedError,
1337
1448
  MessageCorruptedError,
1338
1449
  MessageLockedError,
1339
1450
  MessageNotAvailableError,