@vercel/queue 0.0.0-alpha.11 → 0.0.0-alpha.13

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.d.mts CHANGED
@@ -27,6 +27,12 @@ interface Transport<T = unknown> {
27
27
  */
28
28
  declare class JsonTransport<T = unknown> implements Transport<T> {
29
29
  readonly contentType = "application/json";
30
+ readonly replacer?: Parameters<typeof JSON.parse>[1];
31
+ readonly reviver?: Parameters<typeof JSON.parse>[1];
32
+ constructor(options?: {
33
+ replacer?: Parameters<typeof JSON.parse>[1];
34
+ reviver?: Parameters<typeof JSON.parse>[1];
35
+ });
30
36
  serialize(value: T): Buffer;
31
37
  deserialize(stream: ReadableStream<Uint8Array>): Promise<T>;
32
38
  }
@@ -152,6 +158,8 @@ interface MessageMetadata {
152
158
  messageId: string;
153
159
  deliveryCount: number;
154
160
  createdAt: Date;
161
+ topicName: string;
162
+ consumerGroup: string;
155
163
  }
156
164
  /**
157
165
  * Message handler function type
@@ -325,6 +333,42 @@ type CallbackHandlers = {
325
333
  [consumerGroup: string]: MessageHandler;
326
334
  };
327
335
  };
336
+ /**
337
+ * Parsed callback request information
338
+ */
339
+ type ParsedCallbackRequest = {
340
+ queueName: string;
341
+ consumerGroup: string;
342
+ messageId: string;
343
+ };
344
+ /**
345
+ * Parse and validate callback request using CloudEvent specification
346
+ *
347
+ * Extracts queue information from CloudEvent format and validates
348
+ * that all required fields are present.
349
+ *
350
+ * @param request The incoming webhook request
351
+ * @returns Parsed queue information
352
+ * @throws Error if required fields are missing
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * // In Next.js API route
357
+ * export async function POST(request: Request) {
358
+ * try {
359
+ * const { queueName, consumerGroup, messageId } = parseCallback(request);
360
+ *
361
+ * // Use the parsed information...
362
+ * await myWorkflow.handleWebhook(queueName, consumerGroup, messageId);
363
+ *
364
+ * return Response.json({ status: "success" });
365
+ * } catch (error) {
366
+ * return Response.json({ error: error.message }, { status: 400 });
367
+ * }
368
+ * }
369
+ * ```
370
+ */
371
+ declare function parseCallback(request: Request): Promise<ParsedCallbackRequest>;
328
372
  /**
329
373
  * Simplified queue callback handler for Next.js route handlers
330
374
  *
@@ -359,4 +403,4 @@ type CallbackHandlers = {
359
403
  */
360
404
  declare function handleCallback(handlers: CallbackHandlers): (request: Request) => Promise<Response>;
361
405
 
362
- export { BadRequestError, BufferTransport, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, type PublishOptions, QueueEmptyError, type ReceiveOptions, type SendMessageOptions, type SendMessageResponse, type SendOptions, StreamTransport, type Transport, UnauthorizedError, handleCallback, receive, send };
406
+ export { BadRequestError, BufferTransport, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, type ParsedCallbackRequest, type PublishOptions, QueueEmptyError, type ReceiveOptions, type SendMessageOptions, type SendMessageResponse, type SendOptions, StreamTransport, type Transport, UnauthorizedError, handleCallback, parseCallback, receive, send };
package/dist/index.d.ts CHANGED
@@ -27,6 +27,12 @@ interface Transport<T = unknown> {
27
27
  */
28
28
  declare class JsonTransport<T = unknown> implements Transport<T> {
29
29
  readonly contentType = "application/json";
30
+ readonly replacer?: Parameters<typeof JSON.parse>[1];
31
+ readonly reviver?: Parameters<typeof JSON.parse>[1];
32
+ constructor(options?: {
33
+ replacer?: Parameters<typeof JSON.parse>[1];
34
+ reviver?: Parameters<typeof JSON.parse>[1];
35
+ });
30
36
  serialize(value: T): Buffer;
31
37
  deserialize(stream: ReadableStream<Uint8Array>): Promise<T>;
32
38
  }
@@ -152,6 +158,8 @@ interface MessageMetadata {
152
158
  messageId: string;
153
159
  deliveryCount: number;
154
160
  createdAt: Date;
161
+ topicName: string;
162
+ consumerGroup: string;
155
163
  }
156
164
  /**
157
165
  * Message handler function type
@@ -325,6 +333,42 @@ type CallbackHandlers = {
325
333
  [consumerGroup: string]: MessageHandler;
326
334
  };
327
335
  };
336
+ /**
337
+ * Parsed callback request information
338
+ */
339
+ type ParsedCallbackRequest = {
340
+ queueName: string;
341
+ consumerGroup: string;
342
+ messageId: string;
343
+ };
344
+ /**
345
+ * Parse and validate callback request using CloudEvent specification
346
+ *
347
+ * Extracts queue information from CloudEvent format and validates
348
+ * that all required fields are present.
349
+ *
350
+ * @param request The incoming webhook request
351
+ * @returns Parsed queue information
352
+ * @throws Error if required fields are missing
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * // In Next.js API route
357
+ * export async function POST(request: Request) {
358
+ * try {
359
+ * const { queueName, consumerGroup, messageId } = parseCallback(request);
360
+ *
361
+ * // Use the parsed information...
362
+ * await myWorkflow.handleWebhook(queueName, consumerGroup, messageId);
363
+ *
364
+ * return Response.json({ status: "success" });
365
+ * } catch (error) {
366
+ * return Response.json({ error: error.message }, { status: 400 });
367
+ * }
368
+ * }
369
+ * ```
370
+ */
371
+ declare function parseCallback(request: Request): Promise<ParsedCallbackRequest>;
328
372
  /**
329
373
  * Simplified queue callback handler for Next.js route handlers
330
374
  *
@@ -359,4 +403,4 @@ type CallbackHandlers = {
359
403
  */
360
404
  declare function handleCallback(handlers: CallbackHandlers): (request: Request) => Promise<Response>;
361
405
 
362
- export { BadRequestError, BufferTransport, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, type PublishOptions, QueueEmptyError, type ReceiveOptions, type SendMessageOptions, type SendMessageResponse, type SendOptions, StreamTransport, type Transport, UnauthorizedError, handleCallback, receive, send };
406
+ export { BadRequestError, BufferTransport, ForbiddenError, InternalServerError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, type ParsedCallbackRequest, type PublishOptions, QueueEmptyError, type ReceiveOptions, type SendMessageOptions, type SendMessageResponse, type SendOptions, StreamTransport, type Transport, UnauthorizedError, handleCallback, parseCallback, receive, send };
package/dist/index.js CHANGED
@@ -34,33 +34,43 @@ __export(index_exports, {
34
34
  StreamTransport: () => StreamTransport,
35
35
  UnauthorizedError: () => UnauthorizedError,
36
36
  handleCallback: () => handleCallback,
37
+ parseCallback: () => parseCallback,
37
38
  receive: () => receive,
38
39
  send: () => send
39
40
  });
40
41
  module.exports = __toCommonJS(index_exports);
41
42
 
42
43
  // src/transports.ts
44
+ async function streamToBuffer(stream) {
45
+ let totalLength = 0;
46
+ const reader = stream.getReader();
47
+ const chunks = [];
48
+ try {
49
+ while (true) {
50
+ const { done, value } = await reader.read();
51
+ if (done) break;
52
+ chunks.push(value);
53
+ totalLength += value.length;
54
+ }
55
+ } finally {
56
+ reader.releaseLock();
57
+ }
58
+ return Buffer.concat(chunks, totalLength);
59
+ }
43
60
  var JsonTransport = class {
44
61
  contentType = "application/json";
62
+ replacer;
63
+ reviver;
64
+ constructor(options = {}) {
65
+ this.replacer = options.replacer;
66
+ this.reviver = options.reviver;
67
+ }
45
68
  serialize(value) {
46
- return Buffer.from(JSON.stringify(value), "utf8");
69
+ return Buffer.from(JSON.stringify(value, this.replacer), "utf8");
47
70
  }
48
71
  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();
61
- }
62
- const buffer = Buffer.concat(chunks, totalLength);
63
- return JSON.parse(buffer.toString("utf8"));
72
+ const buffer = await streamToBuffer(stream);
73
+ return JSON.parse(buffer.toString("utf8"), this.reviver);
64
74
  }
65
75
  };
