@vercel/queue 0.0.1 → 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/README.md CHANGED
@@ -4,9 +4,9 @@ A TypeScript client library for interacting with the Vercel Queue Service API, d
4
4
 
5
5
  ## Features
6
6
 
7
- - **Simple API**: `send` and `receive` are all you need
7
+ - **Simple API**: `send` and `handleCallback` are all you need for push-based workflows
8
8
  - **Automatic Triggering on Vercel**: Vercel invokes your route handlers when messages are ready
9
- - **Works Anywhere**: `send` and `receive` work in any Node.js environment
9
+ - **Works Anywhere**: `send` and `receive` work in any Node.js environment, including self-hosted and non-Vercel platforms
10
10
  - **Type Safety**: Full TypeScript generics support
11
11
  - **Customizable Serialization**: Built-in JSON, Buffer, and Stream transports
12
12
  - **Local Dev Mode**: Messages sent locally trigger your handlers automatically
@@ -19,29 +19,17 @@ npm install @vercel/queue
19
19
 
20
20
  ## Quick Start
21
21
 
22
- Set up your region via environment variables. If your framework supports `.env` files (Next.js, Vite, Nuxt, etc.):
23
-
24
- ```bash
25
- # .env.production (on Vercel, inherits the platform's region)
26
- QUEUE_REGION=${VERCEL_REGION}
27
-
28
- # .env.development (fixed region for local dev — iad1 is recommended)
29
- QUEUE_REGION=iad1
30
- ```
31
-
32
- Otherwise, set `QUEUE_REGION` in your environment directly (e.g. via your hosting provider's dashboard or a `dotenv` setup).
33
-
34
- Create a shared queue client:
22
+ **1. Create a shared queue client:**
35
23
 
36
24
  ```typescript
37
25
  // lib/queue.ts
38
26
  import { QueueClient } from "@vercel/queue";
39
27
 
40
- const queue = new QueueClient({ region: process.env.QUEUE_REGION! });
41
- export const { send, receive, handleCallback, handleNodeCallback } = queue;
28
+ const queue = new QueueClient();
29
+ export const { send, handleCallback } = queue;
42
30
  ```
43
31
 
44
- Send a message anywhere in your app:
32
+ **2. Send a message anywhere in your app:**
45
33
 
46
34
  ```typescript
47
35
  import { send } from "@/lib/queue";
@@ -49,7 +37,7 @@ import { send } from "@/lib/queue";
49
37
  await send("my-topic", { message: "Hello world" });
50
38
  ```
51
39
 
52
- Handle incoming messages with a route handler:
40
+ **3. Handle incoming messages with a route handler:**
53
41
 
54
42
  ```typescript
55
43
  // app/api/queue/my-topic/route.ts
@@ -60,7 +48,7 @@ export const POST = handleCallback(async (message, metadata) => {
60
48
  });
61
49
  ```
62
50
 
63
- Configure your `vercel.json`:
51
+ **4. Configure `vercel.json`:**
64
52
 
65
53
  ```json
66
54
  {
@@ -72,9 +60,7 @@ Configure your `vercel.json`:
72
60
  }
73
61
  ```
74
62
 
75
- ### Project Setup
76
-
77
- For local development, link your Vercel project:
63
+ **5. Link your project for local development:**
78
64
 
79
65
  ```bash
80
66
  npm i -g vercel
@@ -82,6 +68,8 @@ vc link
82
68
  vc env pull
83
69
  ```
84
70
 
71
+ That's it. `new QueueClient()` auto-detects the region from `VERCEL_REGION` (set automatically on Vercel). If the region can't be detected (e.g. local dev), it falls back to `iad1`.
72
+
85
73
  ## Local Development
86
74
 
87
75
  **Queues just work locally.** When you `send()` messages in development mode, the library sends them to the real Vercel Queue Service, reads your `vercel.json` configuration, discovers your queue handlers, and triggers them automatically via local HTTP requests. This means your local dev environment behaves identically to production — no surprising behavior differences.
@@ -93,7 +81,7 @@ vc env pull
93
81
  ```typescript
94
82
  import { QueueClient } from "@vercel/queue";
95
83
 
96
- const { send } = new QueueClient({ region: process.env.QUEUE_REGION! });
84
+ const { send } = new QueueClient();
97
85
 
98
86
  // Simple send
99
87
  await send("my-topic", { message: "Hello world" });
@@ -310,37 +298,62 @@ The `retry` option is available on `handleCallback`, `handleNodeCallback`, and `
310
298
 
311
299
  ## Custom Client Configuration
312
300
 
313
- All configuration lives on the `QueueClient`:
301
+ ### QueueClient (push mode)
302
+
303
+ For push-based workflows where Vercel delivers messages to your route handlers:
314
304
 
315
305
  ```typescript
316
306
  import { QueueClient, BufferTransport } from "@vercel/queue";
317
307
 
318
308
  const queue = new QueueClient({
319
- region: process.env.QUEUE_REGION!, // Requiredsee Quick Start for env setup
309
+ region: "iad1", // Optionalauto-detected from VERCEL_REGION, falls back to "iad1"
320
310
  token: "my-token", // Auth token (default: OIDC auto-detection)
321
311
  transport: new BufferTransport(), // Serialization (default: JsonTransport)
322
312
  headers: { "X-Custom": "header" }, // Custom headers on all requests
323
313
  deploymentId: null, // null = unpinned, omit = auto from env, or explicit string
324
314
  });
325
315
 
326
- // Use directly
327
- await queue.send("my-topic", myBuffer);
316
+ export const { send, handleCallback, handleNodeCallback } = queue;
317
+ ```
318
+
319
+ ### PollingQueueClient (poll mode)
328
320
 
329
- // Or destructure
330
- export const { send, receive, handleCallback, handleNodeCallback } = queue;
321
+ For manual polling workflows where you call `receive` to poll for messages. Works anywhere — on Vercel, self-hosted, or any Node.js environment:
322
+
323
+ ```typescript
324
+ import { PollingQueueClient, BufferTransport } from "@vercel/queue";
325
+
326
+ const queue = new PollingQueueClient({
327
+ region: "iad1", // Required — messages must be received from a fixed region
328
+ token: "my-token",
329
+ transport: new BufferTransport(),
330
+ headers: { "X-Custom": "header" },
331
+ deploymentId: null,
332
+ });
333
+
334
+ export const { send, receive } = queue;
331
335
  ```
332
336
 
333
- The client sends requests to `https://${region}.vercel-queue.com`. When `handleCallback` receives a message, it reads the `ce-vqsregion` header and routes follow-up API calls to the correct regional endpoint.
337
+ Both clients send requests to `https://${region}.vercel-queue.com`. When `handleCallback` receives a message, it reads the `ce-vqsregion` header and routes follow-up API calls to the correct regional endpoint.
334
338
 
335
- To customize the URL scheme, provide a `resolveBaseUrl`:
339
+ To customize the URL scheme, provide a `resolveBaseUrl` that returns a `URL`:
336
340
 
337
341
  ```typescript
342
+ // Custom domain
338
343
  const queue = new QueueClient({
339
- region: process.env.QUEUE_REGION!,
340
- resolveBaseUrl: (region) => `https://${region}.my-proxy.example`,
344
+ resolveBaseUrl: (region) => new URL(`https://${region}.my-proxy.example`),
345
+ });
346
+
347
+ // Custom domain with a base path (e.g. reverse proxy prefix)
348
+ const queue = new QueueClient({
349
+ resolveBaseUrl: (region) =>
350
+ new URL(`https://my-proxy.example/queues/${region}`),
351
+ // → requests go to https://my-proxy.example/queues/<region>/api/v3/…
341
352
  });
