@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.mjs CHANGED
@@ -64,8 +64,9 @@ var StreamTransport = class {
64
64
  // src/client.ts
65
65
  import { parseMultipartStream } from "mixpart";
66
66
 
67
- // src/oidc.ts
68
- import { getVercelOidcToken } from "@vercel/oidc";
67
+ // src/dev.ts
68
+ import * as fs from "fs";
69
+ import * as path from "path";
69
70
 
70
71
  // src/types.ts
71
72
  var MessageNotFoundError = class extends Error {
@@ -135,6 +136,263 @@ var InvalidLimitError = class extends Error {
135
136
  this.name = "InvalidLimitError";
136
137
  }
137
138
  };
139
+ var MessageAlreadyProcessedError = class extends Error {
140
+ constructor(messageId) {
141
+ super(`Message ${messageId} has already been processed`);
142
+ this.name = "MessageAlreadyProcessedError";
143
+ }
144
+ };
145
+ var ConcurrencyLimitError = class extends Error {
146
+ currentInflight;
147
+ maxConcurrency;
148
+ constructor(message = "Concurrency limit exceeded", currentInflight, maxConcurrency) {
149
+ super(message);
150
+ this.name = "ConcurrencyLimitError";
151
+ this.currentInflight = currentInflight;
152
+ this.maxConcurrency = maxConcurrency;
153
+ }
154
+ };
155
+ var DuplicateMessageError = class extends Error {
156
+ idempotencyKey;
157
+ constructor(message, idempotencyKey) {
158
+ super(message);
159
+ this.name = "DuplicateMessageError";
160
+ this.idempotencyKey = idempotencyKey;
161
+ }
162
+ };
163
+ var ConsumerDiscoveryError = class extends Error {
164
+ deploymentId;
165
+ constructor(message, deploymentId) {
166
+ super(message);
167
+ this.name = "ConsumerDiscoveryError";
168
+ this.deploymentId = deploymentId;
169
+ }
170
+ };
171
+ var ConsumerRegistryNotConfiguredError = class extends Error {
172
+ constructor(message = "Consumer registry not configured") {
173
+ super(message);
174
+ this.name = "ConsumerRegistryNotConfiguredError";
175
+ }
176
+ };
177
+
178
+ // src/dev.ts
179
+ var ROUTE_MAPPINGS_KEY = Symbol.for("@vercel/queue.devRouteMappings");
180
+ function filePathToUrlPath(filePath) {
181
+ let urlPath = filePath.replace(/^app\//, "/").replace(/^pages\//, "/").replace(/\/route\.(ts|js|tsx|jsx)$/, "").replace(/\.(ts|js|tsx|jsx)$/, "");
182
+ if (!urlPath.startsWith("/")) {
183
+ urlPath = "/" + urlPath;
184
+ }
185
+ return urlPath;
186
+ }
187
+ function getDevRouteMappings() {
188
+ const g = globalThis;
189
+ if (ROUTE_MAPPINGS_KEY in g) {
190
+ return g[ROUTE_MAPPINGS_KEY] ?? null;
191
+ }
192
+ try {
193
+ const vercelJsonPath = path.join(process.cwd(), "vercel.json");
194
+ if (!fs.existsSync(vercelJsonPath)) {
195
+ g[ROUTE_MAPPINGS_KEY] = null;
196
+ return null;
197
+ }
198
+ const vercelJson = JSON.parse(fs.readFileSync(vercelJsonPath, "utf-8"));
199
+ if (!vercelJson.functions) {
200
+ g[ROUTE_MAPPINGS_KEY] = null;
201
+ return null;
202
+ }
203
+ const mappings = [];
204
+ for (const [filePath, config] of Object.entries(vercelJson.functions)) {
205
+ if (!config.experimentalTriggers) continue;
206
+ for (const trigger of config.experimentalTriggers) {
207
+ if (trigger.type?.startsWith("queue/") && trigger.topic && trigger.consumer) {
208
+ mappings.push({
209
+ urlPath: filePathToUrlPath(filePath),
210
+ topic: trigger.topic,
211
+ consumer: trigger.consumer
212
+ });
213
+ }
214
+ }
215
+ }
216
+ g[ROUTE_MAPPINGS_KEY] = mappings.length > 0 ? mappings : null;
217
+ return g[ROUTE_MAPPINGS_KEY];
218
+ } catch (error) {
219
+ console.warn("[Dev Mode] Failed to read vercel.json:", error);
220
+ g[ROUTE_MAPPINGS_KEY] = null;
221
+ return null;
222
+ }
223
+ }
224
+ function findMatchingRoutes(topicName) {
225
+ const mappings = getDevRouteMappings();
226
+ if (!mappings) {
227
+ return [];
228
+ }
229
+ return mappings.filter((mapping) => {
230
+ if (mapping.topic.includes("*")) {
231
+ return matchesWildcardPattern(topicName, mapping.topic);
232
+ }
233
+ return mapping.topic === topicName;
234
+ });
235
+ }
236
+ function isDevMode() {
237
+ return process.env.NODE_ENV === "development";
238
+ }
239
+ var DEV_VISIBILITY_POLL_INTERVAL = 50;
240
+ var DEV_VISIBILITY_MAX_WAIT = 5e3;
241
+ var DEV_VISIBILITY_BACKOFF_MULTIPLIER = 2;
242
+ async function waitForMessageVisibility(topicName, consumerGroup, messageId) {
243
+ const client = new QueueClient();
244
+ const transport = new JsonTransport();
245
+ let elapsed = 0;
246
+ let interval = DEV_VISIBILITY_POLL_INTERVAL;
247
+ while (elapsed < DEV_VISIBILITY_MAX_WAIT) {
248
+ try {
249
+ await client.receiveMessageById(
250
+ {
251
+ queueName: topicName,
252
+ consumerGroup,
253
+ messageId,
254
+ visibilityTimeoutSeconds: 0
255
+ },
256
+ transport
257
+ );
258
+ return true;
259
+ } catch (error) {
260
+ if (error instanceof MessageNotFoundError) {
261
+ await new Promise((resolve) => setTimeout(resolve, interval));
262
+ elapsed += interval;
263
+ interval = Math.min(
264
+ interval * DEV_VISIBILITY_BACKOFF_MULTIPLIER,
265
+ DEV_VISIBILITY_MAX_WAIT - elapsed
266
+ );
267
+ continue;
268
+ }
269
+ if (error instanceof MessageAlreadyProcessedError) {
270
+ console.log(
271
+ `[Dev Mode] Message already processed: topic="${topicName}" messageId="${messageId}"`
272
+ );
273
+ return false;
274
+ }
275
+ console.error(
276
+ `[Dev Mode] Error polling for message visibility: topic="${topicName}" messageId="${messageId}"`,
277
+ error
278
+ );
279
+ return false;
280
+ }
281
+ }
282
+ console.warn(
283
+ `[Dev Mode] Message visibility timeout after ${DEV_VISIBILITY_MAX_WAIT}ms: topic="${topicName}" messageId="${messageId}"`
284
+ );
285
+ return false;
286
+ }
287
+ function triggerDevCallbacks(topicName, messageId, delaySeconds) {
288
+ if (delaySeconds && delaySeconds > 0) {
289
+ console.log(
290
+ `[Dev Mode] Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
291
+ );
292
+ setTimeout(() => {
293
+ triggerDevCallbacks(topicName, messageId);
294
+ }, delaySeconds * 1e3);
295
+ return;
296
+ }
297
+ console.log(
298
+ `[Dev Mode] Message sent: topic="${topicName}" messageId="${messageId}"`
299
+ );
300
+ const matchingRoutes = findMatchingRoutes(topicName);
301
+ if (matchingRoutes.length === 0) {
302
+ console.log(
303
+ `[Dev Mode] No matching routes in vercel.json for topic "${topicName}"`
304
+ );
305
+ return;
306
+ }
307
+ const consumerGroups = matchingRoutes.map((r) => r.consumer);
308
+ console.log(
309
+ `[Dev Mode] Scheduling callbacks for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
310
+ );
311
+ (async () => {
312
+ const firstRoute = matchingRoutes[0];
313
+ const isVisible = await waitForMessageVisibility(
314
+ topicName,
315
+ firstRoute.consumer,
316
+ messageId
317
+ );
318
+ if (!isVisible) {
319
+ console.warn(
320
+ `[Dev Mode] Skipping callbacks - message not visible: topic="${topicName}" messageId="${messageId}"`
321
+ );
322
+ return;
323
+ }
324
+ const port = process.env.PORT || 3e3;
325
+ const baseUrl = `http://localhost:${port}`;
326
+ for (const route of matchingRoutes) {
327
+ const url = `${baseUrl}${route.urlPath}`;
328
+ console.log(
329
+ `[Dev Mode] Invoking handler: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`
330
+ );
331
+ const cloudEvent = {
332
+ type: "com.vercel.queue.v1beta",
333
+ source: `/topic/${topicName}/consumer/${route.consumer}`,
334
+ id: messageId,
335
+ datacontenttype: "application/json",
336
+ data: {
337
+ messageId,
338
+ queueName: topicName,
339
+ consumerGroup: route.consumer
340
+ },
341
+ time: (/* @__PURE__ */ new Date()).toISOString(),
342
+ specversion: "1.0"
343
+ };
344
+ try {
345
+ const response = await fetch(url, {
346
+ method: "POST",
347
+ headers: {
348
+ "Content-Type": "application/cloudevents+json"
349
+ },
350
+ body: JSON.stringify(cloudEvent)
351
+ });
352
+ if (response.ok) {
353
+ try {
354
+ const responseData = await response.json();
355
+ if (responseData.status === "success") {
356
+ console.log(
357
+ `[Dev Mode] \u2713 Message processed successfully: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}"`
358
+ );
359
+ }
360
+ } catch {
361
+ console.warn(
362
+ `[Dev Mode] Handler returned OK but response was not JSON: topic="${topicName}" consumer="${route.consumer}"`
363
+ );
364
+ }
365
+ } else {
366
+ try {
367
+ const errorData = await response.json();
368
+ console.error(
369
+ `[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" error="${errorData.error || response.statusText}"`
370
+ );
371
+ } catch {
372
+ console.error(
373
+ `[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" status=${response.status}`
374
+ );
375
+ }
376
+ }
377
+ } catch (error) {
378
+ console.error(
379
+ `[Dev Mode] \u2717 HTTP request failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`,
380
+ error
381
+ );
382
+ }
383
+ }
384
+ })();
385
+ }
386
+ function clearDevRouteMappings() {
387
+ const g = globalThis;
388
+ delete g[ROUTE_MAPPINGS_KEY];
389
+ }
390
+ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
391
+ globalThis.__clearDevRouteMappings = clearDevRouteMappings;
392
+ }
393
+
394
+ // src/oidc.ts
395
+ import { getVercelOidcToken } from "@vercel/oidc";
138
396
 
139
397
  // src/client.ts
140
398
  function isDebugEnabled() {
@@ -151,14 +409,6 @@ async function consumeStream(stream) {
151
409
  reader.releaseLock();
152
410
  }
153
411
  }
154
- function parseRetryAfter(headers) {
155
- const retryAfterHeader = headers.get("Retry-After");
156
- if (retryAfterHeader) {
157
- const parsed = parseInt(retryAfterHeader, 10);
158
- return Number.isNaN(parsed) ? void 0 : parsed;
159
- }
160
- return void 0;
161
- }
162
412
  function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters") {
163
413
  if (status === 400) {
164
414
  throw new BadRequestError(errorText || badRequestDefault);
@@ -181,8 +431,8 @@ function parseQueueHeaders(headers) {
181
431
  const deliveryCountStr = headers.get("Vqs-Delivery-Count") || "0";
182
432
  const timestamp = headers.get("Vqs-Timestamp");
183
433
  const contentType = headers.get("Content-Type") || "application/octet-stream";
184
- const ticket = headers.get("Vqs-Ticket");
185
- if (!messageId || !timestamp || !ticket) {
434
+ const receiptHandle = headers.get("Vqs-Receipt-Handle");
435
+ if (!messageId || !timestamp || !receiptHandle) {
186
436
  return null;
187
437
  }
188
438
  const deliveryCount = parseInt(deliveryCountStr, 10);
@@ -194,7 +444,7 @@ function parseQueueHeaders(headers) {
194
444
  deliveryCount,
195
445
  createdAt: new Date(timestamp),
196
446
  contentType,
197
- ticket
447
+ receiptHandle
198
448
  };
199
449
  }
200
450
  var QueueClient = class {
@@ -202,15 +452,30 @@ var QueueClient = class {
202
452
  basePath;
203
453
  customHeaders;
204
454
  providedToken;
205
- /**
206
- * Create a new Vercel Queue Service client
207
- * @param options QueueClient configuration options
208
- */
455
+ defaultDeploymentId;
456
+ pinToDeployment;
209
457
  constructor(options = {}) {
210
458
  this.baseUrl = options.baseUrl || process.env.VERCEL_QUEUE_BASE_URL || "https://vercel-queue.com";
211
- this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v2/messages";
459
+ this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v3/topic";
212
460
  this.customHeaders = options.headers || {};
213
461
  this.providedToken = options.token;
462
+ this.defaultDeploymentId = options.deploymentId || process.env.VERCEL_DEPLOYMENT_ID;
463
+ this.pinToDeployment = options.pinToDeployment ?? true;
464
+ }
465
+ getSendDeploymentId() {
466
+ if (isDevMode()) {
467
+ return void 0;
468
+ }
469
+ if (this.pinToDeployment) {
470
+ return this.defaultDeploymentId;
471
+ }
472
+ return void 0;
473
+ }
474
+ getConsumeDeploymentId() {
475
+ if (isDevMode()) {
476
+ return void 0;
477
+ }
478
+ return this.defaultDeploymentId;
214
479
  }
215
480
  async getToken() {
216
481
  if (this.providedToken) {
@@ -224,10 +489,12 @@ var QueueClient = class {
224
489
  }
225
490
  return token;
226
491
  }
227
- /**
228
- * Internal fetch wrapper that automatically handles debug logging
229
- * when VERCEL_QUEUE_DEBUG is enabled
230
- */
492
+ buildUrl(queueName, ...pathSegments) {
493
+ const encodedQueue = encodeURIComponent(queueName);
494
+ const segments = pathSegments.map((s) => encodeURIComponent(s));
495
+ const path2 = segments.length > 0 ? "/" + segments.join("/") : "";
496
+ return `${this.baseUrl}${this.basePath}/${encodedQueue}${path2}`;
497
+ }
231
498
  async fetch(url, init) {
232
499
  const method = init.method || "GET";
233
500
  if (isDebugEnabled()) {
@@ -263,25 +530,20 @@ var QueueClient = class {
263
530
  }
264
531
  return response;
265
532
  }
266
- /**
267
- * Send a message to a queue
268
- * @param options Send message options
269
- * @param transport Serializer/deserializer for the payload
270
- * @returns Promise with the message ID
271
- * @throws {BadRequestError} When request parameters are invalid
272
- * @throws {UnauthorizedError} When authentication fails
273
- * @throws {ForbiddenError} When access is denied (environment mismatch)
274
- * @throws {InternalServerError} When server encounters an error
275
- */
276
533
  async sendMessage(options, transport) {
277
- const { queueName, payload, idempotencyKey, retentionSeconds } = options;
534
+ const {
535
+ queueName,
536
+ payload,
537
+ idempotencyKey,
538
+ retentionSeconds,
539
+ delaySeconds
540
+ } = options;
278
541
  const headers = new Headers({
279
542
  Authorization: `Bearer ${await this.getToken()}`,
280
- "Vqs-Queue-Name": queueName,
281
543
  "Content-Type": transport.contentType,
282
544
  ...this.customHeaders
283
545
  });
284
- const deploymentId = options.deploymentId || process.env.VERCEL_DEPLOYMENT_ID;
546
+ const deploymentId = this.getSendDeploymentId();
285
547
  if (deploymentId) {
286
548
  headers.set("Vqs-Deployment-Id", deploymentId);
287
549
  }
@@ -291,8 +553,12 @@ var QueueClient = class {
291
553
  if (retentionSeconds !== void 0) {
292
554
  headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
293
555
  }
294
- const body = transport.serialize(payload);
295
- const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
556
+ if (delaySeconds !== void 0) {
557
+ headers.set("Vqs-Delay-Seconds", delaySeconds.toString());
558
+ }
559
+ const serialized = transport.serialize(payload);
560
+ const body = Buffer.isBuffer(serialized) ? new Uint8Array(serialized) : serialized;
561
+ const response = await this.fetch(this.buildUrl(queueName), {
296
562
  method: "POST",
297
563
  body,
298
564
  headers
@@ -300,7 +566,21 @@ var QueueClient = class {
300
566
  if (!response.ok) {
301
567
  const errorText = await response.text();
302
568
  if (response.status === 409) {
303
- throw new Error("Duplicate idempotency key detected");
569
+ throw new DuplicateMessageError(
570
+ errorText || "Duplicate idempotency key detected",
571
+ idempotencyKey
572
+ );
573
+ }
574
+ if (response.status === 502) {
575
+ throw new ConsumerDiscoveryError(
576
+ errorText || "Consumer discovery failed",
577
+ deploymentId
578
+ );
579
+ }
580
+ if (response.status === 503) {
581
+ throw new ConsumerRegistryNotConfiguredError(
582
+ errorText || "Consumer registry not configured"
583
+ );
304
584
  }
305
585
  throwCommonHttpError(
306
586
  response.status,
@@ -312,53 +592,60 @@ var QueueClient = class {
312
592
  const responseData = await response.json();
313
593
  return responseData;
314
594
  }
315
- /**
316
- * Receive messages from a queue
317
- * @param options Receive messages options
318
- * @param transport Serializer/deserializer for the payload
319
- * @returns AsyncGenerator that yields messages as they arrive
320
- * @throws {InvalidLimitError} When limit parameter is not between 1 and 10
321
- * @throws {QueueEmptyError} When no messages are available (204)
322
- * @throws {MessageLockedError} When messages are temporarily locked (423)
323
- * @throws {BadRequestError} When request parameters are invalid
324
- * @throws {UnauthorizedError} When authentication fails
325
- * @throws {ForbiddenError} When access is denied (environment mismatch)
326
- * @throws {InternalServerError} When server encounters an error
327
- */
328
595
  async *receiveMessages(options, transport) {
329
- const { queueName, consumerGroup, visibilityTimeoutSeconds, limit } = options;
596
+ const {
597
+ queueName,
598
+ consumerGroup,
599
+ visibilityTimeoutSeconds,
600
+ limit,
601
+ maxConcurrency
602
+ } = options;
330
603
  if (limit !== void 0 && (limit < 1 || limit > 10)) {
331
604
  throw new InvalidLimitError(limit);
332
605
  }
333
606
  const headers = new Headers({
334
607
  Authorization: `Bearer ${await this.getToken()}`,
335
- "Vqs-Queue-Name": queueName,
336
- "Vqs-Consumer-Group": consumerGroup,
337
608
  Accept: "multipart/mixed",
338
609
  ...this.customHeaders
339
610
  });
340
611
  if (visibilityTimeoutSeconds !== void 0) {
341
612
  headers.set(
342
- "Vqs-Visibility-Timeout",
613
+ "Vqs-Visibility-Timeout-Seconds",
343
614
  visibilityTimeoutSeconds.toString()
344
615
  );
345
616
  }
346
617
  if (limit !== void 0) {
347
- headers.set("Vqs-Limit", limit.toString());
618
+ headers.set("Vqs-Max-Messages", limit.toString());
348
619
  }
349
- const response = await this.fetch(`${this.baseUrl}${this.basePath}`, {
350
- method: "GET",
351
- headers
352
- });
620
+ if (maxConcurrency !== void 0) {
621
+ headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
622
+ }
623
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
624
+ if (effectiveDeploymentId) {
625
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
626
+ }
627
+ const response = await this.fetch(
628
+ this.buildUrl(queueName, "consumer", consumerGroup),
629
+ {
630
+ method: "POST",
631
+ headers
632
+ }
633
+ );
353
634
  if (response.status === 204) {
354
635
  throw new QueueEmptyError(queueName, consumerGroup);
355
636
  }
356
637
  if (!response.ok) {
357
638
  const errorText = await response.text();
358
- if (response.status === 423) {
359
- throw new MessageLockedError(
360
- "next message",
361
- parseRetryAfter(response.headers)
639
+ if (response.status === 429) {
640
+ let errorData = {};
641
+ try {
642
+ errorData = JSON.parse(errorText);
643
+ } catch {
644
+ }
645
+ throw new ConcurrencyLimitError(
646
+ errorData.error || "Concurrency limit exceeded or throttled",
647
+ errorData.currentInflight,
648
+ errorData.maxConcurrency
362
649
  );
363
650
  }
364
651
  throwCommonHttpError(
@@ -396,28 +683,30 @@ var QueueClient = class {
396
683
  consumerGroup,
397
684
  messageId,
398
685
  visibilityTimeoutSeconds,
399
- skipPayload
686
+ maxConcurrency
400
687
  } = options;
401
688
  const headers = new Headers({
402
689
  Authorization: `Bearer ${await this.getToken()}`,
403
- "Vqs-Queue-Name": queueName,
404
- "Vqs-Consumer-Group": consumerGroup,
405
690
  Accept: "multipart/mixed",
406
691
  ...this.customHeaders
407
692
  });
408
693
  if (visibilityTimeoutSeconds !== void 0) {
409
694
  headers.set(
410
- "Vqs-Visibility-Timeout",
695
+ "Vqs-Visibility-Timeout-Seconds",
411
696
  visibilityTimeoutSeconds.toString()
412
697
  );
413
698
  }
414
- if (skipPayload) {
415
- headers.set("Vqs-Skip-Payload", "1");
699
+ if (maxConcurrency !== void 0) {
700
+ headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
701
+ }
702
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
703
+ if (effectiveDeploymentId) {
704
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
416
705
  }
417
706
  const response = await this.fetch(
418
- `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
707
+ this.buildUrl(queueName, "consumer", consumerGroup, "id", messageId),
419
708
  {
420
- method: "GET",
709
+ method: "POST",
421
710
  headers
422
711
  }
423
712
  );
@@ -427,12 +716,32 @@ var QueueClient = class {
427
716
  throw new MessageNotFoundError(messageId);
428
717
  }
429
718
  if (response.status === 409) {
719
+ let errorData = {};
720
+ try {
721
+ errorData = JSON.parse(errorText);
722
+ } catch {
723
+ }
724
+ if (errorData.originalMessageId) {
725
+ throw new MessageNotAvailableError(
726
+ messageId,
727
+ `This message was a duplicate - use originalMessageId: ${errorData.originalMessageId}`
728
+ );
729
+ }
430
730
  throw new MessageNotAvailableError(messageId);
431
731
  }
432
- if (response.status === 423) {
433
- throw new MessageLockedError(
434
- messageId,
435
- parseRetryAfter(response.headers)
732
+ if (response.status === 410) {
733
+ throw new MessageAlreadyProcessedError(messageId);
734
+ }
735
+ if (response.status === 429) {
736
+ let errorData = {};
737
+ try {
738
+ errorData = JSON.parse(errorText);
739
+ } catch {
740
+ }
741
+ throw new ConcurrencyLimitError(
742
+ errorData.error || "Concurrency limit exceeded or throttled",
743
+ errorData.currentInflight,
744
+ errorData.maxConcurrency
436
745
  );
437
746
  }
438
747
  throwCommonHttpError(
@@ -442,95 +751,58 @@ var QueueClient = class {
442
751
  "receive message by ID"
443
752
  );
444
753
  }
445
- if (skipPayload && response.status === 204) {
446
- const parsedHeaders = parseQueueHeaders(response.headers);
754
+ for await (const multipartMessage of parseMultipartStream(response)) {
755
+ const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
447
756
  if (!parsedHeaders) {
757
+ await consumeStream(multipartMessage.payload);
448
758
  throw new MessageCorruptedError(
449
759
  messageId,
450
- "Missing required queue headers in 204 response"
760
+ "Missing required queue headers in response"
451
761
  );
452
762
  }
763
+ const deserializedPayload = await transport.deserialize(
764
+ multipartMessage.payload
765
+ );
453
766
  const message = {
454
767
  ...parsedHeaders,
455
- payload: void 0
768
+ payload: deserializedPayload
456
769
  };
457
770
  return { message };
458
771
  }
459
- if (!transport) {
460
- throw new Error("Transport is required when skipPayload is not true");
461
- }
462
- try {
463
- for await (const multipartMessage of parseMultipartStream(response)) {
464
- try {
465
- const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
466
- if (!parsedHeaders) {
467
- console.warn("Missing required queue headers in multipart part");
468
- await consumeStream(multipartMessage.payload);
469
- continue;
470
- }
471
- const deserializedPayload = await transport.deserialize(
472
- multipartMessage.payload
473
- );
474
- const message = {
475
- ...parsedHeaders,
476
- payload: deserializedPayload
477
- };
478
- return { message };
479
- } catch (error) {
480
- console.warn("Failed to deserialize message by ID:", error);
481
- await consumeStream(multipartMessage.payload);
482
- throw new MessageCorruptedError(
483
- messageId,
484
- `Failed to deserialize payload: ${error}`
485
- );
486
- }
487
- }
488
- } catch (error) {
489
- if (error instanceof MessageCorruptedError) {
490
- throw error;
491
- }
492
- throw new MessageCorruptedError(
493
- messageId,
494
- `Failed to parse multipart response: ${error}`
495
- );
496
- }
497
772
  throw new MessageNotFoundError(messageId);
498
773
  }
499
- /**
500
- * Delete a message (acknowledge processing)
501
- * @param options Delete message options
502
- * @returns Promise with delete status
503
- * @throws {MessageNotFoundError} When the message doesn't exist (404)
504
- * @throws {MessageNotAvailableError} When message can't be deleted (409)
505
- * @throws {BadRequestError} When ticket is missing or invalid (400)
506
- * @throws {UnauthorizedError} When authentication fails
507
- * @throws {ForbiddenError} When access is denied (environment mismatch)
508
- * @throws {InternalServerError} When server encounters an error
509
- */
510
774
  async deleteMessage(options) {
511
- const { queueName, consumerGroup, messageId, ticket } = options;
775
+ const { queueName, consumerGroup, receiptHandle } = options;
776
+ const headers = new Headers({
777
+ Authorization: `Bearer ${await this.getToken()}`,
778
+ ...this.customHeaders
779
+ });
780
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
781
+ if (effectiveDeploymentId) {
782
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
783
+ }
512
784
  const response = await this.fetch(
513
- `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
785
+ this.buildUrl(
786
+ queueName,
787
+ "consumer",
788
+ consumerGroup,
789
+ "lease",
790
+ receiptHandle
791
+ ),
514
792
  {
515
793
  method: "DELETE",
516
- headers: new Headers({
517
- Authorization: `Bearer ${await this.getToken()}`,
518
- "Vqs-Queue-Name": queueName,
519
- "Vqs-Consumer-Group": consumerGroup,
520
- "Vqs-Ticket": ticket,
521
- ...this.customHeaders
522
- })
794
+ headers
523
795
  }
524
796
  );
525
797
  if (!response.ok) {
526
798
  const errorText = await response.text();
527
799
  if (response.status === 404) {
528
- throw new MessageNotFoundError(messageId);
800
+ throw new MessageNotFoundError(receiptHandle);
529
801
  }
530
802
  if (response.status === 409) {
531
803
  throw new MessageNotAvailableError(
532
- messageId,
533
- errorText || "Invalid ticket, message not in correct state, or already processed"
804
+ receiptHandle,
805
+ errorText || "Invalid receipt handle, message not in correct state, or already processed"
534
806
  );
535
807
  }
536
808
  throwCommonHttpError(
@@ -538,53 +810,50 @@ var QueueClient = class {
538
810
  response.statusText,
539
811
  errorText,
540
812
  "delete message",
541
- "Missing or invalid ticket"
813
+ "Missing or invalid receipt handle"
542
814
  );
543
815
  }
544
816
  return { deleted: true };
545
817
  }
546
- /**
547
- * Change the visibility timeout of a message
548
- * @param options Change visibility options
549
- * @returns Promise with update status
550
- * @throws {MessageNotFoundError} When the message doesn't exist (404)
551
- * @throws {MessageNotAvailableError} When message can't be updated (409)
552
- * @throws {BadRequestError} When ticket is missing or visibility timeout invalid (400)
553
- * @throws {UnauthorizedError} When authentication fails
554
- * @throws {ForbiddenError} When access is denied (environment mismatch)
555
- * @throws {InternalServerError} When server encounters an error
556
- */
557
818
  async changeVisibility(options) {
558
819
  const {
559
820
  queueName,
560
821
  consumerGroup,
561
- messageId,
562
- ticket,
822
+ receiptHandle,
563
823
  visibilityTimeoutSeconds
564
824
  } = options;
825
+ const headers = new Headers({
826
+ Authorization: `Bearer ${await this.getToken()}`,
827
+ "Content-Type": "application/json",
828
+ ...this.customHeaders
829
+ });
830
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
831
+ if (effectiveDeploymentId) {
832
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
833
+ }
565
834
  const response = await this.fetch(
566
- `${this.baseUrl}${this.basePath}/${encodeURIComponent(messageId)}`,
835
+ this.buildUrl(
836
+ queueName,
837
+ "consumer",
838
+ consumerGroup,
839
+ "lease",
840
+ receiptHandle
841
+ ),
567
842
  {
568
843
  method: "PATCH",
569
- headers: new Headers({
570
- Authorization: `Bearer ${await this.getToken()}`,
571
- "Vqs-Queue-Name": queueName,
572
- "Vqs-Consumer-Group": consumerGroup,
573
- "Vqs-Ticket": ticket,
574
- "Vqs-Visibility-Timeout": visibilityTimeoutSeconds.toString(),
575
- ...this.customHeaders
576
- })
844
+ headers,
845
+ body: JSON.stringify({ visibilityTimeoutSeconds })
577
846
  }
578
847
  );
579
848
  if (!response.ok) {
580
849
  const errorText = await response.text();
581
850
  if (response.status === 404) {
582
- throw new MessageNotFoundError(messageId);
851
+ throw new MessageNotFoundError(receiptHandle);
583
852
  }
584
853
  if (response.status === 409) {
585
854
  throw new MessageNotAvailableError(
586
- messageId,
587
- errorText || "Invalid ticket, message not in correct state, or already processed"
855
+ receiptHandle,
856
+ errorText || "Invalid receipt handle, message not in correct state, or already processed"
588
857
  );
589
858
  }
590
859
  throwCommonHttpError(
@@ -592,194 +861,72 @@ var QueueClient = class {
592
861
  response.statusText,
593
862
  errorText,
594
863
  "change visibility",
595
- "Missing ticket or invalid visibility timeout"
864
+ "Missing receipt handle or invalid visibility timeout"
596
865
  );
597
866
  }
598
- return { updated: true };
599
- }
600
- };
601
-
602
- // src/dev.ts
603
- var GLOBAL_KEY = Symbol.for("@vercel/queue.devHandlers");
604
- function getDevHandlerState() {
605
- const g = globalThis;
606
- if (!g[GLOBAL_KEY]) {
607
- g[GLOBAL_KEY] = {
608
- devRouteHandlers: /* @__PURE__ */ new Map(),
609
- wildcardRouteHandlers: /* @__PURE__ */ new Map()
610
- };
611
- }
612
- return g[GLOBAL_KEY];
613
- }
614
- var { devRouteHandlers, wildcardRouteHandlers } = getDevHandlerState();
615
- function cleanupDeadRefs(key, refs) {
616
- const aliveRefs = refs.filter((ref) => ref.deref() !== void 0);
617
- if (aliveRefs.length === 0) {
618
- wildcardRouteHandlers.delete(key);
619
- } else if (aliveRefs.length < refs.length) {
620
- wildcardRouteHandlers.set(key, aliveRefs);
867
+ return { success: true };
621
868
  }
622
- }
623
- function isDevMode() {
624
- return process.env.NODE_ENV === "development";
625
- }
626
- function registerDevRouteHandler(routeHandler, handlers) {
627
- for (const topicName in handlers) {
628
- for (const consumerGroup in handlers[topicName]) {
629
- const key = `${topicName}:${consumerGroup}`;
630
- if (topicName.includes("*")) {
631
- const existing = wildcardRouteHandlers.get(key) || [];
632
- cleanupDeadRefs(key, existing);
633
- const cleanedRefs = wildcardRouteHandlers.get(key) || [];
634
- const weakRef = new WeakRef(routeHandler);
635
- cleanedRefs.push(weakRef);
636
- wildcardRouteHandlers.set(key, cleanedRefs);
637
- } else {
638
- devRouteHandlers.set(key, {
639
- routeHandler,
640
- topicPattern: topicName
641
- });
642
- }
869
+ /**
870
+ * Alternative endpoint for changing message visibility timeout.
871
+ * Uses the /visibility path suffix and expects visibilityTimeoutSeconds in the body.
872
+ * Functionally equivalent to changeVisibility but follows an alternative API pattern.
873
+ *
874
+ * @param options - Options for changing visibility
875
+ * @returns Promise resolving to change visibility response
876
+ */
877
+ async changeVisibilityAlt(options) {
878
+ const {
879
+ queueName,
880
+ consumerGroup,
881
+ receiptHandle,
882
+ visibilityTimeoutSeconds
883
+ } = options;
884
+ const headers = new Headers({
885
+ Authorization: `Bearer ${await this.getToken()}`,
886
+ "Content-Type": "application/json",
887
+ ...this.customHeaders
888
+ });
889
+ const effectiveDeploymentId = this.getConsumeDeploymentId();
890
+ if (effectiveDeploymentId) {
891
+ headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
643
892
  }
644
- }
645
- }
646
- function findRouteHandlersForTopic(topicName) {
647
- const handlersMap = /* @__PURE__ */ new Map();
648
- for (const [
649
- key,
650
- { routeHandler, topicPattern }
651
- ] of devRouteHandlers.entries()) {
652
- const [_, consumerGroup] = key.split(":");
653
- if (topicPattern === topicName) {
654
- if (!handlersMap.has(routeHandler)) {
655
- handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
893
+ const response = await this.fetch(
894
+ this.buildUrl(
895
+ queueName,
896
+ "consumer",
897
+ consumerGroup,
898
+ "lease",
899
+ receiptHandle,
900
+ "visibility"
901
+ ),
902
+ {
903
+ method: "PATCH",
904
+ headers,
905
+ body: JSON.stringify({ visibilityTimeoutSeconds })
656
906
  }
657
- handlersMap.get(routeHandler).add(consumerGroup);
658
- }
659
- }
660
- for (const [key, refs] of wildcardRouteHandlers.entries()) {
661
- const [pattern, consumerGroup] = key.split(":");
662
- if (matchesWildcardPattern(topicName, pattern)) {
663
- cleanupDeadRefs(key, refs);
664
- const cleanedRefs = wildcardRouteHandlers.get(key) || [];
665
- for (const ref of cleanedRefs) {
666
- const routeHandler = ref.deref();
667
- if (routeHandler) {
668
- if (!handlersMap.has(routeHandler)) {
669
- handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
670
- }
671
- handlersMap.get(routeHandler).add(consumerGroup);
672
- }
907
+ );
908
+ if (!response.ok) {
909
+ const errorText = await response.text();
910
+ if (response.status === 404) {
911
+ throw new MessageNotFoundError(receiptHandle);
673
912
  }
674
- }
675
- }
676
- return handlersMap;
677
- }
678
- function createMockCloudEventRequest(topicName, consumerGroup, messageId) {
679
- const cloudEvent = {
680
- type: "com.vercel.queue.v1beta",
681
- source: `/topic/${topicName}/consumer/${consumerGroup}`,
682
- id: messageId,
683
- datacontenttype: "application/json",
684
- data: {
685
- messageId,
686
- queueName: topicName,
687
- consumerGroup
688
- },
689
- time: (/* @__PURE__ */ new Date()).toISOString(),
690
- specversion: "1.0"
691
- };
692
- return new Request("https://localhost/api/queue/callback", {
693
- method: "POST",
694
- headers: {
695
- "Content-Type": "application/cloudevents+json"
696
- },
697
- body: JSON.stringify(cloudEvent)
698
- });
699
- }
700
- var DEV_CALLBACK_DELAY = 1e3;
701
- function scheduleDevTimeout(topicName, messageId, timeoutSeconds) {
702
- console.log(
703
- `[Dev Mode] Message ${messageId} timed out for ${timeoutSeconds}s, will re-trigger`
704
- );
705
- setTimeout(
706
- () => {
707
- console.log(
708
- `[Dev Mode] Re-triggering callback for timed-out message ${messageId}`
709
- );
710
- triggerDevCallbacks(topicName, messageId);
711
- },
712
- timeoutSeconds * 1e3 + DEV_CALLBACK_DELAY
713
- );
714
- }
715
- function triggerDevCallbacks(topicName, messageId) {
716
- const handlersMap = findRouteHandlersForTopic(topicName);
717
- if (handlersMap.size === 0) {
718
- return;
719
- }
720
- const consumerGroups = Array.from(
721
- new Set(
722
- Array.from(handlersMap.values()).flatMap((groups) => Array.from(groups))
723
- )
724
- );
725
- console.log(
726
- `[Dev Mode] Triggering local callbacks for topic "${topicName}" \u2192 consumers: ${consumerGroups.join(", ")}`
727
- );
728
- setTimeout(async () => {
729
- for (const [routeHandler, consumerGroups2] of handlersMap.entries()) {
730
- for (const consumerGroup of consumerGroups2) {
731
- try {
732
- const request = createMockCloudEventRequest(
733
- topicName,
734
- consumerGroup,
735
- messageId
736
- );
737
- const response = await routeHandler(request);
738
- if (response.ok) {
739
- try {
740
- const responseData = await response.json();
741
- if (responseData.status === "success") {
742
- console.log(
743
- `[Dev Mode] Message processed for ${topicName}/${consumerGroup}`
744
- );
745
- }
746
- } catch (jsonError) {
747
- console.error(
748
- `[Dev Mode] Failed to parse success response for ${topicName}/${consumerGroup}:`,
749
- jsonError
750
- );
751
- }
752
- } else {
753
- try {
754
- const errorData = await response.json();
755
- console.error(
756
- `[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
757
- errorData.error || response.statusText
758
- );
759
- } catch (jsonError) {
760
- console.error(
761
- `[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
762
- response.statusText
763
- );
764
- }
765
- }
766
- } catch (error) {
767
- console.error(
768
- `[Dev Mode] Error triggering callback for ${topicName}/${consumerGroup}:`,
769
- error
770
- );
771
- }
913
+ if (response.status === 409) {
914
+ throw new MessageNotAvailableError(
915
+ receiptHandle,
916
+ errorText || "Invalid receipt handle, message not in correct state, or already processed"
917
+ );
772
918
  }
919
+ throwCommonHttpError(
920
+ response.status,
921
+ response.statusText,
922
+ errorText,
923
+ "change visibility (alt)",
924
+ "Missing receipt handle or invalid visibility timeout"
925
+ );
773
926
  }
774
- }, DEV_CALLBACK_DELAY);
775
- }
776
- function clearDevHandlers() {
777
- devRouteHandlers.clear();
778
- wildcardRouteHandlers.clear();
779
- }
780
- if (process.env.NODE_ENV === "test" || process.env.VITEST) {
781
- globalThis.__clearDevHandlers = clearDevHandlers;
782
- }
927
+ return { success: true };
928
+ }
929
+ };
783
930
 
784
931
  // src/consumer-group.ts
785
932
  var ConsumerGroup = class {
@@ -811,8 +958,7 @@ var ConsumerGroup = class {
811
958
  * The extension loop runs every `refreshInterval` seconds and updates the message's
812
959
  * visibility timeout to `visibilityTimeout` seconds from the current time.
813
960
  *
814
- * @param messageId - The unique identifier of the message to extend visibility for
815
- * @param ticket - The receipt ticket that proves ownership of the message
961
+ * @param receiptHandle - The receipt handle that proves ownership of the message
816
962
  * @returns A function that when called will stop the extension loop
817
963
  *
818
964
  * @remarks
@@ -822,37 +968,43 @@ var ConsumerGroup = class {
822
968
  * - By default, the stop function returns immediately without waiting for in-flight
823
969
  * - Pass `true` to the stop function to wait for any in-flight extension to complete
824
970
  */
825
- startVisibilityExtension(messageId, ticket) {
971
+ startVisibilityExtension(receiptHandle) {
826
972
  let isRunning = true;
973
+ let isResolved = false;
827
974
  let resolveLifecycle;
828
975
  let timeoutId = null;
829
976
  const lifecyclePromise = new Promise((resolve) => {
830
977
  resolveLifecycle = resolve;
831
978
  });
979
+ const safeResolve = () => {
980
+ if (!isResolved) {
981
+ isResolved = true;
982
+ resolveLifecycle();
983
+ }
984
+ };
832
985
  const extend = async () => {
833
986
  if (!isRunning) {
834
- resolveLifecycle();
987
+ safeResolve();
835
988
  return;
836
989
  }
837
990
  try {
838
991
  await this.client.changeVisibility({
839
992
  queueName: this.topicName,
840
993
  consumerGroup: this.consumerGroupName,
841
- messageId,
842
- ticket,
994
+ receiptHandle,
843
995
  visibilityTimeoutSeconds: this.visibilityTimeout
844
996
  });
845
997
  if (isRunning) {
846
998
  timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
847
999
  } else {
848
- resolveLifecycle();
1000
+ safeResolve();
849
1001
  }
850
1002
  } catch (error) {
851
1003
  console.error(
852
- `Failed to extend visibility for message ${messageId}:`,
1004
+ `Failed to extend visibility for receipt handle ${receiptHandle}:`,
853
1005
  error
854
1006
  );
855
- resolveLifecycle();
1007
+ safeResolve();
856
1008
  }
857
1009
  };
858
1010
  timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
@@ -865,22 +1017,14 @@ var ConsumerGroup = class {
865
1017
  if (waitForCompletion) {
866
1018
  await lifecyclePromise;
867
1019
  } else {
868
- resolveLifecycle();
1020
+ safeResolve();
869
1021
  }
870
1022
  };
871
1023
  }
872
- /**
873
- * Process a single message with the given handler
874
- * @param message The message to process
875
- * @param handler Function to process the message
876
- */
877
1024
  async processMessage(message, handler) {
878
- const stopExtension = this.startVisibilityExtension(
879
- message.messageId,
880
- message.ticket
881
- );
1025
+ const stopExtension = this.startVisibilityExtension(message.receiptHandle);
882
1026
  try {
883
- const result = await handler(message.payload, {
1027
+ await handler(message.payload, {
884
1028
  messageId: message.messageId,
885
1029
  deliveryCount: message.deliveryCount,
886
1030
  createdAt: message.createdAt,
@@ -888,29 +1032,11 @@ var ConsumerGroup = class {
888
1032
  consumerGroup: this.consumerGroupName
889
1033
  });
890
1034
  await stopExtension();
891
- if (result && "timeoutSeconds" in result) {
892
- await this.client.changeVisibility({
893
- queueName: this.topicName,
894
- consumerGroup: this.consumerGroupName,
895
- messageId: message.messageId,
896
- ticket: message.ticket,
897
- visibilityTimeoutSeconds: result.timeoutSeconds
898
- });
899
- if (isDevMode()) {
900
- scheduleDevTimeout(
901
- this.topicName,
902
- message.messageId,
903
- result.timeoutSeconds
904
- );
905
- }
906
- } else {
907
- await this.client.deleteMessage({
908
- queueName: this.topicName,
909
- consumerGroup: this.consumerGroupName,
910
- messageId: message.messageId,
911
- ticket: message.ticket
912
- });
913
- }
1035
+ await this.client.deleteMessage({
1036
+ queueName: this.topicName,
1037
+ consumerGroup: this.consumerGroupName,
1038
+ receiptHandle: message.receiptHandle
1039
+ });
914
1040
  } catch (error) {
915
1041
  await stopExtension();
916
1042
  if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
@@ -925,36 +1051,16 @@ var ConsumerGroup = class {
925
1051
  }
926
1052
  async consume(handler, options) {
927
1053
  if (options?.messageId) {
928
- if (options.skipPayload) {
929
- const response = await this.client.receiveMessageById(
930
- {
931
- queueName: this.topicName,
932
- consumerGroup: this.consumerGroupName,
933
- messageId: options.messageId,
934
- visibilityTimeoutSeconds: this.visibilityTimeout,
935
- skipPayload: true
936
- },
937
- this.transport
938
- );
939
- await this.processMessage(
940
- response.message,
941
- handler
942
- );
943
- } else {
944
- const response = await this.client.receiveMessageById(
945
- {
946
- queueName: this.topicName,
947
- consumerGroup: this.consumerGroupName,
948
- messageId: options.messageId,
949
- visibilityTimeoutSeconds: this.visibilityTimeout
950
- },
951
- this.transport
952
- );
953
- await this.processMessage(
954
- response.message,
955
- handler
956
- );
957
- }
1054
+ const response = await this.client.receiveMessageById(
1055
+ {
1056
+ queueName: this.topicName,
1057
+ consumerGroup: this.consumerGroupName,
1058
+ messageId: options.messageId,
1059
+ visibilityTimeoutSeconds: this.visibilityTimeout
1060
+ },
1061
+ this.transport
1062
+ );
1063
+ await this.processMessage(response.message, handler);
958
1064
  } else {
959
1065
  let messageFound = false;
960
1066
  for await (const message of this.client.receiveMessages(
@@ -1022,7 +1128,7 @@ var Topic = class {
1022
1128
  payload,
1023
1129
  idempotencyKey: options?.idempotencyKey,
1024
1130
  retentionSeconds: options?.retentionSeconds,
1025
- deploymentId: options?.deploymentId
1131
+ delaySeconds: options?.delaySeconds
1026
1132
  },
1027
1133
  this.transport
1028
1134
  );
@@ -1182,9 +1288,6 @@ function createCallbackHandler(handlers, client) {
1182
1288
  );
1183
1289
  }
1184
1290
  };
1185
- if (isDevMode()) {
1186
- registerDevRouteHandler(routeHandler, handlers);
1187
- }
1188
1291
  return routeHandler;
1189
1292
  }
1190
1293
  function handleCallback(handlers, client) {
@@ -1201,12 +1304,12 @@ async function send(topicName, payload, options) {
1201
1304
  payload,
1202
1305
  idempotencyKey: options?.idempotencyKey,
1203
1306
  retentionSeconds: options?.retentionSeconds,
1204
- deploymentId: options?.deploymentId
1307
+ delaySeconds: options?.delaySeconds
1205
1308
  },
1206
1309
  transport
1207
1310
  );
1208
1311
  if (isDevMode()) {
1209
- triggerDevCallbacks(topicName, result.messageId);
1312
+ triggerDevCallbacks(topicName, result.messageId, options?.delaySeconds);
1210
1313
  }
1211
1314
  return { messageId: result.messageId };
1212
1315
  }
@@ -1214,22 +1317,10 @@ async function receive(topicName, consumerGroup, handler, options) {
1214
1317
  const transport = options?.transport || new JsonTransport();
1215
1318
  const client = options?.client || new QueueClient();
1216
1319
  const topic = new Topic(client, topicName, transport);
1217
- const {
1218
- messageId,
1219
- skipPayload,
1220
- client: _,
1221
- ...consumerGroupOptions
1222
- } = options || {};
1320
+ const { messageId, client: _, ...consumerGroupOptions } = options || {};
1223
1321
  const consumer = topic.consumerGroup(consumerGroup, consumerGroupOptions);
1224
1322
  if (messageId) {
1225
- if (skipPayload) {
1226
- return consumer.consume(handler, {
1227
- messageId,
1228
- skipPayload: true
1229
- });
1230
- } else {
1231
- return consumer.consume(handler, { messageId });
1232
- }
1323
+ return consumer.consume(handler, { messageId });
1233
1324
  } else {
1234
1325
  return consumer.consume(handler);
1235
1326
  }
@@ -1286,10 +1377,15 @@ export {
1286
1377
  BadRequestError,
1287
1378
  BufferTransport,
1288
1379
  Client,
1380
+ ConcurrencyLimitError,
1381
+ ConsumerDiscoveryError,
1382
+ ConsumerRegistryNotConfiguredError,
1383
+ DuplicateMessageError,
1289
1384
  ForbiddenError,
1290
1385
  InternalServerError,
1291
1386
  InvalidLimitError,
1292
1387
  JsonTransport,
1388
+ MessageAlreadyProcessedError,
1293
1389
  MessageCorruptedError,
1294
1390
  MessageLockedError,
1295
1391
  MessageNotAvailableError,