@vercel/queue 0.0.0-alpha.4 → 0.0.0-alpha.6

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.js CHANGED
@@ -23,11 +23,8 @@ __export(index_exports, {
23
23
  BadRequestError: () => BadRequestError,
24
24
  BufferTransport: () => BufferTransport,
25
25
  ConsumerGroup: () => ConsumerGroup,
26
- FailedDependencyError: () => FailedDependencyError,
27
- FifoOrderingViolationError: () => FifoOrderingViolationError,
28
26
  ForbiddenError: () => ForbiddenError,
29
27
  InternalServerError: () => InternalServerError,
30
- InvalidCallbackError: () => InvalidCallbackError,
31
28
  InvalidLimitError: () => InvalidLimitError,
32
29
  JsonTransport: () => JsonTransport,
33
30
  MessageCorruptedError: () => MessageCorruptedError,
@@ -50,116 +47,6 @@ module.exports = __toCommonJS(index_exports);
50
47
  // src/client.ts
51
48
  var import_mixpart = require("mixpart");
52
49
 
53
- // src/local.ts
54
- var import_node_child_process = require("child_process");
55
- function isLocalhostWithPort(url) {
56
- try {
57
- const parsedUrl = new URL(url);
58
- const isLocalhost = parsedUrl.hostname === "localhost";
59
- const port = parsedUrl.port ? parseInt(parsedUrl.port, 10) : 0;
60
- return { isLocalhost, port };
61
- } catch {
62
- return { isLocalhost: false };
63
- }
64
- }
65
- function isSupportedPlatform() {
66
- const platform = process.platform;
67
- return platform === "darwin" || platform === "linux";
68
- }
69
- function processDevelopmentCallbacks(callbacks) {
70
- const isDevelopment = process.env.NODE_ENV === "development";
71
- if (!isDevelopment) {
72
- return [];
73
- }
74
- if (!isSupportedPlatform()) {
75
- const hasLocalhostCallbacks = Object.values(callbacks).some((config) => {
76
- const { isLocalhost } = isLocalhostWithPort(config.url);
77
- return isLocalhost;
78
- });
79
- if (hasLocalhostCallbacks) {
80
- console.warn(
81
- `Queue Development Mode: Localhost callbacks are not supported on ${process.platform}. Localhost callback handling requires bash, nc, and curl which are available on macOS and Linux only. Consider using a production callback URL or developing on a supported platform.`
82
- );
83
- }
84
- return [];
85
- }
86
- const localhostCallbacks = [];
87
- Object.entries(callbacks).forEach(([group, config]) => {
88
- const { isLocalhost, port } = isLocalhostWithPort(config.url);
89
- if (isLocalhost && port && port > 0) {
90
- localhostCallbacks.push({ group, config, port });
91
- } else {
92
- console.warn(
93
- `Queue Development Mode: Skipping non-localhost callback for group "${group}": ${config.url}. Only localhost callbacks with explicit ports are supported in development.`
94
- );
95
- }
96
- });
97
- return localhostCallbacks;
98
- }
99
- function fireLocalhostCallbacks(localhostCallbacks, queueName, responseData) {
100
- localhostCallbacks.forEach(({ group, config, port }) => {
101
- const callbackHeaders = new Headers();
102
- callbackHeaders.set("Vqs-Message-Id", responseData.messageId);
103
- callbackHeaders.set("Vqs-Queue-Name", queueName);
104
- callbackHeaders.set("Vqs-Consumer-Group", group);
105
- fireAndForgetWaitForHttpReady(
106
- config.url,
107
- port,
108
- config.delay || 0,
109
- 3,
110
- // Default retry frequency
111
- callbackHeaders
112
- );
113
- });
114
- }
115
- function fireAndForgetWaitForHttpReady(url, port, initialDelaySeconds = 0, retryFrequencySeconds = 3, headers) {
116
- if (!isSupportedPlatform()) {
117
- console.warn(
118
- `Queue: fireAndForgetWaitForHttpReady is not supported on ${process.platform}. This function requires bash, nc, and curl which are available on macOS and Linux only.`
119
- );
120
- return;
121
- }
122
- let headerArgs = "";
123
- if (headers) {
124
- const headerArray = [];
125
- headers.forEach((value, key) => {
126
- headerArray.push(`-H '${key}: ${value}'`);
127
- });
128
- headerArgs = headerArray.join(" ");
129
- }
130
- const bashScript = `
131
- # Wait for any initial boot time
132
- sleep ${initialDelaySeconds}
133
-
134
- missed=0
135
- while true; do
136
- # 1) Check if TCP port is listening
137
- if nc -z localhost ${port} 2>/dev/null; then
138
- missed=0
139
- # 2) If port is open, try HTTP POST check
140
- if curl -sSL --fail -o /dev/null -X POST ${headerArgs} "${url}"; then
141
- # Success: port is up AND HTTP returned 2xx (following redirects)
142
- exit 0
143
- fi
144
- else
145
- # Port was closed\u2014increment miss counter
146
- ((missed+=1))
147
- # If closed twice in a row, give up immediately
148
- if [ "$missed" -ge 2 ]; then
149
- exit 1
150
- fi
151
- fi
152
- # Wait before next cycle
153
- sleep ${retryFrequencySeconds}
154
- done
155
- `;
156
- const childProcess = (0, import_node_child_process.spawn)("bash", ["-c", bashScript], {
157
- stdio: "ignore",
158
- detached: true
159
- });
160
- childProcess.unref();
161
- }
162
-
163
50
  // src/types.ts
164
51
  var MessageNotFoundError = class extends Error {
165
52
  constructor(messageId) {
@@ -175,12 +62,6 @@ var MessageNotAvailableError = class extends Error {
175
62
  this.name = "MessageNotAvailableError";
176
63
  }
177
64
  };
178
- var FifoOrderingViolationError = class extends Error {
179
- constructor(messageId, reason) {
180
- super(`FIFO ordering violation for message ${messageId}: ${reason}`);
181
- this.name = "FifoOrderingViolationError";
182
- }
183
- };
184
65
  var MessageCorruptedError = class extends Error {
185
66
  constructor(messageId, reason) {
186
67
  super(`Message ${messageId} is corrupted: ${reason}`);
@@ -222,14 +103,6 @@ var BadRequestError = class extends Error {
222
103
  this.name = "BadRequestError";
223
104
  }
224
105
  };
225
- var FailedDependencyError = class extends Error {
226
- constructor(messageId) {
227
- super(
228
- `Failed dependency: FIFO ordering violation for message ${messageId}`
229
- );
230
- this.name = "FailedDependencyError";
231
- }
232
- };
233
106
  var InternalServerError = class extends Error {
234
107
  constructor(message = "Unexpected server error") {
235
108
  super(message);
@@ -242,12 +115,6 @@ var InvalidLimitError = class extends Error {
242
115
  this.name = "InvalidLimitError";
243
116
  }
244
117
  };
245
- var InvalidCallbackError = class extends Error {
246
- constructor(message) {
247
- super(message);
248
- this.name = "InvalidCallbackError";
249
- }
250
- };
251
118
 
252
119
  // src/client.ts
253
120
  async function consumeStream(stream) {
@@ -344,46 +211,21 @@ var QueueClient = class _QueueClient {
344
211
  * @throws {InternalServerError} When server encounters an error
345
212
  */
346
213
  async sendMessage(options, transport) {
347
- const { queueName, payload, idempotencyKey, retentionSeconds, callback } = options;
214
+ const { queueName, payload, idempotencyKey, retentionSeconds } = options;
348
215
  const headers = new Headers({
349
216
  Authorization: `Bearer ${this.token}`,
350
217
  "Vqs-Queue-Name": queueName,
351
218
  "Content-Type": transport.contentType
352
219
  });
220
+ if (process.env.VERCEL_DEPLOYMENT_ID) {
221
+ headers.set("Vqs-Deployment-Id", process.env.VERCEL_DEPLOYMENT_ID);
222
+ }
353
223
  if (idempotencyKey) {
354
224
  headers.set("Vqs-Idempotency-Key", idempotencyKey);
355
225
  }
356
226
  if (retentionSeconds !== void 0) {
357
227
  headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
358
228
  }
359
- let normalizedCallbacks;
360
- if (callback) {
361
- if ("url" in callback && typeof callback.url === "string") {
362
- normalizedCallbacks = { default: callback };
363
- } else {
364
- normalizedCallbacks = callback;
365
- }
366
- }
367
- let localhostCallbacks = [];
368
- if (normalizedCallbacks) {
369
- const isDevelopment = process.env.NODE_ENV === "development";
370
- if (isDevelopment) {
371
- localhostCallbacks = processDevelopmentCallbacks(normalizedCallbacks);
372
- } else {
373
- const endpoints = Object.entries(normalizedCallbacks).map(
374
- ([group, config]) => `${group}=${Buffer.from(config.url).toString("base64")}`
375
- ).join(",");
376
- headers.set("Vqs-Callback-Url", endpoints);
377
- const delays = Object.entries(normalizedCallbacks).filter(([, config]) => config.delay !== void 0).map(([group, config]) => `${group}=${config.delay}`).join(",");
378
- if (delays) {
379
- headers.set("Vqs-Callback-Delay", delays);
380
- }
381
- const frequencies = Object.entries(normalizedCallbacks).filter(([, config]) => config.frequency !== void 0).map(([group, config]) => `${group}=${config.frequency}`).join(",");
382
- if (frequencies) {
383
- headers.set("Vqs-Callback-Frequency", frequencies);
384
- }
385
- }
386
- }
387
229
  const body = transport.serialize(payload);
388
230
  const response = await fetch(`${this.baseUrl}/api/v2/messages`, {
389
231
  method: "POST",
@@ -414,9 +256,6 @@ var QueueClient = class _QueueClient {
414
256
  );
415
257
  }
416
258
  const responseData = await response.json();
417
- if (localhostCallbacks.length > 0) {
418
- fireLocalhostCallbacks(localhostCallbacks, queueName, responseData);
419
- }
420
259
  return responseData;
421
260
  }
422
261
  /**
@@ -426,7 +265,7 @@ var QueueClient = class _QueueClient {
426
265
  * @returns AsyncGenerator that yields messages as they arrive
427
266
  * @throws {InvalidLimitError} When limit parameter is not between 1 and 10
428
267
  * @throws {QueueEmptyError} When no messages are available (204)
429
- * @throws {MessageLockedError} When FIFO queue has locked messages (423)
268
+ * @throws {MessageLockedError} When messages are temporarily locked (423)
430
269
  * @throws {BadRequestError} When request parameters are invalid
431
270
  * @throws {UnauthorizedError} When authentication fails
432
271
  * @throws {ForbiddenError} When access is denied (environment mismatch)
@@ -477,7 +316,7 @@ var QueueClient = class _QueueClient {
477
316
  const parsed = parseInt(retryAfterHeader, 10);
478
317
  retryAfter = isNaN(parsed) ? void 0 : parsed;
479
318
  }
480
- throw new MessageLockedError("next message in FIFO queue", retryAfter);
319
+ throw new MessageLockedError("next message", retryAfter);
481
320
  }
482
321
  if (response.status >= 500) {
483
322
  throw new InternalServerError(
@@ -563,9 +402,6 @@ var QueueClient = class _QueueClient {
563
402
  }
564
403
  throw new MessageLockedError(messageId, retryAfter);
565
404
  }
566
- if (response.status === 424) {
567
- throw new FailedDependencyError(messageId);
568
- }
569
405
  if (response.status === 409) {
570
406
  throw new MessageNotAvailableError(messageId);
571
407
  }
@@ -761,24 +597,20 @@ var JsonTransport = class {
761
597
  }
762
598
  async deserialize(stream) {
763
599
  const reader = stream.getReader();
600
+ let totalLength = 0;
764
601
  const chunks = [];
765
602
  try {
766
603
  while (true) {
767
604
  const { done, value } = await reader.read();
768
605
  if (done) break;
769
606
  chunks.push(value);
607
+ totalLength += value.length;
770
608
  }
771
609
  } finally {
772
610
  reader.releaseLock();
773
611
  }
774
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
775
- const buffer = new Uint8Array(totalLength);
776
- let offset = 0;
777
- for (const chunk of chunks) {
778
- buffer.set(chunk, offset);
779
- offset += chunk.length;
780
- }
781
- return JSON.parse(Buffer.from(buffer).toString("utf8"));
612
+ const buffer = Buffer.concat(chunks, totalLength);
613
+ return JSON.parse(buffer.toString("utf8"));
782
614
  }
783
615
  };
784
616
  var BufferTransport = class {
@@ -1060,8 +892,7 @@ var Topic = class {
1060
892
  queueName: this.topicName,
1061
893
  payload,
1062
894
  idempotencyKey: options?.idempotencyKey,
1063
- retentionSeconds: options?.retentionSeconds,
1064
- callback: options?.callback
895
+ retentionSeconds: options?.retentionSeconds
1065
896
  },
1066
897
  this.transport
1067
898
  );
@@ -1112,8 +943,7 @@ async function send(topicName, payload, options) {
1112
943
  queueName: topicName,
1113
944
  payload,
1114
945
  idempotencyKey: options?.idempotencyKey,
1115
- retentionSeconds: options?.retentionSeconds,
1116
- callback: options?.callback
946
+ retentionSeconds: options?.retentionSeconds
1117
947
  },
1118
948
  transport
1119
949
  );
@@ -1140,23 +970,22 @@ async function receive(topicName, consumerGroup, handler, options) {
1140
970
 
1141
971
  // src/callback.ts
1142
972
  function parseCallbackRequest(request) {
1143
- const headers = request.headers;
1144
- const messageId = headers.get("Vqs-Message-Id");
1145
- const queueName = headers.get("Vqs-Queue-Name");
1146
- const consumerGroup = headers.get("Vqs-Consumer-Group");
1147
- const missingHeaders = [];
1148
- if (!messageId) missingHeaders.push("Vqs-Message-Id");
1149
- if (!queueName) missingHeaders.push("Vqs-Queue-Name");
1150
- if (!consumerGroup) missingHeaders.push("Vqs-Consumer-Group");
1151
- if (missingHeaders.length > 0) {
1152
- throw new InvalidCallbackError(
1153
- `Missing required queue callback headers: ${missingHeaders.join(", ")}`
973
+ const queueName = request.headers.get("Vqs-Queue-Name");
974
+ const consumerGroup = request.headers.get("Vqs-Consumer-Group");
975
+ const messageId = request.headers.get("Vqs-Message-Id");
976
+ if (!queueName || !consumerGroup || !messageId) {
977
+ const missingHeaders = [];
978
+ if (!queueName) missingHeaders.push("Vqs-Queue-Name");
979
+ if (!consumerGroup) missingHeaders.push("Vqs-Consumer-Group");
980
+ if (!messageId) missingHeaders.push("Vqs-Message-Id");
981
+ throw new Error(
982
+ `Missing required queue headers: ${missingHeaders.join(", ")}`
1154
983
  );
1155
984
  }
1156
985
  return {
1157
- messageId,
1158
986
  queueName,
1159
- consumerGroup
987
+ consumerGroup,
988
+ messageId
1160
989
  };
1161
990
  }
1162
991
  function handleCallback(handlers) {
@@ -1165,41 +994,38 @@ function handleCallback(handlers) {
1165
994
  const { queueName, consumerGroup, messageId } = parseCallbackRequest(request);
1166
995
  const topicHandler = handlers[queueName];
1167
996
  if (!topicHandler) {
1168
- throw new Error(`No handler found for topic: ${queueName}`);
997
+ const availableTopics = Object.keys(handlers).join(", ");
998
+ return Response.json(
999
+ {
1000
+ error: `No handler found for topic: ${queueName}`,
1001
+ availableTopics
1002
+ },
1003
+ { status: 404 }
1004
+ );
1169
1005
  }
1170
- let actualHandler;
1171
- if (typeof topicHandler === "function") {
1172
- if (consumerGroup !== "default") {
1173
- throw new Error(
1174
- `Topic "${queueName}" has a single handler but received consumer group "${consumerGroup}". Expected "default".`
1175
- );
1176
- }
1177
- actualHandler = topicHandler;
1178
- } else {
1179
- const consumerGroupHandler = topicHandler[consumerGroup];
1180
- if (!consumerGroupHandler) {
1181
- const availableGroups = Object.keys(topicHandler).join(", ");
1182
- throw new Error(
1183
- `No handler found for consumer group "${consumerGroup}" in topic "${queueName}". Available groups: ${availableGroups}`
1184
- );
1185
- }
1186
- actualHandler = consumerGroupHandler;
1006
+ const consumerGroupHandler = topicHandler[consumerGroup];
1007
+ if (!consumerGroupHandler) {
1008
+ const availableGroups = Object.keys(topicHandler).join(", ");
1009
+ return Response.json(
1010
+ {
1011
+ error: `No handler found for consumer group "${consumerGroup}" in topic "${queueName}".`,
1012
+ availableGroups
1013
+ },
1014
+ { status: 404 }
1015
+ );
1187
1016
  }
1188
1017
  const client = new QueueClient();
1189
1018
  const topic = new Topic(client, queueName);
1190
1019
  const cg = topic.consumerGroup(consumerGroup);
1191
- await cg.consume(actualHandler, { messageId });
1020
+ await cg.consume(consumerGroupHandler, { messageId });
1192
1021
  return Response.json({ status: "success" });
1193
1022
  } catch (error) {
1194
- console.error("Callback error:", error);
1195
- if (error instanceof InvalidCallbackError) {
1196
- return Response.json(
1197
- { error: "Invalid callback request" },
1198
- { status: 400 }
1199
- );
1023
+ console.error("Queue callback error:", error);
1024
+ if (error instanceof Error && error.message.includes("Missing required queue headers")) {
1025
+ return Response.json({ error: error.message }, { status: 400 });
1200
1026
  }
1201
1027
  return Response.json(
1202
- { error: "Failed to process callback" },
1028
+ { error: "Failed to process queue message" },
1203
1029
  { status: 500 }
1204
1030
  );
1205
1031
  }
@@ -1210,11 +1036,8 @@ function handleCallback(handlers) {
1210
1036
  BadRequestError,
1211
1037
  BufferTransport,
1212
1038
  ConsumerGroup,
1213
- FailedDependencyError,
1214
- FifoOrderingViolationError,
1215
1039
  ForbiddenError,
1216
1040
  InternalServerError,
1217
- InvalidCallbackError,
1218
1041
  InvalidLimitError,
1219
1042
  JsonTransport,
1220
1043
  MessageCorruptedError,