342
353
  ```
343
354
 
355
+ The SDK always appends its own API path (`/api/v3/…`) to the returned URL.
356
+
344
357
  ## Transports
345
358
 
346
359
  The transport controls how message payloads are serialized and deserialized.
@@ -361,7 +374,6 @@ import {
361
374
 
362
375
  // JSON with custom serialization
363
376
  const queue = new QueueClient({
364
- region: process.env.QUEUE_REGION!,
365
377
  transport: new JsonTransport({
366
378
  replacer: (key, value) => (key === "password" ? undefined : value),
367
379
  reviver: (key, value) => (key === "date" ? new Date(value) : value),
@@ -370,14 +382,12 @@ const queue = new QueueClient({
370
382
 
371
383
  // Binary data
372
384
  const binQueue = new QueueClient({
373
- region: process.env.QUEUE_REGION!,
374
385
  transport: new BufferTransport(),
375
386
  });
376
387
  await binQueue.send("binary-topic", myBuffer);
377
388
 
378
389
  // Streaming for large payloads
379
390
  const streamQueue = new QueueClient({
380
- region: process.env.QUEUE_REGION!,
381
391
  transport: new StreamTransport(),
382
392
  });
383
393
  await streamQueue.send("large-file", myReadableStream);
@@ -385,30 +395,25 @@ await streamQueue.send("large-file", myReadableStream);
385
395
 
386
396
  ## Manual Receive
387
397
 
388
- Use `receive` to pull and process messages directly. This is an advanced alternative to `handleCallback` that works in any Node.js environment, both on and off Vercel.
398
+ Use `PollingQueueClient` to poll for and process messages directly. This is an advanced alternative to `handleCallback` that works in any Node.js environment, both on and off Vercel.
389
399
 
390
400
  ### Region considerations
391
401
 
392
- Messages can only be received from the region they were sent to. When using `receive`, use a **fixed region** (e.g. `"iad1"`) for both sending and receiving — do not use `VERCEL_REGION` (or `QUEUE_REGION=${VERCEL_REGION}`), because Vercel may route requests to different regions due to failover or load balancing, distributing your messages across regions unpredictably.
393
-
394
- ```bash
395
- # .env.production — fixed region for manual receive workflows
396
- QUEUE_REGION=iad1
397
-
398
- # .env.development
399
- QUEUE_REGION=iad1
400
- ```
402
+ Messages can only be received from the region they were sent to. When using `receive`, use a **fixed region** (e.g. `"iad1"`) for both sending and receiving — do not use `VERCEL_REGION`, because Vercel may route requests to different regions due to failover or load balancing, distributing your messages across regions unpredictably.
401
403
 
402
404
  A single region is still highly available — Vercel deploys across 3+ availability zones within each region. If you need multi-region availability, you are responsible for designing your own HA strategy (e.g. sending to multiple regions and receiving from each).
403
405
 
404
- For most use cases on Vercel, `handleCallback` is the recommended approach — the platform handles region routing automatically and the SDK routes follow-up calls to the correct region via the `ce-vqsregion` header.
406
+ For most use cases on Vercel, `handleCallback` via `QueueClient` is the recommended approach — the platform handles region routing automatically and the SDK routes follow-up calls to the correct region via the `ce-vqsregion` header.
405
407
 
406
408
  ### Usage
407
409
 
408
410
  ```typescript
