@vercel/queue 0.0.0-alpha.33 → 0.0.0-alpha.35
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 +286 -85
- package/dist/index.d.mts +203 -127
- package/dist/index.d.ts +203 -127
- package/dist/index.js +680 -453
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +665 -453
- 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 +673 -474
- package/dist/nextjs-pages.js.map +1 -1
- package/dist/nextjs-pages.mjs +663 -474
- package/dist/nextjs-pages.mjs.map +1 -1
- package/dist/types-BLG4ASI_.d.mts +504 -0
- package/dist/types-BLG4ASI_.d.ts +504 -0
- package/package.json +2 -6
- package/bin/local-discover.js +0 -196
- package/dist/types-Dw29Fr9y.d.mts +0 -380
- package/dist/types-Dw29Fr9y.d.ts +0 -380
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,49 @@ 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
|
+
/**
|
|
66
|
+
* Create a new JsonTransport.
|
|
67
|
+
* @param options - Optional JSON serialization options
|
|
68
|
+
* @param options.replacer - Custom replacer for JSON.stringify
|
|
69
|
+
* @param options.reviver - Custom reviver for JSON.parse
|
|
70
|
+
*/
|
|
71
|
+
constructor(options = {}) {
|
|
72
|
+
this.replacer = options.replacer;
|
|
73
|
+
this.reviver = options.reviver;
|
|
74
|
+
}
|
|
75
|
+
serialize(value) {
|
|
76
|
+
return Buffer.from(JSON.stringify(value, this.replacer), "utf8");
|
|
77
|
+
}
|
|
78
|
+
async deserialize(stream) {
|
|
79
|
+
const buffer = await streamToBuffer(stream);
|
|
80
|
+
return JSON.parse(buffer.toString("utf8"), this.reviver);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
32
83
|
|
|
33
84
|
// src/types.ts
|
|
34
85
|
var MessageNotFoundError = class extends Error {
|
|
@@ -59,15 +110,6 @@ var QueueEmptyError = class extends Error {
|
|
|
59
110
|
this.name = "QueueEmptyError";
|
|
60
111
|
}
|
|
61
112
|
};
|
|
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
113
|
var UnauthorizedError = class extends Error {
|
|
72
114
|
constructor(message = "Missing or invalid authentication token") {
|
|
73
115
|
super(message);
|
|
@@ -98,6 +140,265 @@ var InvalidLimitError = class extends Error {
|
|
|
98
140
|
this.name = "InvalidLimitError";
|
|
99
141
|
}
|
|
100
142
|
};
|
|
143
|
+
var MessageAlreadyProcessedError = class extends Error {
|
|
144
|
+
constructor(messageId) {
|
|
145
|
+
super(`Message ${messageId} has already been processed`);
|
|
146
|
+
this.name = "MessageAlreadyProcessedError";
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var ConcurrencyLimitError = class extends Error {
|
|
150
|
+
/** Current number of in-flight messages for this consumer group. */
|
|
151
|
+
currentInflight;
|
|
152
|
+
/** Maximum allowed concurrent messages (as configured). */
|
|
153
|
+
maxConcurrency;
|
|
154
|
+
constructor(message = "Concurrency limit exceeded", currentInflight, maxConcurrency) {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = "ConcurrencyLimitError";
|
|
157
|
+
this.currentInflight = currentInflight;
|
|
158
|
+
this.maxConcurrency = maxConcurrency;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var DuplicateMessageError = class extends Error {
|
|
162
|
+
idempotencyKey;
|
|
163
|
+
constructor(message, idempotencyKey) {
|
|
164
|
+
super(message);
|
|
165
|
+
this.name = "DuplicateMessageError";
|
|
166
|
+
this.idempotencyKey = idempotencyKey;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var ConsumerDiscoveryError = class extends Error {
|
|
170
|
+
deploymentId;
|
|
171
|
+
constructor(message, deploymentId) {
|
|
172
|
+
super(message);
|
|
173
|
+
this.name = "ConsumerDiscoveryError";
|
|
174
|
+
this.deploymentId = deploymentId;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var ConsumerRegistryNotConfiguredError = class extends Error {
|
|
178
|
+
constructor(message = "Consumer registry not configured") {
|
|
179
|
+
super(message);
|
|
180
|
+
this.name = "ConsumerRegistryNotConfiguredError";
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/dev.ts
|
|
185
|
+
var ROUTE_MAPPINGS_KEY = Symbol.for("@vercel/queue.devRouteMappings");
|
|
186
|
+
function filePathToUrlPath(filePath) {
|
|
187
|
+
let urlPath = filePath.replace(/^app\//, "/").replace(/^pages\//, "/").replace(/\/route\.(ts|js|tsx|jsx)$/, "").replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
188
|
+
if (!urlPath.startsWith("/")) {
|
|
189
|
+
urlPath = "/" + urlPath;
|
|
190
|
+
}
|
|
191
|
+
return urlPath;
|
|
192
|
+
}
|
|
193
|
+
function getDevRouteMappings() {
|
|
194
|
+
const g = globalThis;
|
|
195
|
+
if (ROUTE_MAPPINGS_KEY in g) {
|
|
196
|
+
return g[ROUTE_MAPPINGS_KEY] ?? null;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const vercelJsonPath = path.join(process.cwd(), "vercel.json");
|
|
200
|
+
if (!fs.existsSync(vercelJsonPath)) {
|
|
201
|
+
g[ROUTE_MAPPINGS_KEY] = null;
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const vercelJson = JSON.parse(fs.readFileSync(vercelJsonPath, "utf-8"));
|
|
205
|
+
if (!vercelJson.functions) {
|
|
206
|
+
g[ROUTE_MAPPINGS_KEY] = null;
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
const mappings = [];
|
|
210
|
+
for (const [filePath, config] of Object.entries(vercelJson.functions)) {
|
|
211
|
+
if (!config.experimentalTriggers) continue;
|
|
212
|
+
for (const trigger of config.experimentalTriggers) {
|
|
213
|
+
if (trigger.type?.startsWith("queue/") && trigger.topic && trigger.consumer) {
|
|
214
|
+
mappings.push({
|
|
215
|
+
urlPath: filePathToUrlPath(filePath),
|
|
216
|
+
topic: trigger.topic,
|
|
217
|
+
consumer: trigger.consumer
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
g[ROUTE_MAPPINGS_KEY] = mappings.length > 0 ? mappings : null;
|
|
223
|
+
return g[ROUTE_MAPPINGS_KEY];
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.warn("[Dev Mode] Failed to read vercel.json:", error);
|
|
226
|
+
g[ROUTE_MAPPINGS_KEY] = null;
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function findMatchingRoutes(topicName) {
|
|
231
|
+
const mappings = getDevRouteMappings();
|
|
232
|
+
if (!mappings) {
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
return mappings.filter((mapping) => {
|
|
236
|
+
if (mapping.topic.includes("*")) {
|
|
237
|
+
return matchesWildcardPattern(topicName, mapping.topic);
|
|
238
|
+
}
|
|
239
|
+
return mapping.topic === topicName;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function isDevMode() {
|
|
243
|
+
return process.env.NODE_ENV === "development";
|
|
244
|
+
}
|
|
245
|
+
var DEV_VISIBILITY_POLL_INTERVAL = 50;
|
|
246
|
+
var DEV_VISIBILITY_MAX_WAIT = 5e3;
|
|
247
|
+
var DEV_VISIBILITY_BACKOFF_MULTIPLIER = 2;
|
|
248
|
+
async function waitForMessageVisibility(topicName, consumerGroup, messageId) {
|
|
249
|
+
const client = new QueueClient();
|
|
250
|
+
const transport = new JsonTransport();
|
|
251
|
+
let elapsed = 0;
|
|
252
|
+
let interval = DEV_VISIBILITY_POLL_INTERVAL;
|
|
253
|
+
while (elapsed < DEV_VISIBILITY_MAX_WAIT) {
|
|
254
|
+
try {
|
|
255
|
+
await client.receiveMessageById(
|
|
256
|
+
{
|
|
257
|
+
queueName: topicName,
|
|
258
|
+
consumerGroup,
|
|
259
|
+
messageId,
|
|
260
|
+
visibilityTimeoutSeconds: 0
|
|
261
|
+
},
|
|
262
|
+
transport
|
|
263
|
+
);
|
|
264
|
+
return true;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
if (error instanceof MessageNotFoundError) {
|
|
267
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
268
|
+
elapsed += interval;
|
|
269
|
+
interval = Math.min(
|
|
270
|
+
interval * DEV_VISIBILITY_BACKOFF_MULTIPLIER,
|
|
271
|
+
DEV_VISIBILITY_MAX_WAIT - elapsed
|
|
272
|
+
);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (error instanceof MessageAlreadyProcessedError) {
|
|
276
|
+
console.log(
|
|
277
|
+
`[Dev Mode] Message already processed: topic="${topicName}" messageId="${messageId}"`
|
|
278
|
+
);
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
console.error(
|
|
282
|
+
`[Dev Mode] Error polling for message visibility: topic="${topicName}" messageId="${messageId}"`,
|
|
283
|
+
error
|
|
284
|
+
);
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
console.warn(
|
|
289
|
+
`[Dev Mode] Message visibility timeout after ${DEV_VISIBILITY_MAX_WAIT}ms: topic="${topicName}" messageId="${messageId}"`
|
|
290
|
+
);
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
function triggerDevCallbacks(topicName, messageId, delaySeconds) {
|
|
294
|
+
if (delaySeconds && delaySeconds > 0) {
|
|
295
|
+
console.log(
|
|
296
|
+
`[Dev Mode] Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
|
|
297
|
+
);
|
|
298
|
+
setTimeout(() => {
|
|
299
|
+
triggerDevCallbacks(topicName, messageId);
|
|
300
|
+
}, delaySeconds * 1e3);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
console.log(
|
|
304
|
+
`[Dev Mode] Message sent: topic="${topicName}" messageId="${messageId}"`
|
|
305
|
+
);
|
|
306
|
+
const matchingRoutes = findMatchingRoutes(topicName);
|
|
307
|
+
if (matchingRoutes.length === 0) {
|
|
308
|
+
console.log(
|
|
309
|
+
`[Dev Mode] No matching routes in vercel.json for topic "${topicName}"`
|
|
310
|
+
);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const consumerGroups = matchingRoutes.map((r) => r.consumer);
|
|
314
|
+
console.log(
|
|
315
|
+
`[Dev Mode] Scheduling callbacks for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
|
|
316
|
+
);
|
|
317
|
+
(async () => {
|
|
318
|
+
const firstRoute = matchingRoutes[0];
|
|
319
|
+
const isVisible = await waitForMessageVisibility(
|
|
320
|
+
topicName,
|
|
321
|
+
firstRoute.consumer,
|
|
322
|
+
messageId
|
|
323
|
+
);
|
|
324
|
+
if (!isVisible) {
|
|
325
|
+
console.warn(
|
|
326
|
+
`[Dev Mode] Skipping callbacks - message not visible: topic="${topicName}" messageId="${messageId}"`
|
|
327
|
+
);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const port = process.env.PORT || 3e3;
|
|
331
|
+
const baseUrl = `http://localhost:${port}`;
|
|
332
|
+
for (const route of matchingRoutes) {
|
|
333
|
+
const url = `${baseUrl}${route.urlPath}`;
|
|
334
|
+
console.log(
|
|
335
|
+
`[Dev Mode] Invoking handler: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`
|
|
336
|
+
);
|
|
337
|
+
const cloudEvent = {
|
|
338
|
+
type: "com.vercel.queue.v1beta",
|
|
339
|
+
source: `/topic/${topicName}/consumer/${route.consumer}`,
|
|
340
|
+
id: messageId,
|
|
341
|
+
datacontenttype: "application/json",
|
|
342
|
+
data: {
|
|
343
|
+
messageId,
|
|
344
|
+
queueName: topicName,
|
|
345
|
+
consumerGroup: route.consumer
|
|
346
|
+
},
|
|
347
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
348
|
+
specversion: "1.0"
|
|
349
|
+
};
|
|
350
|
+
try {
|
|
351
|
+
const response = await fetch(url, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
headers: {
|
|
354
|
+
"Content-Type": "application/cloudevents+json"
|
|
355
|
+
},
|
|
356
|
+
body: JSON.stringify(cloudEvent)
|
|
357
|
+
});
|
|
358
|
+
if (response.ok) {
|
|
359
|
+
try {
|
|
360
|
+
const responseData = await response.json();
|
|
361
|
+
if (responseData.status === "success") {
|
|
362
|
+
console.log(
|
|
363
|
+
`[Dev Mode] \u2713 Message processed successfully: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}"`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
} catch {
|
|
367
|
+
console.warn(
|
|
368
|
+
`[Dev Mode] Handler returned OK but response was not JSON: topic="${topicName}" consumer="${route.consumer}"`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
try {
|
|
373
|
+
const errorData = await response.json();
|
|
374
|
+
console.error(
|
|
375
|
+
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" error="${errorData.error || response.statusText}"`
|
|
376
|
+
);
|
|
377
|
+
} catch {
|
|
378
|
+
console.error(
|
|
379
|
+
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" status=${response.status}`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error(
|
|
385
|
+
`[Dev Mode] \u2717 HTTP request failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" url="${url}"`,
|
|
386
|
+
error
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
})();
|
|
391
|
+
}
|
|
392
|
+
function clearDevRouteMappings() {
|
|
393
|
+
const g = globalThis;
|
|
394
|
+
delete g[ROUTE_MAPPINGS_KEY];
|
|
395
|
+
}
|
|
396
|
+
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
397
|
+
globalThis.__clearDevRouteMappings = clearDevRouteMappings;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/oidc.ts
|
|
401
|
+
var import_oidc = require("@vercel/oidc");
|
|
101
402
|
|
|
102
403
|
// src/client.ts
|
|
103
404
|
function isDebugEnabled() {
|
|
@@ -114,14 +415,6 @@ async function consumeStream(stream) {
|
|
|
114
415
|
reader.releaseLock();
|
|
115
416
|
}
|
|
116
417
|
}
|
|
117
|
-
function parseRetryAfter(headers) {
|
|
118
|
-
const retryAfterHeader = headers.get("Retry-After");
|
|
119
|
-
if (retryAfterHeader) {
|
|
120
|
-
const parsed = parseInt(retryAfterHeader, 10);
|
|
121
|
-
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
122
|
-
}
|
|
123
|
-
return void 0;
|
|
124
|
-
}
|
|
125
418
|
function throwCommonHttpError(status, statusText, errorText, operation, badRequestDefault = "Invalid parameters") {
|
|
126
419
|
if (status === 400) {
|
|
127
420
|
throw new BadRequestError(errorText || badRequestDefault);
|
|
@@ -144,8 +437,8 @@ function parseQueueHeaders(headers) {
|
|
|
144
437
|
const deliveryCountStr = headers.get("Vqs-Delivery-Count") || "0";
|
|
145
438
|
const timestamp = headers.get("Vqs-Timestamp");
|
|
146
439
|
const contentType = headers.get("Content-Type") || "application/octet-stream";
|
|
147
|
-
const
|
|
148
|
-
if (!messageId || !timestamp || !
|
|
440
|
+
const receiptHandle = headers.get("Vqs-Receipt-Handle");
|
|
441
|
+
if (!messageId || !timestamp || !receiptHandle) {
|
|
149
442
|
return null;
|
|
150
443
|
}
|
|
151
444
|
const deliveryCount = parseInt(deliveryCountStr, 10);
|
|
@@ -157,7 +450,7 @@ function parseQueueHeaders(headers) {
|
|
|
157
450
|
deliveryCount,
|
|
158
451
|
createdAt: new Date(timestamp),
|
|
159
452
|
contentType,
|
|
160
|
-
|
|
453
|
+
receiptHandle
|
|
161
454
|
};
|
|
162
455
|
}
|
|
163
456
|
var QueueClient = class {
|
|
@@ -165,15 +458,30 @@ var QueueClient = class {
|
|
|
165
458
|
basePath;
|
|
166
459
|
customHeaders;
|
|
167
460
|
providedToken;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
* @param options QueueClient configuration options
|
|
171
|
-
*/
|
|
461
|
+
defaultDeploymentId;
|
|
462
|
+
pinToDeployment;
|
|
172
463
|
constructor(options = {}) {
|
|
173
464
|
this.baseUrl = options.baseUrl || process.env.VERCEL_QUEUE_BASE_URL || "https://vercel-queue.com";
|
|
174
|
-
this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/
|
|
465
|
+
this.basePath = options.basePath || process.env.VERCEL_QUEUE_BASE_PATH || "/api/v3/topic";
|
|
175
466
|
this.customHeaders = options.headers || {};
|
|
176
467
|
this.providedToken = options.token;
|
|
468
|
+
this.defaultDeploymentId = options.deploymentId || process.env.VERCEL_DEPLOYMENT_ID;
|
|
469
|
+
this.pinToDeployment = options.pinToDeployment ?? true;
|
|
470
|
+
}
|
|
471
|
+
getSendDeploymentId() {
|
|
472
|
+
if (isDevMode()) {
|
|
473
|
+
return void 0;
|
|
474
|
+
}
|
|
475
|
+
if (this.pinToDeployment) {
|
|
476
|
+
return this.defaultDeploymentId;
|
|
477
|
+
}
|
|
478
|
+
return void 0;
|
|
479
|
+
}
|
|
480
|
+
getConsumeDeploymentId() {
|
|
481
|
+
if (isDevMode()) {
|
|
482
|
+
return void 0;
|
|
483
|
+
}
|
|
484
|
+
return this.defaultDeploymentId;
|
|
177
485
|
}
|
|
178
486
|
async getToken() {
|
|
179
487
|
if (this.providedToken) {
|
|
@@ -187,10 +495,12 @@ var QueueClient = class {
|
|
|
187
495
|
}
|
|
188
496
|
return token;
|
|
189
497
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
498
|
+
buildUrl(queueName, ...pathSegments) {
|
|
499
|
+
const encodedQueue = encodeURIComponent(queueName);
|
|
500
|
+
const segments = pathSegments.map((s) => encodeURIComponent(s));
|
|
501
|
+
const path2 = segments.length > 0 ? "/" + segments.join("/") : "";
|
|
502
|
+
return `${this.baseUrl}${this.basePath}/${encodedQueue}${path2}`;
|
|
503
|
+
}
|
|
194
504
|
async fetch(url, init) {
|
|
195
505
|
const method = init.method || "GET";
|
|
196
506
|
if (isDebugEnabled()) {
|
|
@@ -227,24 +537,38 @@ var QueueClient = class {
|
|
|
227
537
|
return response;
|
|
228
538
|
}
|
|
229
539
|
/**
|
|
230
|
-
* Send a message to a
|
|
231
|
-
*
|
|
232
|
-
* @param
|
|
233
|
-
* @
|
|
234
|
-
* @
|
|
540
|
+
* Send a message to a topic.
|
|
541
|
+
*
|
|
542
|
+
* @param options - Message options including queue name, payload, and optional settings
|
|
543
|
+
* @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
|
|
544
|
+
* @param options.payload - Message payload
|
|
545
|
+
* @param options.idempotencyKey - Optional deduplication key (dedup window: min(retention, 24h))
|
|
546
|
+
* @param options.retentionSeconds - Message TTL (default: 86400, min: 60, max: 86400)
|
|
547
|
+
* @param options.delaySeconds - Delivery delay (default: 0, max: retentionSeconds)
|
|
548
|
+
* @param transport - Serializer for the payload
|
|
549
|
+
* @returns Promise with the generated messageId
|
|
550
|
+
* @throws {DuplicateMessageError} When idempotency key was already used
|
|
551
|
+
* @throws {ConsumerDiscoveryError} When consumer discovery fails
|
|
552
|
+
* @throws {ConsumerRegistryNotConfiguredError} When registry not configured
|
|
553
|
+
* @throws {BadRequestError} When parameters are invalid
|
|
235
554
|
* @throws {UnauthorizedError} When authentication fails
|
|
236
|
-
* @throws {ForbiddenError} When access is denied
|
|
555
|
+
* @throws {ForbiddenError} When access is denied
|
|
237
556
|
* @throws {InternalServerError} When server encounters an error
|
|
238
557
|
*/
|
|
239
558
|
async sendMessage(options, transport) {
|
|
240
|
-
const {
|
|
559
|
+
const {
|
|
560
|
+
queueName,
|
|
561
|
+
payload,
|
|
562
|
+
idempotencyKey,
|
|
563
|
+
retentionSeconds,
|
|
564
|
+
delaySeconds
|
|
565
|
+
} = options;
|
|
241
566
|
const headers = new Headers({
|
|
242
567
|
Authorization: `Bearer ${await this.getToken()}`,
|
|
243
|
-
"Vqs-Queue-Name": queueName,
|
|
244
568
|
"Content-Type": transport.contentType,
|
|
245
569
|
...this.customHeaders
|
|
246
570
|
});
|
|
247
|
-
const deploymentId =
|
|
571
|
+
const deploymentId = this.getSendDeploymentId();
|
|
248
572
|
if (deploymentId) {
|
|
249
573
|
headers.set("Vqs-Deployment-Id", deploymentId);
|
|
250
574
|
}
|
|
@@ -254,8 +578,12 @@ var QueueClient = class {
|
|
|
254
578
|
if (retentionSeconds !== void 0) {
|
|
255
579
|
headers.set("Vqs-Retention-Seconds", retentionSeconds.toString());
|
|
256
580
|
}
|
|
257
|
-
|
|
258
|
-
|
|
581
|
+
if (delaySeconds !== void 0) {
|
|
582
|
+
headers.set("Vqs-Delay-Seconds", delaySeconds.toString());
|
|
583
|
+
}
|
|
584
|
+
const serialized = transport.serialize(payload);
|
|
585
|
+
const body = Buffer.isBuffer(serialized) ? new Uint8Array(serialized) : serialized;
|
|
586
|
+
const response = await this.fetch(this.buildUrl(queueName), {
|
|
259
587
|
method: "POST",
|
|
260
588
|
body,
|
|
261
589
|
headers
|
|
@@ -263,7 +591,21 @@ var QueueClient = class {
|
|
|
263
591
|
if (!response.ok) {
|
|
264
592
|
const errorText = await response.text();
|
|
265
593
|
if (response.status === 409) {
|
|
266
|
-
throw new
|
|
594
|
+
throw new DuplicateMessageError(
|
|
595
|
+
errorText || "Duplicate idempotency key detected",
|
|
596
|
+
idempotencyKey
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
if (response.status === 502) {
|
|
600
|
+
throw new ConsumerDiscoveryError(
|
|
601
|
+
errorText || "Consumer discovery failed",
|
|
602
|
+
deploymentId
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
if (response.status === 503) {
|
|
606
|
+
throw new ConsumerRegistryNotConfiguredError(
|
|
607
|
+
errorText || "Consumer registry not configured"
|
|
608
|
+
);
|
|
267
609
|
}
|
|
268
610
|
throwCommonHttpError(
|
|
269
611
|
response.status,
|
|
@@ -276,52 +618,78 @@ var QueueClient = class {
|
|
|
276
618
|
return responseData;
|
|
277
619
|
}
|
|
278
620
|
/**
|
|
279
|
-
* Receive messages from a
|
|
280
|
-
*
|
|
281
|
-
* @param
|
|
282
|
-
* @
|
|
283
|
-
* @
|
|
284
|
-
* @
|
|
285
|
-
* @
|
|
286
|
-
* @
|
|
621
|
+
* Receive messages from a topic as an async generator.
|
|
622
|
+
*
|
|
623
|
+
* @param options - Receive options
|
|
624
|
+
* @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
|
|
625
|
+
* @param options.consumerGroup - Consumer group name (pattern: `[A-Za-z0-9_-]+`)
|
|
626
|
+
* @param options.visibilityTimeoutSeconds - Lock duration (default: 30, min: 0, max: 3600)
|
|
627
|
+
* @param options.limit - Max messages to retrieve (default: 1, min: 1, max: 10)
|
|
628
|
+
* @param options.maxConcurrency - Max in-flight messages (default: unlimited, min: 1)
|
|
629
|
+
* @param transport - Deserializer for message payloads
|
|
630
|
+
* @yields Message objects with payload, messageId, receiptHandle, etc.
|
|
631
|
+
* @throws {QueueEmptyError} When no messages available
|
|
632
|
+
* @throws {InvalidLimitError} When limit is outside 1-10 range
|
|
633
|
+
* @throws {ConcurrencyLimitError} When maxConcurrency exceeded
|
|
634
|
+
* @throws {BadRequestError} When parameters are invalid
|
|
287
635
|
* @throws {UnauthorizedError} When authentication fails
|
|
288
|
-
* @throws {ForbiddenError} When access is denied
|
|
636
|
+
* @throws {ForbiddenError} When access is denied
|
|
289
637
|
* @throws {InternalServerError} When server encounters an error
|
|
290
638
|
*/
|
|
291
639
|
async *receiveMessages(options, transport) {
|
|
292
|
-
const {
|
|
640
|
+
const {
|
|
641
|
+
queueName,
|
|
642
|
+
consumerGroup,
|
|
643
|
+
visibilityTimeoutSeconds,
|
|
644
|
+
limit,
|
|
645
|
+
maxConcurrency
|
|
646
|
+
} = options;
|
|
293
647
|
if (limit !== void 0 && (limit < 1 || limit > 10)) {
|
|
294
648
|
throw new InvalidLimitError(limit);
|
|
295
649
|
}
|
|
296
650
|
const headers = new Headers({
|
|
297
651
|
Authorization: `Bearer ${await this.getToken()}`,
|
|
298
|
-
"Vqs-Queue-Name": queueName,
|
|
299
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
300
652
|
Accept: "multipart/mixed",
|
|
301
653
|
...this.customHeaders
|
|
302
654
|
});
|
|
303
655
|
if (visibilityTimeoutSeconds !== void 0) {
|
|
304
656
|
headers.set(
|
|
305
|
-
"Vqs-Visibility-Timeout",
|
|
657
|
+
"Vqs-Visibility-Timeout-Seconds",
|
|
306
658
|
visibilityTimeoutSeconds.toString()
|
|
307
659
|
);
|
|
308
660
|
}
|
|
309
661
|
if (limit !== void 0) {
|
|
310
|
-
headers.set("Vqs-
|
|
662
|
+
headers.set("Vqs-Max-Messages", limit.toString());
|
|
311
663
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
664
|
+
if (maxConcurrency !== void 0) {
|
|
665
|
+
headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
|
|
666
|
+
}
|
|
667
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
668
|
+
if (effectiveDeploymentId) {
|
|
669
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
670
|
+
}
|
|
671
|
+
const response = await this.fetch(
|
|
672
|
+
this.buildUrl(queueName, "consumer", consumerGroup),
|
|
673
|
+
{
|
|
674
|
+
method: "POST",
|
|
675
|
+
headers
|
|
676
|
+
}
|
|
677
|
+
);
|
|
316
678
|
if (response.status === 204) {
|
|
317
679
|
throw new QueueEmptyError(queueName, consumerGroup);
|
|
318
680
|
}
|
|
319
681
|
if (!response.ok) {
|
|
320
682
|
const errorText = await response.text();
|
|
321
|
-
if (response.status ===
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
683
|
+
if (response.status === 429) {
|
|
684
|
+
let errorData = {};
|
|
685
|
+
try {
|
|
686
|
+
errorData = JSON.parse(errorText);
|
|
687
|
+
} catch {
|
|
688
|
+
}
|
|
689
|
+
throw new ConcurrencyLimitError(
|
|
690
|
+
errorData.error || "Concurrency limit exceeded or throttled",
|
|
691
|
+
errorData.currentInflight,
|
|
692
|
+
errorData.maxConcurrency
|
|
325
693
|
);
|
|
326
694
|
}
|
|
327
695
|
throwCommonHttpError(
|
|
@@ -353,34 +721,56 @@ var QueueClient = class {
|
|
|
353
721
|
}
|
|
354
722
|
}
|
|
355
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Receive a specific message by its ID.
|
|
726
|
+
*
|
|
727
|
+
* @param options - Receive options
|
|
728
|
+
* @param options.queueName - Topic name (pattern: `[A-Za-z0-9_-]+`)
|
|
729
|
+
* @param options.consumerGroup - Consumer group name (pattern: `[A-Za-z0-9_-]+`)
|
|
730
|
+
* @param options.messageId - Message ID to retrieve
|
|
731
|
+
* @param options.visibilityTimeoutSeconds - Lock duration (default: 30, min: 0, max: 3600)
|
|
732
|
+
* @param options.maxConcurrency - Max in-flight messages (default: unlimited, min: 1)
|
|
733
|
+
* @param transport - Deserializer for the message payload
|
|
734
|
+
* @returns Promise with the message
|
|
735
|
+
* @throws {MessageNotFoundError} When message doesn't exist
|
|
736
|
+
* @throws {MessageNotAvailableError} When message is in wrong state or was a duplicate
|
|
737
|
+
* @throws {MessageAlreadyProcessedError} When message was already processed
|
|
738
|
+
* @throws {ConcurrencyLimitError} When maxConcurrency exceeded
|
|
739
|
+
* @throws {BadRequestError} When parameters are invalid
|
|
740
|
+
* @throws {UnauthorizedError} When authentication fails
|
|
741
|
+
* @throws {ForbiddenError} When access is denied
|
|
742
|
+
* @throws {InternalServerError} When server encounters an error
|
|
743
|
+
*/
|
|
356
744
|
async receiveMessageById(options, transport) {
|
|
357
745
|
const {
|
|
358
746
|
queueName,
|
|
359
747
|
consumerGroup,
|
|
360
748
|
messageId,
|
|
361
749
|
visibilityTimeoutSeconds,
|
|
362
|
-
|
|
750
|
+
maxConcurrency
|
|
363
751
|
} = options;
|
|
364
752
|
const headers = new Headers({
|
|
365
753
|
Authorization: `Bearer ${await this.getToken()}`,
|
|
366
|
-
"Vqs-Queue-Name": queueName,
|
|
367
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
368
754
|
Accept: "multipart/mixed",
|
|
369
755
|
...this.customHeaders
|
|
370
756
|
});
|
|
371
757
|
if (visibilityTimeoutSeconds !== void 0) {
|
|
372
758
|
headers.set(
|
|
373
|
-
"Vqs-Visibility-Timeout",
|
|
759
|
+
"Vqs-Visibility-Timeout-Seconds",
|
|
374
760
|
visibilityTimeoutSeconds.toString()
|
|
375
761
|
);
|
|
376
762
|
}
|
|
377
|
-
if (
|
|
378
|
-
headers.set("Vqs-
|
|
763
|
+
if (maxConcurrency !== void 0) {
|
|
764
|
+
headers.set("Vqs-Max-Concurrency", maxConcurrency.toString());
|
|
765
|
+
}
|
|
766
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
767
|
+
if (effectiveDeploymentId) {
|
|
768
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
379
769
|
}
|
|
380
770
|
const response = await this.fetch(
|
|
381
|
-
|
|
771
|
+
this.buildUrl(queueName, "consumer", consumerGroup, "id", messageId),
|
|
382
772
|
{
|
|
383
|
-
method: "
|
|
773
|
+
method: "POST",
|
|
384
774
|
headers
|
|
385
775
|
}
|
|
386
776
|
);
|
|
@@ -390,12 +780,32 @@ var QueueClient = class {
|
|
|
390
780
|
throw new MessageNotFoundError(messageId);
|
|
391
781
|
}
|
|
392
782
|
if (response.status === 409) {
|
|
783
|
+
let errorData = {};
|
|
784
|
+
try {
|
|
785
|
+
errorData = JSON.parse(errorText);
|
|
786
|
+
} catch {
|
|
787
|
+
}
|
|
788
|
+
if (errorData.originalMessageId) {
|
|
789
|
+
throw new MessageNotAvailableError(
|
|
790
|
+
messageId,
|
|
791
|
+
`This message was a duplicate - use originalMessageId: ${errorData.originalMessageId}`
|
|
792
|
+
);
|
|
793
|
+
}
|
|
393
794
|
throw new MessageNotAvailableError(messageId);
|
|
394
795
|
}
|
|
395
|
-
if (response.status ===
|
|
396
|
-
throw new
|
|
397
|
-
|
|
398
|
-
|
|
796
|
+
if (response.status === 410) {
|
|
797
|
+
throw new MessageAlreadyProcessedError(messageId);
|
|
798
|
+
}
|
|
799
|
+
if (response.status === 429) {
|
|
800
|
+
let errorData = {};
|
|
801
|
+
try {
|
|
802
|
+
errorData = JSON.parse(errorText);
|
|
803
|
+
} catch {
|
|
804
|
+
}
|
|
805
|
+
throw new ConcurrencyLimitError(
|
|
806
|
+
errorData.error || "Concurrency limit exceeded or throttled",
|
|
807
|
+
errorData.currentInflight,
|
|
808
|
+
errorData.maxConcurrency
|
|
399
809
|
);
|
|
400
810
|
}
|
|
401
811
|
throwCommonHttpError(
|
|
@@ -405,95 +815,73 @@ var QueueClient = class {
|
|
|
405
815
|
"receive message by ID"
|
|
406
816
|
);
|
|
407
817
|
}
|
|
408
|
-
|
|
409
|
-
const parsedHeaders = parseQueueHeaders(
|
|
818
|
+
for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
|
|
819
|
+
const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
|
|
410
820
|
if (!parsedHeaders) {
|
|
821
|
+
await consumeStream(multipartMessage.payload);
|
|
411
822
|
throw new MessageCorruptedError(
|
|
412
823
|
messageId,
|
|
413
|
-
"Missing required queue headers in
|
|
824
|
+
"Missing required queue headers in response"
|
|
414
825
|
);
|
|
415
826
|
}
|
|
827
|
+
const deserializedPayload = await transport.deserialize(
|
|
828
|
+
multipartMessage.payload
|
|
829
|
+
);
|
|
416
830
|
const message = {
|
|
417
831
|
...parsedHeaders,
|
|
418
|
-
payload:
|
|
832
|
+
payload: deserializedPayload
|
|
419
833
|
};
|
|
420
834
|
return { message };
|
|
421
835
|
}
|
|
422
|
-
if (!transport) {
|
|
423
|
-
throw new Error("Transport is required when skipPayload is not true");
|
|
424
|
-
}
|
|
425
|
-
try {
|
|
426
|
-
for await (const multipartMessage of (0, import_mixpart.parseMultipartStream)(response)) {
|
|
427
|
-
try {
|
|
428
|
-
const parsedHeaders = parseQueueHeaders(multipartMessage.headers);
|
|
429
|
-
if (!parsedHeaders) {
|
|
430
|
-
console.warn("Missing required queue headers in multipart part");
|
|
431
|
-
await consumeStream(multipartMessage.payload);
|
|
432
|
-
continue;
|
|
433
|
-
}
|
|
434
|
-
const deserializedPayload = await transport.deserialize(
|
|
435
|
-
multipartMessage.payload
|
|
436
|
-
);
|
|
437
|
-
const message = {
|
|
438
|
-
...parsedHeaders,
|
|
439
|
-
payload: deserializedPayload
|
|
440
|
-
};
|
|
441
|
-
return { message };
|
|
442
|
-
} catch (error) {
|
|
443
|
-
console.warn("Failed to deserialize message by ID:", error);
|
|
444
|
-
await consumeStream(multipartMessage.payload);
|
|
445
|
-
throw new MessageCorruptedError(
|
|
446
|
-
messageId,
|
|
447
|
-
`Failed to deserialize payload: ${error}`
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
} catch (error) {
|
|
452
|
-
if (error instanceof MessageCorruptedError) {
|
|
453
|
-
throw error;
|
|
454
|
-
}
|
|
455
|
-
throw new MessageCorruptedError(
|
|
456
|
-
messageId,
|
|
457
|
-
`Failed to parse multipart response: ${error}`
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
836
|
throw new MessageNotFoundError(messageId);
|
|
461
837
|
}
|
|
462
838
|
/**
|
|
463
|
-
* Delete a message
|
|
464
|
-
*
|
|
465
|
-
* @
|
|
466
|
-
* @
|
|
467
|
-
* @
|
|
468
|
-
* @
|
|
839
|
+
* Delete (acknowledge) a message after successful processing.
|
|
840
|
+
*
|
|
841
|
+
* @param options - Delete options
|
|
842
|
+
* @param options.queueName - Topic name
|
|
843
|
+
* @param options.consumerGroup - Consumer group name
|
|
844
|
+
* @param options.receiptHandle - Receipt handle from the received message (must use same deployment ID as receive)
|
|
845
|
+
* @returns Promise indicating deletion success
|
|
846
|
+
* @throws {MessageNotFoundError} When receipt handle not found
|
|
847
|
+
* @throws {MessageNotAvailableError} When receipt handle invalid or message already processed
|
|
848
|
+
* @throws {BadRequestError} When parameters are invalid
|
|
469
849
|
* @throws {UnauthorizedError} When authentication fails
|
|
470
|
-
* @throws {ForbiddenError} When access is denied
|
|
850
|
+
* @throws {ForbiddenError} When access is denied
|
|
471
851
|
* @throws {InternalServerError} When server encounters an error
|
|
472
852
|
*/
|
|
473
853
|
async deleteMessage(options) {
|
|
474
|
-
const { queueName, consumerGroup,
|
|
854
|
+
const { queueName, consumerGroup, receiptHandle } = options;
|
|
855
|
+
const headers = new Headers({
|
|
856
|
+
Authorization: `Bearer ${await this.getToken()}`,
|
|
857
|
+
...this.customHeaders
|
|
858
|
+
});
|
|
859
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
860
|
+
if (effectiveDeploymentId) {
|
|
861
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
862
|
+
}
|
|
475
863
|
const response = await this.fetch(
|
|
476
|
-
|
|
864
|
+
this.buildUrl(
|
|
865
|
+
queueName,
|
|
866
|
+
"consumer",
|
|
867
|
+
consumerGroup,
|
|
868
|
+
"lease",
|
|
869
|
+
receiptHandle
|
|
870
|
+
),
|
|
477
871
|
{
|
|
478
872
|
method: "DELETE",
|
|
479
|
-
headers
|
|
480
|
-
Authorization: `Bearer ${await this.getToken()}`,
|
|
481
|
-
"Vqs-Queue-Name": queueName,
|
|
482
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
483
|
-
"Vqs-Ticket": ticket,
|
|
484
|
-
...this.customHeaders
|
|
485
|
-
})
|
|
873
|
+
headers
|
|
486
874
|
}
|
|
487
875
|
);
|
|
488
876
|
if (!response.ok) {
|
|
489
877
|
const errorText = await response.text();
|
|
490
878
|
if (response.status === 404) {
|
|
491
|
-
throw new MessageNotFoundError(
|
|
879
|
+
throw new MessageNotFoundError(receiptHandle);
|
|
492
880
|
}
|
|
493
881
|
if (response.status === 409) {
|
|
494
882
|
throw new MessageNotAvailableError(
|
|
495
|
-
|
|
496
|
-
errorText || "Invalid
|
|
883
|
+
receiptHandle,
|
|
884
|
+
errorText || "Invalid receipt handle, message not in correct state, or already processed"
|
|
497
885
|
);
|
|
498
886
|
}
|
|
499
887
|
throwCommonHttpError(
|
|
@@ -501,53 +889,67 @@ var QueueClient = class {
|
|
|
501
889
|
response.statusText,
|
|
502
890
|
errorText,
|
|
503
891
|
"delete message",
|
|
504
|
-
"Missing or invalid
|
|
892
|
+
"Missing or invalid receipt handle"
|
|
505
893
|
);
|
|
506
894
|
}
|
|
507
895
|
return { deleted: true };
|
|
508
896
|
}
|
|
509
897
|
/**
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
513
|
-
* @
|
|
514
|
-
* @
|
|
515
|
-
* @
|
|
898
|
+
* Extend or change the visibility timeout of a message.
|
|
899
|
+
* Used to prevent message redelivery while still processing.
|
|
900
|
+
*
|
|
901
|
+
* @param options - Visibility options
|
|
902
|
+
* @param options.queueName - Topic name
|
|
903
|
+
* @param options.consumerGroup - Consumer group name
|
|
904
|
+
* @param options.receiptHandle - Receipt handle from the received message (must use same deployment ID as receive)
|
|
905
|
+
* @param options.visibilityTimeoutSeconds - New timeout (min: 0, max: 3600, cannot exceed message expiration)
|
|
906
|
+
* @returns Promise indicating success
|
|
907
|
+
* @throws {MessageNotFoundError} When receipt handle not found
|
|
908
|
+
* @throws {MessageNotAvailableError} When receipt handle invalid or message already processed
|
|
909
|
+
* @throws {BadRequestError} When parameters are invalid
|
|
516
910
|
* @throws {UnauthorizedError} When authentication fails
|
|
517
|
-
* @throws {ForbiddenError} When access is denied
|
|
911
|
+
* @throws {ForbiddenError} When access is denied
|
|
518
912
|
* @throws {InternalServerError} When server encounters an error
|
|
519
913
|
*/
|
|
520
914
|
async changeVisibility(options) {
|
|
521
915
|
const {
|
|
522
916
|
queueName,
|
|
523
917
|
consumerGroup,
|
|
524
|
-
|
|
525
|
-
ticket,
|
|
918
|
+
receiptHandle,
|
|
526
919
|
visibilityTimeoutSeconds
|
|
527
920
|
} = options;
|
|
921
|
+
const headers = new Headers({
|
|
922
|
+
Authorization: `Bearer ${await this.getToken()}`,
|
|
923
|
+
"Content-Type": "application/json",
|
|
924
|
+
...this.customHeaders
|
|
925
|
+
});
|
|
926
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
927
|
+
if (effectiveDeploymentId) {
|
|
928
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
929
|
+
}
|
|
528
930
|
const response = await this.fetch(
|
|
529
|
-
|
|
931
|
+
this.buildUrl(
|
|
932
|
+
queueName,
|
|
933
|
+
"consumer",
|
|
934
|
+
consumerGroup,
|
|
935
|
+
"lease",
|
|
936
|
+
receiptHandle
|
|
937
|
+
),
|
|
530
938
|
{
|
|
531
939
|
method: "PATCH",
|
|
532
|
-
headers
|
|
533
|
-
|
|
534
|
-
"Vqs-Queue-Name": queueName,
|
|
535
|
-
"Vqs-Consumer-Group": consumerGroup,
|
|
536
|
-
"Vqs-Ticket": ticket,
|
|
537
|
-
"Vqs-Visibility-Timeout": visibilityTimeoutSeconds.toString(),
|
|
538
|
-
...this.customHeaders
|
|
539
|
-
})
|
|
940
|
+
headers,
|
|
941
|
+
body: JSON.stringify({ visibilityTimeoutSeconds })
|
|
540
942
|
}
|
|
541
943
|
);
|
|
542
944
|
if (!response.ok) {
|
|
543
945
|
const errorText = await response.text();
|
|
544
946
|
if (response.status === 404) {
|
|
545
|
-
throw new MessageNotFoundError(
|
|
947
|
+
throw new MessageNotFoundError(receiptHandle);
|
|
546
948
|
}
|
|
547
949
|
if (response.status === 409) {
|
|
548
950
|
throw new MessageNotAvailableError(
|
|
549
|
-
|
|
550
|
-
errorText || "Invalid
|
|
951
|
+
receiptHandle,
|
|
952
|
+
errorText || "Invalid receipt handle, message not in correct state, or already processed"
|
|
551
953
|
);
|
|
552
954
|
}
|
|
553
955
|
throwCommonHttpError(
|
|
@@ -555,228 +957,72 @@ var QueueClient = class {
|
|
|
555
957
|
response.statusText,
|
|
556
958
|
errorText,
|
|
557
959
|
"change visibility",
|
|
558
|
-
"Missing
|
|
960
|
+
"Missing receipt handle or invalid visibility timeout"
|
|
559
961
|
);
|
|
560
962
|
}
|
|
561
|
-
return {
|
|
963
|
+
return { success: true };
|
|
562
964
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
965
|
+
/**
|
|
966
|
+
* Alternative endpoint for changing message visibility timeout.
|
|
967
|
+
* Uses the /visibility path suffix and expects visibilityTimeoutSeconds in the body.
|
|
968
|
+
* Functionally equivalent to changeVisibility but follows an alternative API pattern.
|
|
969
|
+
*
|
|
970
|
+
* @param options - Options for changing visibility
|
|
971
|
+
* @returns Promise resolving to change visibility response
|
|
972
|
+
*/
|
|
973
|
+
async changeVisibilityAlt(options) {
|
|
974
|
+
const {
|
|
975
|
+
queueName,
|
|
976
|
+
consumerGroup,
|
|
977
|
+
receiptHandle,
|
|
978
|
+
visibilityTimeoutSeconds
|
|
979
|
+
} = options;
|
|
980
|
+
const headers = new Headers({
|
|
981
|
+
Authorization: `Bearer ${await this.getToken()}`,
|
|
982
|
+
"Content-Type": "application/json",
|
|
983
|
+
...this.customHeaders
|
|
984
|
+
});
|
|
985
|
+
const effectiveDeploymentId = this.getConsumeDeploymentId();
|
|
986
|
+
if (effectiveDeploymentId) {
|
|
987
|
+
headers.set("Vqs-Deployment-Id", effectiveDeploymentId);
|
|
576
988
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
serialize(value) {
|
|
591
|
-
return Buffer.from(JSON.stringify(value, this.replacer), "utf8");
|
|
592
|
-
}
|
|
593
|
-
async deserialize(stream) {
|
|
594
|
-
const buffer = await streamToBuffer(stream);
|
|
595
|
-
return JSON.parse(buffer.toString("utf8"), this.reviver);
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
// src/dev.ts
|
|
600
|
-
var GLOBAL_KEY = Symbol.for("@vercel/queue.devHandlers");
|
|
601
|
-
function getDevHandlerState() {
|
|
602
|
-
const g = globalThis;
|
|
603
|
-
if (!g[GLOBAL_KEY]) {
|
|
604
|
-
g[GLOBAL_KEY] = {
|
|
605
|
-
devRouteHandlers: /* @__PURE__ */ new Map(),
|
|
606
|
-
wildcardRouteHandlers: /* @__PURE__ */ new Map()
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
return g[GLOBAL_KEY];
|
|
610
|
-
}
|
|
611
|
-
var { devRouteHandlers, wildcardRouteHandlers } = getDevHandlerState();
|
|
612
|
-
function cleanupDeadRefs(key, refs) {
|
|
613
|
-
const aliveRefs = refs.filter((ref) => ref.deref() !== void 0);
|
|
614
|
-
if (aliveRefs.length === 0) {
|
|
615
|
-
wildcardRouteHandlers.delete(key);
|
|
616
|
-
} else if (aliveRefs.length < refs.length) {
|
|
617
|
-
wildcardRouteHandlers.set(key, aliveRefs);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
function isDevMode() {
|
|
621
|
-
return process.env.NODE_ENV === "development";
|
|
622
|
-
}
|
|
623
|
-
function registerDevRouteHandler(routeHandler, handlers) {
|
|
624
|
-
for (const topicName in handlers) {
|
|
625
|
-
for (const consumerGroup in handlers[topicName]) {
|
|
626
|
-
const key = `${topicName}:${consumerGroup}`;
|
|
627
|
-
if (topicName.includes("*")) {
|
|
628
|
-
const existing = wildcardRouteHandlers.get(key) || [];
|
|
629
|
-
cleanupDeadRefs(key, existing);
|
|
630
|
-
const cleanedRefs = wildcardRouteHandlers.get(key) || [];
|
|
631
|
-
const weakRef = new WeakRef(routeHandler);
|
|
632
|
-
cleanedRefs.push(weakRef);
|
|
633
|
-
wildcardRouteHandlers.set(key, cleanedRefs);
|
|
634
|
-
} else {
|
|
635
|
-
devRouteHandlers.set(key, {
|
|
636
|
-
routeHandler,
|
|
637
|
-
topicPattern: topicName
|
|
638
|
-
});
|
|
989
|
+
const response = await this.fetch(
|
|
990
|
+
this.buildUrl(
|
|
991
|
+
queueName,
|
|
992
|
+
"consumer",
|
|
993
|
+
consumerGroup,
|
|
994
|
+
"lease",
|
|
995
|
+
receiptHandle,
|
|
996
|
+
"visibility"
|
|
997
|
+
),
|
|
998
|
+
{
|
|
999
|
+
method: "PATCH",
|
|
1000
|
+
headers,
|
|
1001
|
+
body: JSON.stringify({ visibilityTimeoutSeconds })
|
|
639
1002
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
for (const [
|
|
646
|
-
key,
|
|
647
|
-
{ routeHandler, topicPattern }
|
|
648
|
-
] of devRouteHandlers.entries()) {
|
|
649
|
-
const [_, consumerGroup] = key.split(":");
|
|
650
|
-
if (topicPattern === topicName) {
|
|
651
|
-
if (!handlersMap.has(routeHandler)) {
|
|
652
|
-
handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
|
|
1003
|
+
);
|
|
1004
|
+
if (!response.ok) {
|
|
1005
|
+
const errorText = await response.text();
|
|
1006
|
+
if (response.status === 404) {
|
|
1007
|
+
throw new MessageNotFoundError(receiptHandle);
|
|
653
1008
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (matchesWildcardPattern(topicName, pattern)) {
|
|
660
|
-
cleanupDeadRefs(key, refs);
|
|
661
|
-
const cleanedRefs = wildcardRouteHandlers.get(key) || [];
|
|
662
|
-
for (const ref of cleanedRefs) {
|
|
663
|
-
const routeHandler = ref.deref();
|
|
664
|
-
if (routeHandler) {
|
|
665
|
-
if (!handlersMap.has(routeHandler)) {
|
|
666
|
-
handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
|
|
667
|
-
}
|
|
668
|
-
handlersMap.get(routeHandler).add(consumerGroup);
|
|
669
|
-
}
|
|
1009
|
+
if (response.status === 409) {
|
|
1010
|
+
throw new MessageNotAvailableError(
|
|
1011
|
+
receiptHandle,
|
|
1012
|
+
errorText || "Invalid receipt handle, message not in correct state, or already processed"
|
|
1013
|
+
);
|
|
670
1014
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
type: "com.vercel.queue.v1beta",
|
|
678
|
-
source: `/topic/${topicName}/consumer/${consumerGroup}`,
|
|
679
|
-
id: messageId,
|
|
680
|
-
datacontenttype: "application/json",
|
|
681
|
-
data: {
|
|
682
|
-
messageId,
|
|
683
|
-
queueName: topicName,
|
|
684
|
-
consumerGroup
|
|
685
|
-
},
|
|
686
|
-
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
687
|
-
specversion: "1.0"
|
|
688
|
-
};
|
|
689
|
-
return new Request("https://localhost/api/queue/callback", {
|
|
690
|
-
method: "POST",
|
|
691
|
-
headers: {
|
|
692
|
-
"Content-Type": "application/cloudevents+json"
|
|
693
|
-
},
|
|
694
|
-
body: JSON.stringify(cloudEvent)
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
var DEV_CALLBACK_DELAY = 1e3;
|
|
698
|
-
function scheduleDevTimeout(topicName, messageId, timeoutSeconds) {
|
|
699
|
-
console.log(
|
|
700
|
-
`[Dev Mode] Message ${messageId} timed out for ${timeoutSeconds}s, will re-trigger`
|
|
701
|
-
);
|
|
702
|
-
setTimeout(
|
|
703
|
-
() => {
|
|
704
|
-
console.log(
|
|
705
|
-
`[Dev Mode] Re-triggering callback for timed-out message ${messageId}`
|
|
1015
|
+
throwCommonHttpError(
|
|
1016
|
+
response.status,
|
|
1017
|
+
response.statusText,
|
|
1018
|
+
errorText,
|
|
1019
|
+
"change visibility (alt)",
|
|
1020
|
+
"Missing receipt handle or invalid visibility timeout"
|
|
706
1021
|
);
|
|
707
|
-
triggerDevCallbacks(topicName, messageId);
|
|
708
|
-
},
|
|
709
|
-
timeoutSeconds * 1e3 + DEV_CALLBACK_DELAY
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
function triggerDevCallbacks(topicName, messageId) {
|
|
713
|
-
const handlersMap = findRouteHandlersForTopic(topicName);
|
|
714
|
-
if (handlersMap.size === 0) {
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
const consumerGroups = Array.from(
|
|
718
|
-
new Set(
|
|
719
|
-
Array.from(handlersMap.values()).flatMap((groups) => Array.from(groups))
|
|
720
|
-
)
|
|
721
|
-
);
|
|
722
|
-
console.log(
|
|
723
|
-
`[Dev Mode] Triggering local callbacks for topic "${topicName}" \u2192 consumers: ${consumerGroups.join(", ")}`
|
|
724
|
-
);
|
|
725
|
-
setTimeout(async () => {
|
|
726
|
-
for (const [routeHandler, consumerGroups2] of handlersMap.entries()) {
|
|
727
|
-
for (const consumerGroup of consumerGroups2) {
|
|
728
|
-
try {
|
|
729
|
-
const request = createMockCloudEventRequest(
|
|
730
|
-
topicName,
|
|
731
|
-
consumerGroup,
|
|
732
|
-
messageId
|
|
733
|
-
);
|
|
734
|
-
const response = await routeHandler(request);
|
|
735
|
-
if (response.ok) {
|
|
736
|
-
try {
|
|
737
|
-
const responseData = await response.json();
|
|
738
|
-
if (responseData.status === "success") {
|
|
739
|
-
console.log(
|
|
740
|
-
`[Dev Mode] Message processed for ${topicName}/${consumerGroup}`
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
} catch (jsonError) {
|
|
744
|
-
console.error(
|
|
745
|
-
`[Dev Mode] Failed to parse success response for ${topicName}/${consumerGroup}:`,
|
|
746
|
-
jsonError
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
} else {
|
|
750
|
-
try {
|
|
751
|
-
const errorData = await response.json();
|
|
752
|
-
console.error(
|
|
753
|
-
`[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
|
|
754
|
-
errorData.error || response.statusText
|
|
755
|
-
);
|
|
756
|
-
} catch (jsonError) {
|
|
757
|
-
console.error(
|
|
758
|
-
`[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
|
|
759
|
-
response.statusText
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
} catch (error) {
|
|
764
|
-
console.error(
|
|
765
|
-
`[Dev Mode] Error triggering callback for ${topicName}/${consumerGroup}:`,
|
|
766
|
-
error
|
|
767
|
-
);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
1022
|
}
|
|
771
|
-
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
devRouteHandlers.clear();
|
|
775
|
-
wildcardRouteHandlers.clear();
|
|
776
|
-
}
|
|
777
|
-
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
778
|
-
globalThis.__clearDevHandlers = clearDevHandlers;
|
|
779
|
-
}
|
|
1023
|
+
return { success: true };
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
780
1026
|
|
|
781
1027
|
// src/consumer-group.ts
|
|
782
1028
|
var ConsumerGroup = class {
|
|
@@ -787,69 +1033,64 @@ var ConsumerGroup = class {
|
|
|
787
1033
|
refreshInterval;
|
|
788
1034
|
transport;
|
|
789
1035
|
/**
|
|
790
|
-
* Create a new ConsumerGroup instance
|
|
791
|
-
*
|
|
792
|
-
* @param
|
|
793
|
-
* @param
|
|
794
|
-
* @param
|
|
1036
|
+
* Create a new ConsumerGroup instance.
|
|
1037
|
+
*
|
|
1038
|
+
* @param client - QueueClient instance to use for API calls
|
|
1039
|
+
* @param topicName - Name of the topic to consume from (pattern: `[A-Za-z0-9_-]+`)
|
|
1040
|
+
* @param consumerGroupName - Name of the consumer group (pattern: `[A-Za-z0-9_-]+`)
|
|
1041
|
+
* @param options - Optional configuration
|
|
1042
|
+
* @param options.transport - Payload serializer (default: JsonTransport)
|
|
1043
|
+
* @param options.visibilityTimeoutSeconds - Message lock duration (default: 30, max: 3600)
|
|
1044
|
+
* @param options.visibilityRefreshInterval - Lock refresh interval in seconds (default: visibilityTimeout / 3)
|
|
795
1045
|
*/
|
|
796
1046
|
constructor(client, topicName, consumerGroupName, options = {}) {
|
|
797
1047
|
this.client = client;
|
|
798
1048
|
this.topicName = topicName;
|
|
799
1049
|
this.consumerGroupName = consumerGroupName;
|
|
800
|
-
this.visibilityTimeout = options.visibilityTimeoutSeconds
|
|
801
|
-
this.refreshInterval = options.
|
|
1050
|
+
this.visibilityTimeout = options.visibilityTimeoutSeconds ?? 30;
|
|
1051
|
+
this.refreshInterval = options.visibilityRefreshInterval ?? Math.floor(this.visibilityTimeout / 3);
|
|
802
1052
|
this.transport = options.transport || new JsonTransport();
|
|
803
1053
|
}
|
|
804
1054
|
/**
|
|
805
1055
|
* Starts a background loop that periodically extends the visibility timeout for a message.
|
|
806
|
-
* This prevents the message from becoming visible to other consumers while it's being processed.
|
|
807
|
-
*
|
|
808
|
-
* The extension loop runs every `refreshInterval` seconds and updates the message's
|
|
809
|
-
* visibility timeout to `visibilityTimeout` seconds from the current time.
|
|
810
|
-
*
|
|
811
|
-
* @param messageId - The unique identifier of the message to extend visibility for
|
|
812
|
-
* @param ticket - The receipt ticket that proves ownership of the message
|
|
813
|
-
* @returns A function that when called will stop the extension loop
|
|
814
|
-
*
|
|
815
|
-
* @remarks
|
|
816
|
-
* - The first extension attempt occurs after `refreshInterval` seconds, not immediately
|
|
817
|
-
* - If an extension fails, the loop terminates with an error logged to console
|
|
818
|
-
* - The returned stop function is idempotent - calling it multiple times is safe
|
|
819
|
-
* - By default, the stop function returns immediately without waiting for in-flight
|
|
820
|
-
* - Pass `true` to the stop function to wait for any in-flight extension to complete
|
|
821
1056
|
*/
|
|
822
|
-
startVisibilityExtension(
|
|
1057
|
+
startVisibilityExtension(receiptHandle) {
|
|
823
1058
|
let isRunning = true;
|
|
1059
|
+
let isResolved = false;
|
|
824
1060
|
let resolveLifecycle;
|
|
825
1061
|
let timeoutId = null;
|
|
826
1062
|
const lifecyclePromise = new Promise((resolve) => {
|
|
827
1063
|
resolveLifecycle = resolve;
|
|
828
1064
|
});
|
|
1065
|
+
const safeResolve = () => {
|
|
1066
|
+
if (!isResolved) {
|
|
1067
|
+
isResolved = true;
|
|
1068
|
+
resolveLifecycle();
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
829
1071
|
const extend = async () => {
|
|
830
1072
|
if (!isRunning) {
|
|
831
|
-
|
|
1073
|
+
safeResolve();
|
|
832
1074
|
return;
|
|
833
1075
|
}
|
|
834
1076
|
try {
|
|
835
1077
|
await this.client.changeVisibility({
|
|
836
1078
|
queueName: this.topicName,
|
|
837
1079
|
consumerGroup: this.consumerGroupName,
|
|
838
|
-
|
|
839
|
-
ticket,
|
|
1080
|
+
receiptHandle,
|
|
840
1081
|
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
841
1082
|
});
|
|
842
1083
|
if (isRunning) {
|
|
843
1084
|
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
844
1085
|
} else {
|
|
845
|
-
|
|
1086
|
+
safeResolve();
|
|
846
1087
|
}
|
|
847
1088
|
} catch (error) {
|
|
848
1089
|
console.error(
|
|
849
|
-
`Failed to extend visibility for
|
|
1090
|
+
`Failed to extend visibility for receipt handle ${receiptHandle}:`,
|
|
850
1091
|
error
|
|
851
1092
|
);
|
|
852
|
-
|
|
1093
|
+
safeResolve();
|
|
853
1094
|
}
|
|
854
1095
|
};
|
|
855
1096
|
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
@@ -862,22 +1103,14 @@ var ConsumerGroup = class {
|
|
|
862
1103
|
if (waitForCompletion) {
|
|
863
1104
|
await lifecyclePromise;
|
|
864
1105
|
} else {
|
|
865
|
-
|
|
1106
|
+
safeResolve();
|
|
866
1107
|
}
|
|
867
1108
|
};
|
|
868
1109
|
}
|
|
869
|
-
/**
|
|
870
|
-
* Process a single message with the given handler
|
|
871
|
-
* @param message The message to process
|
|
872
|
-
* @param handler Function to process the message
|
|
873
|
-
*/
|
|
874
1110
|
async processMessage(message, handler) {
|
|
875
|
-
const stopExtension = this.startVisibilityExtension(
|
|
876
|
-
message.messageId,
|
|
877
|
-
message.ticket
|
|
878
|
-
);
|
|
1111
|
+
const stopExtension = this.startVisibilityExtension(message.receiptHandle);
|
|
879
1112
|
try {
|
|
880
|
-
|
|
1113
|
+
await handler(message.payload, {
|
|
881
1114
|
messageId: message.messageId,
|
|
882
1115
|
deliveryCount: message.deliveryCount,
|
|
883
1116
|
createdAt: message.createdAt,
|
|
@@ -885,29 +1118,11 @@ var ConsumerGroup = class {
|
|
|
885
1118
|
consumerGroup: this.consumerGroupName
|
|
886
1119
|
});
|
|
887
1120
|
await stopExtension();
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
ticket: message.ticket,
|
|
894
|
-
visibilityTimeoutSeconds: result.timeoutSeconds
|
|
895
|
-
});
|
|
896
|
-
if (isDevMode()) {
|
|
897
|
-
scheduleDevTimeout(
|
|
898
|
-
this.topicName,
|
|
899
|
-
message.messageId,
|
|
900
|
-
result.timeoutSeconds
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
} else {
|
|
904
|
-
await this.client.deleteMessage({
|
|
905
|
-
queueName: this.topicName,
|
|
906
|
-
consumerGroup: this.consumerGroupName,
|
|
907
|
-
messageId: message.messageId,
|
|
908
|
-
ticket: message.ticket
|
|
909
|
-
});
|
|
910
|
-
}
|
|
1121
|
+
await this.client.deleteMessage({
|
|
1122
|
+
queueName: this.topicName,
|
|
1123
|
+
consumerGroup: this.consumerGroupName,
|
|
1124
|
+
receiptHandle: message.receiptHandle
|
|
1125
|
+
});
|
|
911
1126
|
} catch (error) {
|
|
912
1127
|
await stopExtension();
|
|
913
1128
|
if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
|
|
@@ -922,36 +1137,16 @@ var ConsumerGroup = class {
|
|
|
922
1137
|
}
|
|
923
1138
|
async consume(handler, options) {
|
|
924
1139
|
if (options?.messageId) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
);
|
|
936
|
-
await this.processMessage(
|
|
937
|
-
response.message,
|
|
938
|
-
handler
|
|
939
|
-
);
|
|
940
|
-
} else {
|
|
941
|
-
const response = await this.client.receiveMessageById(
|
|
942
|
-
{
|
|
943
|
-
queueName: this.topicName,
|
|
944
|
-
consumerGroup: this.consumerGroupName,
|
|
945
|
-
messageId: options.messageId,
|
|
946
|
-
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
947
|
-
},
|
|
948
|
-
this.transport
|
|
949
|
-
);
|
|
950
|
-
await this.processMessage(
|
|
951
|
-
response.message,
|
|
952
|
-
handler
|
|
953
|
-
);
|
|
954
|
-
}
|
|
1140
|
+
const response = await this.client.receiveMessageById(
|
|
1141
|
+
{
|
|
1142
|
+
queueName: this.topicName,
|
|
1143
|
+
consumerGroup: this.consumerGroupName,
|
|
1144
|
+
messageId: options.messageId,
|
|
1145
|
+
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
1146
|
+
},
|
|
1147
|
+
this.transport
|
|
1148
|
+
);
|
|
1149
|
+
await this.processMessage(response.message, handler);
|
|
955
1150
|
} else {
|
|
956
1151
|
let messageFound = false;
|
|
957
1152
|
for await (const message of this.client.receiveMessages(
|
|
@@ -1019,7 +1214,7 @@ var Topic = class {
|
|
|
1019
1214
|
payload,
|
|
1020
1215
|
idempotencyKey: options?.idempotencyKey,
|
|
1021
1216
|
retentionSeconds: options?.retentionSeconds,
|
|
1022
|
-
|
|
1217
|
+
delaySeconds: options?.delaySeconds
|
|
1023
1218
|
},
|
|
1024
1219
|
this.transport
|
|
1025
1220
|
);
|
|
@@ -1129,7 +1324,7 @@ async function parseCallback(request) {
|
|
|
1129
1324
|
messageId
|
|
1130
1325
|
};
|
|
1131
1326
|
}
|
|
1132
|
-
function createCallbackHandler(handlers, client) {
|
|
1327
|
+
function createCallbackHandler(handlers, client, visibilityTimeoutSeconds) {
|
|
1133
1328
|
for (const topicPattern in handlers) {
|
|
1134
1329
|
if (topicPattern.includes("*")) {
|
|
1135
1330
|
if (!validateWildcardPattern(topicPattern)) {
|
|
@@ -1165,7 +1360,10 @@ function createCallbackHandler(handlers, client) {
|
|
|
1165
1360
|
);
|
|
1166
1361
|
}
|
|
1167
1362
|
const topic = new Topic(client, queueName);
|
|
1168
|
-
const cg = topic.consumerGroup(
|
|
1363
|
+
const cg = topic.consumerGroup(
|
|
1364
|
+
consumerGroup,
|
|
1365
|
+
visibilityTimeoutSeconds !== void 0 ? { visibilityTimeoutSeconds } : void 0
|
|
1366
|
+
);
|
|
1169
1367
|
await cg.consume(consumerGroupHandler, { messageId });
|
|
1170
1368
|
return Response.json({ status: "success" });
|
|
1171
1369
|
} catch (error) {
|
|
@@ -1179,13 +1377,14 @@ function createCallbackHandler(handlers, client) {
|
|
|
1179
1377
|
);
|
|
1180
1378
|
}
|
|
1181
1379
|
};
|
|
1182
|
-
if (isDevMode()) {
|
|
1183
|
-
registerDevRouteHandler(routeHandler, handlers);
|
|
1184
|
-
}
|
|
1185
1380
|
return routeHandler;
|
|
1186
1381
|
}
|
|
1187
|
-
function handleCallback(handlers,
|
|
1188
|
-
return createCallbackHandler(
|
|
1382
|
+
function handleCallback(handlers, options) {
|
|
1383
|
+
return createCallbackHandler(
|
|
1384
|
+
handlers,
|
|
1385
|
+
options?.client || new QueueClient(),
|
|
1386
|
+
options?.visibilityTimeoutSeconds
|
|
1387
|
+
);
|
|
1189
1388
|
}
|
|
1190
1389
|
|
|
1191
1390
|
// src/nextjs-pages.ts
|