@vercel/queue 0.0.0-alpha.5 → 0.0.0-alpha.7

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
@@ -22,143 +22,97 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  BadRequestError: () => BadRequestError,
24
24
  BufferTransport: () => BufferTransport,
25
- ConsumerGroup: () => ConsumerGroup,
26
- FailedDependencyError: () => FailedDependencyError,
27
- FifoOrderingViolationError: () => FifoOrderingViolationError,
28
25
  ForbiddenError: () => ForbiddenError,
29
26
  InternalServerError: () => InternalServerError,
30
- InvalidCallbackError: () => InvalidCallbackError,
31
27
  InvalidLimitError: () => InvalidLimitError,
32
28
  JsonTransport: () => JsonTransport,
33
29
  MessageCorruptedError: () => MessageCorruptedError,
34
30
  MessageLockedError: () => MessageLockedError,
35
31
  MessageNotAvailableError: () => MessageNotAvailableError,
36
32
  MessageNotFoundError: () => MessageNotFoundError,
37
- QueueClient: () => QueueClient,
38
33
  QueueEmptyError: () => QueueEmptyError,
39
34
  StreamTransport: () => StreamTransport,
40
- Topic: () => Topic,
41
35
  UnauthorizedError: () => UnauthorizedError,
42
- createTopic: () => createTopic,
43
36
  handleCallback: () => handleCallback,
44
- parseCallbackRequest: () => parseCallbackRequest,
45
37
  receive: () => receive,
46
38
  send: () => send
47
39
  });
48
40
  module.exports = __toCommonJS(index_exports);
49
41
 
