@vercel/queue 0.0.0-alpha.34 → 0.0.0-alpha.36

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.
@@ -62,6 +62,12 @@ var JsonTransport = class {
62
62
  contentType = "application/json";
63
63
  replacer;
64
64
  reviver;
65
+ /**
66
+ * Create a new JsonTransport.
67
+ * @param options - Optional JSON serialization options
68
+ * @param options.replacer - Custom replacer for JSON.stringify
69
+ * @param options.reviver - Custom reviver for JSON.parse
70
+ */
65
71
  constructor(options = {}) {
66
72
  this.replacer = options.replacer;
67
73
  this.reviver = options.reviver;
@@ -141,7 +147,9 @@ var MessageAlreadyProcessedError = class extends Error {
141
147
  }
142
148
  };
143
149
  var ConcurrencyLimitError = class extends Error {
150
+ /** Current number of in-flight messages for this consumer group. */
144
151
  currentInflight;
152
+ /** Maximum allowed concurrent messages (as configured). */
145
153
  maxConcurrency;
146
154
  constructor(message = "Concurrency limit exceeded", currentInflight, maxConcurrency) {
147
155
  super(message);
@@ -528,19 +536,55 @@ var QueueClient = class {
528
536
  }
529
537
  return response;
530
538
  }
539
+ /**
540
+ * Send a message to a topic.
541
+ *
542
+ * @param options - Message options including queue name, payload, and optional settings
543
+ * @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
544
+ * @param options.payload - Message payload
545
+ * @param options.idempotencyKey - Optional deduplication key (dedup window: min(retention, 24h))
546
+ * @param options.retentionSeconds - Message TTL (default: 86400, min: 60, max: 86400)
547
+ * @param options.delaySeconds - Delivery delay (default: 0, max: retentionSeconds)
548
+ * @param transport - Serializer for the payload
549
+ * @returns Promise with the generated messageId
550
+ * @throws {DuplicateMessageError} When idempotency key was already used
551
+ * @throws {ConsumerDiscoveryError} When consumer discovery fails
552
+ * @throws {ConsumerRegistryNotConfiguredError} When registry not configured
553
+ * @throws {BadRequestError} When parameters are invalid
554
+ * @throws {UnauthorizedError} When authentication fails
555
+ * @throws {ForbiddenError} When access is denied
556
+ * @throws {InternalServerError} When server encounters an error
557
+ */
531
558
  async sendMessage(options, transport) {
532
559
  const {
533
560
  queueName,
534
561
  payload,
535
562
  idempotencyKey,
536
563
  retentionSeconds,
537
- delaySeconds
564
+ delaySeconds,
565
+ headers: optionHeaders
538
566
  } = options;
539
- const headers = new Headers({
540
- Authorization: `Bearer ${await this.getToken()}`,
541
- "Content-Type": transport.contentType,
542
- ...this.customHeaders
543
- });
567
+ const headers = new Headers();
568
+ if (this.customHeaders) {
569
+ for (const [name, value] of Object.entries(this.customHeaders)) {
570
+ headers.append(name, value);
571
+ }
572
+ }
573
+ if (optionHeaders) {
574
+ const protectedHeaderNames = /* @__PURE__ */ new Set(["authorization", "content-type"]);
575
+ const isProtectedHeader = (name) => {
576
+ const lower = name.toLowerCase();
577
+ if (protectedHeaderNames.has(lower)) return true;
578
+ return lower.startsWith("vqs-");
579
+ };
580
+ for (const [name, value] of Object.entries(optionHeaders)) {
581
+ if (!isProtectedHeader(name) && value !== void 0) {
582
+ headers.append(name, value);
583
+ }
584
+ }
585
+ }
586
+ headers.set("Authorization", `Bearer ${await this.getToken()}`);
587
+ headers.set("Content-Type", transport.contentType);
544
588
  const deploymentId = this.getSendDeploymentId();
545
589
  if (deploymentId) {
546
590
  headers.set("Vqs-Deployment-Id", deploymentId);
@@ -590,6 +634,25 @@ var QueueClient = class {
590
634
  const responseData = await response.json();
591
635
  return responseData;
592
636
  }
637
+ /**
638
+ * Receive messages from a topic as an async generator.
639
+ *
640
+ * @param options - Receive options
641
+ * @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
642
+ * @param options.consumerGroup - Consumer group name (pattern: `[A-Za-z0-9_-]+`)
643
+ * @param options.visibilityTimeoutSeconds - Lock duration (default: 30, min: 0, max: 3600)
644
+ * @param options.limit - Max messages to retrieve (default: 1, min: 1, max: 10)
645
+ * @param options.maxConcurrency - Max in-flight messages (default: unlimited, min: 1)
646
+ * @param transport - Deserializer for message payloads
647
+ * @yields Message objects with payload, messageId, receiptHandle, etc.
648
+ * @throws {QueueEmptyError} When no messages available
649
+ * @throws {InvalidLimitError} When limit is outside 1-10 range
650
+ * @throws {ConcurrencyLimitError} When maxConcurrency exceeded
651
+ * @throws {BadRequestError} When parameters are invalid
652
+ * @throws {UnauthorizedError} When authentication fails
653
+ * @throws {ForbiddenError} When access is denied
654
+ * @throws {InternalServerError} When server encounters an error
655
+ */
593
656
  async *receiveMessages(options, transport) {
594
657
  const {
595
658
  queueName,
@@ -675,6 +738,26 @@ var QueueClient = class {
675
738
  }
676
739
  }
677
740
  }
741
+ /**
742
+ * Receive a specific message by its ID.
743
+ *
744
+ * @param options - Receive options
745
+ * @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
746
+ * @param options.consumerGroup - Consumer group name (pattern: `[A-Za-z0-9_-]+`)
747
+ * @param options.messageId - Message ID to retrieve
748
+ * @param options.visibilityTimeoutSeconds - Lock duration (default: 30, min: 0, max: 3600)
749
+ * @param options.maxConcurrency - Max in-flight messages (default: unlimited, min: 1)
750
+ * @param transport - Deserializer for the message payload
751
+ * @returns Promise with the message
752
+ * @throws {MessageNotFoundError} When message doesn't exist
753
+ * @throws {MessageNotAvailableError} When message is in wrong state or was a duplicate
754
+ * @throws {MessageAlreadyProcessedError} When message was already processed
755
+ * @throws {ConcurrencyLimitError} When maxConcurrency exceeded
756
+ * @throws {BadRequestError} When parameters are invalid
757
+ * @throws {UnauthorizedError} When authentication fails
758
+ * @throws {ForbiddenError} When access is denied
759
+ * @throws {InternalServerError} When server encounters an error
760
+ */
678
761
  async receiveMessageById(options, transport) {
679
762
  const {
680
763
  queueName,
@@ -769,6 +852,21 @@ var QueueClient = class {
769
852
  }
770
853
  throw new MessageNotFoundError(messageId);
771
854
  }
855
+ /**
856
+ * Delete (acknowledge) a message after successful processing.
857
+ *
858
+ * @param options - Delete options
859
+ * @param options.queueName - Topic name
860
+ * @param options.consumerGroup - Consumer group name
861
+ * @param options.receiptHandle - Receipt handle from the received message (must use same deployment ID as receive)
862
+ * @returns Promise indicating deletion success
863
+ * @throws {MessageNotFoundError} When receipt handle not found
864
+ * @throws {MessageNotAvailableError} When receipt handle invalid or message already processed
865
+ * @throws {BadRequestError} When parameters are invalid
866
+ * @throws {UnauthorizedError} When authentication fails
867
+ * @throws {ForbiddenError} When access is denied
868
+ * @throws {InternalServerError} When server encounters an error
869
+ */
772
870
  async deleteMessage(options) {
773
871
  const { queueName, consumerGroup, receiptHandle } = options;
774
872
  const headers = new Headers({
@@ -813,6 +911,23 @@ var QueueClient = class {
813
911
  }
814
912
  return { deleted: true };
815
913
  }
914
+ /**
915
+ * Extend or change the visibility timeout of a message.
916
+ * Used to prevent message redelivery while still processing.
917
+ *
918
+ * @param options - Visibility options
919
+ * @param options.queueName - Topic name
920
+ * @param options.consumerGroup - Consumer group name
921
+ * @param options.receiptHandle - Receipt handle from the received message (must use same deployment ID as receive)
922
+ * @param options.visibilityTimeoutSeconds - New timeout (min: 0, max: 3600, cannot exceed message expiration)
923
+ * @returns Promise indicating success
924
+ * @throws {MessageNotFoundError} When receipt handle not found
925
+ * @throws {MessageNotAvailableError} When receipt handle invalid or message already processed
926
+ * @throws {BadRequestError} When parameters are invalid
927
+ * @throws {UnauthorizedError} When authentication fails
928
+ * @throws {ForbiddenError} When access is denied
929
+ * @throws {InternalServerError} When server encounters an error
930
+ */
816
931
  async changeVisibility(options) {
817
932
  const {
818
933
  queueName,
@@ -935,36 +1050,26 @@ var ConsumerGroup = class {
935
1050
  refreshInterval;
936
1051
  transport;
937
1052
  /**
938
- * Create a new ConsumerGroup instance
939
- * @param client QueueClient instance to use for API calls
940
- * @param topicName Name of the topic to consume from
941
- * @param consumerGroupName Name of the consumer group
942
- * @param options Optional configuration
1053
+ * Create a new ConsumerGroup instance.
1054
+ *
1055
+ * @param client - QueueClient instance to use for API calls
1056
+ * @param topicName - Name of the topic to consume from (pattern: `[A-Za-z0-9_-]+`)
1057
+ * @param consumerGroupName - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
1058
+ * @param options - Optional configuration
1059
+ * @param options.transport - Payload serializer (default: JsonTransport)
1060
+ * @param options.visibilityTimeoutSeconds - Message lock duration (default: 30, max: 3600)
1061
+ * @param options.visibilityRefreshInterval - Lock refresh interval in seconds (default: visibilityTimeout / 3)
943
1062
  */
944
1063
  constructor(client, topicName, consumerGroupName, options = {}) {
945
1064
  this.client = client;
946
1065
  this.topicName = topicName;
947
1066
  this.consumerGroupName = consumerGroupName;
948
- this.visibilityTimeout = options.visibilityTimeoutSeconds || 30;
949
- this.refreshInterval = options.refreshInterval || 10;
1067
+ this.visibilityTimeout = options.visibilityTimeoutSeconds ?? 30;
1068
+ this.refreshInterval = options.visibilityRefreshInterval ?? Math.floor(this.visibilityTimeout / 3);
950
1069
  this.transport = options.transport || new JsonTransport();
951
1070
  }
952
1071
  /**
953
1072
  * Starts a background loop that periodically extends the visibility timeout for a message.
954
- * This prevents the message from becoming visible to other consumers while it's being processed.
955
- *
956
- * The extension loop runs every `refreshInterval` seconds and updates the message's
957
- * visibility timeout to `visibilityTimeout` seconds from the current time.
958
- *
959
- * @param receiptHandle - The receipt handle that proves ownership of the message
960
- * @returns A function that when called will stop the extension loop
961
- *
962
- * @remarks
963
- * - The first extension attempt occurs after `refreshInterval` seconds, not immediately
964
- * - If an extension fails, the loop terminates with an error logged to console
965
- * - The returned stop function is idempotent - calling it multiple times is safe
966
- * - By default, the stop function returns immediately without waiting for in-flight
967
- * - Pass `true` to the stop function to wait for any in-flight extension to complete
968
1073
  */
969
1074
  startVisibilityExtension(receiptHandle) {
970
1075
  let isRunning = true;
@@ -1126,7 +1231,8 @@ var Topic = class {
1126
1231
  payload,
1127
1232
  idempotencyKey: options?.idempotencyKey,
1128
1233
  retentionSeconds: options?.retentionSeconds,
1129
- delaySeconds: options?.delaySeconds
1234
+ delaySeconds: options?.delaySeconds,
1235
+ headers: options?.headers
1130
1236
  },
1131
1237
  this.transport
1132
1238
  );
@@ -1236,7 +1342,7 @@ async function parseCallback(request) {
1236
1342
  messageId
1237
1343
  };
1238
1344
  }
1239
- function createCallbackHandler(handlers, client) {
1345
+ function createCallbackHandler(handlers, client, visibilityTimeoutSeconds) {
1240
1346
  for (const topicPattern in handlers) {
1241
1347
  if (topicPattern.includes("*")) {
1242
1348
  if (!validateWildcardPattern(topicPattern)) {
@@ -1272,7 +1378,10 @@ function createCallbackHandler(handlers, client) {
1272
1378
  );
1273
1379
  }
1274
1380
  const topic = new Topic(client, queueName);
1275
- const cg = topic.consumerGroup(consumerGroup);
1381
+ const cg = topic.consumerGroup(
1382
+ consumerGroup,
1383
+ visibilityTimeoutSeconds !== void 0 ? { visibilityTimeoutSeconds } : void 0
1384
+ );
1276
1385
  await cg.consume(consumerGroupHandler, { messageId });
1277
1386
  return Response.json({ status: "success" });
1278
1387
  } catch (error) {
@@ -1288,8 +1397,12 @@ function createCallbackHandler(handlers, client) {
1288
1397
  };
1289
1398
  return routeHandler;
1290
1399
  }
1291
- function handleCallback(handlers, client) {
1292
- return createCallbackHandler(handlers, client || new QueueClient());
1400
+ function handleCallback(handlers, options) {
1401
+ return createCallbackHandler(
1402
+ handlers,
1403
+ options?.client || new QueueClient(),
1404
+ options?.visibilityTimeoutSeconds
1405
+ );
1293
1406
  }
1294
1407
 
1295
1408
  // src/nextjs-pages.ts