@vercel/queue 0.0.2 → 0.1.0

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.d.ts CHANGED
@@ -37,12 +37,11 @@ interface Transport<T = unknown> {
37
37
  * @example
38
38
  * ```typescript
39
39
  * // Default (JsonTransport is used automatically)
40
- * const queue = new QueueClient({ region: process.env.QUEUE_REGION! });
40
+ * const queue = new QueueClient();
41
41
  * await queue.send("topic", { data: "example" });
42
42
  *
43
43
  * // With custom serialization
44
44
  * const queue = new QueueClient({
45
- * region: process.env.QUEUE_REGION!,
46
45
  * transport: new JsonTransport({
47
46
  * replacer: (key, value) => key === "password" ? undefined : value,
48
47
  * reviver: (key, value) => key === "date" ? new Date(value) : value,
@@ -75,7 +74,7 @@ declare class JsonTransport<T = unknown> implements Transport<T> {
75
74
  *
76
75
  * @example
77
76
  * ```typescript
78
- * const queue = new QueueClient({ region: "iad1", transport: new BufferTransport() });
77
+ * const queue = new QueueClient({ transport: new BufferTransport() });
79
78
  * await queue.send("binary-topic", myBuffer);
80
79
  * ```
81
80
  */
@@ -95,13 +94,13 @@ declare class BufferTransport implements Transport<Buffer> {
95
94
  *
96
95
  * @example
97
96
  * ```typescript
98
- * const queue = new QueueClient({ region: "iad1", transport: new StreamTransport() });
99
- *
100
97
  * // Sending a stream
98
+ * const queue = new QueueClient({ transport: new StreamTransport() });
101
99
  * await queue.send("large-file", myReadableStream);
102
100
  *
103
101
  * // Receiving - cleanup handled automatically
104
- * await queue.receive("large-file", "processor", async (stream, meta) => {
102
+ * const poller = new PollingQueueClient({ region: "iad1", transport: new StreamTransport() });
103
+ * await poller.receive("large-file", "processor", async (stream, meta) => {
105
104
  * const reader = stream.getReader();
106
105
  * // Process chunks...
107
106
  * });
@@ -155,15 +154,17 @@ interface QueueClientOptions {
155
154
  * Requests are sent to the regional endpoint resolved by
156
155
  * {@link BaseUrlResolver} (default: `https://${region}.vercel-queue.com`).
157
156
  *
158
- * Use a `QUEUE_REGION` env var set to `${VERCEL_REGION}` in production
159
- * and a fixed region (e.g. `"iad1"`) in development.
157
+ * When omitted, the region is auto-detected from the `VERCEL_REGION`
158
+ * environment variable (set automatically on Vercel). If not available,
159
+ * defaults to `"iad1"` with a console warning.
160
160
  *
161
161
  * @example
162
162
  * ```ts
163
- * new QueueClient({ region: process.env.QUEUE_REGION! })
163
+ * new QueueClient() // auto-detect, falls back to "iad1"
164
+ * new QueueClient({ region: "iad1" }) // explicit
164
165
  * ```
165
166
  */
166
- region: VercelRegion;
167
+ region?: VercelRegion;
167
168
  /**
168
169
  * Custom resolver that maps a region code to a base {@link URL}.
169
170
  *
@@ -205,6 +206,28 @@ interface QueueClientOptions {
205
206
  */
206
207
  transport?: Transport;
207
208
  }
209
+ /**
210
+ * Options for creating a {@link PollingQueueClient}.
211
+ *
212
+ * Identical to {@link QueueClientOptions} except `region` is **required**
213
+ * because messages can only be received from the region they were sent to.
214
+ * Using a fixed region (e.g. `"iad1"`) ensures that `send` and `receive`
215
+ * target the same endpoint.
216
+ */
217
+ interface PollingQueueClientOptions extends QueueClientOptions {
218
+ /**
219
+ * Vercel region code for API routing — **required** for polling.
220
+ *
221
+ * Messages can only be received from the region they were sent to.
222
+ * Use a fixed region (e.g. `"iad1"`) for both sending and receiving.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * new PollingQueueClient({ region: "iad1" })
227
+ * ```
228
+ */
229
+ region: VercelRegion;
230
+ }
208
231
  /**
209
232
  * Options for sending messages.
210
233
  */
@@ -496,14 +519,37 @@ declare class ConsumerRegistryNotConfiguredError extends Error {
496
519
  constructor(message?: string);
497
520
  }
498
521
 
522
+ /**
523
+ * Queue client for push-based (callback) workflows.
524
+ *
525
+ * Use this client when Vercel delivers messages to your route handlers.
526
+ * Provides {@link send}, {@link handleCallback}, and {@link handleNodeCallback}.
527
+ *
528
+ * Region is resolved automatically:
529
+ * 1. Explicit `region` option (highest priority)
530
+ * 2. `VERCEL_REGION` environment variable (set automatically on Vercel)
531
+ * 3. Falls back to `"iad1"` with a console warning
532
+ *
533
+ * The constructor never throws — `new QueueClient()` always works.
534
+ *
535
+ * For manual polling workflows, use {@link PollingQueueClient} instead.
536
+ *
537
+ * @example
538
+ * ```typescript
539
+ * import { QueueClient } from "@vercel/queue";
540
+ *
541
+ * const queue = new QueueClient();
542
+ * export const { send, handleCallback, handleNodeCallback } = queue;
543
+ * ```
544
+ */
499
545
  declare class QueueClient {
500
- constructor(options: QueueClientOptions);
546
+ constructor(options?: QueueClientOptions);
501
547
  /**
502
548
  * Send a message to a topic.
503
549
  *
504
550
  * This is an arrow function property so it can be destructured:
505
551
  * ```typescript
506
- * const { send } = new QueueClient({ region: process.env.QUEUE_REGION! });
552
+ * const { send } = new QueueClient();
507
553
  * await send("my-topic", payload);
508
554
  * ```
509
555
  *
@@ -514,28 +560,6 @@ declare class QueueClient {
514
560
  * the message for deferred processing (no ID available yet)
515
561
  */
516
562
  send: <T = unknown>(topicName: string, payload: T, options?: SendOptions) => Promise<SendResult>;
517
- /**
518
- * Receive and process messages from a topic.
519
- *
520
- * Each message is automatically locked, kept alive via periodic visibility
521
- * extensions during processing, and acknowledged upon successful handler completion.
522
- * The handler is not called when the queue is empty — check `result.ok` instead.
523
- *
524
- * This is an arrow function property so it can be destructured:
525
- * ```typescript
526
- * const { receive } = new QueueClient({ region: process.env.QUEUE_REGION! });
527
- * const result = await receive("my-topic", "my-group", handler);
528
- * if (!result.ok) console.log(result.reason);
529
- * ```
530
- *
531
- * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
532
- * @param consumerGroup - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
533
- * @param handler - Function to process each message payload and metadata.
534
- * Not called when the queue is empty.
535
- * @param options - Optional receive options (visibilityTimeoutSeconds, limit, or messageId)
536
- * @returns Discriminated result: `{ ok: true }` on success, `{ ok: false, reason }` otherwise
537
- */
538
- receive: <T = unknown>(topicName: string, consumerGroup: string, handler: MessageHandler<T>, options?: ReceiveOptions) => Promise<ReceiveResult>;
539
563
  /**
540
564
  * Create a Web API route handler for processing queue callback messages.
541
565
  *
@@ -544,7 +568,7 @@ declare class QueueClient {
544
568
  *
545
569
  * This is an arrow function property so it can be destructured:
546
570
  * ```typescript
547
- * const { handleCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
571
+ * const { handleCallback } = new QueueClient();
548
572
  * export const POST = handleCallback(handler);
549
573
  * ```
550
574
  *
@@ -568,7 +592,7 @@ declare class QueueClient {
568
592
  *
569
593
  * This is an arrow function property so it can be destructured:
570
594
  * ```typescript
571
- * const { handleNodeCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
595
+ * const { handleNodeCallback } = new QueueClient();
572
596
  * app.post("/api/queue", handleNodeCallback(handler));
573
597
  * ```
574
598
  *
@@ -594,6 +618,67 @@ declare class QueueClient {
594
618
  end(): void;
595
619
  }) => Promise<void>);
596
620
  }
621
+ /**
622
+ * Queue client for poll-based (manual receive) workflows.
623
+ *
624
+ * Use this client when you manually poll for messages with {@link receive}.
625
+ * Also provides {@link send} for publishing messages.
626
+ *
627
+ * Region is **required** because messages can only be received from the
628
+ * region they were sent to. Use a fixed region (e.g. `"iad1"`) for both
629
+ * sending and receiving.
630
+ *
631
+ * For push-based (callback) workflows on Vercel, use {@link QueueClient} instead.
632
+ *
633
+ * @example
634
+ * ```typescript
635
+ * import { PollingQueueClient } from "@vercel/queue";
636
+ *
637
+ * const queue = new PollingQueueClient({ region: "iad1" });
638
+ * export const { send, receive } = queue;
639
+ * ```
640
+ */
641
+ declare class PollingQueueClient {
642
+ constructor(options: PollingQueueClientOptions);
643
+ /**
644
+ * Send a message to a topic.
645
+ *
646
+ * This is an arrow function property so it can be destructured:
647
+ * ```typescript
648
+ * const { send } = new PollingQueueClient({ region: "iad1" });
649
+ * await send("my-topic", payload);
650
+ * ```
651
+ *
652
+ * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
653
+ * @param payload - The data to send (serialized via the configured transport)
654
+ * @param options - Optional send options (idempotencyKey, retentionSeconds, delaySeconds, headers)
655
+ * @returns `{ messageId }` — `messageId` is `null` when the server accepted
656
+ * the message for deferred processing (no ID available yet)
657
+ */
658
+ send: <T = unknown>(topicName: string, payload: T, options?: SendOptions) => Promise<SendResult>;
659
+ /**
660
+ * Receive and process messages from a topic.
661
+ *
662
+ * Each message is automatically locked, kept alive via periodic visibility
663
+ * extensions during processing, and acknowledged upon successful handler completion.
664
+ * The handler is not called when the queue is empty — check `result.ok` instead.
665
+ *
666
+ * This is an arrow function property so it can be destructured:
667
+ * ```typescript
668
+ * const { receive } = new PollingQueueClient({ region: "iad1" });
669
+ * const result = await receive("my-topic", "my-group", handler);
670
+ * if (!result.ok) console.log(result.reason);
671
+ * ```
672
+ *
673
+ * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
674
+ * @param consumerGroup - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
675
+ * @param handler - Function to process each message payload and metadata.
676
+ * Not called when the queue is empty.
677
+ * @param options - Optional receive options (visibilityTimeoutSeconds, limit, or messageId)
678
+ * @returns Discriminated result: `{ ok: true }` on success, `{ ok: false, reason }` otherwise
679
+ */
680
+ receive: <T = unknown>(topicName: string, consumerGroup: string, handler: MessageHandler<T>, options?: ReceiveOptions) => Promise<ReceiveResult>;
681
+ }
597
682
 
598
683
  /**
599
684
  * Core queue callback utilities for handling incoming webhook payloads
@@ -668,4 +753,4 @@ declare function parseRawCallback(body: unknown, headers: Record<string, string
668
753
  */
669
754
  declare function parseCallback(request: Request): Promise<ParsedCallbackRequest>;
670
755
 
671
- export { BadRequestError, type BaseUrlResolver, BufferTransport, CLOUD_EVENT_TYPE_V1BETA, CLOUD_EVENT_TYPE_V2BETA, ConsumerDiscoveryError, ConsumerRegistryNotConfiguredError, DuplicateMessageError, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageAlreadyProcessedError, MessageCorruptedError, type MessageHandler, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type ParsedCallbackRequest, type ParsedCallbackV1, type ParsedCallbackV2, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveBatchOptions, type ReceiveByIdOptions, type ReceiveOptions, type ReceiveResult, type RetryDirective, type RetryHandler, type SendOptions, type SendResult, StreamTransport, type Transport, UnauthorizedError, type VercelRegion, parseCallback, parseRawCallback };
756
+ export { BadRequestError, type BaseUrlResolver, BufferTransport, CLOUD_EVENT_TYPE_V1BETA, CLOUD_EVENT_TYPE_V2BETA, ConsumerDiscoveryError, ConsumerRegistryNotConfiguredError, DuplicateMessageError, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageAlreadyProcessedError, MessageCorruptedError, type MessageHandler, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type ParsedCallbackRequest, type ParsedCallbackV1, type ParsedCallbackV2, PollingQueueClient, type PollingQueueClientOptions, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveBatchOptions, type ReceiveByIdOptions, type ReceiveOptions, type ReceiveResult, type RetryDirective, type RetryHandler, type SendOptions, type SendResult, StreamTransport, type Transport, UnauthorizedError, type VercelRegion, parseCallback, parseRawCallback };
package/dist/index.js CHANGED
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  MessageLockedError: () => MessageLockedError,
47
47
  MessageNotAvailableError: () => MessageNotAvailableError,
48
48
  MessageNotFoundError: () => MessageNotFoundError,
49
+ PollingQueueClient: () => PollingQueueClient,
49
50
  QueueClient: () => QueueClient,
50
51
  QueueEmptyError: () => QueueEmptyError,
51
52
  StreamTransport: () => StreamTransport,
@@ -1079,7 +1080,7 @@ var ApiClient = class _ApiClient {
1079
1080
  return;
1080
1081
  }
1081
1082
  throw new Error(
1082
- 'No deployment ID available. VERCEL_DEPLOYMENT_ID is not set.\n\nThis usually means the code is running outside a Vercel deployment (e.g. during build or in a non-Vercel environment).\n\nTo fix this, create a new QueueClient with an explicit deploymentId:\n new QueueClient({ region: "iad1", deploymentId: "dpl_xxx" })\nOr explicitly opt out of deployment pinning:\n new QueueClient({ region: "iad1", deploymentId: null })'
1083
+ 'No deployment ID available. VERCEL_DEPLOYMENT_ID is not set.\n\nThis usually means the code is running outside a Vercel deployment (e.g. during build or in a non-Vercel environment).\n\nTo fix this, create a client with an explicit deploymentId:\n new QueueClient({ deploymentId: "dpl_xxx" })\nOr explicitly opt out of deployment pinning:\n new QueueClient({ deploymentId: null })'
1083
1084
  );
1084
1085
  }
1085
1086
  getSendDeploymentId() {
@@ -1137,7 +1138,7 @@ var ApiClient = class _ApiClient {
1137
1138
  }
1138
1139
  console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
1139
1140
  }
1140
- init.headers.set("User-Agent", `@vercel/queue/${"0.0.2"}`);
1141
+ init.headers.set("User-Agent", `@vercel/queue/${"0.1.0"}`);
1141
1142
  init.headers.set("Vqs-Client-Ts", (/* @__PURE__ */ new Date()).toISOString());
1142
1143
  const response = await fetch(url, init);
1143
1144
  if (isDebugEnabled()) {
@@ -1473,23 +1474,37 @@ var ApiClient = class _ApiClient {
1473
1474
 
1474
1475
  // src/client.ts
1475
1476
  var apiClients = /* @__PURE__ */ new WeakMap();
1476
- function getApiClient(client) {
1477
+ function getApi(client) {
1477
1478
  const api = apiClients.get(client);
1478
1479
  if (!api) {
1479
- throw new Error("QueueClient not initialized");
1480
+ throw new Error("Client not initialized");
1480
1481
  }
1481
1482
  return api;
1482
1483
  }
1484
+ function getApiClient(client) {
1485
+ return getApi(client);
1486
+ }
1487
+ var DEFAULT_REGION = "iad1";
1488
+ function resolveRegion(region) {
1489
+ if (region) return region;
1490
+ const fromEnv = process.env.VERCEL_REGION;
1491
+ if (fromEnv) return fromEnv;
1492
+ console.warn(
1493
+ `[QueueClient] Region not detected \u2014 defaulting to "${DEFAULT_REGION}". On Vercel this is set automatically via VERCEL_REGION. To silence this warning, pass region explicitly: new QueueClient({ region: "iad1" })`
1494
+ );
1495
+ return DEFAULT_REGION;
1496
+ }
1483
1497
  var QueueClient = class {
1484
- constructor(options) {
1485
- apiClients.set(this, new ApiClient(options));
1498
+ constructor(options = {}) {
1499
+ const region = resolveRegion(options.region);
1500
+ apiClients.set(this, new ApiClient({ ...options, region }));
1486
1501
  }
1487
1502
  /**
1488
1503
  * Send a message to a topic.
1489
1504
  *
1490
1505
  * This is an arrow function property so it can be destructured:
1491
1506
  * ```typescript
1492
- * const { send } = new QueueClient({ region: process.env.QUEUE_REGION! });
1507
+ * const { send } = new QueueClient();
1493
1508
  * await send("my-topic", payload);
1494
1509
  * ```
1495
1510
  *
@@ -1500,7 +1515,7 @@ var QueueClient = class {
1500
1515
  * the message for deferred processing (no ID available yet)
1501
1516
  */
1502
1517
  send = async (topicName, payload, options) => {
1503
- const api = getApiClient(this);
1518
+ const api = getApi(this);
1504
1519
  const result = await api.sendMessage({
1505
1520
  queueName: topicName,
1506
1521
  payload,
@@ -1519,75 +1534,6 @@ var QueueClient = class {
1519
1534
  }
1520
1535
  return { messageId: result.messageId };
1521
1536
  };
1522
- /**
1523
- * Receive and process messages from a topic.
1524
- *
1525
- * Each message is automatically locked, kept alive via periodic visibility
1526
- * extensions during processing, and acknowledged upon successful handler completion.
1527
- * The handler is not called when the queue is empty — check `result.ok` instead.
1528
- *
1529
- * This is an arrow function property so it can be destructured:
1530
- * ```typescript
1531
- * const { receive } = new QueueClient({ region: process.env.QUEUE_REGION! });
1532
- * const result = await receive("my-topic", "my-group", handler);
1533
- * if (!result.ok) console.log(result.reason);
1534
- * ```
1535
- *
1536
- * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
1537
- * @param consumerGroup - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
1538
- * @param handler - Function to process each message payload and metadata.
1539
- * Not called when the queue is empty.
1540
- * @param options - Optional receive options (visibilityTimeoutSeconds, limit, or messageId)
1541
- * @returns Discriminated result: `{ ok: true }` on success, `{ ok: false, reason }` otherwise
1542
- */
1543
- receive = async (topicName, consumerGroup, handler, options) => {
1544
- const api = getApiClient(this);
1545
- const topic = new Topic(api, topicName);
1546
- const visibilityTimeoutSeconds = options && "visibilityTimeoutSeconds" in options ? options.visibilityTimeoutSeconds : void 0;
1547
- const consumer = topic.consumerGroup(
1548
- consumerGroup,
1549
- visibilityTimeoutSeconds !== void 0 ? { visibilityTimeoutSeconds } : {}
1550
- );
1551
- try {
1552
- let count;
1553
- const retry = options?.retry;
1554
- if (options && "messageId" in options) {
1555
- count = await consumer.consume(handler, {
1556
- messageId: options.messageId,
1557
- retry
1558
- });
1559
- } else {
1560
- const limit = options && "limit" in options ? options.limit : void 0;
1561
- count = await consumer.consume(handler, {
1562
- ...limit !== void 0 ? { limit } : {},
1563
- retry
1564
- });
1565
- }
1566
- if (count === 0) {
1567
- return { ok: false, reason: "empty" };
1568
- }
1569
- return { ok: true };
1570
- } catch (error) {
1571
- if (options && "messageId" in options && error instanceof MessageNotFoundError) {
1572
- return { ok: false, reason: "not_found", messageId: options.messageId };
1573
- }
1574
- if (options && "messageId" in options && error instanceof MessageNotAvailableError) {
1575
- return {
1576
- ok: false,
1577
- reason: "not_available",
1578
- messageId: options.messageId
1579
- };
1580
- }
1581
- if (options && "messageId" in options && error instanceof MessageAlreadyProcessedError) {
1582
- return {
1583
- ok: false,
1584
- reason: "already_processed",
1585
- messageId: options.messageId
1586
- };
1587
- }
1588
- throw error;
1589
- }
1590
- };
1591
1537
  /**
1592
1538
  * Create a Web API route handler for processing queue callback messages.
1593
1539
  *
@@ -1596,7 +1542,7 @@ var QueueClient = class {
1596
1542
  *
1597
1543
  * This is an arrow function property so it can be destructured:
1598
1544
  * ```typescript
1599
- * const { handleCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
1545
+ * const { handleCallback } = new QueueClient();
1600
1546
  * export const POST = handleCallback(handler);
1601
1547
  * ```
1602
1548
  *
@@ -1638,7 +1584,7 @@ var QueueClient = class {
1638
1584
  *
1639
1585
  * This is an arrow function property so it can be destructured:
1640
1586
  * ```typescript
1641
- * const { handleNodeCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
1587
+ * const { handleNodeCallback } = new QueueClient();
1642
1588
  * app.post("/api/queue", handleNodeCallback(handler));
1643
1589
  * ```
1644
1590
  *
@@ -1674,6 +1620,107 @@ var QueueClient = class {
1674
1620
  };
1675
1621
  };
1676
1622
  };
1623
+ var PollingQueueClient = class {
1624
+ constructor(options) {
1625
+ apiClients.set(this, new ApiClient(options));
1626
+ }
1627
+ /**
1628
+ * Send a message to a topic.
1629
+ *
1630
+ * This is an arrow function property so it can be destructured:
1631
+ * ```typescript
1632
+ * const { send } = new PollingQueueClient({ region: "iad1" });
1633
+ * await send("my-topic", payload);
1634
+ * ```
1635
+ *
1636
+ * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
1637
+ * @param payload - The data to send (serialized via the configured transport)
1638
+ * @param options - Optional send options (idempotencyKey, retentionSeconds, delaySeconds, headers)
1639
+ * @returns `{ messageId }` — `messageId` is `null` when the server accepted
1640
+ * the message for deferred processing (no ID available yet)
1641
+ */
1642
+ send = async (topicName, payload, options) => {
1643
+ const api = getApi(this);
1644
+ const result = await api.sendMessage({
1645
+ queueName: topicName,
1646
+ payload,
1647
+ idempotencyKey: options?.idempotencyKey,
1648
+ retentionSeconds: options?.retentionSeconds,
1649
+ delaySeconds: options?.delaySeconds,
1650
+ headers: options?.headers
1651
+ });
1652
+ return { messageId: result.messageId };
1653
+ };
1654
+ /**
1655
+ * Receive and process messages from a topic.
1656
+ *
1657
+ * Each message is automatically locked, kept alive via periodic visibility
1658
+ * extensions during processing, and acknowledged upon successful handler completion.
1659
+ * The handler is not called when the queue is empty — check `result.ok` instead.
1660
+ *
1661
+ * This is an arrow function property so it can be destructured:
1662
+ * ```typescript
1663
+ * const { receive } = new PollingQueueClient({ region: "iad1" });
1664
+ * const result = await receive("my-topic", "my-group", handler);
1665
+ * if (!result.ok) console.log(result.reason);
1666
+ * ```
1667
+ *
1668
+ * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
1669
+ * @param consumerGroup - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
1670
+ * @param handler - Function to process each message payload and metadata.
1671
+ * Not called when the queue is empty.
1672
+ * @param options - Optional receive options (visibilityTimeoutSeconds, limit, or messageId)
1673
+ * @returns Discriminated result: `{ ok: true }` on success, `{ ok: false, reason }` otherwise
1674
+ */
1675
+ receive = async (topicName, consumerGroup, handler, options) => {
1676
+ const api = getApi(this);
1677
+ const topic = new Topic(api, topicName);
1678
+ const visibilityTimeoutSeconds = options && "visibilityTimeoutSeconds" in options ? options.visibilityTimeoutSeconds : void 0;
1679
+ const consumer = topic.consumerGroup(
1680
+ consumerGroup,
1681
+ visibilityTimeoutSeconds !== void 0 ? { visibilityTimeoutSeconds } : {}
1682
+ );
1683
+ try {
1684
+ let count;
1685
+ const retry = options?.retry;
1686
+ if (options && "messageId" in options) {
1687
+ count = await consumer.consume(handler, {
1688
+ messageId: options.messageId,
1689
+ retry
1690
+ });
1691
+ } else {
1692
+ const limit = options && "limit" in options ? options.limit : void 0;
1693
+ count = await consumer.consume(handler, {
1694
+ ...limit !== void 0 ? { limit } : {},
1695
+ retry
1696
+ });
1697
+ }
1698
+ if (count === 0) {
1699
+ return { ok: false, reason: "empty" };
1700
+ }
1701
+ return { ok: true };
1702
+ } catch (error) {
1703
+ if (options && "messageId" in options && error instanceof MessageNotFoundError) {
1704
+ return { ok: false, reason: "not_found", messageId: options.messageId };
1705
+ }
1706
+ if (options && "messageId" in options && error instanceof MessageNotAvailableError) {
1707
+ return {
1708
+ ok: false,
1709
+ reason: "not_available",
1710
+ messageId: options.messageId
1711
+ };
1712
+ }
1713
+ if (options && "messageId" in options && error instanceof MessageAlreadyProcessedError) {
1714
+ return {
1715
+ ok: false,
1716
+ reason: "already_processed",
1717
+ messageId: options.messageId
1718
+ };
1719
+ }
1720
+ throw error;
1721
+ }
1722
+ };
1723
+ };
1677
1724
  // Annotate the CommonJS export names for ESM import in node:
1678
1725
  0 && (module.exports = {
1679
1726
  BadRequestError,
@@ -1692,6 +1739,7 @@ var QueueClient = class {
1692
1739
  MessageLockedError,
1693
1740
  MessageNotAvailableError,
1694
1741
  MessageNotFoundError,
1742
+ PollingQueueClient,
1695
1743
  QueueClient,
1696
1744
  QueueEmptyError,
1697
1745
  StreamTransport,