50
- // src/client.ts
51
- var import_mixpart = require("mixpart");
52
-
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 [];
42
+ // src/transports.ts
43
+ var JsonTransport = class {
44
+ contentType = "application/json";
45
+ serialize(value) {
46
+ return Buffer.from(JSON.stringify(value), "utf8");
73
47
  }
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
- );
48
+ async deserialize(stream) {
49
+ const reader = stream.getReader();
50
+ let totalLength = 0;
51
+ const chunks = [];
52
+ try {
53
+ while (true) {
54
+ const { done, value } = await reader.read();
55
+ if (done) break;
56
+ chunks.push(value);
57
+ totalLength += value.length;
58
+ }
59
+ } finally {
60
+ reader.releaseLock();
83
61
  }
84
- return [];
62
+ const buffer = Buffer.concat(chunks, totalLength);
63
+ return JSON.parse(buffer.toString("utf8"));
85
64
  }
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
- );
65
+ };
66
+ var BufferTransport = class {
67
+ contentType = "application/octet-stream";
68
+ serialize(value) {
69
+ return value;
70
+ }
71
+ async deserialize(stream) {
72
+ const reader = stream.getReader();
73
+ const chunks = [];
74
+ try {
75
+ while (true) {
76
+ const { done, value } = await reader.read();
77
+ if (done) break;
78
+ chunks.push(value);
79
+ }
80
+ } finally {
81
+ reader.releaseLock();
95
82
  }
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;
83
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
84
+ const buffer = new Uint8Array(totalLength);
85
+ let offset = 0;
86
+ for (const chunk of chunks) {
87
+ buffer.set(chunk, offset);
88
+ offset += chunk.length;
89
+ }
90
+ return Buffer.from(buffer);
121
91
  }
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(" ");
92
+ };
93
+ var StreamTransport = class {
94
+ contentType = "application/octet-stream";
95
+ serialize(value) {
96
+ return value;
129
97
  }
130
- const bashScript = `
131
- # Wait for any initial boot time
132
- sleep ${initialDelaySeconds}
98
+ async deserialize(stream) {
99
+ return stream;
100
+ }
101
+ async finalize(payload) {
102
+ const reader = payload.getReader();
103
+ try {
104
+ while (true) {
105
+ const { done } = await reader.read();
106
+ if (done) break;
107
+ }
108
+ } finally {
109
+ reader.releaseLock();
110
+ }
111
+ }
112
+ };
133
113
 
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
- }
114
+ // src/client.ts
115
+ var import_mixpart = require("mixpart");
162
116
 
163
117
  // src/types.ts
164
118
  var MessageNotFoundError = class extends Error {
@@ -175,12 +129,6 @@ var MessageNotAvailableError = class extends Error {
175
129
  this.name = "MessageNotAvailableError";
176
130
  }
177
131
  };
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
132
  var MessageCorruptedError = class extends Error {
185
133
  constructor(messageId, reason) {
186
134
  super(`Message ${messageId} is corrupted: ${reason}`);
@@ -222,14 +170,6 @@ var BadRequestError = class extends Error {
222
170
  this.name = "BadRequestError";
223
171
  }
224
172
  };
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
173
  var InternalServerError = class extends Error {
234
174
  constructor(message = "Unexpected server error") {
235
175
  super(message);
@@ -242,12 +182,6 @@ var InvalidLimitError = class extends Error {
242
182
  this.name = "InvalidLimitError";
243
183
  }
244
184
  };
245
- var InvalidCallbackError = class extends Error {
246
- constructor(message) {
247
- super(message);
248
- this.name = "InvalidCallbackError";
249
- }
250
- };
251
185
 
252
186
  // src/client.ts
253
187
  async function consumeStream(stream) {
@@ -286,7 +220,7 @@ var QueueClient = class _QueueClient {
286
220
  baseUrl;
287
221
  token;
288
222
  /**
289
- * Internal default instance for use by createTopic and other convenience functions
223
+ * Internal default instance for use by convenience functions
290
224
  * @internal
291
225
  */
292
226
  static _defaultInstance = null;
@@ -344,7 +278,7 @@ var QueueClient = class _QueueClient {
344
278
  * @throws {InternalServerError} When server encounters an error
345
279
  */
346
280
  async sendMessage(options, transport) {
347
- const { queueName, payload, idempotencyKey, retentionSeconds, callback } = options;
281
+ const { queueName, payload, idempotencyKey, retentionSeconds } = options;
348
282
  const headers = new Headers({
349
283
  Authorization: `Bearer ${this.token}`,
350
284
  "Vqs-Queue-Name": queueName,
@@ -359,34 +293,6 @@ var QueueClient = class _QueueClient {
359
293
  if (retentionSeconds !== void 0) {
360
294
  headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
361
295
  }
362
- let normalizedCallbacks;
363
- if (callback) {
364
- if ("url" in callback && typeof callback.url === "string") {
365
- normalizedCallbacks = { default: callback };
366
- } else {
367
- normalizedCallbacks = callback;
368
- }
369
- }
370
- let localhostCallbacks = [];
371
- if (normalizedCallbacks) {
372
- const isDevelopment = process.env.NODE_ENV === "development";
373
- if (isDevelopment) {
374
- localhostCallbacks = processDevelopmentCallbacks(normalizedCallbacks);
375
- } else {
376
- const endpoints = Object.entries(normalizedCallbacks).map(
377
- ([group, config]) => `${group}=${Buffer.from(config.url).toString("base64")}`
378
- ).join(",");
379
- headers.set("Vqs-Callback-Url", endpoints);
380
- const delays = Object.entries(normalizedCallbacks).filter(([, config]) => config.delay !== void 0).map(([group, config]) => `${group}=${config.delay}`).join(",");
381
- if (delays) {
382
- headers.set("Vqs-Callback-Delay", delays);
383
- }
384
- const frequencies = Object.entries(normalizedCallbacks).filter(([, config]) => config.frequency !== void 0).map(([group, config]) => `${group}=${config.frequency}`).join(",");
385
- if (frequencies) {
386
- headers.set("Vqs-Callback-Frequency", frequencies);
387
- }
388
- }
389
- }
390
296
  const body = transport.serialize(payload);
391
297
  const response = await fetch(`${this.baseUrl}/api/v2/messages`, {
392
298
  method: "POST",
@@ -417,9 +323,6 @@ var QueueClient = class _QueueClient {
417
323
  );
418
324
  }
419
325
  const responseData = await response.json();
420
- if (localhostCallbacks.length > 0) {
421
- fireLocalhostCallbacks(localhostCallbacks, queueName, responseData);
422
- }
423
326
  return responseData;
424
327
  }
425
328
  /**
@@ -429,7 +332,7 @@ var QueueClient = class _QueueClient {
429
332
  * @returns AsyncGenerator that yields messages as they arrive
430
333
  * @throws {InvalidLimitError} When limit parameter is not between 1 and 10
431
334
  * @throws {QueueEmptyError} When no messages are available (204)
432
- * @throws {MessageLockedError} When FIFO queue has locked messages (423)
335
+ * @throws {MessageLockedError} When messages are temporarily locked (423)
433
336
  * @throws {BadRequestError} When request parameters are invalid
434
337
  * @throws {UnauthorizedError} When authentication fails
435
338
  * @throws {ForbiddenError} When access is denied (environment mismatch)
@@ -480,7 +383,7 @@ var QueueClient = class _QueueClient {
480
383
  const parsed = parseInt(retryAfterHeader, 10);
481
384
  retryAfter = isNaN(parsed) ? void 0 : parsed;
482
385
  }
483
- throw new MessageLockedError("next message in FIFO queue", retryAfter);
386
+ throw new MessageLockedError("next message", retryAfter);
484
387
  }
485
388
  if (response.status >= 500) {
486
389
  throw new InternalServerError(
@@ -566,9 +469,6 @@ var QueueClient = class _QueueClient {
566
469
  }
567
470
  throw new MessageLockedError(messageId, retryAfter);
568
471
  }
569
- if (response.status === 424) {
570
- throw new FailedDependencyError(messageId);
571
- }
572
472
  if (response.status === 409) {
573
473
  throw new MessageNotAvailableError(messageId);
574
474
  }
@@ -756,82 +656,6 @@ var QueueClient = class _QueueClient {
756
656
  }
757
657
  };
758
658
 
759
- // src/transports.ts
760
- var JsonTransport = class {
761
- contentType = "application/json";
762
- serialize(value) {
763
- return Buffer.from(JSON.stringify(value), "utf8");
764
- }
765
- async deserialize(stream) {
766
- const reader = stream.getReader();
767
- const chunks = [];
768
- try {
769
- while (true) {
770
- const { done, value } = await reader.read();
771
- if (done) break;
772
- chunks.push(value);
773
- }
774
- } finally {
775
- reader.releaseLock();
776
- }
777
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
778
- const buffer = new Uint8Array(totalLength);
779
- let offset = 0;
780
- for (const chunk of chunks) {
781
- buffer.set(chunk, offset);
782
- offset += chunk.length;
783
- }
784
- return JSON.parse(Buffer.from(buffer).toString("utf8"));
785
- }
786
- };
787
- var BufferTransport = class {
788
- contentType = "application/octet-stream";
789
- serialize(value) {
790
- return value;
791
- }
792
- async deserialize(stream) {
793
- const reader = stream.getReader();
794
- const chunks = [];
795
- try {
796
- while (true) {
797
- const { done, value } = await reader.read();
798
- if (done) break;
799
- chunks.push(value);
800
- }
801
- } finally {
802
- reader.releaseLock();
803
- }
804
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
805
- const buffer = new Uint8Array(totalLength);
806
- let offset = 0;
807
- for (const chunk of chunks) {
808
- buffer.set(chunk, offset);
809
- offset += chunk.length;
810
- }
811
- return Buffer.from(buffer);
812
- }
813
- };
814
- var StreamTransport = class {
815
- contentType = "application/octet-stream";
816
- serialize(value) {
817
- return value;
818
- }
819
- async deserialize(stream) {
820
- return stream;
821
- }
822
- async finalize(payload) {
823
- const reader = payload.getReader();
824
- try {
825
- while (true) {
826
- const { done } = await reader.read();
827
- if (done) break;
828
- }
829
- } finally {
830
- reader.releaseLock();
831
- }
832
- }
833
- };
834
-
835
659
  // src/consumer-group.ts
836
660
  var ConsumerGroup = class {
837
661
  client;
@@ -1063,8 +887,7 @@ var Topic = class {
1063
887
  queueName: this.topicName,
1064
888
  payload,
1065
889
  idempotencyKey: options?.idempotencyKey,
1066
- retentionSeconds: options?.retentionSeconds,
1067
- callback: options?.callback
890
+ retentionSeconds: options?.retentionSeconds
1068
891
  },
1069
892
  this.transport
1070
893
  );
@@ -1103,10 +926,6 @@ var Topic = class {
1103
926
  };
1104
927
 
1105
928
  // src/factory.ts
1106
- function createTopic(topicName, transport) {
1107
- const client = QueueClient._getDefaultInstance();
1108
- return new Topic(client, topicName, transport);
1109
- }
1110
929
  async function send(topicName, payload, options) {
1111
930
  const transport = options?.transport || new JsonTransport();
1112
931
  const client = QueueClient._getDefaultInstance();
@@ -1115,8 +934,7 @@ async function send(topicName, payload, options) {
1115
934
  queueName: topicName,
1116
935
  payload,
1117
936
  idempotencyKey: options?.idempotencyKey,
1118
- retentionSeconds: options?.retentionSeconds,
1119
- callback: options?.callback
937
+ retentionSeconds: options?.retentionSeconds
1120
938
  },
1121
939
  transport
1122
940
  );
@@ -1124,7 +942,8 @@ async function send(topicName, payload, options) {
1124
942
  }
1125
943
  async function receive(topicName, consumerGroup, handler, options) {
1126
944
  const transport = options?.transport || new JsonTransport();
1127
- const topic = createTopic(topicName, transport);
945
+ const client = QueueClient._getDefaultInstance();
946
+ const topic = new Topic(client, topicName, transport);
1128
947
  const { messageId, skipPayload, ...consumerGroupOptions } = options || {};
1129
948
  const consumer = topic.consumerGroup(consumerGroup, consumerGroupOptions);
1130
949
  if (messageId) {
@@ -1143,23 +962,22 @@ async function receive(topicName, consumerGroup, handler, options) {
1143
962
 
1144
963
  // src/callback.ts
1145
964
  function parseCallbackRequest(request) {
1146
- const headers = request.headers;
1147
- const messageId = headers.get("Vqs-Message-Id");
1148
- const queueName = headers.get("Vqs-Queue-Name");
1149
- const consumerGroup = headers.get("Vqs-Consumer-Group");
1150
- const missingHeaders = [];
1151
- if (!messageId) missingHeaders.push("Vqs-Message-Id");
1152
- if (!queueName) missingHeaders.push("Vqs-Queue-Name");
1153
- if (!consumerGroup) missingHeaders.push("Vqs-Consumer-Group");
1154
- if (missingHeaders.length > 0) {
1155
- throw new InvalidCallbackError(
1156
- `Missing required queue callback headers: ${missingHeaders.join(", ")}`
965
+ const queueName = request.headers.get("Vqs-Queue-Name");
966
+ const consumerGroup = request.headers.get("Vqs-Consumer-Group");
967
+ const messageId = request.headers.get("Vqs-Message-Id");
968
+ if (!queueName || !consumerGroup || !messageId) {
969
+ const missingHeaders = [];
970
+ if (!queueName) missingHeaders.push("Vqs-Queue-Name");
971
+ if (!consumerGroup) missingHeaders.push("Vqs-Consumer-Group");
972
+ if (!messageId) missingHeaders.push("Vqs-Message-Id");
973
+ throw new Error(
974
+ `Missing required queue headers: ${missingHeaders.join(", ")}`
1157
975
  );
1158
976
  }
1159
977
  return {
1160
- messageId,
1161
978
  queueName,
1162
- consumerGroup
979
+ consumerGroup,
980
+ messageId
1163
981
  };
1164
982
  }
1165
983
  function handleCallback(handlers) {
@@ -1168,41 +986,38 @@ function handleCallback(handlers) {
1168
986
  const { queueName, consumerGroup, messageId } = parseCallbackRequest(request);
1169
987
  const topicHandler = handlers[queueName];
1170
988
  if (!topicHandler) {
1171
- throw new Error(`No handler found for topic: ${queueName}`);
989
+ const availableTopics = Object.keys(handlers).join(", ");
990
+ return Response.json(
991
+ {
992
+ error: `No handler found for topic: ${queueName}`,
993
+ availableTopics
994
+ },
995
+ { status: 404 }
996
+ );
1172
997
  }
1173
- let actualHandler;
1174
- if (typeof topicHandler === "function") {
1175
- if (consumerGroup !== "default") {
1176
- throw new Error(
1177
- `Topic "${queueName}" has a single handler but received consumer group "${consumerGroup}". Expected "default".`
1178
- );
1179
- }
1180
- actualHandler = topicHandler;
1181
- } else {
1182
- const consumerGroupHandler = topicHandler[consumerGroup];
1183
- if (!consumerGroupHandler) {
1184
- const availableGroups = Object.keys(topicHandler).join(", ");
1185
- throw new Error(
1186
- `No handler found for consumer group "${consumerGroup}" in topic "${queueName}". Available groups: ${availableGroups}`
1187
- );
1188
- }
1189
- actualHandler = consumerGroupHandler;
998
+ const consumerGroupHandler = topicHandler[consumerGroup];
999
+ if (!consumerGroupHandler) {
1000
+ const availableGroups = Object.keys(topicHandler).join(", ");
1001
+ return Response.json(
1002
+ {
1003
+ error: `No handler found for consumer group "${consumerGroup}" in topic "${queueName}".`,
1004
+ availableGroups
1005
+ },
1006
+ { status: 404 }
1007
+ );
1190
1008
  }
1191
1009
  const client = new QueueClient();
1192
1010
  const topic = new Topic(client, queueName);
1193
1011
  const cg = topic.consumerGroup(consumerGroup);
1194
- await cg.consume(actualHandler, { messageId });
1012
+ await cg.consume(consumerGroupHandler, { messageId });
1195
1013
  return Response.json({ status: "success" });
1196
1014
  } catch (error) {
1197
- console.error("Callback error:", error);
1198
- if (error instanceof InvalidCallbackError) {
1199
- return Response.json(
1200
- { error: "Invalid callback request" },
1201
- { status: 400 }
1202
- );
1015
+ console.error("Queue callback error:", error);
1016
+ if (error instanceof Error && error.message.includes("Missing required queue headers")) {
1017
+ return Response.json({ error: error.message }, { status: 400 });
1203
1018
  }
1204
1019
  return Response.json(
1205
- { error: "Failed to process callback" },
1020
+ { error: "Failed to process queue message" },
1206
1021
  { status: 500 }
1207
1022
  );
1208
1023
  }
@@ -1212,26 +1027,18 @@ function handleCallback(handlers) {
1212
1027
  0 && (module.exports = {
1213
1028
  BadRequestError,
1214
1029
  BufferTransport,
1215
- ConsumerGroup,
1216
- FailedDependencyError,
1217
- FifoOrderingViolationError,
1218
1030
  ForbiddenError,
1219
1031
  InternalServerError,
1220
- InvalidCallbackError,
1221
1032
  InvalidLimitError,
1222
1033
  JsonTransport,
1223
1034
  MessageCorruptedError,
1224
1035
  MessageLockedError,
1225
1036
  MessageNotAvailableError,
1226
1037
  MessageNotFoundError,
1227
- QueueClient,
1228
1038
  QueueEmptyError,
1229
1039
  StreamTransport,
1230
- Topic,
1231
1040
  UnauthorizedError,
1232
- createTopic,
1233
1041
  handleCallback,
1234
- parseCallbackRequest,
1235
1042
  receive,
1236
1043
  send
1237
1044
  });