@vercel/queue 0.0.0-alpha.2 → 0.0.0-alpha.4
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/README.md +409 -282
- package/dist/index.d.mts +184 -111
- package/dist/index.d.ts +184 -111
- package/dist/index.js +196 -287
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +192 -285
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -40,25 +40,13 @@ __export(index_exports, {
|
|
|
40
40
|
Topic: () => Topic,
|
|
41
41
|
UnauthorizedError: () => UnauthorizedError,
|
|
42
42
|
createTopic: () => createTopic,
|
|
43
|
-
|
|
44
|
-
parseCallbackRequest: () => parseCallbackRequest
|
|
43
|
+
handleCallback: () => handleCallback,
|
|
44
|
+
parseCallbackRequest: () => parseCallbackRequest,
|
|
45
|
+
receive: () => receive,
|
|
46
|
+
send: () => send
|
|
45
47
|
});
|
|
46
48
|
module.exports = __toCommonJS(index_exports);
|
|
47
49
|
|
|
48
|
-
// src/oidc.ts
|
|
49
|
-
async function getVercelOidcToken() {
|
|
50
|
-
const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
|
|
51
|
-
const fromSymbol = globalThis;
|
|
52
|
-
const context = fromSymbol[SYMBOL_FOR_REQ_CONTEXT]?.get?.() ?? {};
|
|
53
|
-
const token = context.headers?.["x-vercel-oidc-token"] ?? process.env.VERCEL_OIDC_TOKEN;
|
|
54
|
-
if (!token) {
|
|
55
|
-
throw new Error(
|
|
56
|
-
`The 'x-vercel-oidc-token' header is missing from the request. Do you have the OIDC option enabled in the Vercel project settings?`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
return token;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
50
|
// src/client.ts
|
|
63
51
|
var import_mixpart = require("mixpart");
|
|
64
52
|
|
|
@@ -188,13 +176,9 @@ var MessageNotAvailableError = class extends Error {
|
|
|
188
176
|
}
|
|
189
177
|
};
|
|
190
178
|
var FifoOrderingViolationError = class extends Error {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
super(
|
|
194
|
-
`FIFO ordering violation for message ${messageId}: ${reason}. Process message ${nextMessageId} first.`
|
|
195
|
-
);
|
|
179
|
+
constructor(messageId, reason) {
|
|
180
|
+
super(`FIFO ordering violation for message ${messageId}: ${reason}`);
|
|
196
181
|
this.name = "FifoOrderingViolationError";
|
|
197
|
-
this.nextMessageId = nextMessageId;
|
|
198
182
|
}
|
|
199
183
|
};
|
|
200
184
|
var MessageCorruptedError = class extends Error {
|
|
@@ -239,13 +223,11 @@ var BadRequestError = class extends Error {
|
|
|
239
223
|
}
|
|
240
224
|
};
|
|
241
225
|
var FailedDependencyError = class extends Error {
|
|
242
|
-
|
|
243
|
-
constructor(messageId, nextMessageId) {
|
|
226
|
+
constructor(messageId) {
|
|
244
227
|
super(
|
|
245
|
-
`Failed dependency: FIFO ordering violation for message ${messageId}
|
|
228
|
+
`Failed dependency: FIFO ordering violation for message ${messageId}`
|
|
246
229
|
);
|
|
247
230
|
this.name = "FailedDependencyError";
|
|
248
|
-
this.nextMessageId = nextMessageId;
|
|
249
231
|
}
|
|
250
232
|
};
|
|
251
233
|
var InternalServerError = class extends Error {
|
|
@@ -303,32 +285,53 @@ function parseQueueHeaders(headers) {
|
|
|
303
285
|
var QueueClient = class _QueueClient {
|
|
304
286
|
baseUrl;
|
|
305
287
|
token;
|
|
288
|
+
/**
|
|
289
|
+
* Internal default instance for use by createTopic and other convenience functions
|
|
290
|
+
* @internal
|
|
291
|
+
*/
|
|
292
|
+
static _defaultInstance = null;
|
|
306
293
|
/**
|
|
307
294
|
* Create a new Vercel Queue Service client
|
|
308
|
-
* @param options Client configuration options
|
|
295
|
+
* @param options Client configuration options (optional - will auto-detect Vercel Function environment)
|
|
309
296
|
*/
|
|
310
|
-
constructor(options) {
|
|
297
|
+
constructor(options = {}) {
|
|
311
298
|
this.baseUrl = options.baseUrl || "https://vqs.vercel.sh";
|
|
312
|
-
|
|
299
|
+
if (options.token) {
|
|
300
|
+
this.token = options.token;
|
|
301
|
+
} else {
|
|
302
|
+
const token = this.getVercelOidcTokenSync();
|
|
303
|
+
if (!token) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
"Failed to get OIDC token from Vercel Functions. Make sure you are running in a Vercel Function environment, or provide a token explicitly.\n\nTo set up your environment:\n1. Link your project: 'vercel link'\n2. Pull environment variables: 'vercel env pull'\n3. Run with environment: 'dotenv -e .env.local -- your-command'"
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
this.token = token;
|
|
309
|
+
}
|
|
313
310
|
}
|
|
314
311
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
* Always creates a fresh instance since OIDC tokens expire after 15 minutes
|
|
318
|
-
* @param baseUrl Optional base URL override
|
|
319
|
-
* @returns Promise resolving to a new QueueClient instance
|
|
312
|
+
* Get the default client instance for internal use by convenience functions
|
|
313
|
+
* @internal
|
|
320
314
|
*/
|
|
321
|
-
static
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
315
|
+
static _getDefaultInstance() {
|
|
316
|
+
if (!this._defaultInstance) {
|
|
317
|
+
this._defaultInstance = new _QueueClient();
|
|
318
|
+
}
|
|
319
|
+
return this._defaultInstance;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Synchronously get OIDC token from environment
|
|
323
|
+
* Used internally by constructor - mirrors the logic from getVercelOidcToken but synchronously
|
|
324
|
+
*/
|
|
325
|
+
getVercelOidcTokenSync() {
|
|
326
|
+
try {
|
|
327
|
+
const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
|
|
328
|
+
const fromSymbol = globalThis;
|
|
329
|
+
const context = fromSymbol[SYMBOL_FOR_REQ_CONTEXT]?.get?.() ?? {};
|
|
330
|
+
const token = context.headers?.["x-vercel-oidc-token"] ?? process.env.VERCEL_OIDC_TOKEN;
|
|
331
|
+
return token || null;
|
|
332
|
+
} catch {
|
|
333
|
+
return null;
|
|
327
334
|
}
|
|
328
|
-
return new _QueueClient({
|
|
329
|
-
token,
|
|
330
|
-
baseUrl
|
|
331
|
-
});
|
|
332
335
|
}
|
|
333
336
|
/**
|
|
334
337
|
* Send a message to a queue
|
|
@@ -341,7 +344,7 @@ var QueueClient = class _QueueClient {
|
|
|
341
344
|
* @throws {InternalServerError} When server encounters an error
|
|
342
345
|
*/
|
|
343
346
|
async sendMessage(options, transport) {
|
|
344
|
-
const { queueName, payload, idempotencyKey, retentionSeconds,
|
|
347
|
+
const { queueName, payload, idempotencyKey, retentionSeconds, callback } = options;
|
|
345
348
|
const headers = new Headers({
|
|
346
349
|
Authorization: `Bearer ${this.token}`,
|
|
347
350
|
"Vqs-Queue-Name": queueName,
|
|
@@ -353,21 +356,29 @@ var QueueClient = class _QueueClient {
|
|
|
353
356
|
if (retentionSeconds !== void 0) {
|
|
354
357
|
headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
|
|
355
358
|
}
|
|
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
|
+
}
|
|
356
367
|
let localhostCallbacks = [];
|
|
357
|
-
if (
|
|
368
|
+
if (normalizedCallbacks) {
|
|
358
369
|
const isDevelopment = process.env.NODE_ENV === "development";
|
|
359
370
|
if (isDevelopment) {
|
|
360
|
-
localhostCallbacks = processDevelopmentCallbacks(
|
|
371
|
+
localhostCallbacks = processDevelopmentCallbacks(normalizedCallbacks);
|
|
361
372
|
} else {
|
|
362
|
-
const endpoints = Object.entries(
|
|
373
|
+
const endpoints = Object.entries(normalizedCallbacks).map(
|
|
363
374
|
([group, config]) => `${group}=${Buffer.from(config.url).toString("base64")}`
|
|
364
375
|
).join(",");
|
|
365
376
|
headers.set("Vqs-Callback-Url", endpoints);
|
|
366
|
-
const delays = Object.entries(
|
|
377
|
+
const delays = Object.entries(normalizedCallbacks).filter(([, config]) => config.delay !== void 0).map(([group, config]) => `${group}=${config.delay}`).join(",");
|
|
367
378
|
if (delays) {
|
|
368
379
|
headers.set("Vqs-Callback-Delay", delays);
|
|
369
380
|
}
|
|
370
|
-
const frequencies = Object.entries(
|
|
381
|
+
const frequencies = Object.entries(normalizedCallbacks).filter(([, config]) => config.frequency !== void 0).map(([group, config]) => `${group}=${config.frequency}`).join(",");
|
|
371
382
|
if (frequencies) {
|
|
372
383
|
headers.set("Vqs-Callback-Frequency", frequencies);
|
|
373
384
|
}
|
|
@@ -553,36 +564,9 @@ var QueueClient = class _QueueClient {
|
|
|
553
564
|
throw new MessageLockedError(messageId, retryAfter);
|
|
554
565
|
}
|
|
555
566
|
if (response.status === 424) {
|
|
556
|
-
|
|
557
|
-
const errorData = await response.json();
|
|
558
|
-
if (errorData.meta?.nextMessageId) {
|
|
559
|
-
throw new FailedDependencyError(
|
|
560
|
-
messageId,
|
|
561
|
-
errorData.meta.nextMessageId
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
} catch (parseError) {
|
|
565
|
-
if (parseError instanceof FailedDependencyError) {
|
|
566
|
-
throw parseError;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
throw new MessageNotAvailableError(
|
|
570
|
-
messageId,
|
|
571
|
-
"FIFO ordering violation"
|
|
572
|
-
);
|
|
567
|
+
throw new FailedDependencyError(messageId);
|
|
573
568
|
}
|
|
574
569
|
if (response.status === 409) {
|
|
575
|
-
try {
|
|
576
|
-
const errorData = await response.json();
|
|
577
|
-
if (errorData.nextMessageId) {
|
|
578
|
-
throw new FifoOrderingViolationError(
|
|
579
|
-
messageId,
|
|
580
|
-
errorData.nextMessageId,
|
|
581
|
-
errorData.error
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
} catch (parseError) {
|
|
585
|
-
}
|
|
586
570
|
throw new MessageNotAvailableError(messageId);
|
|
587
571
|
}
|
|
588
572
|
if (response.status >= 500) {
|
|
@@ -944,7 +928,11 @@ var ConsumerGroup = class {
|
|
|
944
928
|
message.ticket
|
|
945
929
|
);
|
|
946
930
|
try {
|
|
947
|
-
const result = await handler(message
|
|
931
|
+
const result = await handler(message.payload, {
|
|
932
|
+
messageId: message.messageId,
|
|
933
|
+
deliveryCount: message.deliveryCount,
|
|
934
|
+
timestamp: message.timestamp
|
|
935
|
+
});
|
|
948
936
|
await stopExtension();
|
|
949
937
|
if (result && "timeoutSeconds" in result) {
|
|
950
938
|
await this.client.changeVisibility({
|
|
@@ -974,219 +962,58 @@ var ConsumerGroup = class {
|
|
|
974
962
|
throw error;
|
|
975
963
|
}
|
|
976
964
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
* @param options Processing options
|
|
982
|
-
* @returns Promise that resolves when processing stops (due to signal or error)
|
|
983
|
-
*/
|
|
984
|
-
async subscribe(signal, handler, options = {}) {
|
|
985
|
-
const pollingInterval = options.pollingInterval || 1e3;
|
|
986
|
-
while (!signal.aborted) {
|
|
987
|
-
try {
|
|
988
|
-
for await (const message of this.client.receiveMessages(
|
|
965
|
+
async consume(handler, options) {
|
|
966
|
+
if (options?.messageId) {
|
|
967
|
+
if (options.skipPayload) {
|
|
968
|
+
const response = await this.client.receiveMessageById(
|
|
989
969
|
{
|
|
990
970
|
queueName: this.topicName,
|
|
991
971
|
consumerGroup: this.consumerGroupName,
|
|
972
|
+
messageId: options.messageId,
|
|
992
973
|
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
993
|
-
|
|
994
|
-
// Always process one message at a time
|
|
974
|
+
skipPayload: true
|
|
995
975
|
},
|
|
996
976
|
this.transport
|
|
997
|
-
)
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
}
|
|
1035
|
-
continue;
|
|
1036
|
-
}
|
|
1037
|
-
if (error instanceof MessageLockedError) {
|
|
1038
|
-
const waitTime = error.retryAfter ? error.retryAfter * 1e3 : pollingInterval;
|
|
1039
|
-
if (!signal.aborted) {
|
|
1040
|
-
await new Promise((resolve) => {
|
|
1041
|
-
const timeoutId = setTimeout(resolve, waitTime);
|
|
1042
|
-
signal.addEventListener(
|
|
1043
|
-
"abort",
|
|
1044
|
-
() => {
|
|
1045
|
-
clearTimeout(timeoutId);
|
|
1046
|
-
resolve();
|
|
1047
|
-
},
|
|
1048
|
-
{ once: true }
|
|
1049
|
-
);
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
continue;
|
|
1053
|
-
}
|
|
1054
|
-
console.error("Error polling topic:", error);
|
|
1055
|
-
throw error;
|
|
977
|
+
);
|
|
978
|
+
await this.processMessage(
|
|
979
|
+
response.message,
|
|
980
|
+
handler
|
|
981
|
+
);
|
|
982
|
+
} else {
|
|
983
|
+
const response = await this.client.receiveMessageById(
|
|
984
|
+
{
|
|
985
|
+
queueName: this.topicName,
|
|
986
|
+
consumerGroup: this.consumerGroupName,
|
|
987
|
+
messageId: options.messageId,
|
|
988
|
+
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
989
|
+
},
|
|
990
|
+
this.transport
|
|
991
|
+
);
|
|
992
|
+
await this.processMessage(
|
|
993
|
+
response.message,
|
|
994
|
+
handler
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
} else {
|
|
998
|
+
let messageFound = false;
|
|
999
|
+
for await (const message of this.client.receiveMessages(
|
|
1000
|
+
{
|
|
1001
|
+
queueName: this.topicName,
|
|
1002
|
+
consumerGroup: this.consumerGroupName,
|
|
1003
|
+
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1004
|
+
limit: 1
|
|
1005
|
+
},
|
|
1006
|
+
this.transport
|
|
1007
|
+
)) {
|
|
1008
|
+
messageFound = true;
|
|
1009
|
+
await this.processMessage(message, handler);
|
|
1010
|
+
break;
|
|
1011
|
+
}
|
|
1012
|
+
if (!messageFound) {
|
|
1013
|
+
throw new Error("No messages available");
|
|
1056
1014
|
}
|
|
1057
1015
|
}
|
|
1058
1016
|
}
|
|
1059
|
-
/**
|
|
1060
|
-
* Receive and process a specific message by its ID with full payload
|
|
1061
|
-
* @param messageId The ID of the message to receive and process
|
|
1062
|
-
* @param handler Function to process the message with full payload
|
|
1063
|
-
* @returns Promise that resolves when the message is processed or rejects with specific errors
|
|
1064
|
-
* @throws {MessageNotFoundError} When the message doesn't exist (404)
|
|
1065
|
-
* @throws {MessageNotAvailableError} When the message exists but isn't available for processing (409)
|
|
1066
|
-
* @throws {MessageLockedError} When the message is temporarily locked (423)
|
|
1067
|
-
* @throws {FifoOrderingViolationError} When there's a FIFO ordering violation (409 with nextMessageId)
|
|
1068
|
-
* @throws {FailedDependencyError} When FIFO ordering is violated (424)
|
|
1069
|
-
* @throws {MessageCorruptedError} When the message data is corrupted
|
|
1070
|
-
* @throws {BadRequestError} When request parameters are invalid
|
|
1071
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
1072
|
-
* @throws {ForbiddenError} When access is denied
|
|
1073
|
-
* @throws {InternalServerError} When server encounters an error
|
|
1074
|
-
*/
|
|
1075
|
-
async receiveMessage(messageId, handler) {
|
|
1076
|
-
const response = await this.client.receiveMessageById(
|
|
1077
|
-
{
|
|
1078
|
-
queueName: this.topicName,
|
|
1079
|
-
consumerGroup: this.consumerGroupName,
|
|
1080
|
-
messageId,
|
|
1081
|
-
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
1082
|
-
},
|
|
1083
|
-
this.transport
|
|
1084
|
-
);
|
|
1085
|
-
await this.processMessage(response.message, handler);
|
|
1086
|
-
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Receive and process the next available message from the queue
|
|
1089
|
-
* @param handler Function to process the message
|
|
1090
|
-
* @returns Promise that resolves when the message is processed or rejects with specific errors
|
|
1091
|
-
* @throws {QueueEmptyError} When no messages are available in the queue (204)
|
|
1092
|
-
* @throws {MessageLockedError} When the next message in a FIFO queue is locked (423)
|
|
1093
|
-
* @throws {BadRequestError} When request parameters are invalid
|
|
1094
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
1095
|
-
* @throws {ForbiddenError} When access is denied
|
|
1096
|
-
* @throws {InternalServerError} When server encounters an error
|
|
1097
|
-
*/
|
|
1098
|
-
async receiveNextMessage(handler) {
|
|
1099
|
-
let messageFound = false;
|
|
1100
|
-
for await (const message of this.client.receiveMessages(
|
|
1101
|
-
{
|
|
1102
|
-
queueName: this.topicName,
|
|
1103
|
-
consumerGroup: this.consumerGroupName,
|
|
1104
|
-
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1105
|
-
limit: 1
|
|
1106
|
-
},
|
|
1107
|
-
this.transport
|
|
1108
|
-
)) {
|
|
1109
|
-
messageFound = true;
|
|
1110
|
-
await this.processMessage(message, handler);
|
|
1111
|
-
break;
|
|
1112
|
-
}
|
|
1113
|
-
if (!messageFound) {
|
|
1114
|
-
throw new Error("No messages available");
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Receive and process multiple next available messages from the queue
|
|
1119
|
-
* @param limit Number of messages to process (1-10)
|
|
1120
|
-
* @param handler Function to process each message
|
|
1121
|
-
* @returns Promise that resolves to an array of PromiseSettledResult (same as Promise.allSettled)
|
|
1122
|
-
* @throws {InvalidLimitError} When limit parameter is not between 1 and 10
|
|
1123
|
-
* @throws {QueueEmptyError} When no messages are available in the queue (204)
|
|
1124
|
-
* @throws {MessageLockedError} When the next message in a FIFO queue is locked (423)
|
|
1125
|
-
* @throws {BadRequestError} When request parameters are invalid
|
|
1126
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
1127
|
-
* @throws {ForbiddenError} When access is denied
|
|
1128
|
-
* @throws {InternalServerError} When server encounters an error
|
|
1129
|
-
*/
|
|
1130
|
-
async receiveNextMessages(limit, handler) {
|
|
1131
|
-
if (limit < 1 || limit > 10) {
|
|
1132
|
-
throw new InvalidLimitError(limit);
|
|
1133
|
-
}
|
|
1134
|
-
const processingPromises = [];
|
|
1135
|
-
let messageCount = 0;
|
|
1136
|
-
for await (const message of this.client.receiveMessages(
|
|
1137
|
-
{
|
|
1138
|
-
queueName: this.topicName,
|
|
1139
|
-
consumerGroup: this.consumerGroupName,
|
|
1140
|
-
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1141
|
-
limit
|
|
1142
|
-
},
|
|
1143
|
-
this.transport
|
|
1144
|
-
)) {
|
|
1145
|
-
messageCount++;
|
|
1146
|
-
const wrappedPromise = this.processMessage(message, handler).then(
|
|
1147
|
-
(value) => ({
|
|
1148
|
-
status: "fulfilled",
|
|
1149
|
-
value
|
|
1150
|
-
}),
|
|
1151
|
-
(reason) => ({ status: "rejected", reason })
|
|
1152
|
-
);
|
|
1153
|
-
processingPromises.push(wrappedPromise);
|
|
1154
|
-
}
|
|
1155
|
-
if (messageCount === 0) {
|
|
1156
|
-
throw new Error("No messages available");
|
|
1157
|
-
}
|
|
1158
|
-
const results = await Promise.all(processingPromises);
|
|
1159
|
-
return results;
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Handle a specific message by its ID without downloading the payload (metadata only)
|
|
1163
|
-
* @param messageId The ID of the message to handle
|
|
1164
|
-
* @param handler Function to process the message metadata (payload will be void)
|
|
1165
|
-
* @returns Promise that resolves when the message is handled or rejects with specific errors
|
|
1166
|
-
* @throws {MessageNotFoundError} When the message doesn't exist (404)
|
|
1167
|
-
* @throws {MessageNotAvailableError} When the message exists but isn't available for processing (409)
|
|
1168
|
-
* @throws {MessageLockedError} When the message is temporarily locked (423)
|
|
1169
|
-
* @throws {FifoOrderingViolationError} When there's a FIFO ordering violation (409 with nextMessageId)
|
|
1170
|
-
* @throws {FailedDependencyError} When FIFO ordering is violated (424)
|
|
1171
|
-
* @throws {MessageCorruptedError} When the message data is corrupted
|
|
1172
|
-
* @throws {BadRequestError} When request parameters are invalid
|
|
1173
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
1174
|
-
* @throws {ForbiddenError} When access is denied
|
|
1175
|
-
* @throws {InternalServerError} When server encounters an error
|
|
1176
|
-
*/
|
|
1177
|
-
async handleMessage(messageId, handler) {
|
|
1178
|
-
const response = await this.client.receiveMessageById(
|
|
1179
|
-
{
|
|
1180
|
-
queueName: this.topicName,
|
|
1181
|
-
consumerGroup: this.consumerGroupName,
|
|
1182
|
-
messageId,
|
|
1183
|
-
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1184
|
-
skipPayload: true
|
|
1185
|
-
},
|
|
1186
|
-
this.transport
|
|
1187
|
-
);
|
|
1188
|
-
await this.processMessage(response.message, handler);
|
|
1189
|
-
}
|
|
1190
1017
|
/**
|
|
1191
1018
|
* Get the consumer group name
|
|
1192
1019
|
*/
|
|
@@ -1234,7 +1061,7 @@ var Topic = class {
|
|
|
1234
1061
|
payload,
|
|
1235
1062
|
idempotencyKey: options?.idempotencyKey,
|
|
1236
1063
|
retentionSeconds: options?.retentionSeconds,
|
|
1237
|
-
|
|
1064
|
+
callback: options?.callback
|
|
1238
1065
|
},
|
|
1239
1066
|
this.transport
|
|
1240
1067
|
);
|
|
@@ -1273,9 +1100,43 @@ var Topic = class {
|
|
|
1273
1100
|
};
|
|
1274
1101
|
|
|
1275
1102
|
// src/factory.ts
|
|
1276
|
-
function createTopic(
|
|
1103
|
+
function createTopic(topicName, transport) {
|
|
1104
|
+
const client = QueueClient._getDefaultInstance();
|
|
1277
1105
|
return new Topic(client, topicName, transport);
|
|
1278
1106
|
}
|
|
1107
|
+
async function send(topicName, payload, options) {
|
|
1108
|
+
const transport = options?.transport || new JsonTransport();
|
|
1109
|
+
const client = QueueClient._getDefaultInstance();
|
|
1110
|
+
const result = await client.sendMessage(
|
|
1111
|
+
{
|
|
1112
|
+
queueName: topicName,
|
|
1113
|
+
payload,
|
|
1114
|
+
idempotencyKey: options?.idempotencyKey,
|
|
1115
|
+
retentionSeconds: options?.retentionSeconds,
|
|
1116
|
+
callback: options?.callback
|
|
1117
|
+
},
|
|
1118
|
+
transport
|
|
1119
|
+
);
|
|
1120
|
+
return { messageId: result.messageId };
|
|
1121
|
+
}
|
|
1122
|
+
async function receive(topicName, consumerGroup, handler, options) {
|
|
1123
|
+
const transport = options?.transport || new JsonTransport();
|
|
1124
|
+
const topic = createTopic(topicName, transport);
|
|
1125
|
+
const { messageId, skipPayload, ...consumerGroupOptions } = options || {};
|
|
1126
|
+
const consumer = topic.consumerGroup(consumerGroup, consumerGroupOptions);
|
|
1127
|
+
if (messageId) {
|
|
1128
|
+
if (skipPayload) {
|
|
1129
|
+
return consumer.consume(handler, {
|
|
1130
|
+
messageId,
|
|
1131
|
+
skipPayload: true
|
|
1132
|
+
});
|
|
1133
|
+
} else {
|
|
1134
|
+
return consumer.consume(handler, { messageId });
|
|
1135
|
+
}
|
|
1136
|
+
} else {
|
|
1137
|
+
return consumer.consume(handler);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1279
1140
|
|
|
1280
1141
|
// src/callback.ts
|
|
1281
1142
|
function parseCallbackRequest(request) {
|
|
@@ -1298,6 +1159,52 @@ function parseCallbackRequest(request) {
|
|
|
1298
1159
|
consumerGroup
|
|
1299
1160
|
};
|
|
1300
1161
|
}
|
|
1162
|
+
function handleCallback(handlers) {
|
|
1163
|
+
return async (request) => {
|
|
1164
|
+
try {
|
|
1165
|
+
const { queueName, consumerGroup, messageId } = parseCallbackRequest(request);
|
|
1166
|
+
const topicHandler = handlers[queueName];
|
|
1167
|
+
if (!topicHandler) {
|
|
1168
|
+
throw new Error(`No handler found for topic: ${queueName}`);
|
|
1169
|
+
}
|
|
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;
|
|
1187
|
+
}
|
|
1188
|
+
const client = new QueueClient();
|
|
1189
|
+
const topic = new Topic(client, queueName);
|
|
1190
|
+
const cg = topic.consumerGroup(consumerGroup);
|
|
1191
|
+
await cg.consume(actualHandler, { messageId });
|
|
1192
|
+
return Response.json({ status: "success" });
|
|
1193
|
+
} 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
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
return Response.json(
|
|
1202
|
+
{ error: "Failed to process callback" },
|
|
1203
|
+
{ status: 500 }
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1301
1208
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1302
1209
|
0 && (module.exports = {
|
|
1303
1210
|
BadRequestError,
|
|
@@ -1320,7 +1227,9 @@ function parseCallbackRequest(request) {
|
|
|
1320
1227
|
Topic,
|
|
1321
1228
|
UnauthorizedError,
|
|
1322
1229
|
createTopic,
|
|
1323
|
-
|
|
1324
|
-
parseCallbackRequest
|
|
1230
|
+
handleCallback,
|
|
1231
|
+
parseCallbackRequest,
|
|
1232
|
+
receive,
|
|
1233
|
+
send
|
|
1325
1234
|
});
|
|
1326
1235
|
//# sourceMappingURL=index.js.map
|