@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.
@@ -26,6 +26,12 @@ var JsonTransport = class {
26
26
  contentType = "application/json";
27
27
  replacer;
28
28
  reviver;
29
+ /**
30
+ * Create a new JsonTransport.
31
+ * @param options - Optional JSON serialization options
32
+ * @param options.replacer - Custom replacer for JSON.stringify
33
+ * @param options.reviver - Custom reviver for JSON.parse
34
+ */
29
35
  constructor(options = {}) {
30
36
  this.replacer = options.replacer;
31
37
  this.reviver = options.reviver;
@@ -105,7 +111,9 @@ var MessageAlreadyProcessedError = class extends Error {
105
111
  }
106
112
  };
107
113
  var ConcurrencyLimitError = class extends Error {
114
+ /** Current number of in-flight messages for this consumer group. */
108
115
  currentInflight;
116
+ /** Maximum allowed concurrent messages (as configured). */
109
117
  maxConcurrency;
110
118
  constructor(message = "Concurrency limit exceeded", currentInflight, maxConcurrency) {
111
119
  super(message);
@@ -492,19 +500,55 @@ var QueueClient = class {
492
500
  }
493
501
  return response;
494
502
  }
503
+ /**
504
+ * Send a message to a topic.
505
+ *
506
+ * @param options - Message options including queue name, payload, and optional settings
507
+ * @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
508
+ * @param options.payload - Message payload
509
+ * @param options.idempotencyKey - Optional deduplication key (dedup window: min(retention, 24h))
510
+ * @param options.retentionSeconds - Message TTL (default: 86400, min: 60, max: 86400)
511
+ * @param options.delaySeconds - Delivery delay (default: 0, max: retentionSeconds)
512
+ * @param transport - Serializer for the payload
513
+ * @returns Promise with the generated messageId
514
+ * @throws {DuplicateMessageError} When idempotency key was already used
515
+ * @throws {ConsumerDiscoveryError} When consumer discovery fails
516
+ * @throws {ConsumerRegistryNotConfiguredError} When registry not configured
517
+ * @throws {BadRequestError} When parameters are invalid
518
+ * @throws {UnauthorizedError} When authentication fails
519
+ * @throws {ForbiddenError} When access is denied
520
+ * @throws {InternalServerError} When server encounters an error
521
+ */
495
522
  async sendMessage(options, transport) {
496
523
  const {
497
524
  queueName,
498
525
  payload,
499
526
  idempotencyKey,
500
527
  retentionSeconds,
501
- delaySeconds
528
+ delaySeconds,
529
+ headers: optionHeaders
502
530
  } = options;
503
- const headers = new Headers({
504
- Authorization: `Bearer ${await this.getToken()}`,
505
- "Content-Type": transport.contentType,
506
- ...this.customHeaders
507
- });
531
+ const headers = new Headers();
532
+ if (this.customHeaders) {
533
+ for (const [name, value] of Object.entries(this.customHeaders)) {
534
+ headers.append(name, value);
535
+ }
536
+ }
537
+ if (optionHeaders) {
538
+ const protectedHeaderNames = /* @__PURE__ */ new Set(["authorization", "content-type"]);
539
+ const isProtectedHeader = (name) => {
540
+ const lower = name.toLowerCase();
541
+ if (protectedHeaderNames.has(lower)) return true;
542
+ return lower.startsWith("vqs-");
543
+ };
544
+ for (const [name, value] of Object.entries(optionHeaders)) {
545
+ if (!isProtectedHeader(name) && value !== void 0) {
546
+ headers.append(name, value);
547
+ }
548
+ }
549
+ }
550
+ headers.set("Authorization", `Bearer ${await this.getToken()}`);
551
+ headers.set("Content-Type", transport.contentType);
508
552
  const deploymentId = this.getSendDeploymentId();
509
553
  if (deploymentId) {
510
554
  headers.set("Vqs-Deployment-Id", deploymentId);
@@ -554,6 +598,25 @@ var QueueClient = class {
554
598
  const responseData = await response.json();
555
599
  return responseData;
556
600
  }
601
+ /**
602
+ * Receive messages from a topic as an async generator.
603
+ *
604
+ * @param options - Receive options
605
+ * @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
606
+ * @param options.consumerGroup - Consumer group name (pattern: `[A-Za-z0-9_-]+`)
607
+ * @param options.visibilityTimeoutSeconds - Lock duration (default: 30, min: 0, max: 3600)
608
+ * @param options.limit - Max messages to retrieve (default: 1, min: 1, max: 10)
609
+ * @param options.maxConcurrency - Max in-flight messages (default: unlimited, min: 1)
610
+ * @param transport - Deserializer for message payloads
611
+ * @yields Message objects with payload, messageId, receiptHandle, etc.
612
+ * @throws {QueueEmptyError} When no messages available
613
+ * @throws {InvalidLimitError} When limit is outside 1-10 range
614
+ * @throws {ConcurrencyLimitError} When maxConcurrency exceeded
615
+ * @throws {BadRequestError} When parameters are invalid
616
+ * @throws {UnauthorizedError} When authentication fails
617
+ * @throws {ForbiddenError} When access is denied
618
+ * @throws {InternalServerError} When server encounters an error
619
+ */
557
620
  async *receiveMessages(options, transport) {
558
621
  const {
559
622
  queueName,
@@ -639,6 +702,26 @@ var QueueClient = class {
639
702
  }
640
703
  }
641
704
  }
705
+ /**
706
+ * Receive a specific message by its ID.
707
+ *
708
+ * @param options - Receive options
709
+ * @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
710
+ * @param options.consumerGroup - Consumer group name (pattern: `[A-Za-z0-9_-]+`)
711
+ * @param options.messageId - Message ID to retrieve
712
+ * @param options.visibilityTimeoutSeconds - Lock duration (default: 30, min: 0, max: 3600)
713
+ * @param options.maxConcurrency - Max in-flight messages (default: unlimited, min: 1)
714
+ * @param transport - Deserializer for the message payload
715
+ * @returns Promise with the message
716
+ * @throws {MessageNotFoundError} When message doesn't exist
717
+ * @throws {MessageNotAvailableError} When message is in wrong state or was a duplicate
718
+ * @throws {MessageAlreadyProcessedError} When message was already processed
719
+ * @throws {ConcurrencyLimitError} When maxConcurrency exceeded
720
+ * @throws {BadRequestError} When parameters are invalid
721
+ * @throws {UnauthorizedError} When authentication fails
722
+ * @throws {ForbiddenError} When access is denied
723
+ * @throws {InternalServerError} When server encounters an error
724
+ */
642
725
  async receiveMessageById(options, transport) {
643
726
  const {
644
727
  queueName,
@@ -733,6 +816,21 @@ var QueueClient = class {
733
816
  }
734
817
  throw new MessageNotFoundError(messageId);
735
818
  }
819
+ /**
820
+ * Delete (acknowledge) a message after successful processing.
821
+ *
822
+ * @param options - Delete options
823
+ * @param options.queueName - Topic name
824
+ * @param options.consumerGroup - Consumer group name
825
+ * @param options.receiptHandle - Receipt handle from the received message (must use same deployment ID as receive)
826
+ * @returns Promise indicating deletion success
827
+ * @throws {MessageNotFoundError} When receipt handle not found
828
+ * @throws {MessageNotAvailableError} When receipt handle invalid or message already processed
829
+ * @throws {BadRequestError} When parameters are invalid
830
+ * @throws {UnauthorizedError} When authentication fails
831
+ * @throws {ForbiddenError} When access is denied
832
+ * @throws {InternalServerError} When server encounters an error
833
+ */
736
834
  async deleteMessage(options) {
737
835
  const { queueName, consumerGroup, receiptHandle } = options;
738
836
  const headers = new Headers({
@@ -777,6 +875,23 @@ var QueueClient = class {
777
875
  }
778
876
  return { deleted: true };
779
877
  }
878
+ /**
879
+ * Extend or change the visibility timeout of a message.
880
+ * Used to prevent message redelivery while still processing.
881
+ *
882
+ * @param options - Visibility options
883
+ * @param options.queueName - Topic name
884
+ * @param options.consumerGroup - Consumer group name
885
+ * @param options.receiptHandle - Receipt handle from the received message (must use same deployment ID as receive)
886
+ * @param options.visibilityTimeoutSeconds - New timeout (min: 0, max: 3600, cannot exceed message expiration)
887
+ * @returns Promise indicating success
888
+ * @throws {MessageNotFoundError} When receipt handle not found
889
+ * @throws {MessageNotAvailableError} When receipt handle invalid or message already processed
890
+ * @throws {BadRequestError} When parameters are invalid
891
+ * @throws {UnauthorizedError} When authentication fails
892
+ * @throws {ForbiddenError} When access is denied
893
+ * @throws {InternalServerError} When server encounters an error
894
+ */
780
895
  async changeVisibility(options) {
781
896
  const {
782
897
  queueName,
@@ -899,36 +1014,26 @@ var ConsumerGroup = class {
899
1014
  refreshInterval;
900
1015
  transport;
901
1016
  /**
902
- * Create a new ConsumerGroup instance
903
- * @param client QueueClient instance to use for API calls
904
- * @param topicName Name of the topic to consume from
905
- * @param consumerGroupName Name of the consumer group
906
- * @param options Optional configuration
1017
+ * Create a new ConsumerGroup instance.
1018
+ *
1019
+ * @param client - QueueClient instance to use for API calls
1020
+ * @param topicName - Name of the topic to consume from (pattern: `[A-Za-z0-9_-]+`)
1021
+ * @param consumerGroupName - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
1022
+ * @param options - Optional configuration
1023
+ * @param options.transport - Payload serializer (default: JsonTransport)
1024
+ * @param options.visibilityTimeoutSeconds - Message lock duration (default: 30, max: 3600)
1025
+ * @param options.visibilityRefreshInterval - Lock refresh interval in seconds (default: visibilityTimeout / 3)
907
1026
  */
908
1027
  constructor(client, topicName, consumerGroupName, options = {}) {
909
1028
  this.client = client;
910
1029
  this.topicName = topicName;
911
1030
  this.consumerGroupName = consumerGroupName;
912
- this.visibilityTimeout = options.visibilityTimeoutSeconds || 30;
913
- this.refreshInterval = options.refreshInterval || 10;
1031
+ this.visibilityTimeout = options.visibilityTimeoutSeconds ?? 30;
1032
+ this.refreshInterval = options.visibilityRefreshInterval ?? Math.floor(this.visibilityTimeout / 3);
914
1033
  this.transport = options.transport || new JsonTransport();
915
1034
  }
916
1035
  /**
917
1036
  * Starts a background loop that periodically extends the visibility timeout for a message.
918
- * This prevents the message from becoming visible to other consumers while it's being processed.
919
- *
920
- * The extension loop runs every `refreshInterval` seconds and updates the message's
921
- * visibility timeout to `visibilityTimeout` seconds from the current time.
922
- *
923
- * @param receiptHandle - The receipt handle that proves ownership of the message
924
- * @returns A function that when called will stop the extension loop
925
- *
926
- * @remarks
927
- * - The first extension attempt occurs after `refreshInterval` seconds, not immediately
928
- * - If an extension fails, the loop terminates with an error logged to console
929
- * - The returned stop function is idempotent - calling it multiple times is safe
930
- * - By default, the stop function returns immediately without waiting for in-flight
931
- * - Pass `true` to the stop function to wait for any in-flight extension to complete
932
1037
  */
933
1038
  startVisibilityExtension(receiptHandle) {
934
1039
  let isRunning = true;
@@ -1090,7 +1195,8 @@ var Topic = class {
1090
1195
  payload,
1091
1196
  idempotencyKey: options?.idempotencyKey,
1092
1197
  retentionSeconds: options?.retentionSeconds,
1093
- delaySeconds: options?.delaySeconds
1198
+ delaySeconds: options?.delaySeconds,
1199
+ headers: options?.headers
1094
1200
  },
1095
1201
  this.transport
1096
1202
  );
@@ -1200,7 +1306,7 @@ async function parseCallback(request) {
1200
1306
  messageId
1201
1307
  };
1202
1308
  }
1203
- function createCallbackHandler(handlers, client) {
1309
+ function createCallbackHandler(handlers, client, visibilityTimeoutSeconds) {
1204
1310
  for (const topicPattern in handlers) {
1205
1311
  if (topicPattern.includes("*")) {
1206
1312
  if (!validateWildcardPattern(topicPattern)) {
@@ -1236,7 +1342,10 @@ function createCallbackHandler(handlers, client) {
1236
1342
  );
1237
1343
  }
1238
1344
  const topic = new Topic(client, queueName);
1239
- const cg = topic.consumerGroup(consumerGroup);
1345
+ const cg = topic.consumerGroup(
1346
+ consumerGroup,
1347
+ visibilityTimeoutSeconds !== void 0 ? { visibilityTimeoutSeconds } : void 0
1348
+ );
1240
1349
  await cg.consume(consumerGroupHandler, { messageId });
1241
1350
  return Response.json({ status: "success" });
1242
1351
  } catch (error) {
@@ -1252,8 +1361,12 @@ function createCallbackHandler(handlers, client) {
1252
1361
  };
1253
1362
  return routeHandler;
1254
1363
  }
1255
- function handleCallback(handlers, client) {
1256
- return createCallbackHandler(handlers, client || new QueueClient());
1364
+ function handleCallback(handlers, options) {
1365
+ return createCallbackHandler(
1366
+ handlers,
1367
+ options?.client || new QueueClient(),
1368
+ options?.visibilityTimeoutSeconds
1369
+ );
1257
1370
  }
1258
1371
 
1259
1372
  // src/nextjs-pages.ts