66
76
  var BufferTransport = class {
@@ -69,25 +79,7 @@ var BufferTransport = class {
69
79
  return value;
70
80
  }
71
81
  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();
82
- }
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);
82
+ return await streamToBuffer(stream);
91
83
  }
92
84
  };
93
85
  var StreamTransport = class {
@@ -237,8 +229,8 @@ var QueueClient = class {
237
229
  * @param options Client configuration options (optional - will auto-detect Vercel Function environment)
238
230
  */
239
231
  constructor(options = {}) {
240
- this.baseUrl = options.baseUrl || "https://api.vercel.com";
241
- this.basePath = options.basePath || "/v1/queues/messages";
232
+ this.baseUrl = options.baseUrl || "https://vercel-queue.com";
233
+ this.basePath = options.basePath || "/api/v2/messages";
242
234
  if (options.token) {
243
235
  this.token = options.token;
244
236
  } else {
@@ -742,7 +734,9 @@ var ConsumerGroup = class {
742
734
  const result = await handler(message.payload, {
743
735
  messageId: message.messageId,
744
736
  deliveryCount: message.deliveryCount,
745
- createdAt: message.createdAt
737
+ createdAt: message.createdAt,
738
+ topicName: this.topicName,
739
+ consumerGroup: this.consumerGroupName
746
740
  });
747
741
  await stopExtension();
748
742
  if (result && "timeoutSeconds" in result) {
@@ -945,7 +939,37 @@ async function receive(topicName, consumerGroup, handler, options) {
945
939
  }
946
940
 
947
941
  // src/callback.ts
948
- async function parseCallbackRequest(request) {
942
+ function validateWildcardPattern(pattern) {
943
+ const firstIndex = pattern.indexOf("*");
944
+ const lastIndex = pattern.lastIndexOf("*");
945
+ if (firstIndex !== lastIndex) {
946
+ return false;
947
+ }
948
+ if (firstIndex === -1) {
949
+ return false;
950
+ }
951
+ if (firstIndex !== pattern.length - 1) {
952
+ return false;
953
+ }
954
+ return true;
955
+ }
956
+ function matchesWildcardPattern(topicName, pattern) {
957
+ const prefix = pattern.slice(0, -1);
958
+ return topicName.startsWith(prefix);
959
+ }
960
+ function findTopicHandler(queueName, handlers) {
961
+ const exactHandler = handlers[queueName];
962
+ if (exactHandler) {
963
+ return exactHandler;
964
+ }
965
+ for (const pattern in handlers) {
966
+ if (pattern.includes("*") && matchesWildcardPattern(queueName, pattern)) {
967
+ return handlers[pattern];
968
+ }
969
+ }
970
+ return null;
971
+ }
972
+ async function parseCallback(request) {
949
973
  const contentType = request.headers.get("content-type");
950
974
  if (!contentType || !contentType.includes("application/cloudevents+json")) {
951
975
  throw new Error(
@@ -984,10 +1008,19 @@ async function parseCallbackRequest(request) {
984
1008
  };
985
1009
  }
986
1010
  function handleCallback(handlers) {
1011
+ for (const topicPattern in handlers) {
1012
+ if (topicPattern.includes("*")) {
1013
+ if (!validateWildcardPattern(topicPattern)) {
1014
+ throw new Error(
1015
+ `Invalid wildcard pattern "${topicPattern}": * may only appear once and must be at the end of the topic name`
1016
+ );
1017
+ }
1018
+ }
1019
+ }
987
1020
  return async (request) => {
988
1021
  try {
989
- const { queueName, consumerGroup, messageId } = await parseCallbackRequest(request);
990
- const topicHandler = handlers[queueName];
1022
+ const { queueName, consumerGroup, messageId } = await parseCallback(request);
1023
+ const topicHandler = findTopicHandler(queueName, handlers);
991
1024
  if (!topicHandler) {
992
1025
  const availableTopics = Object.keys(handlers).join(", ");
993
1026
  return Response.json(
@@ -1042,6 +1075,7 @@ function handleCallback(handlers) {
1042
1075
  StreamTransport,
1043
1076
  UnauthorizedError,
1044
1077
  handleCallback,
1078
+ parseCallback,
1045
1079
  receive,
1046
1080
  send
1047
1081
  });