409
- import { QueueClient } from "@vercel/queue";
411
+ import { PollingQueueClient } from "@vercel/queue";
410
412
 
411
- const { receive } = new QueueClient({ region: "iad1" });
413
+ const { send, receive } = new PollingQueueClient({ region: "iad1" });
414
+
415
+ // Send a message
416
+ await send("my-topic", { message: "Hello world" });
412
417
 
413
418
  // Process next available message
414
419
  const result = await receive(
@@ -481,12 +486,11 @@ All error types:
481
486
 
482
487
  ## Environment Variables
483
488
 
484
- | Variable | Description | Default |
485
- | ---------------------- | ----------------------------------------------- | ------- |
486
- | `QUEUE_REGION` | Region code for the queue client (user-defined) | - |
487
- | `VERCEL_REGION` | Current region (auto-set by Vercel) | - |
488
- | `VERCEL_QUEUE_DEBUG` | Enable debug logging (`1` or `true`) | - |
489
- | `VERCEL_DEPLOYMENT_ID` | Deployment ID (auto-set by Vercel) | - |
489
+ | Variable | Description | Default |
490
+ | ---------------------- | ------------------------------------ | ------- |
491
+ | `VERCEL_REGION` | Current region (auto-set by Vercel) | - |
492
+ | `VERCEL_QUEUE_DEBUG` | Enable debug logging (`1` or `true`) | - |
493
+ | `VERCEL_DEPLOYMENT_ID` | Deployment ID (auto-set by Vercel) | - |
490
494
 
491
495
  ## Service Limits & Constraints
492
496
 
@@ -547,12 +551,14 @@ All error types:
547
551
 
548
552
  ### `QueueClient`
549
553
 
554
+ Push-based client for workflows where Vercel delivers messages to your route handlers. Region is auto-detected from `VERCEL_REGION` (set automatically on Vercel), falling back to `"iad1"` with a console warning.
555
+
550
556
  ```typescript
551
557
  import { QueueClient } from "@vercel/queue";
552
558
 
553
559
  const queue = new QueueClient({
554
- region: process.env.QUEUE_REGION!, // Requiredsee Quick Start for env setup
555
- resolveBaseUrl: (r) => `https://${r}.vercel-queue.com`, // Default resolver
560
+ region: "iad1", // Optionalauto-detected from VERCEL_REGION, falls back to "iad1"
561
+ resolveBaseUrl: (r) => new URL(`https://${r}.vercel-queue.com`), // Default resolver
556
562
  token: "my-token", // Auto-fetched via OIDC if omitted
557
563
  headers: { "X-Custom": "value" },
558
564
  transport: new JsonTransport(), // Default: JsonTransport
@@ -560,11 +566,29 @@ const queue = new QueueClient({
560
566
  });
561
567
 
562
568
  // Methods (arrow functions — safe to destructure)
563
- const { send, receive, handleCallback, handleNodeCallback } = queue;
569
+ const { send, handleCallback, handleNodeCallback } = queue;
570
+ ```
571
+
572
+ ### `PollingQueueClient`
573
+
574
+ Poll-based client for manually receiving messages. Works in any Node.js environment, including self-hosted and non-Vercel platforms. Region is required to ensure send and receive target the same endpoint.
575
+
576
+ ```typescript
577
+ import { PollingQueueClient } from "@vercel/queue";
578
+
579
+ const queue = new PollingQueueClient({
580
+ region: "iad1", // Required — use a fixed region for polling
581
+ // ... same options as QueueClient (except region is required)
582
+ });
583
+
584
+ // Methods (arrow functions — safe to destructure)
585
+ const { send, receive } = queue;
564
586
  ```
565
587
 
566
588
  ### `send(topicName, payload, options?)`
567
589
 
590
+ Available on both `QueueClient` and `PollingQueueClient`.
591
+
568
592
  Returns `{ messageId: string | null }`. `messageId` is `null` when the server accepted the message for deferred processing (e.g. during a server-side outage).
569
593
 
570
594
  ```typescript
@@ -578,6 +602,8 @@ const { messageId } = await send("my-topic", payload, {
578
602
 
579
603
  ### `receive(topicName, consumerGroup, handler, options?)`
580
604
 
605
+ Available on `PollingQueueClient` only.
606
+
581
607
  Returns a discriminated result: `{ ok: true }` on success, or `{ ok: false, reason }` when no message was processed. The handler is never called when the queue is empty.
582
608
 
583
609
  For receive-by-id, operational errors are returned instead of thrown:
@@ -602,7 +628,9 @@ const result = await receive("my-topic", "my-group", handler, {
602
628
 
603
629
  ### `handleCallback(handler, options?)`
604
630
 
605
- Vercel only. Returns `(request: Request) => Promise<Response>` — for frameworks that export Web API route handlers.
631
+ Available on `QueueClient` only. Vercel only.
632
+
633
+ Returns `(request: Request) => Promise<Response>` — for frameworks that export Web API route handlers.
606
634
 
607
635
  ```typescript
608
636
  export const POST = handleCallback(
@@ -620,7 +648,9 @@ export const POST = handleCallback(
620
648
 
621
649
  ### `handleNodeCallback(handler, options?)`
622
650
 
623
- Vercel only. Returns `(req, res) => Promise<void>` — for frameworks that export Connect-style handlers.
651
+ Available on `QueueClient` only. Vercel only.
652
+
653
+ Returns `(req, res) => Promise<void>` — for frameworks that export Connect-style handlers.
624
654
 
625
655
  ```typescript
626
656
  // pages/api/queue/my-topic.ts
package/dist/index.d.mts 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
  * });
@@ -130,11 +129,24 @@ declare class StreamTransport implements Transport<ReadableStream<Uint8Array>> {
130
129
  */
131
130
  type VercelRegion = "arn1" | "bom1" | "cdg1" | "cle1" | "cpt1" | "dub1" | "dxb1" | "fra1" | "gru1" | "hkg1" | "hnd1" | "iad1" | "icn1" | "kix1" | "lhr1" | "pdx1" | "sfo1" | "sin1" | "syd1" | "yul1" | (string & {});
132
131
  /**
133
- * Resolves a region code to a base URL for the Vercel Queue Service API.
132
+ * Resolves a region code to a base {@link URL} for the Vercel Queue Service API.
133
+ *
134
+ * The SDK appends its own API path (`/api/v3/…`) to the returned URL.
135
+ * To add a prefix (e.g. when routing through a reverse proxy), include it
136
+ * in the pathname of the returned URL:
134
137
  *
135
- * Default: `` (region) => `https://${region}.vercel-queue.com` ``
138
+ * ```ts
139
+ * // Default — domain only, no prefix:
140
+ * (region) => new URL(`https://${region}.vercel-queue.com`)
141
+ *
142
+ * // Custom domain with a base path:
143
+ * (region) => new URL(`https://my-proxy.example/custom-prefix`)
144
+ * // → requests go to https://my-proxy.example/custom-prefix/api/v3/…
145
+ * ```
146
+ *
147
+ * Default: `` (region) => new URL(`https://${region}.vercel-queue.com`) ``
136
148
  */
137
- type BaseUrlResolver = (region: string) => string;
149
+ type BaseUrlResolver = (region: string) => URL;
138
150
  interface QueueClientOptions {
139
151
  /**
140
152
  * Vercel region code for API routing.
@@ -142,18 +154,28 @@ interface QueueClientOptions {
142
154
  * Requests are sent to the regional endpoint resolved by
143
155
  * {@link BaseUrlResolver} (default: `https://${region}.vercel-queue.com`).
144
156
  *
145
- * Use a `QUEUE_REGION` env var set to `${VERCEL_REGION}` in production
146
- * 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.
147
160
  *
148
161
  * @example
149
162
  * ```ts
150
- * new QueueClient({ region: process.env.QUEUE_REGION! })
163
+ * new QueueClient() // auto-detect, falls back to "iad1"
164
+ * new QueueClient({ region: "iad1" }) // explicit
151
165
  * ```
152
166
  */
153
- region: VercelRegion;
167
+ region?: VercelRegion;
154
168
  /**
155
- * Custom resolver that maps a region code to a base URL.
156
- * @default (region) => `https://${region}.vercel-queue.com`
169
+ * Custom resolver that maps a region code to a base {@link URL}.
170
+ *
171
+ * The SDK always appends its own API path (`/api/v3/…`) to the returned URL.
172
+ * Include a pathname to add a prefix (e.g. for reverse-proxy routing):
173
+ *
174
+ * ```ts
175
+ * resolveBaseUrl: (region) => new URL(`https://my-proxy.example/prefix`)
176
+ * ```
177
+ *
178
+ * @default (region) => new URL(`https://${region}.vercel-queue.com`)
157
179
  */
158
180
  resolveBaseUrl?: BaseUrlResolver;
159
181
  /**
@@ -184,6 +206,28 @@ interface QueueClientOptions {
184
206
  */
185
207
  transport?: Transport;
186
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
+ }
187
231
  /**
188
232
  * Options for sending messages.
189
233
  */
@@ -475,14 +519,37 @@ declare class ConsumerRegistryNotConfiguredError extends Error {
475
519
  constructor(message?: string);
476
520
  }
477
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
+ */
478
545
  declare class QueueClient {
479
- constructor(options: QueueClientOptions);
546
+ constructor(options?: QueueClientOptions);
480
547
  /**
481
548
  * Send a message to a topic.
482
549
  *
483
550
  * This is an arrow function property so it can be destructured:
484
551
  * ```typescript
485
- * const { send } = new QueueClient({ region: process.env.QUEUE_REGION! });
552
+ * const { send } = new QueueClient();
486
553
  * await send("my-topic", payload);
487
554
  * ```
488
555
  *
@@ -493,28 +560,6 @@ declare class QueueClient {
493
560
  * the message for deferred processing (no ID available yet)
494
561
  */
495
562
  send: <T = unknown>(topicName: string, payload: T, options?: SendOptions) => Promise<SendResult>;
496
- /**
497
- * Receive and process messages from a topic.
498
- *
499
- * Each message is automatically locked, kept alive via periodic visibility
500
- * extensions during processing, and acknowledged upon successful handler completion.
501
- * The handler is not called when the queue is empty — check `result.ok` instead.
502
- *
503
- * This is an arrow function property so it can be destructured:
504
- * ```typescript
505
- * const { receive } = new QueueClient({ region: process.env.QUEUE_REGION! });
506
- * const result = await receive("my-topic", "my-group", handler);
507
- * if (!result.ok) console.log(result.reason);
508
- * ```
509
- *
510
- * @param topicName - Name of the topic (pattern: `[A-Za-z0-9_-]+`)
511
- * @param consumerGroup - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
512
- * @param handler - Function to process each message payload and metadata.
513
- * Not called when the queue is empty.
514
- * @param options - Optional receive options (visibilityTimeoutSeconds, limit, or messageId)
515
- * @returns Discriminated result: `{ ok: true }` on success, `{ ok: false, reason }` otherwise
516
- */
517
- receive: <T = unknown>(topicName: string, consumerGroup: string, handler: MessageHandler<T>, options?: ReceiveOptions) => Promise<ReceiveResult>;
518
563
  /**
519
564
  * Create a Web API route handler for processing queue callback messages.
520
565
  *
@@ -523,7 +568,7 @@ declare class QueueClient {
523
568
  *
524
569
  * This is an arrow function property so it can be destructured:
525
570
  * ```typescript
526
- * const { handleCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
571
+ * const { handleCallback } = new QueueClient();
527
572
  * export const POST = handleCallback(handler);
528
573
  * ```
529
574
  *
@@ -547,7 +592,7 @@ declare class QueueClient {
547
592
  *
548
593
  * This is an arrow function property so it can be destructured:
549
594
  * ```typescript
550
- * const { handleNodeCallback } = new QueueClient({ region: process.env.QUEUE_REGION! });
595
+ * const { handleNodeCallback } = new QueueClient();
551
596
  * app.post("/api/queue", handleNodeCallback(handler));
552
597
  * ```
553
598
  *
@@ -573,6 +618,67 @@ declare class QueueClient {
573
618
  end(): void;
574
619
  }) => Promise<void>);
575
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
+ }
576
682
 
577
683
  /**
578
684
  * Core queue callback utilities for handling incoming webhook payloads
@@ -647,4 +753,4 @@ declare function parseRawCallback(body: unknown, headers: Record<string, string
647
753
  */
648
754
  declare function parseCallback(request: Request): Promise<ParsedCallbackRequest>;
649
755
 
650
- 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 };