@vercel/queue 0.0.0-alpha.32 → 0.0.0-alpha.34
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 +3 -52
- package/dist/index.d.mts +184 -75
- package/dist/index.d.ts +184 -75
- package/dist/index.js +818 -644
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +802 -644
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs-pages.d.mts +1 -1
- package/dist/nextjs-pages.d.ts +1 -1
- package/dist/nextjs-pages.js +667 -555
- package/dist/nextjs-pages.js.map +1 -1
- package/dist/nextjs-pages.mjs +657 -555
- package/dist/nextjs-pages.mjs.map +1 -1
- package/dist/{types-JvOenjfT.d.mts → types-BHtRP_i_.d.mts} +173 -18
- package/dist/{types-JvOenjfT.d.ts → types-BHtRP_i_.d.ts} +173 -18
- package/package.json +2 -6
- package/bin/local-discover.js +0 -196
package/dist/nextjs-pages.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/nextjs-pages.ts
|
|
@@ -27,8 +37,43 @@ module.exports = __toCommonJS(nextjs_pages_exports);
|
|
|
27
37
|
// src/client.ts
|
|
28
38
|
var import_mixpart = require("mixpart");
|
|
29
39
|
|
|
30
|
-
// src/
|
|
31
|
-
var
|
|
40
|
+
// src/dev.ts
|
|
41
|
+
var fs = __toESM(require("fs"));
|
|
42
|
+
var path = __toESM(require("path"));
|
|
43
|
+
|
|
44
|
+
// src/transports.ts
|
|
45
|
+
async function streamToBuffer(stream) {
|
|
46
|
+
let totalLength = 0;
|
|
47
|
+
const reader = stream.getReader();
|
|
48
|
+
const chunks = [];
|
|
49
|
+
try {
|
|
50
|
+
while (true) {
|
|
51
|
+
const { done, value } = await reader.read();
|
|
52
|
+
if (done) break;
|
|
53
|
+
chunks.push(value);
|
|
54
|
+
totalLength += value.length;
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
reader.releaseLock();
|
|
58
|
+
}
|
|
59
|
+
return Buffer.concat(chunks, totalLength);
|
|
60
|
+
}
|
|
61
|
+
var JsonTransport = class {
|
|
62
|
+
contentType = "application/json";
|
|
63
|
+
replacer;
|
|
64
|
+
reviver;
|
|
65
|
+
constructor(options = {}) {
|
|
66
|
+
this.replacer = options.replacer;
|
|
67
|
+
this.reviver = options.reviver;
|
|
68
|
+
}
|
|
69
|
+
serialize(value) {
|
|
70
|
+
return Buffer.from(JSON.stringify(value, this.replacer), "utf8");
|
|
71
|
+
}
|
|
72
|
+
async deserialize(stream) {
|
|
73
|
+
const buffer = await streamToBuffer(stream);
|
|
74
|
+
return JSON.parse(buffer.toString("utf8"), this.reviver);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
32
77
|
|
|
33
78
|
// src/types.ts
|
|
34
79
|
var MessageNotFoundError = class extends Error {
|
|
@@ -59,15 +104,6 @@ var QueueEmptyError = class extends Error {
|
|
|
59
104
|
this.name = "QueueEmptyError";
|
|
60
105
|
}
|
|
61
106
|
};
|
|
62
|
-
var MessageLockedError = class extends Error {
|
|
63
|
-
retryAfter;
|
|
64
|
-
constructor(messageId, retryAfter) {
|
|
65
|
-
const retryMessage = retryAfter ? ` Retry after ${retryAfter} seconds.` : " Try again later.";
|
|
66
|
-
super(`Message ${messageId} is temporarily locked.${retryMessage}`);
|
|
67
|
-
this.name = "MessageLockedError";
|
|
68
|
-
this.retryAfter = retryAfter;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
107
|
var UnauthorizedError = class extends Error {
|
|
72
108
|
constructor(message = "Missing or invalid authentication token") {
|
|
73
109
|
super(message);
|
|
@@ -98,8 +134,268 @@ var InvalidLimitError = class extends Error {
|
|
|
98
134
|
this.name = "InvalidLimitError";
|
|
99
135
|
}
|
|
100
136
|
};
|
|
137
|
+
var MessageAlreadyProcessedError = class extends Error {
|
|
138
|
+
constructor(messageId) {
|
|
139
|
+
super(`Message ${messageId} has already been processed`);
|
|
140
|
+
this.name = "MessageAlreadyProcessedError";
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var ConcurrencyLimitError = class extends Error {
|
|
144
|
+
currentInflight;
|
|
145
|
+
maxConcurrency;
|
|
146
|
+
constructor(message = "Concurrency limit exceeded", currentInflight, maxConcurrency) {
|
|
147
|
+
super(message);
|
|
148
|
+
this.name = "ConcurrencyLimitError";
|
|
149
|
+
this.currentInflight = currentInflight;
|
|
150
|
+
this.maxConcurrency = maxConcurrency;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var DuplicateMessageError = class extends Error {
|
|
154
|
+
idempotencyKey;
|
|
155
|
+
constructor(message, idempotencyKey) {
|
|
156
|
+
super(message);
|
|
157
|
+
this.name = "DuplicateMessageError";
|
|
158
|
+
this.idempotencyKey = idempotencyKey;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var ConsumerDiscoveryError = class extends Error {
|
|
162
|
+
deploymentId;
|
|
163
|
+
constructor(message, deploymentId) {
|
|
164
|
+
super(message);
|
|
165
|
+
this.name = "ConsumerDiscoveryError";
|
|
166
|
+
this.deploymentId = deploymentId;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var ConsumerRegistryNotConfiguredError = class extends Error {
|
|
170
|
+
constructor(message = "Consumer registry not configured") {
|
|
171
|
+
super(message);
|
|
172
|
+
this.name = "ConsumerRegistryNotConfiguredError";
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// src/dev.ts
|
|
177
|
+
var ROUTE_MAPPINGS_KEY = Symbol.for("@vercel/queue.devRouteMappings");
|
|
178
|
+
function filePathToUrlPath(filePath) {
|
|
179
|
+
let urlPath = filePath.replace(/^app\//, "/").replace(/^pages\//, "/").replace(/\/route\.(ts|js|tsx|jsx)$/, "").replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
180
|
+
if (!urlPath.startsWith("/")) {
|
|
181
|
+
urlPath = "/" + urlPath;
|
|
182
|
+
}
|
|
183
|
+
return urlPath;
|
|
184
|
+
}
|
|
185
|
+
function getDevRouteMappings() {
|
|
186
|
+
const g = globalThis;
|
|
187
|
+
if (ROUTE_MAPPINGS_KEY in g) {
|
|
188
|
+
return g[ROUTE_MAPPINGS_KEY] ?? null;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const vercelJsonPath = path.join(process.cwd(), "vercel.json");
|
|
192
|
+
if (!fs.existsSync(vercelJsonPath)) {
|
|
193
|
+
g[ROUTE_MAPPINGS_KEY] = null;
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const vercelJson = JSON.parse(fs.readFileSync(vercelJsonPath, "utf-8"));
|
|
197
|
+
if (!vercelJson.functions) {
|
|
198
|
+
g[ROUTE_MAPPINGS_KEY] = null;
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const mappings = [];
|
|
202
|
+
for (const [filePath, config] of Object.entries(vercelJson.functions)) {
|
|
203
|
+
if (!config.experimentalTriggers) continue;
|
|
204
|
+
for (const trigger of config.experimentalTriggers) {
|
|
205
|
+
if (trigger.type?.startsWith("queue/") && trigger.topic && trigger.consumer) {
|
|
206
|
+
mappings.push({
|
|
207
|
+
urlPath: filePathToUrlPath(filePath),
|
|
208
|
+
topic: trigger.topic,
|
|
209
|
+
consumer: trigger.consumer
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
g[ROUTE_MAPPINGS_KEY] = mappings.length > 0 ? mappings : null;
|
|
215
|
+
return g[ROUTE_MAPPINGS_KEY];
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.warn("[Dev Mode] Failed to read vercel.json:", error);
|
|
218
|
+
g[ROUTE_MAPPINGS_KEY] = null;
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function findMatchingRoutes(topicName) {
|
|
223
|
+
const mappings = getDevRouteMappings();
|
|
224
|
+
if (!mappings) {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
return mappings.filter((mapping) => {
|
|
228
|
+
if (mapping.topic.includes("*")) {
|
|
229
|
+
return matchesWildcardPattern(topicName, mapping.topic);
|
|
230
|
+
}
|
|
231
|
+
return mapping.topic === topicName;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function isDevMode() {
|
|
235
|
+
return process.env.NODE_ENV === "development";
|
|
236
|
+
}
|
|
237
|
+
var DEV_VISIBILITY_POLL_INTERVAL = 50;
|
|
238
|
+
var DEV_VISIBILITY_MAX_WAIT = 5e3;
|
|
239
|
+
var DEV_VISIBILITY_BACKOFF_MULTIPLIER = 2;
|
|
240
|
+
async function waitForMessageVisibility(topicName, consumerGroup, messageId) {
|
|
241
|
+
const client = new QueueClient();
|
|
242
|
+
const transport = new JsonTransport();
|
|
243
|
+
let elapsed = 0;
|
|
244
|
+
let interval = DEV_VISIBILITY_POLL_INTERVAL;
|
|
245
|
+
while (elapsed < DEV_VISIBILITY_MAX_WAIT) {
|
|
246
|
+
try {
|
|
247
|
+
await client.receiveMessageById(
|
|
248
|
+
{
|
|
249
|
+
queueName: topicName,
|
|
250
|
+
consumerGroup,
|
|
251
|
+
messageId,
|
|
252
|
+
visibilityTimeoutSeconds: 0
|
|
253
|
+
},
|
|
254
|
+
transport
|
|
255
|
+
);
|
|
256
|
+
return true;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (error instanceof MessageNotFoundError) {
|
|
259
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
260
|
+
elapsed += interval;
|
|
261
|
+
interval = Math.min(
|
|
262
|
+
interval * DEV_VISIBILITY_BACKOFF_MULTIPLIER,
|
|
263
|
+
DEV_VISIBILITY_MAX_WAIT - elapsed
|
|
264
|
+
);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (error instanceof MessageAlreadyProcessedError) {
|
|
268
|
+
console.log(
|
|
269
|
+
`[Dev Mode] Message already processed: topic="${topicName}" messageId="${messageId}"`
|
|
270
|
+
);
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
console.error(
|
|
274
|
+
`[Dev Mode] Error polling for message visibility: topic="${topicName}" messageId="${messageId}"`,
|
|
275
|
+
error
|
|
276
|
+
);
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
console.warn(
|
|
281
|
+
`[Dev Mode] Message visibility timeout after ${DEV_VISIBILITY_MAX_WAIT}ms: topic="${topicName}" messageId="${messageId}"`
|
|
282
|
+
);
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
function triggerDevCallbacks(topicName, messageId, delaySeconds) {
|
|
286
|
+
if (delaySeconds && delaySeconds > 0) {
|
|
287
|
+
console.log(
|
|
288
|
+
`[Dev Mode] Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
|
|
289
|
+
);
|
|
290
|
+
setTimeout(() => {
|
|
291
|
+
triggerDevCallbacks(topicName, messageId);
|
|
292
|
+
}, delaySeconds * 1e3);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
console.log(
|
|
296
|
+
`[Dev Mode] Message sent: topic="${topicName}" messageId="${messageId}"`
|
|
297
|
+
);
|
|
298
|
+
const matchingRoutes = findMatchingRoutes(topicName);
|
|
299
|
+
if (matchingRoutes.length === 0) {
|
|
300
|
+
console.log(
|
|
301
|
+
`[Dev Mode] No matching routes in vercel.json for topic "${topicName}"`
|
|
302
|
+
);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const consumerGroups = matchingRoutes.map((r) => r.consumer);
|
|
306
|
+
console.log(
|
|
307
|
+
`[Dev Mode] Scheduling callbacks for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
|
|
308
|
+
);
|
|
309
|
+
(async () => {
|
|
310
|
+
const firstRoute = matchingRoutes[0];
|
|
311
|
+
const isVisible = await waitForMessageVisibility(
|
|
312
|
+
topicName,
|
|
313
|
+
firstRoute.consumer,
|
|
314
|
+
messageId
|
|
315
|
+
);
|
|
316
|
+
if (!isVisible) {
|
|
317
|
+
console.warn(
|
|
318
|
+
`[Dev Mode] Skipping callbacks - message not visible: topic="${topicName}" messageId="${messageId}"`
|
|
319
|
+
);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const port = process.env.PORT || 3e3;
|
|
323
|
+
const baseUrl = `http://localhost:${port}`;
|
|
324
|
+
for (const route of matchingRoutes) {
|
|
325
|
+
const url = `${baseUrl}${route.urlPath}`;
|
|
326
|
+
console.log(
|
|
327
|
+
`[Dev Mode] Invoking handler: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`
|
|
328
|
+
);
|
|
329
|
+
const cloudEvent = {
|
|
330
|
+
type: "com.vercel.queue.v1beta",
|
|
331
|
+
source: `/topic/${topicName}/consumer/${route.consumer}`,
|
|
332
|
+
id: messageId,
|
|
333
|
+
datacontenttype: "application/json",
|
|
334
|
+
data: {
|
|
335
|
+
messageId,
|
|
336
|
+
queueName: topicName,
|
|
337
|
+
consumerGroup: route.consumer
|
|
338
|
+
},
|
|
339
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
340
|
+
specversion: "1.0"
|
|
341
|
+
};
|
|
342
|
+
try {
|
|
343
|
+
const response = await fetch(url, {
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: {
|
|
346
|
+
"Content-Type": "application/cloudevents+json"
|
|
347
|
+
},
|
|
348
|
+
body: JSON.stringify(cloudEvent)
|
|
349
|
+
});
|
|
350
|
+
if (response.ok) {
|
|
351
|
+
try {
|
|
352
|
+
const responseData = await response.json();
|
|
353
|
+
if (responseData.status === "success") {
|
|
354
|
+
console.log(
|
|
355
|
+
`[Dev Mode] \u2713 Message processed successfully: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}"`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
} catch {
|
|
359
|
+
console.warn(
|
|
360
|
+
`[Dev Mode] Handler returned OK but response was not JSON: topic="${topicName}" consumer="${route.consumer}"`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
try {
|
|
365
|
+
const errorData = await response.json();
|
|
366
|
+
console.error(
|
|
367
|
+
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" error="${errorData.error || response.statusText}"`
|
|
368
|
+
);
|
|
369
|
+
} catch {
|
|
370
|
+
console.error(
|
|
371
|
+
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" status=${response.status}`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
} catch (error) {
|
|
376
|
+
console.error(
|
|
377
|
+
`[Dev Mode] \u2717 HTTP request failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`,
|
|
378
|
+
error
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
})();
|
|
383
|
+
}
|
|
384
|
+
function clearDevRouteMappings() {
|
|
385
|
+
const g = globalThis;
|
|
386
|
+
delete g[ROUTE_MAPPINGS_KEY];
|
|
387
|
+
}
|
|
388
|
+
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
389
|
+
globalThis.__clearDevRouteMappings = clearDevRouteMappings;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/oidc.ts
|
|
393
|
+
var import_oidc = require("@vercel/oidc");
|
|
101
394
|
|
|
102
395
|
// src/client.ts
|
|
396
|
+
function isDebugEnabled() {
|
|
397
|
+
return process.env.VERCEL_QUEUE_DEBUG === "1" || process.env.VERCEL_QUEUE_DEBUG === "true";
|
|
398
|
+
}
|
|
103
399
|
async function consumeStream(stream) {
|
|
104
400
|
const reader = stream.getReader();
|
|
105
401
|
try {
|
|
@@ -111,17 +407,34 @@ async function consumeStream(stream) {
|
|
|
111
407
|
reader.releaseLock();
|
|
112
408
|
}
|
|
113
409
|
}
|
|
410
|
+
function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters") {
|
|
411
|
+
if (status === 400) {
|
|
412
|
+
throw new BadRequestError(errorText || badRequestDefault);
|
|
413
|
+
}
|
|
414
|
+
if (status === 401) {
|
|
415
|
+
throw new UnauthorizedError(errorText || void 0);
|
|
416
|
+
}
|
|
417
|
+
if (status === 403) {
|
|
418
|
+
throw new ForbiddenError(errorText || void 0);
|
|
419
|
+
}
|
|
420
|
+
if (status >= 500) {
|
|
421
|
+
throw new InternalServerError(
|
|
422
|
+
errorText || `Server error: ${status} ${statusText}`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
throw new Error(`Failed to ${operation}: ${status} ${statusText}`);
|
|
426
|
+
}
|
|
114
427
|
function parseQueueHeaders(headers) {
|
|
115
428
|
const messageId = headers.get("Vqs-Message-Id");
|
|
116
429
|
const deliveryCountStr = headers.get("Vqs-Delivery-Count") || "0";
|
|
117
430
|
const timestamp = headers.get("Vqs-Timestamp");
|
|
118
431
|
const contentType = headers.get("Content-Type") || "application/octet-stream";
|
|
119
|
-
const
|
|
120
|
-
if (!messageId || !timestamp || !
|
|
432
|
+
const receiptHandle = headers.get("Vqs-Receipt-Handle");
|
|
433
|
+
if (!messageId || !timestamp || !receiptHandle) {
|
|
121
434
|
return null;
|
|
122
435
|
}
|
|
123
436
|
const deliveryCount = parseInt(deliveryCountStr, 10);
|
|
124
|
-
if (isNaN(deliveryCount)) {
|
|
437
|
+
if (Number.isNaN(deliveryCount)) {
|
|
125
438
|
return null;
|
|
126
439
|
}
|
|
127
440
|
return {
|
|
@@ -129,30 +442,43 @@ function parseQueueHeaders(headers) {
|
|
|
129
442
|
deliveryCount,
|
|
130
443
|
createdAt: new Date(timestamp),
|
|
131
444
|
contentType,
|
|
132
|
-
|
|
445
|
+
receiptHandle
|
|
133
446
|
};
|
|
134
447
|
}
|
|
135
448
|
var QueueClient = class {
|
|
136
449
|
baseUrl;
|
|
137
450
|
basePath;
|
|
138
|
-
customHeaders
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
*/
|
|
451
|
+
customHeaders;
|
|
452
|
+
providedToken;
|
|
453
|
+
defaultDeploymentId;
|
|
454
|
+
pinToDeployment;
|
|
143
455
|
constructor(options = {}) {
|
|
144
456
|
this.baseUrl = options.baseUrl || process.env.VERCEL_QUEUE_BASE_URL || "https://vercel-queue.com";
|
|
145
|
-
this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/
|
|
146
|
-
|
|
147
|
-
this.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
457
|
+
this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v3/topic";
|
|
458
|
+
this.customHeaders = options.headers || {};
|
|
459
|
+
this.providedToken = options.token;
|
|
460
|
+
this.defaultDeploymentId = options.deploymentId || process.env.VERCEL_DEPLOYMENT_ID;
|
|
461
|
+
this.pinToDeployment = options.pinToDeployment ?? true;
|
|
462
|
+
}
|
|
463
|
+
getSendDeploymentId() {
|
|
464
|
+
if (isDevMode()) {
|
|
465
|
+
return void 0;
|
|
466
|
+
}
|
|
467
|
+
if (this.pinToDeployment) {
|
|
468
|
+
return this.defaultDeploymentId;
|
|
469
|
+
}
|
|
470
|
+
return void 0;
|
|
471
|
+
}
|
|
472
|
+
getConsumeDeploymentId() {
|
|
473
|
+
if (isDevMode()) {
|
|
474
|
+
return void 0;
|
|
475
|
+
}
|
|
476
|
+
return this.defaultDeploymentId;
|
|
154
477
|
}
|
|
155
478
|
async getToken() {
|
|
479
|
+
if (this.providedToken) {
|
|
480
|
+
return this.providedToken;
|
|
481
|
+
}
|
|
156
482
|
const token = await (0, import_oidc.getVercelOidcToken)();
|
|
157
483
|
if (!token) {
|
|
158
484
|
throw new Error(
|
|
@@ -161,25 +487,61 @@ var QueueClient = class {
|
|
|
161
487
|
}
|
|
162
488
|
return token;
|
|
163
489
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
490
|
+
buildUrl(queueName, ...pathSegments) {
|
|
491
|
+
const encodedQueue = encodeURIComponent(queueName);
|
|
492
|
+
const segments = pathSegments.map((s) => encodeURIComponent(s));
|
|
493
|
+
const path2 = segments.length > 0 ? "/" + segments.join("/") : "";
|
|
494
|
+
return `${this.baseUrl}${this.basePath}/${encodedQueue}${path2}`;
|
|
495
|
+
}
|
|
496
|
+
async fetch(url, init) {
|
|
497
|
+
const method = init.method || "GET";
|
|
498
|
+
if (isDebugEnabled()) {
|
|
499
|
+
const logData = {
|
|
500
|
+
method,
|
|
501
|
+
url,
|
|
502
|
+
headers: init.headers
|
|
503
|
+
};
|
|
504
|
+
const body = init.body;
|
|
505
|
+
if (body !== void 0 && body !== null) {
|
|
506
|
+
if (body instanceof ArrayBuffer) {
|
|
507
|
+
logData.bodySize = body.byteLength;
|
|
508
|
+
} else if (body instanceof Uint8Array) {
|
|
509
|
+
logData.bodySize = body.byteLength;
|
|
510
|
+
} else if (typeof body === "string") {
|
|
511
|
+
logData.bodySize = body.length;
|
|
512
|
+
} else {
|
|
513
|
+
logData.bodyType = typeof body;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
|
|
517
|
+
}
|
|
518
|
+
const response = await fetch(url, init);
|
|
519
|
+
if (isDebugEnabled()) {
|
|
520
|
+
const logData = {
|
|
521
|
+
method,
|
|
522
|
+
url,
|
|
523
|
+
status: response.status,
|
|
524
|
+
statusText: response.statusText,
|
|
525
|
+
headers: response.headers
|
|
526
|
+
};
|
|
527
|
+
console.debug("[VQS Debug] Response:", JSON.stringify(logData, null, 2));
|
|
528
|
+
}
|
|
529
|
+
return response;
|
|
530
|
+
}
|
|
174
531
|
async sendMessage(options, transport) {
|
|
175
|
-
const {
|
|
532
|
+
const {
|
|
533
|
+
queueName,
|
|
534
|
+
payload,
|
|
535
|
+
idempotencyKey,
|
|
536
|
+
retentionSeconds,
|
|
537
|
+
delaySeconds
|
|
538
|
+
} = options;
|
|
176
539
|
const headers = new Headers({
|
|
177
540
|
Authorization: `Bearer ${await this.getToken()}`,
|
|
178
|
-
"Vqs-Queue-Name": queueName,
|
|
179
541
|
"Content-Type": transport.contentType,
|
|
180
542
|
...this.customHeaders
|
|
181
543
|
});
|
|
182
|
-
const deploymentId =
|
|
544
|
+
const deploymentId = this.getSendDeploymentId();
|
|
183
545
|
if (deploymentId) {
|
|
184
546
|
headers.set("Vqs-Deployment-Id", deploymentId);
|
|
185
547
|
}
|
|
@@ -189,106 +551,106 @@ var QueueClient = class {
|
|
|
189
551
|
if (retentionSeconds !== void 0) {
|
|
190
552
|
headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
|
|
191
553
|
}
|
|
192
|
-
|
|
193
|
-
|
|
554
|
+
if (delaySeconds !== void 0) {
|
|
555
|
+
headers.set("Vqs-Delay-Seconds", delaySeconds.toString());
|
|
556
|
+
}
|
|
557
|
+
const serialized = transport.serialize(payload);
|
|
558
|
+
const body = Buffer.isBuffer(serialized) ? new Uint8Array(serialized) : serialized;
|
|
559
|
+
const response = await this.fetch(this.buildUrl(queueName), {
|
|
194
560
|
method: "POST",
|
|
195
561
|
body,
|
|
196
562
|
headers
|
|
197
563
|
});
|
|
198
564
|
if (!response.ok) {
|
|
199
|
-
|
|
200
|
-
const errorText = await response.text();
|
|
201
|
-
throw new BadRequestError(errorText || "Invalid parameters");
|
|
202
|
-
}
|
|
203
|
-
if (response.status === 401) {
|
|
204
|
-
throw new UnauthorizedError();
|
|
205
|
-
}
|
|
206
|
-
if (response.status === 403) {
|
|
207
|
-
throw new ForbiddenError();
|
|
208
|
-
}
|
|
565
|
+
const errorText = await response.text();
|
|
209
566
|
if (response.status === 409) {
|
|
210
|
-
throw new
|
|
567
|
+
throw new DuplicateMessageError(
|
|
568
|
+
errorText || "Duplicate idempotency key detected",
|
|
569
|
+
idempotencyKey
|
|
570
|
+
);
|
|
211
571
|
}
|
|
212
|
-
if (response.status
|
|
213
|
-
throw new
|
|
214
|
-
|
|
572
|
+
if (response.status === 502) {
|
|
573
|
+
throw new ConsumerDiscoveryError(
|
|
574
|
+
errorText || "Consumer discovery failed",
|
|
575
|
+
deploymentId
|
|
215
576
|
);
|
|
216
577
|
}
|
|
217
|
-
|
|
218
|
-
|
|
578
|
+
if (response.status === 503) {
|
|
579
|
+
throw new ConsumerRegistryNotConfiguredError(
|
|
580
|
+
errorText || "Consumer registry not configured"
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
throwCommonHttpError(
|
|
584
|
+
response.status,
|
|
585
|
+
response.statusText,
|
|
586
|
+
errorText,
|
|
587
|
+
"send message"
|
|
219
588
|
);
|
|
220
589
|
}
|
|
221
590
|
const responseData = await response.json();
|
|
222
591
|
return responseData;
|
|
223
592
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Receive messages from a queue
|
|
226
|
-
* @param options Receive messages options
|
|
227
|
-
* @param transport Serializer/deserializer for the payload
|
|
228
|
-
* @returns AsyncGenerator that yields messages as they arrive
|
|
229
|
-
* @throws {InvalidLimitError} When limit parameter is not between 1 and 10
|
|
230
|
-
* @throws {QueueEmptyError} When no messages are available (204)
|
|
231
|
-
* @throws {MessageLockedError} When messages are temporarily locked (423)
|
|
232
|
-
* @throws {BadRequestError} When request parameters are invalid
|
|
233
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
234
|
-
* @throws {ForbiddenError} When access is denied (environment mismatch)
|
|
235
|
-
* @throws {InternalServerError} When server encounters an error
|
|
236
|
-
*/
|
|
237
593
|
async *receiveMessages(options, transport) {
|
|
238
|
-
const {
|
|
594
|
+
const {
|
|
595
|
+
queueName,
|
|
596
|
+
consumerGroup,
|
|
597
|
+
visibilityTimeoutSeconds,
|
|
598
|
+
limit,
|
|
599
|
+
maxConcurrency
|
|
600
|
+
} = options;
|
|
239
601
|
if (limit !== void 0 && (limit < 1 || limit > 10)) {
|
|
240
602
|
throw new InvalidLimitError(limit);
|
|
241
603
|
}
|
|
242
604
|
const headers = new Headers({
|
|
243
605
|
Authorization: `Bearer ${await this.getToken()}`,
|
|
244
|
-
"Vqs-Queue-Name": queueName,
|
|
245
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
246
606
|
Accept: "multipart/mixed",
|
|
247
607
|
...this.customHeaders
|
|
248
608
|
});
|
|
249
609
|
if (visibilityTimeoutSeconds !== void 0) {
|
|
250
610
|
headers.set(
|
|
251
|
-
"Vqs-Visibility-Timeout",
|
|
611
|
+
"Vqs-Visibility-Timeout-Seconds",
|
|
252
612
|
visibilityTimeoutSeconds.toString()
|
|
253
613
|
);
|
|
254
614
|
}
|
|
255
615
|
if (limit !== void 0) {
|
|
256
|
-
headers.set("Vqs-
|
|
616
|
+
headers.set("Vqs-Max-Messages", limit.toString());
|
|
257
617
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
618
|
+
if (maxConcurrency !== void 0) {
|
|
619
|
+
headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
|
|
620
|
+
}
|
|
621
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
622
|
+
if (effectiveDeploymentId) {
|
|
623
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
624
|
+
}
|
|
625
|
+
const response = await this.fetch(
|
|
626
|
+
this.buildUrl(queueName, "consumer", consumerGroup),
|
|
627
|
+
{
|
|
628
|
+
method: "POST",
|
|
629
|
+
headers
|
|
630
|
+
}
|
|
631
|
+
);
|
|
262
632
|
if (response.status === 204) {
|
|
263
633
|
throw new QueueEmptyError(queueName, consumerGroup);
|
|
264
634
|
}
|
|
265
635
|
if (!response.ok) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
if (response.status === 403) {
|
|
274
|
-
throw new ForbiddenError();
|
|
275
|
-
}
|
|
276
|
-
if (response.status === 423) {
|
|
277
|
-
const retryAfterHeader = response.headers.get("Retry-After");
|
|
278
|
-
let retryAfter;
|
|
279
|
-
if (retryAfterHeader) {
|
|
280
|
-
const parsed = parseInt(retryAfterHeader, 10);
|
|
281
|
-
retryAfter = isNaN(parsed) ? void 0 : parsed;
|
|
636
|
+
const errorText = await response.text();
|
|
637
|
+
if (response.status === 429) {
|
|
638
|
+
let errorData = {};
|
|
639
|
+
try {
|
|
640
|
+
errorData = JSON.parse(errorText);
|
|
641
|
+
} catch {
|
|
282
642
|
}
|
|
283
|
-
throw new
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
`Server error: ${response.status} ${response.statusText}`
|
|
643
|
+
throw new ConcurrencyLimitError(
|
|
644
|
+
errorData.error || "Concurrency limit exceeded or throttled",
|
|
645
|
+
errorData.currentInflight,
|
|
646
|
+
errorData.maxConcurrency
|
|
288
647
|
);
|
|
289
648
|
}
|
|
290
|
-
|
|
291
|
-
|
|
649
|
+
throwCommonHttpError(
|
|
650
|
+
response.status,
|
|
651
|
+
response.statusText,
|
|
652
|
+
errorText,
|
|
653
|
+
"receive messages"
|
|
292
654
|
);
|
|
293
655
|
}
|
|
294
656
|
for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
|
|
@@ -319,458 +681,250 @@ var QueueClient = class {
|
|
|
319
681
|
consumerGroup,
|
|
320
682
|
messageId,
|
|
321
683
|
visibilityTimeoutSeconds,
|
|
322
|
-
|
|
684
|
+
maxConcurrency
|
|
323
685
|
} = options;
|
|
324
686
|
const headers = new Headers({
|
|
325
687
|
Authorization: `Bearer ${await this.getToken()}`,
|
|
326
|
-
"Vqs-Queue-Name": queueName,
|
|
327
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
328
688
|
Accept: "multipart/mixed",
|
|
329
689
|
...this.customHeaders
|
|
330
690
|
});
|
|
331
691
|
if (visibilityTimeoutSeconds !== void 0) {
|
|
332
692
|
headers.set(
|
|
333
|
-
"Vqs-Visibility-Timeout",
|
|
693
|
+
"Vqs-Visibility-Timeout-Seconds",
|
|
334
694
|
visibilityTimeoutSeconds.toString()
|
|
335
695
|
);
|
|
336
696
|
}
|
|
337
|
-
if (
|
|
338
|
-
headers.set("Vqs-
|
|
697
|
+
if (maxConcurrency !== void 0) {
|
|
698
|
+
headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
|
|
339
699
|
}
|
|
340
|
-
const
|
|
341
|
-
|
|
700
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
701
|
+
if (effectiveDeploymentId) {
|
|
702
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
703
|
+
}
|
|
704
|
+
const response = await this.fetch(
|
|
705
|
+
this.buildUrl(queueName, "consumer", consumerGroup, "id", messageId),
|
|
342
706
|
{
|
|
343
|
-
method: "
|
|
707
|
+
method: "POST",
|
|
344
708
|
headers
|
|
345
709
|
}
|
|
346
710
|
);
|
|
347
711
|
if (!response.ok) {
|
|
348
|
-
|
|
349
|
-
const errorText = await response.text();
|
|
350
|
-
throw new BadRequestError(errorText || "Invalid parameters");
|
|
351
|
-
}
|
|
352
|
-
if (response.status === 401) {
|
|
353
|
-
throw new UnauthorizedError();
|
|
354
|
-
}
|
|
355
|
-
if (response.status === 403) {
|
|
356
|
-
throw new ForbiddenError();
|
|
357
|
-
}
|
|
712
|
+
const errorText = await response.text();
|
|
358
713
|
if (response.status === 404) {
|
|
359
714
|
throw new MessageNotFoundError(messageId);
|
|
360
715
|
}
|
|
361
|
-
if (response.status === 423) {
|
|
362
|
-
const retryAfterHeader = response.headers.get("Retry-After");
|
|
363
|
-
let retryAfter;
|
|
364
|
-
if (retryAfterHeader) {
|
|
365
|
-
const parsed = parseInt(retryAfterHeader, 10);
|
|
366
|
-
retryAfter = isNaN(parsed) ? void 0 : parsed;
|
|
367
|
-
}
|
|
368
|
-
throw new MessageLockedError(messageId, retryAfter);
|
|
369
|
-
}
|
|
370
716
|
if (response.status === 409) {
|
|
717
|
+
let errorData = {};
|
|
718
|
+
try {
|
|
719
|
+
errorData = JSON.parse(errorText);
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
if (errorData.originalMessageId) {
|
|
723
|
+
throw new MessageNotAvailableError(
|
|
724
|
+
messageId,
|
|
725
|
+
`This message was a duplicate - use originalMessageId: ${errorData.originalMessageId}`
|
|
726
|
+
);
|
|
727
|
+
}
|
|
371
728
|
throw new MessageNotAvailableError(messageId);
|
|
372
729
|
}
|
|
373
|
-
if (response.status
|
|
374
|
-
throw new
|
|
375
|
-
|
|
730
|
+
if (response.status === 410) {
|
|
731
|
+
throw new MessageAlreadyProcessedError(messageId);
|
|
732
|
+
}
|
|
733
|
+
if (response.status === 429) {
|
|
734
|
+
let errorData = {};
|
|
735
|
+
try {
|
|
736
|
+
errorData = JSON.parse(errorText);
|
|
737
|
+
} catch {
|
|
738
|
+
}
|
|
739
|
+
throw new ConcurrencyLimitError(
|
|
740
|
+
errorData.error || "Concurrency limit exceeded or throttled",
|
|
741
|
+
errorData.currentInflight,
|
|
742
|
+
errorData.maxConcurrency
|
|
376
743
|
);
|
|
377
744
|
}
|
|
378
|
-
|
|
379
|
-
|
|
745
|
+
throwCommonHttpError(
|
|
746
|
+
response.status,
|
|
747
|
+
response.statusText,
|
|
748
|
+
errorText,
|
|
749
|
+
"receive message by ID"
|
|
380
750
|
);
|
|
381
751
|
}
|
|
382
|
-
|
|
383
|
-
const parsedHeaders = parseQueueHeaders(
|
|
752
|
+
for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
|
|
753
|
+
const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
|
|
384
754
|
if (!parsedHeaders) {
|
|
755
|
+
await consumeStream(multipartMessage.payload);
|
|
385
756
|
throw new MessageCorruptedError(
|
|
386
757
|
messageId,
|
|
387
|
-
"Missing required queue headers in
|
|
758
|
+
"Missing required queue headers in response"
|
|
388
759
|
);
|
|
389
760
|
}
|
|
761
|
+
const deserializedPayload = await transport.deserialize(
|
|
762
|
+
multipartMessage.payload
|
|
763
|
+
);
|
|
390
764
|
const message = {
|
|
391
765
|
...parsedHeaders,
|
|
392
|
-
payload:
|
|
766
|
+
payload: deserializedPayload
|
|
393
767
|
};
|
|
394
768
|
return { message };
|
|
395
769
|
}
|
|
396
|
-
if (!transport) {
|
|
397
|
-
throw new Error("Transport is required when skipPayload is not true");
|
|
398
|
-
}
|
|
399
|
-
try {
|
|
400
|
-
for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
|
|
401
|
-
try {
|
|
402
|
-
const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
|
|
403
|
-
if (!parsedHeaders) {
|
|
404
|
-
console.warn("Missing required queue headers in multipart part");
|
|
405
|
-
await consumeStream(multipartMessage.payload);
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
const deserializedPayload = await transport.deserialize(
|
|
409
|
-
multipartMessage.payload
|
|
410
|
-
);
|
|
411
|
-
const message = {
|
|
412
|
-
...parsedHeaders,
|
|
413
|
-
payload: deserializedPayload
|
|
414
|
-
};
|
|
415
|
-
return { message };
|
|
416
|
-
} catch (error) {
|
|
417
|
-
console.warn("Failed to deserialize message by ID:", error);
|
|
418
|
-
await consumeStream(multipartMessage.payload);
|
|
419
|
-
throw new MessageCorruptedError(
|
|
420
|
-
messageId,
|
|
421
|
-
`Failed to deserialize payload: ${error}`
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
} catch (error) {
|
|
426
|
-
if (error instanceof MessageCorruptedError) {
|
|
427
|
-
throw error;
|
|
428
|
-
}
|
|
429
|
-
throw new MessageCorruptedError(
|
|
430
|
-
messageId,
|
|
431
|
-
`Failed to parse multipart response: ${error}`
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
770
|
throw new MessageNotFoundError(messageId);
|
|
435
771
|
}
|
|
436
|
-
/**
|
|
437
|
-
* Delete a message (acknowledge processing)
|
|
438
|
-
* @param options Delete message options
|
|
439
|
-
* @returns Promise with delete status
|
|
440
|
-
* @throws {MessageNotFoundError} When the message doesn't exist (404)
|
|
441
|
-
* @throws {MessageNotAvailableError} When message can't be deleted (409)
|
|
442
|
-
* @throws {BadRequestError} When ticket is missing or invalid (400)
|
|
443
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
444
|
-
* @throws {ForbiddenError} When access is denied (environment mismatch)
|
|
445
|
-
* @throws {InternalServerError} When server encounters an error
|
|
446
|
-
*/
|
|
447
772
|
async deleteMessage(options) {
|
|
448
|
-
const { queueName, consumerGroup,
|
|
449
|
-
const
|
|
450
|
-
|
|
773
|
+
const { queueName, consumerGroup, receiptHandle } = options;
|
|
774
|
+
const headers = new Headers({
|
|
775
|
+
Authorization: `Bearer ${await this.getToken()}`,
|
|
776
|
+
...this.customHeaders
|
|
777
|
+
});
|
|
778
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
779
|
+
if (effectiveDeploymentId) {
|
|
780
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
781
|
+
}
|
|
782
|
+
const response = await this.fetch(
|
|
783
|
+
this.buildUrl(
|
|
784
|
+
queueName,
|
|
785
|
+
"consumer",
|
|
786
|
+
consumerGroup,
|
|
787
|
+
"lease",
|
|
788
|
+
receiptHandle
|
|
789
|
+
),
|
|
451
790
|
{
|
|
452
791
|
method: "DELETE",
|
|
453
|
-
headers
|
|
454
|
-
Authorization: `Bearer ${await this.getToken()}`,
|
|
455
|
-
"Vqs-Queue-Name": queueName,
|
|
456
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
457
|
-
"Vqs-Ticket": ticket,
|
|
458
|
-
...this.customHeaders
|
|
459
|
-
})
|
|
792
|
+
headers
|
|
460
793
|
}
|
|
461
794
|
);
|
|
462
795
|
if (!response.ok) {
|
|
463
|
-
|
|
464
|
-
throw new BadRequestError("Missing or invalid ticket");
|
|
465
|
-
}
|
|
466
|
-
if (response.status === 401) {
|
|
467
|
-
throw new UnauthorizedError();
|
|
468
|
-
}
|
|
469
|
-
if (response.status === 403) {
|
|
470
|
-
throw new ForbiddenError();
|
|
471
|
-
}
|
|
796
|
+
const errorText = await response.text();
|
|
472
797
|
if (response.status === 404) {
|
|
473
|
-
throw new MessageNotFoundError(
|
|
798
|
+
throw new MessageNotFoundError(receiptHandle);
|
|
474
799
|
}
|
|
475
800
|
if (response.status === 409) {
|
|
476
801
|
throw new MessageNotAvailableError(
|
|
477
|
-
|
|
478
|
-
"Invalid
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
if (response.status >= 500) {
|
|
482
|
-
throw new InternalServerError(
|
|
483
|
-
`Server error: ${response.status} ${response.statusText}`
|
|
802
|
+
receiptHandle,
|
|
803
|
+
errorText || "Invalid receipt handle, message not in correct state, or already processed"
|
|
484
804
|
);
|
|
485
805
|
}
|
|
486
|
-
|
|
487
|
-
|
|
806
|
+
throwCommonHttpError(
|
|
807
|
+
response.status,
|
|
808
|
+
response.statusText,
|
|
809
|
+
errorText,
|
|
810
|
+
"delete message",
|
|
811
|
+
"Missing or invalid receipt handle"
|
|
488
812
|
);
|
|
489
813
|
}
|
|
490
814
|
return { deleted: true };
|
|
491
815
|
}
|
|
492
|
-
/**
|
|
493
|
-
* Change the visibility timeout of a message
|
|
494
|
-
* @param options Change visibility options
|
|
495
|
-
* @returns Promise with update status
|
|
496
|
-
* @throws {MessageNotFoundError} When the message doesn't exist (404)
|
|
497
|
-
* @throws {MessageNotAvailableError} When message can't be updated (409)
|
|
498
|
-
* @throws {BadRequestError} When ticket is missing or visibility timeout invalid (400)
|
|
499
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
500
|
-
* @throws {ForbiddenError} When access is denied (environment mismatch)
|
|
501
|
-
* @throws {InternalServerError} When server encounters an error
|
|
502
|
-
*/
|
|
503
816
|
async changeVisibility(options) {
|
|
504
817
|
const {
|
|
505
818
|
queueName,
|
|
506
819
|
consumerGroup,
|
|
507
|
-
|
|
508
|
-
ticket,
|
|
820
|
+
receiptHandle,
|
|
509
821
|
visibilityTimeoutSeconds
|
|
510
822
|
} = options;
|
|
511
|
-
const
|
|
512
|
-
|
|
823
|
+
const headers = new Headers({
|
|
824
|
+
Authorization: `Bearer ${await this.getToken()}`,
|
|
825
|
+
"Content-Type": "application/json",
|
|
826
|
+
...this.customHeaders
|
|
827
|
+
});
|
|
828
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
829
|
+
if (effectiveDeploymentId) {
|
|
830
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
831
|
+
}
|
|
832
|
+
const response = await this.fetch(
|
|
833
|
+
this.buildUrl(
|
|
834
|
+
queueName,
|
|
835
|
+
"consumer",
|
|
836
|
+
consumerGroup,
|
|
837
|
+
"lease",
|
|
838
|
+
receiptHandle
|
|
839
|
+
),
|
|
513
840
|
{
|
|
514
841
|
method: "PATCH",
|
|
515
|
-
headers
|
|
516
|
-
|
|
517
|
-
"Vqs-Queue-Name": queueName,
|
|
518
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
519
|
-
"Vqs-Ticket": ticket,
|
|
520
|
-
"Vqs-Visibility-Timeout": visibilityTimeoutSeconds.toString(),
|
|
521
|
-
...this.customHeaders
|
|
522
|
-
})
|
|
842
|
+
headers,
|
|
843
|
+
body: JSON.stringify({ visibilityTimeoutSeconds })
|
|
523
844
|
}
|
|
524
845
|
);
|
|
525
846
|
if (!response.ok) {
|
|
526
|
-
|
|
527
|
-
throw new BadRequestError(
|
|
528
|
-
"Missing ticket or invalid visibility timeout"
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
if (response.status === 401) {
|
|
532
|
-
throw new UnauthorizedError();
|
|
533
|
-
}
|
|
534
|
-
if (response.status === 403) {
|
|
535
|
-
throw new ForbiddenError();
|
|
536
|
-
}
|
|
847
|
+
const errorText = await response.text();
|
|
537
848
|
if (response.status === 404) {
|
|
538
|
-
throw new MessageNotFoundError(
|
|
849
|
+
throw new MessageNotFoundError(receiptHandle);
|
|
539
850
|
}
|
|
540
851
|
if (response.status === 409) {
|
|
541
852
|
throw new MessageNotAvailableError(
|
|
542
|
-
|
|
543
|
-
"Invalid
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
if (response.status >= 500) {
|
|
547
|
-
throw new InternalServerError(
|
|
548
|
-
`Server error: ${response.status} ${response.statusText}`
|
|
853
|
+
receiptHandle,
|
|
854
|
+
errorText || "Invalid receipt handle, message not in correct state, or already processed"
|
|
549
855
|
);
|
|
550
856
|
}
|
|
551
|
-
|
|
552
|
-
|
|
857
|
+
throwCommonHttpError(
|
|
858
|
+
response.status,
|
|
859
|
+
response.statusText,
|
|
860
|
+
errorText,
|
|
861
|
+
"change visibility",
|
|
862
|
+
"Missing receipt handle or invalid visibility timeout"
|
|
553
863
|
);
|
|
554
864
|
}
|
|
555
|
-
return {
|
|
865
|
+
return { success: true };
|
|
556
866
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
867
|
+
/**
|
|
868
|
+
* Alternative endpoint for changing message visibility timeout.
|
|
869
|
+
* Uses the /visibility path suffix and expects visibilityTimeoutSeconds in the body.
|
|
870
|
+
* Functionally equivalent to changeVisibility but follows an alternative API pattern.
|
|
871
|
+
*
|
|
872
|
+
* @param options - Options for changing visibility
|
|
873
|
+
* @returns Promise resolving to change visibility response
|
|
874
|
+
*/
|
|
875
|
+
async changeVisibilityAlt(options) {
|
|
876
|
+
const {
|
|
877
|
+
queueName,
|
|
878
|
+
consumerGroup,
|
|
879
|
+
receiptHandle,
|
|
880
|
+
visibilityTimeoutSeconds
|
|
881
|
+
} = options;
|
|
882
|
+
const headers = new Headers({
|
|
883
|
+
Authorization: `Bearer ${await this.getToken()}`,
|
|
884
|
+
"Content-Type": "application/json",
|
|
885
|
+
...this.customHeaders
|
|
886
|
+
});
|
|
887
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
888
|
+
if (effectiveDeploymentId) {
|
|
889
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
570
890
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
serialize(value) {
|
|
585
|
-
return Buffer.from(JSON.stringify(value, this.replacer), "utf8");
|
|
586
|
-
}
|
|
587
|
-
async deserialize(stream) {
|
|
588
|
-
const buffer = await streamToBuffer(stream);
|
|
589
|
-
return JSON.parse(buffer.toString("utf8"), this.reviver);
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
// src/dev.ts
|
|
594
|
-
var GLOBAL_KEY = Symbol.for("@vercel/queue.devHandlers");
|
|
595
|
-
function getDevHandlerState() {
|
|
596
|
-
const g = globalThis;
|
|
597
|
-
if (!g[GLOBAL_KEY]) {
|
|
598
|
-
g[GLOBAL_KEY] = {
|
|
599
|
-
devRouteHandlers: /* @__PURE__ */ new Map(),
|
|
600
|
-
wildcardRouteHandlers: /* @__PURE__ */ new Map()
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
return g[GLOBAL_KEY];
|
|
604
|
-
}
|
|
605
|
-
var { devRouteHandlers, wildcardRouteHandlers } = getDevHandlerState();
|
|
606
|
-
function cleanupDeadRefs(key, refs) {
|
|
607
|
-
const aliveRefs = refs.filter((ref) => ref.deref() !== void 0);
|
|
608
|
-
if (aliveRefs.length === 0) {
|
|
609
|
-
wildcardRouteHandlers.delete(key);
|
|
610
|
-
} else if (aliveRefs.length < refs.length) {
|
|
611
|
-
wildcardRouteHandlers.set(key, aliveRefs);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
function isDevMode() {
|
|
615
|
-
return process.env.NODE_ENV === "development";
|
|
616
|
-
}
|
|
617
|
-
function registerDevRouteHandler(routeHandler, handlers) {
|
|
618
|
-
for (const topicName in handlers) {
|
|
619
|
-
for (const consumerGroup in handlers[topicName]) {
|
|
620
|
-
const key = `${topicName}:${consumerGroup}`;
|
|
621
|
-
if (topicName.includes("*")) {
|
|
622
|
-
const existing = wildcardRouteHandlers.get(key) || [];
|
|
623
|
-
cleanupDeadRefs(key, existing);
|
|
624
|
-
const cleanedRefs = wildcardRouteHandlers.get(key) || [];
|
|
625
|
-
const weakRef = new WeakRef(routeHandler);
|
|
626
|
-
cleanedRefs.push(weakRef);
|
|
627
|
-
wildcardRouteHandlers.set(key, cleanedRefs);
|
|
628
|
-
} else {
|
|
629
|
-
devRouteHandlers.set(key, {
|
|
630
|
-
routeHandler,
|
|
631
|
-
topicPattern: topicName
|
|
632
|
-
});
|
|
891
|
+
const response = await this.fetch(
|
|
892
|
+
this.buildUrl(
|
|
893
|
+
queueName,
|
|
894
|
+
"consumer",
|
|
895
|
+
consumerGroup,
|
|
896
|
+
"lease",
|
|
897
|
+
receiptHandle,
|
|
898
|
+
"visibility"
|
|
899
|
+
),
|
|
900
|
+
{
|
|
901
|
+
method: "PATCH",
|
|
902
|
+
headers,
|
|
903
|
+
body: JSON.stringify({ visibilityTimeoutSeconds })
|
|
633
904
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
for (const [
|
|
640
|
-
key,
|
|
641
|
-
{ routeHandler, topicPattern }
|
|
642
|
-
] of devRouteHandlers.entries()) {
|
|
643
|
-
const [_, consumerGroup] = key.split(":");
|
|
644
|
-
if (topicPattern === topicName) {
|
|
645
|
-
if (!handlersMap.has(routeHandler)) {
|
|
646
|
-
handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
|
|
905
|
+
);
|
|
906
|
+
if (!response.ok) {
|
|
907
|
+
const errorText = await response.text();
|
|
908
|
+
if (response.status === 404) {
|
|
909
|
+
throw new MessageNotFoundError(receiptHandle);
|
|
647
910
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
if (matchesWildcardPattern(topicName, pattern)) {
|
|
654
|
-
cleanupDeadRefs(key, refs);
|
|
655
|
-
const cleanedRefs = wildcardRouteHandlers.get(key) || [];
|
|
656
|
-
for (const ref of cleanedRefs) {
|
|
657
|
-
const routeHandler = ref.deref();
|
|
658
|
-
if (routeHandler) {
|
|
659
|
-
if (!handlersMap.has(routeHandler)) {
|
|
660
|
-
handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
|
|
661
|
-
}
|
|
662
|
-
handlersMap.get(routeHandler).add(consumerGroup);
|
|
663
|
-
}
|
|
911
|
+
if (response.status === 409) {
|
|
912
|
+
throw new MessageNotAvailableError(
|
|
913
|
+
receiptHandle,
|
|
914
|
+
errorText || "Invalid receipt handle, message not in correct state, or already processed"
|
|
915
|
+
);
|
|
664
916
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
type: "com.vercel.queue.v1beta",
|
|
672
|
-
source: `/topic/${topicName}/consumer/${consumerGroup}`,
|
|
673
|
-
id: messageId,
|
|
674
|
-
datacontenttype: "application/json",
|
|
675
|
-
data: {
|
|
676
|
-
messageId,
|
|
677
|
-
queueName: topicName,
|
|
678
|
-
consumerGroup
|
|
679
|
-
},
|
|
680
|
-
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
681
|
-
specversion: "1.0"
|
|
682
|
-
};
|
|
683
|
-
return new Request("https://localhost/api/queue/callback", {
|
|
684
|
-
method: "POST",
|
|
685
|
-
headers: {
|
|
686
|
-
"Content-Type": "application/cloudevents+json"
|
|
687
|
-
},
|
|
688
|
-
body: JSON.stringify(cloudEvent)
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
|
-
var DEV_CALLBACK_DELAY = 1e3;
|
|
692
|
-
function scheduleDevTimeout(topicName, messageId, timeoutSeconds) {
|
|
693
|
-
console.log(
|
|
694
|
-
`[Dev Mode] Message ${messageId} timed out for ${timeoutSeconds}s, will re-trigger`
|
|
695
|
-
);
|
|
696
|
-
setTimeout(
|
|
697
|
-
() => {
|
|
698
|
-
console.log(
|
|
699
|
-
`[Dev Mode] Re-triggering callback for timed-out message ${messageId}`
|
|
917
|
+
throwCommonHttpError(
|
|
918
|
+
response.status,
|
|
919
|
+
response.statusText,
|
|
920
|
+
errorText,
|
|
921
|
+
"change visibility (alt)",
|
|
922
|
+
"Missing receipt handle or invalid visibility timeout"
|
|
700
923
|
);
|
|
701
|
-
triggerDevCallbacks(topicName, messageId);
|
|
702
|
-
},
|
|
703
|
-
timeoutSeconds * 1e3 + DEV_CALLBACK_DELAY
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
function triggerDevCallbacks(topicName, messageId) {
|
|
707
|
-
const handlersMap = findRouteHandlersForTopic(topicName);
|
|
708
|
-
if (handlersMap.size === 0) {
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
const consumerGroups = Array.from(
|
|
712
|
-
new Set(
|
|
713
|
-
Array.from(handlersMap.values()).flatMap((groups) => Array.from(groups))
|
|
714
|
-
)
|
|
715
|
-
);
|
|
716
|
-
console.log(
|
|
717
|
-
`[Dev Mode] Triggering local callbacks for topic "${topicName}" \u2192 consumers: ${consumerGroups.join(", ")}`
|
|
718
|
-
);
|
|
719
|
-
setTimeout(async () => {
|
|
720
|
-
for (const [routeHandler, consumerGroups2] of handlersMap.entries()) {
|
|
721
|
-
for (const consumerGroup of consumerGroups2) {
|
|
722
|
-
try {
|
|
723
|
-
const request = createMockCloudEventRequest(
|
|
724
|
-
topicName,
|
|
725
|
-
consumerGroup,
|
|
726
|
-
messageId
|
|
727
|
-
);
|
|
728
|
-
const response = await routeHandler(request);
|
|
729
|
-
if (response.ok) {
|
|
730
|
-
try {
|
|
731
|
-
const responseData = await response.json();
|
|
732
|
-
if (responseData.status === "success") {
|
|
733
|
-
console.log(
|
|
734
|
-
`[Dev Mode] Message processed for ${topicName}/${consumerGroup}`
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
} catch (jsonError) {
|
|
738
|
-
console.error(
|
|
739
|
-
`[Dev Mode] Failed to parse success response for ${topicName}/${consumerGroup}:`,
|
|
740
|
-
jsonError
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
} else {
|
|
744
|
-
try {
|
|
745
|
-
const errorData = await response.json();
|
|
746
|
-
console.error(
|
|
747
|
-
`[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
|
|
748
|
-
errorData.error || response.statusText
|
|
749
|
-
);
|
|
750
|
-
} catch (jsonError) {
|
|
751
|
-
console.error(
|
|
752
|
-
`[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
|
|
753
|
-
response.statusText
|
|
754
|
-
);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
} catch (error) {
|
|
758
|
-
console.error(
|
|
759
|
-
`[Dev Mode] Error triggering callback for ${topicName}/${consumerGroup}:`,
|
|
760
|
-
error
|
|
761
|
-
);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
924
|
}
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
devRouteHandlers.clear();
|
|
769
|
-
wildcardRouteHandlers.clear();
|
|
770
|
-
}
|
|
771
|
-
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
772
|
-
globalThis.__clearDevHandlers = clearDevHandlers;
|
|
773
|
-
}
|
|
925
|
+
return { success: true };
|
|
926
|
+
}
|
|
927
|
+
};
|
|
774
928
|
|
|
775
929
|
// src/consumer-group.ts
|
|
776
930
|
var ConsumerGroup = class {
|
|
@@ -802,8 +956,7 @@ var ConsumerGroup = class {
|
|
|
802
956
|
* The extension loop runs every `refreshInterval` seconds and updates the message's
|
|
803
957
|
* visibility timeout to `visibilityTimeout` seconds from the current time.
|
|
804
958
|
*
|
|
805
|
-
* @param
|
|
806
|
-
* @param ticket - The receipt ticket that proves ownership of the message
|
|
959
|
+
* @param receiptHandle - The receipt handle that proves ownership of the message
|
|
807
960
|
* @returns A function that when called will stop the extension loop
|
|
808
961
|
*
|
|
809
962
|
* @remarks
|
|
@@ -813,37 +966,43 @@ var ConsumerGroup = class {
|
|
|
813
966
|
* - By default, the stop function returns immediately without waiting for in-flight
|
|
814
967
|
* - Pass `true` to the stop function to wait for any in-flight extension to complete
|
|
815
968
|
*/
|
|
816
|
-
startVisibilityExtension(
|
|
969
|
+
startVisibilityExtension(receiptHandle) {
|
|
817
970
|
let isRunning = true;
|
|
971
|
+
let isResolved = false;
|
|
818
972
|
let resolveLifecycle;
|
|
819
973
|
let timeoutId = null;
|
|
820
974
|
const lifecyclePromise = new Promise((resolve) => {
|
|
821
975
|
resolveLifecycle = resolve;
|
|
822
976
|
});
|
|
977
|
+
const safeResolve = () => {
|
|
978
|
+
if (!isResolved) {
|
|
979
|
+
isResolved = true;
|
|
980
|
+
resolveLifecycle();
|
|
981
|
+
}
|
|
982
|
+
};
|
|
823
983
|
const extend = async () => {
|
|
824
984
|
if (!isRunning) {
|
|
825
|
-
|
|
985
|
+
safeResolve();
|
|
826
986
|
return;
|
|
827
987
|
}
|
|
828
988
|
try {
|
|
829
989
|
await this.client.changeVisibility({
|
|
830
990
|
queueName: this.topicName,
|
|
831
991
|
consumerGroup: this.consumerGroupName,
|
|
832
|
-
|
|
833
|
-
ticket,
|
|
992
|
+
receiptHandle,
|
|
834
993
|
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
835
994
|
});
|
|
836
995
|
if (isRunning) {
|
|
837
996
|
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
838
997
|
} else {
|
|
839
|
-
|
|
998
|
+
safeResolve();
|
|
840
999
|
}
|
|
841
1000
|
} catch (error) {
|
|
842
1001
|
console.error(
|
|
843
|
-
`Failed to extend visibility for
|
|
1002
|
+
`Failed to extend visibility for receipt handle ${receiptHandle}:`,
|
|
844
1003
|
error
|
|
845
1004
|
);
|
|
846
|
-
|
|
1005
|
+
safeResolve();
|
|
847
1006
|
}
|
|
848
1007
|
};
|
|
849
1008
|
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
@@ -856,22 +1015,14 @@ var ConsumerGroup = class {
|
|
|
856
1015
|
if (waitForCompletion) {
|
|
857
1016
|
await lifecyclePromise;
|
|
858
1017
|
} else {
|
|
859
|
-
|
|
1018
|
+
safeResolve();
|
|
860
1019
|
}
|
|
861
1020
|
};
|
|
862
1021
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Process a single message with the given handler
|
|
865
|
-
* @param message The message to process
|
|
866
|
-
* @param handler Function to process the message
|
|
867
|
-
*/
|
|
868
1022
|
async processMessage(message, handler) {
|
|
869
|
-
const stopExtension = this.startVisibilityExtension(
|
|
870
|
-
message.messageId,
|
|
871
|
-
message.ticket
|
|
872
|
-
);
|
|
1023
|
+
const stopExtension = this.startVisibilityExtension(message.receiptHandle);
|
|
873
1024
|
try {
|
|
874
|
-
|
|
1025
|
+
await handler(message.payload, {
|
|
875
1026
|
messageId: message.messageId,
|
|
876
1027
|
deliveryCount: message.deliveryCount,
|
|
877
1028
|
createdAt: message.createdAt,
|
|
@@ -879,29 +1030,11 @@ var ConsumerGroup = class {
|
|
|
879
1030
|
consumerGroup: this.consumerGroupName
|
|
880
1031
|
});
|
|
881
1032
|
await stopExtension();
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
ticket: message.ticket,
|
|
888
|
-
visibilityTimeoutSeconds: result.timeoutSeconds
|
|
889
|
-
});
|
|
890
|
-
if (isDevMode()) {
|
|
891
|
-
scheduleDevTimeout(
|
|
892
|
-
this.topicName,
|
|
893
|
-
message.messageId,
|
|
894
|
-
result.timeoutSeconds
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
} else {
|
|
898
|
-
await this.client.deleteMessage({
|
|
899
|
-
queueName: this.topicName,
|
|
900
|
-
consumerGroup: this.consumerGroupName,
|
|
901
|
-
messageId: message.messageId,
|
|
902
|
-
ticket: message.ticket
|
|
903
|
-
});
|
|
904
|
-
}
|
|
1033
|
+
await this.client.deleteMessage({
|
|
1034
|
+
queueName: this.topicName,
|
|
1035
|
+
consumerGroup: this.consumerGroupName,
|
|
1036
|
+
receiptHandle: message.receiptHandle
|
|
1037
|
+
});
|
|
905
1038
|
} catch (error) {
|
|
906
1039
|
await stopExtension();
|
|
907
1040
|
if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
|
|
@@ -916,36 +1049,16 @@ var ConsumerGroup = class {
|
|
|
916
1049
|
}
|
|
917
1050
|
async consume(handler, options) {
|
|
918
1051
|
if (options?.messageId) {
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
);
|
|
930
|
-
await this.processMessage(
|
|
931
|
-
response.message,
|
|
932
|
-
handler
|
|
933
|
-
);
|
|
934
|
-
} else {
|
|
935
|
-
const response = await this.client.receiveMessageById(
|
|
936
|
-
{
|
|
937
|
-
queueName: this.topicName,
|
|
938
|
-
consumerGroup: this.consumerGroupName,
|
|
939
|
-
messageId: options.messageId,
|
|
940
|
-
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
941
|
-
},
|
|
942
|
-
this.transport
|
|
943
|
-
);
|
|
944
|
-
await this.processMessage(
|
|
945
|
-
response.message,
|
|
946
|
-
handler
|
|
947
|
-
);
|
|
948
|
-
}
|
|
1052
|
+
const response = await this.client.receiveMessageById(
|
|
1053
|
+
{
|
|
1054
|
+
queueName: this.topicName,
|
|
1055
|
+
consumerGroup: this.consumerGroupName,
|
|
1056
|
+
messageId: options.messageId,
|
|
1057
|
+
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
1058
|
+
},
|
|
1059
|
+
this.transport
|
|
1060
|
+
);
|
|
1061
|
+
await this.processMessage(response.message, handler);
|
|
949
1062
|
} else {
|
|
950
1063
|
let messageFound = false;
|
|
951
1064
|
for await (const message of this.client.receiveMessages(
|
|
@@ -1013,7 +1126,7 @@ var Topic = class {
|
|
|
1013
1126
|
payload,
|
|
1014
1127
|
idempotencyKey: options?.idempotencyKey,
|
|
1015
1128
|
retentionSeconds: options?.retentionSeconds,
|
|
1016
|
-
|
|
1129
|
+
delaySeconds: options?.delaySeconds
|
|
1017
1130
|
},
|
|
1018
1131
|
this.transport
|
|
1019
1132
|
);
|
|
@@ -1123,7 +1236,7 @@ async function parseCallback(request) {
|
|
|
1123
1236
|
messageId
|
|
1124
1237
|
};
|
|
1125
1238
|
}
|
|
1126
|
-
function
|
|
1239
|
+
function createCallbackHandler(handlers, client) {
|
|
1127
1240
|
for (const topicPattern in handlers) {
|
|
1128
1241
|
if (topicPattern.includes("*")) {
|
|
1129
1242
|
if (!validateWildcardPattern(topicPattern)) {
|
|
@@ -1158,7 +1271,6 @@ function handleCallback(handlers) {
|
|
|
1158
1271
|
{ status: 404 }
|
|
1159
1272
|
);
|
|
1160
1273
|
}
|
|
1161
|
-
const client = new QueueClient();
|
|
1162
1274
|
const topic = new Topic(client, queueName);
|
|
1163
1275
|
const cg = topic.consumerGroup(consumerGroup);
|
|
1164
1276
|
await cg.consume(consumerGroupHandler, { messageId });
|
|
@@ -1174,11 +1286,11 @@ function handleCallback(handlers) {
|
|
|
1174
1286
|
);
|
|
1175
1287
|
}
|
|
1176
1288
|
};
|
|
1177
|
-
if (isDevMode()) {
|
|
1178
|
-
registerDevRouteHandler(routeHandler, handlers);
|
|
1179
|
-
}
|
|
1180
1289
|
return routeHandler;
|
|
1181
1290
|
}
|
|
1291
|
+
function handleCallback(handlers, client) {
|
|
1292
|
+
return createCallbackHandler(handlers, client || new QueueClient());
|
|
1293
|
+
}
|
|
1182
1294
|
|
|
1183
1295
|
// src/nextjs-pages.ts
|
|
1184
1296
|
function getHeader(headers, name) {
|