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