@vercel/queue 0.1.0 → 0.1.2
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 +140 -66
- package/dist/index.d.mts +61 -5
- package/dist/index.d.ts +61 -5
- package/dist/index.js +703 -139
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +699 -138
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -51,8 +51,10 @@ __export(index_exports, {
|
|
|
51
51
|
QueueEmptyError: () => QueueEmptyError,
|
|
52
52
|
StreamTransport: () => StreamTransport,
|
|
53
53
|
UnauthorizedError: () => UnauthorizedError,
|
|
54
|
+
handleCallback: () => handleCallback2,
|
|
54
55
|
parseCallback: () => parseCallback,
|
|
55
|
-
parseRawCallback: () => parseRawCallback
|
|
56
|
+
parseRawCallback: () => parseRawCallback,
|
|
57
|
+
send: () => send
|
|
56
58
|
});
|
|
57
59
|
module.exports = __toCommonJS(index_exports);
|
|
58
60
|
|
|
@@ -134,7 +136,9 @@ var import_mixpart = require("mixpart");
|
|
|
134
136
|
|
|
135
137
|
// src/dev.ts
|
|
136
138
|
var fs = __toESM(require("fs"));
|
|
139
|
+
var net = __toESM(require("net"));
|
|
137
140
|
var path = __toESM(require("path"));
|
|
141
|
+
var import_minimatch = require("minimatch");
|
|
138
142
|
|
|
139
143
|
// src/types.ts
|
|
140
144
|
var MessageNotFoundError = class extends Error {
|
|
@@ -319,8 +323,8 @@ var ConsumerGroup = class {
|
|
|
319
323
|
firstDelayMs = 0;
|
|
320
324
|
}
|
|
321
325
|
}
|
|
322
|
-
const lifecyclePromise = new Promise((
|
|
323
|
-
resolveLifecycle =
|
|
326
|
+
const lifecyclePromise = new Promise((resolve2) => {
|
|
327
|
+
resolveLifecycle = resolve2;
|
|
324
328
|
});
|
|
325
329
|
const safeResolve = () => {
|
|
326
330
|
if (!isResolved) {
|
|
@@ -397,11 +401,12 @@ var ConsumerGroup = class {
|
|
|
397
401
|
message.receiptHandle,
|
|
398
402
|
options
|
|
399
403
|
);
|
|
404
|
+
const DEFAULT_RETENTION_MS = 864e5;
|
|
400
405
|
const metadata = {
|
|
401
406
|
messageId: message.messageId,
|
|
402
407
|
deliveryCount: message.deliveryCount,
|
|
403
408
|
createdAt: message.createdAt,
|
|
404
|
-
expiresAt: message.expiresAt,
|
|
409
|
+
expiresAt: message.expiresAt ?? new Date(message.createdAt.getTime() + DEFAULT_RETENTION_MS),
|
|
405
410
|
topicName: this.topicName,
|
|
406
411
|
consumerGroup: this.consumerGroupName,
|
|
407
412
|
region: this.client.getRegion()
|
|
@@ -552,10 +557,12 @@ var Topic = class {
|
|
|
552
557
|
headers: options?.headers
|
|
553
558
|
});
|
|
554
559
|
if (result.messageId && isDevMode()) {
|
|
555
|
-
|
|
560
|
+
invokeDevHandlers(
|
|
556
561
|
this.topicName,
|
|
557
562
|
result.messageId,
|
|
558
|
-
this.client.getRegion()
|
|
563
|
+
this.client.getRegion(),
|
|
564
|
+
options?.delaySeconds,
|
|
565
|
+
options?.retentionSeconds
|
|
559
566
|
);
|
|
560
567
|
}
|
|
561
568
|
return { messageId: result.messageId };
|
|
@@ -761,16 +768,27 @@ async function handleCallback(handler, request, options) {
|
|
|
761
768
|
}
|
|
762
769
|
|
|
763
770
|
// src/dev.ts
|
|
764
|
-
var
|
|
765
|
-
function
|
|
766
|
-
|
|
767
|
-
if (!urlPath.startsWith("/")) {
|
|
768
|
-
urlPath = "/" + urlPath;
|
|
769
|
-
}
|
|
770
|
-
return urlPath;
|
|
771
|
+
var import_meta = {};
|
|
772
|
+
function isDevMode() {
|
|
773
|
+
return process.env.NODE_ENV === "development";
|
|
771
774
|
}
|
|
775
|
+
var ROUTE_MAPPINGS_KEY = Symbol.for("@vercel/queue.devRouteMappings");
|
|
772
776
|
function filePathToConsumerGroup(filePath) {
|
|
773
|
-
|
|
777
|
+
let result = "";
|
|
778
|
+
for (const char of filePath) {
|
|
779
|
+
if (char === "_") {
|
|
780
|
+
result += "__";
|
|
781
|
+
} else if (char === "/") {
|
|
782
|
+
result += "_S";
|
|
783
|
+
} else if (char === ".") {
|
|
784
|
+
result += "_D";
|
|
785
|
+
} else if (/[A-Za-z0-9-]/.test(char)) {
|
|
786
|
+
result += char;
|
|
787
|
+
} else {
|
|
788
|
+
result += "_" + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0");
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return result;
|
|
774
792
|
}
|
|
775
793
|
function getDevRouteMappings() {
|
|
776
794
|
const g = globalThis;
|
|
@@ -792,13 +810,19 @@ function getDevRouteMappings() {
|
|
|
792
810
|
for (const [filePath, config] of Object.entries(vercelJson.functions)) {
|
|
793
811
|
if (!config.experimentalTriggers) continue;
|
|
794
812
|
for (const trigger of config.experimentalTriggers) {
|
|
795
|
-
if (trigger.type?.startsWith("queue/")
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
topic
|
|
799
|
-
|
|
800
|
-
|
|
813
|
+
if (!trigger.type?.startsWith("queue/") || !trigger.topic) continue;
|
|
814
|
+
if (trigger.type !== "queue/v2beta") {
|
|
815
|
+
console.warn(
|
|
816
|
+
`[Dev Mode] Unsupported trigger type "${trigger.type}" for topic "${trigger.topic}" in ${filePath}. Use "queue/v2beta" instead.`
|
|
817
|
+
);
|
|
818
|
+
continue;
|
|
801
819
|
}
|
|
820
|
+
mappings.push({
|
|
821
|
+
filePath,
|
|
822
|
+
topic: trigger.topic,
|
|
823
|
+
consumer: filePathToConsumerGroup(filePath),
|
|
824
|
+
retryAfterSeconds: trigger.retryAfterSeconds
|
|
825
|
+
});
|
|
802
826
|
}
|
|
803
827
|
}
|
|
804
828
|
g[ROUTE_MAPPINGS_KEY] = mappings.length > 0 ? mappings : null;
|
|
@@ -811,9 +835,7 @@ function getDevRouteMappings() {
|
|
|
811
835
|
}
|
|
812
836
|
function findMatchingRoutes(topicName) {
|
|
813
837
|
const mappings = getDevRouteMappings();
|
|
814
|
-
if (!mappings)
|
|
815
|
-
return [];
|
|
816
|
-
}
|
|
838
|
+
if (!mappings) return [];
|
|
817
839
|
return mappings.filter((mapping) => {
|
|
818
840
|
if (mapping.topic.includes("*")) {
|
|
819
841
|
return matchesWildcardPattern(topicName, mapping.topic);
|
|
@@ -821,149 +843,607 @@ function findMatchingRoutes(topicName) {
|
|
|
821
843
|
return mapping.topic === topicName;
|
|
822
844
|
});
|
|
823
845
|
}
|
|
824
|
-
function
|
|
825
|
-
|
|
846
|
+
function findRetryAfterSeconds(topicName, consumerGroup) {
|
|
847
|
+
const routes = findMatchingRoutes(topicName);
|
|
848
|
+
const route = routes.find((r) => r.consumer === consumerGroup);
|
|
849
|
+
return route?.retryAfterSeconds;
|
|
850
|
+
}
|
|
851
|
+
function stripSrcPrefix(filePath) {
|
|
852
|
+
if (/^src\/(app|pages|server)\//.test(filePath)) {
|
|
853
|
+
return filePath.slice(4);
|
|
854
|
+
}
|
|
855
|
+
return null;
|
|
856
|
+
}
|
|
857
|
+
function matchesFunctionsPattern(sourceFile, pattern) {
|
|
858
|
+
return sourceFile === pattern || (0, import_minimatch.minimatch)(sourceFile, pattern);
|
|
859
|
+
}
|
|
860
|
+
function findMappingsForFile(absolutePath) {
|
|
861
|
+
const mappings = getDevRouteMappings();
|
|
862
|
+
if (!mappings) return [];
|
|
863
|
+
const cwd = process.cwd();
|
|
864
|
+
let relative2;
|
|
865
|
+
try {
|
|
866
|
+
relative2 = path.relative(cwd, absolutePath);
|
|
867
|
+
} catch {
|
|
868
|
+
return [];
|
|
869
|
+
}
|
|
870
|
+
const normalized = relative2.replace(/\\/g, "/");
|
|
871
|
+
const stripped = stripSrcPrefix(normalized);
|
|
872
|
+
return mappings.filter(
|
|
873
|
+
(m) => matchesFunctionsPattern(normalized, m.filePath) || stripped !== null && matchesFunctionsPattern(stripped, m.filePath)
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
function parseFrameFilePath(line) {
|
|
877
|
+
let match = line.match(/\((.+?):\d+:\d+\)/);
|
|
878
|
+
if (!match) match = line.match(/at\s+(.+?):\d+:\d+/);
|
|
879
|
+
if (!match) return null;
|
|
880
|
+
let filePath = match[1].trim();
|
|
881
|
+
if (filePath === "native" || filePath.startsWith("node:") || filePath.startsWith("internal")) {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
if (filePath.startsWith("file://")) {
|
|
885
|
+
try {
|
|
886
|
+
filePath = new URL(filePath).pathname;
|
|
887
|
+
} catch {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(filePath)) {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
if (filePath.startsWith("./")) {
|
|
895
|
+
filePath = filePath.slice(2);
|
|
896
|
+
}
|
|
897
|
+
return filePath;
|
|
898
|
+
}
|
|
899
|
+
var _sdkPackageDir;
|
|
900
|
+
function getSdkPackageDir() {
|
|
901
|
+
if (_sdkPackageDir) return _sdkPackageDir;
|
|
902
|
+
try {
|
|
903
|
+
const thisDir = typeof __dirname !== "undefined" ? __dirname : path.dirname(new URL(import_meta.url).pathname);
|
|
904
|
+
_sdkPackageDir = path.resolve(thisDir, "..");
|
|
905
|
+
} catch {
|
|
906
|
+
_sdkPackageDir = "";
|
|
907
|
+
}
|
|
908
|
+
return _sdkPackageDir;
|
|
909
|
+
}
|
|
910
|
+
function extractCallerFilePath() {
|
|
911
|
+
const stack = new Error().stack;
|
|
912
|
+
if (!stack) return null;
|
|
913
|
+
const lines = stack.split("\n").slice(1);
|
|
914
|
+
const pkgDir = getSdkPackageDir();
|
|
915
|
+
for (const line of lines) {
|
|
916
|
+
const fp = parseFrameFilePath(line);
|
|
917
|
+
if (!fp) continue;
|
|
918
|
+
const absolute = path.isAbsolute(fp) ? fp : path.resolve(process.cwd(), fp);
|
|
919
|
+
let realFp;
|
|
920
|
+
try {
|
|
921
|
+
realFp = fs.realpathSync(absolute);
|
|
922
|
+
} catch {
|
|
923
|
+
realFp = absolute;
|
|
924
|
+
}
|
|
925
|
+
if (pkgDir && realFp.startsWith(pkgDir)) continue;
|
|
926
|
+
return realFp;
|
|
927
|
+
}
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
var HANDLER_REGISTRY_KEY = Symbol.for("@vercel/queue.devHandlerRegistry");
|
|
931
|
+
function getHandlerRegistry() {
|
|
932
|
+
const g = globalThis;
|
|
933
|
+
if (!g[HANDLER_REGISTRY_KEY]) {
|
|
934
|
+
g[HANDLER_REGISTRY_KEY] = /* @__PURE__ */ new Map();
|
|
935
|
+
}
|
|
936
|
+
return g[HANDLER_REGISTRY_KEY];
|
|
937
|
+
}
|
|
938
|
+
function registerHandlerForFile(filePath, handler, client, options) {
|
|
939
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
940
|
+
const fileMappings = findMappingsForFile(absolutePath);
|
|
941
|
+
if (fileMappings.length === 0) return false;
|
|
942
|
+
const registry = getHandlerRegistry();
|
|
943
|
+
for (const mapping of fileMappings) {
|
|
944
|
+
const key = mapping.topic;
|
|
945
|
+
const existing = registry.get(key) ?? [];
|
|
946
|
+
const nextEntry = {
|
|
947
|
+
consumerGroup: mapping.consumer,
|
|
948
|
+
handler,
|
|
949
|
+
client,
|
|
950
|
+
options
|
|
951
|
+
};
|
|
952
|
+
const existingIndex = existing.findIndex(
|
|
953
|
+
(e) => e.consumerGroup === mapping.consumer
|
|
954
|
+
);
|
|
955
|
+
if (existingIndex >= 0) {
|
|
956
|
+
existing[existingIndex] = nextEntry;
|
|
957
|
+
} else {
|
|
958
|
+
existing.push(nextEntry);
|
|
959
|
+
}
|
|
960
|
+
registry.set(key, existing);
|
|
961
|
+
}
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
function registerDevHandler(handler, client, options, _testCallerPath) {
|
|
965
|
+
const callerPath = _testCallerPath ?? extractCallerFilePath();
|
|
966
|
+
if (!callerPath) {
|
|
967
|
+
console.warn(
|
|
968
|
+
"[Dev Mode] Could not determine caller file path for handler registration."
|
|
969
|
+
);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const registered = registerHandlerForFile(
|
|
973
|
+
callerPath,
|
|
974
|
+
handler,
|
|
975
|
+
client,
|
|
976
|
+
options
|
|
977
|
+
);
|
|
978
|
+
if (!registered) {
|
|
979
|
+
const allMappings = getDevRouteMappings();
|
|
980
|
+
if (allMappings && allMappings.length > 0) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const cwd = process.cwd();
|
|
984
|
+
let relative2;
|
|
985
|
+
try {
|
|
986
|
+
relative2 = path.relative(cwd, callerPath).replace(/\\/g, "/");
|
|
987
|
+
} catch {
|
|
988
|
+
relative2 = callerPath;
|
|
989
|
+
}
|
|
990
|
+
console.warn(
|
|
991
|
+
`[Dev Mode] handleCallback() in ${relative2} has no matching experimentalTriggers in vercel.json. This handler won't receive messages.
|
|
992
|
+
|
|
993
|
+
Add a trigger to vercel.json:
|
|
994
|
+
"${relative2}": {
|
|
995
|
+
"experimentalTriggers": [{ "type": "queue/v2beta", "topic": "your-topic" }]
|
|
996
|
+
}`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
function lookupHandlers(topicName) {
|
|
1001
|
+
const registry = getHandlerRegistry();
|
|
1002
|
+
const result = [];
|
|
1003
|
+
for (const [pattern, handlers] of registry) {
|
|
1004
|
+
const matches = pattern.includes("*") ? matchesWildcardPattern(topicName, pattern) : pattern === topicName;
|
|
1005
|
+
if (matches) {
|
|
1006
|
+
result.push(...handlers);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return result;
|
|
1010
|
+
}
|
|
1011
|
+
var DEV_RETRY_INITIAL_DELAY_MS = 50;
|
|
1012
|
+
var DEV_RETRY_MAX_WAIT_MS = 5e3;
|
|
1013
|
+
var DEV_RETRY_BACKOFF = 2;
|
|
1014
|
+
var PORT_CHECK_TIMEOUT_MS = 250;
|
|
1015
|
+
var PRIME_PORT_ENV_KEYS = [
|
|
1016
|
+
"PORT",
|
|
1017
|
+
"NEXT_PORT",
|
|
1018
|
+
"NEXTJS_PORT",
|
|
1019
|
+
"NUXT_PORT",
|
|
1020
|
+
"NITRO_PORT",
|
|
1021
|
+
"SVELTEKIT_PORT",
|
|
1022
|
+
"VITE_PORT",
|
|
1023
|
+
"DEV_PORT",
|
|
1024
|
+
"npm_config_port"
|
|
1025
|
+
];
|
|
1026
|
+
var PRIME_URL_ENV_KEYS = [
|
|
1027
|
+
"__NEXT_PRIVATE_ORIGIN",
|
|
1028
|
+
"NUXT_PUBLIC_SITE_URL",
|
|
1029
|
+
"URL"
|
|
1030
|
+
];
|
|
1031
|
+
function formatErrorReason(error) {
|
|
1032
|
+
if (error instanceof Error) {
|
|
1033
|
+
return error.message;
|
|
1034
|
+
}
|
|
1035
|
+
return String(error);
|
|
1036
|
+
}
|
|
1037
|
+
function isMessageNotFoundError(error) {
|
|
1038
|
+
if (error instanceof MessageNotFoundError) {
|
|
1039
|
+
return true;
|
|
1040
|
+
}
|
|
1041
|
+
if (error instanceof Error && error.name === "MessageNotFoundError") {
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
function parsePort(value) {
|
|
1047
|
+
if (!value) return null;
|
|
1048
|
+
const parsed = Number.parseInt(value, 10);
|
|
1049
|
+
if (!Number.isFinite(parsed) || parsed < 1 || parsed > 65535) return null;
|
|
1050
|
+
return parsed;
|
|
1051
|
+
}
|
|
1052
|
+
function parsePortFromUrl(value) {
|
|
1053
|
+
if (!value) return null;
|
|
1054
|
+
try {
|
|
1055
|
+
const parsed = new URL(value).port;
|
|
1056
|
+
return parsePort(parsed);
|
|
1057
|
+
} catch {
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
function collectPrimePorts() {
|
|
1062
|
+
const result = [];
|
|
1063
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1064
|
+
const add = (port) => {
|
|
1065
|
+
if (port && !seen.has(port)) {
|
|
1066
|
+
seen.add(port);
|
|
1067
|
+
result.push(port);
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
for (const key of PRIME_PORT_ENV_KEYS) {
|
|
1071
|
+
add(parsePort(process.env[key]));
|
|
1072
|
+
}
|
|
1073
|
+
for (const key of PRIME_URL_ENV_KEYS) {
|
|
1074
|
+
add(parsePortFromUrl(process.env[key]));
|
|
1075
|
+
}
|
|
1076
|
+
return result;
|
|
1077
|
+
}
|
|
1078
|
+
function isPortListening(port) {
|
|
1079
|
+
return new Promise((resolve2) => {
|
|
1080
|
+
const socket = net.connect({ host: "localhost", port });
|
|
1081
|
+
let settled = false;
|
|
1082
|
+
const finish = (listening) => {
|
|
1083
|
+
if (settled) return;
|
|
1084
|
+
settled = true;
|
|
1085
|
+
socket.destroy();
|
|
1086
|
+
resolve2(listening);
|
|
1087
|
+
};
|
|
1088
|
+
socket.once("connect", () => finish(true));
|
|
1089
|
+
socket.once("error", () => finish(false));
|
|
1090
|
+
socket.setTimeout(PORT_CHECK_TIMEOUT_MS, () => finish(false));
|
|
1091
|
+
});
|
|
826
1092
|
}
|
|
827
|
-
|
|
828
|
-
var DEV_VISIBILITY_MAX_WAIT = 5e3;
|
|
829
|
-
var DEV_VISIBILITY_BACKOFF_MULTIPLIER = 2;
|
|
830
|
-
async function waitForMessageVisibility(topicName, consumerGroup, messageId, region) {
|
|
831
|
-
const client = new ApiClient({ region });
|
|
1093
|
+
async function invokeWithRetry(handler, request, options) {
|
|
832
1094
|
let elapsed = 0;
|
|
833
|
-
let
|
|
834
|
-
while (
|
|
1095
|
+
let delay = DEV_RETRY_INITIAL_DELAY_MS;
|
|
1096
|
+
while (true) {
|
|
835
1097
|
try {
|
|
836
|
-
await
|
|
837
|
-
|
|
838
|
-
consumerGroup,
|
|
839
|
-
messageId,
|
|
840
|
-
visibilityTimeoutSeconds: 0
|
|
841
|
-
});
|
|
842
|
-
return true;
|
|
1098
|
+
await handleCallback(handler, request, options);
|
|
1099
|
+
return;
|
|
843
1100
|
} catch (error) {
|
|
844
|
-
if (error
|
|
845
|
-
await new Promise((
|
|
846
|
-
elapsed +=
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1101
|
+
if (isMessageNotFoundError(error) && elapsed < DEV_RETRY_MAX_WAIT_MS) {
|
|
1102
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1103
|
+
elapsed += delay;
|
|
1104
|
+
delay = Math.min(
|
|
1105
|
+
delay * DEV_RETRY_BACKOFF,
|
|
1106
|
+
DEV_RETRY_MAX_WAIT_MS - elapsed
|
|
850
1107
|
);
|
|
851
1108
|
continue;
|
|
852
1109
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1110
|
+
throw error;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
function filePathToUrlPath(filePath) {
|
|
1115
|
+
let urlPath = filePath.replace(/^src\/app\//, "/").replace(/^src\/pages\//, "/").replace(/^src\/server\//, "/").replace(/^src\/routes\//, "/").replace(/^app\//, "/").replace(/^pages\//, "/").replace(/^server\//, "/").replace(/\/route\.(ts|mts|js|mjs|tsx|jsx)$/, "").replace(/\/\+server\.(ts|mts|js|mjs|tsx|jsx)$/, "").replace(/\.(ts|mts|js|mjs|tsx|jsx)$/, "");
|
|
1116
|
+
if (!urlPath.startsWith("/")) {
|
|
1117
|
+
urlPath = "/" + urlPath;
|
|
1118
|
+
}
|
|
1119
|
+
return urlPath;
|
|
1120
|
+
}
|
|
1121
|
+
async function ensureHandlersLoaded(topicName, options = {}) {
|
|
1122
|
+
const diagnostics = {
|
|
1123
|
+
triedPorts: collectPrimePorts(),
|
|
1124
|
+
listeningPorts: [],
|
|
1125
|
+
unavailablePorts: [],
|
|
1126
|
+
importFailures: [],
|
|
1127
|
+
primeFailures: []
|
|
1128
|
+
};
|
|
1129
|
+
const matchingRoutes = findMatchingRoutes(topicName);
|
|
1130
|
+
if (matchingRoutes.length === 0) return diagnostics;
|
|
1131
|
+
const shouldRefreshRegistered = options.refreshRegistered === true;
|
|
1132
|
+
for (const port of diagnostics.triedPorts) {
|
|
1133
|
+
if (await isPortListening(port)) {
|
|
1134
|
+
diagnostics.listeningPorts.push(port);
|
|
1135
|
+
} else {
|
|
1136
|
+
diagnostics.unavailablePorts.push(port);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
for (const route of matchingRoutes) {
|
|
1140
|
+
const alreadyRegistered = isHandlerRegistered(topicName, route.consumer);
|
|
1141
|
+
if (alreadyRegistered && !shouldRefreshRegistered) {
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
if (!alreadyRegistered) {
|
|
1145
|
+
const absolutePath = path.resolve(process.cwd(), route.filePath);
|
|
1146
|
+
try {
|
|
1147
|
+
await import(absolutePath);
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
diagnostics.importFailures.push({
|
|
1150
|
+
filePath: route.filePath,
|
|
1151
|
+
reason: formatErrorReason(error)
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
if (isHandlerRegistered(topicName, route.consumer)) continue;
|
|
1155
|
+
}
|
|
1156
|
+
for (const port of diagnostics.listeningPorts) {
|
|
1157
|
+
const url = `http://localhost:${port}${filePathToUrlPath(route.filePath)}`;
|
|
1158
|
+
try {
|
|
1159
|
+
const response = await fetch(url, {
|
|
1160
|
+
method: "POST",
|
|
1161
|
+
headers: {
|
|
1162
|
+
"x-vercel-queue-prime": "1",
|
|
1163
|
+
"x-vercel-queue-prime-file": route.filePath
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
try {
|
|
1167
|
+
await response.text();
|
|
1168
|
+
} catch {
|
|
1169
|
+
}
|
|
1170
|
+
if (isHandlerRegistered(topicName, route.consumer)) {
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1173
|
+
diagnostics.primeFailures.push({
|
|
1174
|
+
filePath: route.filePath,
|
|
1175
|
+
url,
|
|
1176
|
+
reason: `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`.trim()
|
|
1177
|
+
});
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
diagnostics.primeFailures.push({
|
|
1180
|
+
filePath: route.filePath,
|
|
1181
|
+
url,
|
|
1182
|
+
reason: formatErrorReason(error)
|
|
1183
|
+
});
|
|
858
1184
|
}
|
|
859
|
-
console.error(
|
|
860
|
-
`[Dev Mode] Error polling for message visibility: topic="${topicName}" messageId="${messageId}"`,
|
|
861
|
-
error
|
|
862
|
-
);
|
|
863
|
-
return false;
|
|
864
1185
|
}
|
|
865
1186
|
}
|
|
866
|
-
|
|
867
|
-
|
|
1187
|
+
return diagnostics;
|
|
1188
|
+
}
|
|
1189
|
+
function buildNoHandlerWarning(topicName, routes, diagnostics) {
|
|
1190
|
+
const files = routes.map((r) => r.filePath);
|
|
1191
|
+
const suggestedPort = diagnostics.listeningPorts[0] ?? diagnostics.triedPorts[0];
|
|
1192
|
+
const suggestedUrls = suggestedPort ? routes.map(
|
|
1193
|
+
(r) => `http://localhost:${suggestedPort}${filePathToUrlPath(r.filePath)}`
|
|
1194
|
+
) : [];
|
|
1195
|
+
let portSummary;
|
|
1196
|
+
if (diagnostics.triedPorts.length === 0) {
|
|
1197
|
+
portSummary = "No local dev port detected from env. Set PORT (or NEXT_PORT/NUXT_PORT/VITE_PORT).";
|
|
1198
|
+
} else if (diagnostics.listeningPorts.length === 0) {
|
|
1199
|
+
portSummary = `Detected env ports: [${diagnostics.triedPorts.join(", ")}], but none are listening.`;
|
|
1200
|
+
} else {
|
|
1201
|
+
const unavailable = diagnostics.unavailablePorts.length > 0 ? ` Not listening: [${diagnostics.unavailablePorts.join(", ")}].` : "";
|
|
1202
|
+
portSummary = `Detected env ports: [${diagnostics.triedPorts.join(", ")}]. Listening: [${diagnostics.listeningPorts.join(", ")}].` + unavailable;
|
|
1203
|
+
}
|
|
1204
|
+
const importSummary = diagnostics.importFailures.length > 0 ? `
|
|
1205
|
+
Import failures: ` + diagnostics.importFailures.slice(0, 2).map((f) => `${f.filePath} (${f.reason})`).join("; ") : "";
|
|
1206
|
+
const primeSummary = diagnostics.primeFailures.length > 0 ? `
|
|
1207
|
+
Prime failures: ` + diagnostics.primeFailures.slice(0, 3).map((f) => `${f.url} (${f.reason})`).join("; ") : "";
|
|
1208
|
+
return `[Dev Mode] No registered handler for topic "${topicName}". vercel.json maps this topic to [${files.join(", ")}] but auto-loading failed.
|
|
1209
|
+
${portSummary}${importSummary}${primeSummary}
|
|
1210
|
+
Ensure your dev server is running, set PORT if needed, and confirm mapped route files call handleCallback()/handleNodeCallback() at module scope.
|
|
1211
|
+
` + (suggestedUrls.length > 0 ? `Try opening: ${suggestedUrls.join(" or ")}` : "Set PORT (or NEXT_PORT/NUXT_PORT/VITE_PORT) and try sending again.");
|
|
1212
|
+
}
|
|
1213
|
+
function isHandlerRegistered(topicName, consumerGroup) {
|
|
1214
|
+
return lookupHandlers(topicName).some(
|
|
1215
|
+
(h) => h.consumerGroup === consumerGroup
|
|
868
1216
|
);
|
|
869
|
-
return false;
|
|
870
1217
|
}
|
|
871
|
-
|
|
1218
|
+
var DEV_REDELIVERY_MAX_DELAY_S = 10;
|
|
1219
|
+
var DEV_REDELIVERY_DEFAULT_DELAY_S = 2;
|
|
1220
|
+
var DEV_REDELIVERY_MAX_ATTEMPTS = 10;
|
|
1221
|
+
var DEFAULT_RETENTION_S = 86400;
|
|
1222
|
+
function scheduleDevRedelivery(ctx, delayS) {
|
|
1223
|
+
const cappedDelay = Math.min(Math.max(delayS, 0), DEV_REDELIVERY_MAX_DELAY_S);
|
|
1224
|
+
console.log(
|
|
1225
|
+
`[Dev Mode] \u21BB Scheduling re-delivery in ${cappedDelay}s: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
|
|
1226
|
+
);
|
|
1227
|
+
setTimeout(async () => {
|
|
1228
|
+
const nextDeliveryCount = ctx.deliveryCount + 1;
|
|
1229
|
+
const expiresAt = new Date(
|
|
1230
|
+
ctx.createdAt.getTime() + ctx.retentionSeconds * 1e3
|
|
1231
|
+
);
|
|
1232
|
+
if (Date.now() >= expiresAt.getTime()) {
|
|
1233
|
+
console.log(
|
|
1234
|
+
`[Dev Mode] Message expired, stopping retries: topic="${ctx.topicName}" messageId="${ctx.messageId}"`
|
|
1235
|
+
);
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (nextDeliveryCount > DEV_REDELIVERY_MAX_ATTEMPTS) {
|
|
1239
|
+
console.log(
|
|
1240
|
+
`[Dev Mode] Max re-deliveries (${DEV_REDELIVERY_MAX_ATTEMPTS}) reached: topic="${ctx.topicName}" messageId="${ctx.messageId}"`
|
|
1241
|
+
);
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
const metadata = {
|
|
1245
|
+
messageId: ctx.messageId,
|
|
1246
|
+
deliveryCount: nextDeliveryCount,
|
|
1247
|
+
createdAt: ctx.createdAt,
|
|
1248
|
+
expiresAt,
|
|
1249
|
+
topicName: ctx.topicName,
|
|
1250
|
+
consumerGroup: ctx.consumerGroup,
|
|
1251
|
+
region: ctx.region
|
|
1252
|
+
};
|
|
1253
|
+
console.log(
|
|
1254
|
+
`[Dev Mode] Re-delivering: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}" deliveryCount=${nextDeliveryCount}`
|
|
1255
|
+
);
|
|
1256
|
+
let succeeded = true;
|
|
1257
|
+
let nextRetryAfterS = null;
|
|
1258
|
+
let nextAcknowledged = false;
|
|
1259
|
+
try {
|
|
1260
|
+
await ctx.handler(ctx.payload, metadata);
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
succeeded = false;
|
|
1263
|
+
if (ctx.retry) {
|
|
1264
|
+
let directive;
|
|
1265
|
+
try {
|
|
1266
|
+
directive = ctx.retry(error, metadata);
|
|
1267
|
+
} catch (retryErr) {
|
|
1268
|
+
console.warn("[Dev Mode] retry handler threw:", retryErr);
|
|
1269
|
+
}
|
|
1270
|
+
if (directive && "afterSeconds" in directive) {
|
|
1271
|
+
nextRetryAfterS = directive.afterSeconds;
|
|
1272
|
+
} else if (directive && "acknowledge" in directive) {
|
|
1273
|
+
nextAcknowledged = true;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (!nextAcknowledged) {
|
|
1277
|
+
console.error(
|
|
1278
|
+
`[Dev Mode] \u2717 Handler error on re-delivery: topic="${ctx.topicName}" messageId="${ctx.messageId}"`,
|
|
1279
|
+
error
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
if (succeeded) {
|
|
1284
|
+
console.log(
|
|
1285
|
+
`[Dev Mode] \u2713 Message processed on re-delivery: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
|
|
1286
|
+
);
|
|
1287
|
+
} else if (nextAcknowledged) {
|
|
1288
|
+
console.log(
|
|
1289
|
+
`[Dev Mode] \u2713 Message acknowledged (will not retry): topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
|
|
1290
|
+
);
|
|
1291
|
+
} else {
|
|
1292
|
+
const nextDelay = nextRetryAfterS ?? ctx.defaultRetryDelayS;
|
|
1293
|
+
scheduleDevRedelivery(
|
|
1294
|
+
{ ...ctx, deliveryCount: nextDeliveryCount },
|
|
1295
|
+
nextDelay
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
}, cappedDelay * 1e3);
|
|
1299
|
+
}
|
|
1300
|
+
function invokeDevHandlers(topicName, messageId, region, delaySeconds, retentionSeconds) {
|
|
872
1301
|
if (delaySeconds && delaySeconds > 0) {
|
|
873
1302
|
console.log(
|
|
874
1303
|
`[Dev Mode] Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
|
|
875
1304
|
);
|
|
876
1305
|
setTimeout(() => {
|
|
877
|
-
|
|
1306
|
+
invokeDevHandlers(
|
|
1307
|
+
topicName,
|
|
1308
|
+
messageId,
|
|
1309
|
+
region,
|
|
1310
|
+
void 0,
|
|
1311
|
+
retentionSeconds
|
|
1312
|
+
);
|
|
878
1313
|
}, delaySeconds * 1e3);
|
|
879
1314
|
return;
|
|
880
1315
|
}
|
|
881
1316
|
console.log(
|
|
882
1317
|
`[Dev Mode] Message sent: topic="${topicName}" messageId="${messageId}"`
|
|
883
1318
|
);
|
|
884
|
-
const matchingRoutes = findMatchingRoutes(topicName);
|
|
885
|
-
if (matchingRoutes.length === 0) {
|
|
886
|
-
console.log(
|
|
887
|
-
`[Dev Mode] No matching routes in vercel.json for topic "${topicName}"`
|
|
888
|
-
);
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
const consumerGroups = matchingRoutes.map((r) => r.consumer);
|
|
892
|
-
console.log(
|
|
893
|
-
`[Dev Mode] Scheduling callbacks for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
|
|
894
|
-
);
|
|
895
1319
|
(async () => {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
);
|
|
1320
|
+
let handlers = lookupHandlers(topicName);
|
|
1321
|
+
let diagnostics = null;
|
|
1322
|
+
if (handlers.length > 0) {
|
|
1323
|
+
await ensureHandlersLoaded(topicName, { refreshRegistered: true });
|
|
1324
|
+
handlers = lookupHandlers(topicName);
|
|
1325
|
+
} else {
|
|
1326
|
+
diagnostics = await ensureHandlersLoaded(topicName);
|
|
1327
|
+
handlers = lookupHandlers(topicName);
|
|
1328
|
+
}
|
|
1329
|
+
if (handlers.length === 0) {
|
|
1330
|
+
const matchingRoutes = findMatchingRoutes(topicName);
|
|
1331
|
+
if (matchingRoutes.length > 0) {
|
|
1332
|
+
const safeDiagnostics = diagnostics ?? {
|
|
1333
|
+
triedPorts: collectPrimePorts(),
|
|
1334
|
+
listeningPorts: [],
|
|
1335
|
+
unavailablePorts: [],
|
|
1336
|
+
importFailures: [],
|
|
1337
|
+
primeFailures: []
|
|
1338
|
+
};
|
|
1339
|
+
console.warn(
|
|
1340
|
+
buildNoHandlerWarning(topicName, matchingRoutes, safeDiagnostics)
|
|
1341
|
+
);
|
|
1342
|
+
} else {
|
|
1343
|
+
console.warn(
|
|
1344
|
+
`[Dev Mode] No registered handler for topic "${topicName}".
|
|
1345
|
+
Ensure vercel.json has a matching experimentalTriggers entry and the route file calls handleCallback().`
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
907
1348
|
return;
|
|
908
1349
|
}
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1350
|
+
const consumerGroups = handlers.map((h) => h.consumerGroup);
|
|
1351
|
+
console.log(
|
|
1352
|
+
`[Dev Mode] Invoking handlers for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
|
|
1353
|
+
);
|
|
1354
|
+
const effectiveRetention = retentionSeconds ?? DEFAULT_RETENTION_S;
|
|
1355
|
+
for (const entry of handlers) {
|
|
1356
|
+
let capturedPayload;
|
|
1357
|
+
let capturedCreatedAt = /* @__PURE__ */ new Date();
|
|
1358
|
+
let capturedDeliveryCount = 1;
|
|
1359
|
+
let handlerSucceeded = true;
|
|
1360
|
+
let retryAfterS = null;
|
|
1361
|
+
let retryAcknowledged = false;
|
|
1362
|
+
const wrappedHandler = async (message, metadata) => {
|
|
1363
|
+
capturedPayload = message;
|
|
1364
|
+
capturedCreatedAt = metadata.createdAt;
|
|
1365
|
+
capturedDeliveryCount = metadata.deliveryCount;
|
|
1366
|
+
try {
|
|
1367
|
+
await entry.handler(message, metadata);
|
|
1368
|
+
} catch (error) {
|
|
1369
|
+
handlerSucceeded = false;
|
|
1370
|
+
throw error;
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
const wrappedRetry = entry.options?.retry ? (error, metadata) => {
|
|
1374
|
+
const directive = entry.options.retry(error, metadata);
|
|
1375
|
+
if (directive && "afterSeconds" in directive) {
|
|
1376
|
+
retryAfterS = directive.afterSeconds;
|
|
1377
|
+
} else if (directive && "acknowledge" in directive) {
|
|
1378
|
+
retryAcknowledged = true;
|
|
1379
|
+
}
|
|
1380
|
+
return directive;
|
|
1381
|
+
} : void 0;
|
|
1382
|
+
const request = {
|
|
1383
|
+
queueName: topicName,
|
|
1384
|
+
consumerGroup: entry.consumerGroup,
|
|
1385
|
+
messageId,
|
|
1386
|
+
region
|
|
1387
|
+
};
|
|
1388
|
+
const callbackOptions = {
|
|
1389
|
+
client: entry.client,
|
|
1390
|
+
visibilityTimeoutSeconds: entry.options?.visibilityTimeoutSeconds,
|
|
1391
|
+
retry: wrappedRetry
|
|
1392
|
+
};
|
|
1393
|
+
const consumerDefaultDelay = Math.min(
|
|
1394
|
+
findRetryAfterSeconds(topicName, entry.consumerGroup) ?? DEV_REDELIVERY_DEFAULT_DELAY_S,
|
|
1395
|
+
DEV_REDELIVERY_MAX_DELAY_S
|
|
915
1396
|
);
|
|
1397
|
+
const buildRedeliveryCtx = () => ({
|
|
1398
|
+
handler: entry.handler,
|
|
1399
|
+
retry: entry.options?.retry,
|
|
1400
|
+
payload: capturedPayload,
|
|
1401
|
+
topicName,
|
|
1402
|
+
consumerGroup: entry.consumerGroup,
|
|
1403
|
+
messageId,
|
|
1404
|
+
region,
|
|
1405
|
+
createdAt: capturedCreatedAt,
|
|
1406
|
+
retentionSeconds: effectiveRetention,
|
|
1407
|
+
deliveryCount: capturedDeliveryCount,
|
|
1408
|
+
defaultRetryDelayS: consumerDefaultDelay
|
|
1409
|
+
});
|
|
916
1410
|
try {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
"
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
"
|
|
925
|
-
|
|
926
|
-
})
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
const responseData = await response.json();
|
|
930
|
-
if (responseData.status === "success") {
|
|
931
|
-
console.log(
|
|
932
|
-
`[Dev Mode] \u2713 Message processed successfully: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}"`
|
|
933
|
-
);
|
|
934
|
-
}
|
|
935
|
-
} catch {
|
|
936
|
-
console.warn(
|
|
937
|
-
`[Dev Mode] Handler returned OK but response was not JSON: topic="${topicName}" consumer="${route.consumer}"`
|
|
938
|
-
);
|
|
939
|
-
}
|
|
940
|
-
} else {
|
|
941
|
-
try {
|
|
942
|
-
const errorData = await response.json();
|
|
943
|
-
console.error(
|
|
944
|
-
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" error="${errorData.error || response.statusText}"`
|
|
945
|
-
);
|
|
946
|
-
} catch {
|
|
947
|
-
console.error(
|
|
948
|
-
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" status=${response.status}`
|
|
949
|
-
);
|
|
950
|
-
}
|
|
1411
|
+
await invokeWithRetry(wrappedHandler, request, callbackOptions);
|
|
1412
|
+
if (handlerSucceeded) {
|
|
1413
|
+
console.log(
|
|
1414
|
+
`[Dev Mode] \u2713 Message processed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`
|
|
1415
|
+
);
|
|
1416
|
+
} else if (retryAcknowledged) {
|
|
1417
|
+
console.log(
|
|
1418
|
+
`[Dev Mode] \u2713 Message acknowledged (will not retry): topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`
|
|
1419
|
+
);
|
|
1420
|
+
} else if (retryAfterS !== null) {
|
|
1421
|
+
const devDelay = Math.min(retryAfterS, DEV_REDELIVERY_MAX_DELAY_S);
|
|
1422
|
+
scheduleDevRedelivery(buildRedeliveryCtx(), devDelay);
|
|
951
1423
|
}
|
|
952
1424
|
} catch (error) {
|
|
953
1425
|
console.error(
|
|
954
|
-
`[Dev Mode] \u2717
|
|
1426
|
+
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`,
|
|
955
1427
|
error
|
|
956
1428
|
);
|
|
1429
|
+
if (!handlerSucceeded) {
|
|
1430
|
+
scheduleDevRedelivery(buildRedeliveryCtx(), consumerDefaultDelay);
|
|
1431
|
+
}
|
|
957
1432
|
}
|
|
958
1433
|
}
|
|
959
1434
|
})();
|
|
960
1435
|
}
|
|
961
|
-
function
|
|
1436
|
+
function clearDevState() {
|
|
962
1437
|
const g = globalThis;
|
|
963
1438
|
delete g[ROUTE_MAPPINGS_KEY];
|
|
1439
|
+
delete g[HANDLER_REGISTRY_KEY];
|
|
964
1440
|
}
|
|
965
1441
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
966
|
-
globalThis.
|
|
1442
|
+
globalThis.__clearDevState = clearDevState;
|
|
1443
|
+
globalThis.__filePathToConsumerGroup = filePathToConsumerGroup;
|
|
1444
|
+
globalThis.__filePathToUrlPath = filePathToUrlPath;
|
|
1445
|
+
globalThis.__matchesFunctionsPattern = matchesFunctionsPattern;
|
|
1446
|
+
globalThis.__stripSrcPrefix = stripSrcPrefix;
|
|
967
1447
|
}
|
|
968
1448
|
|
|
969
1449
|
// src/oidc.ts
|
|
@@ -1007,6 +1487,7 @@ function parseQueueHeaders(headers) {
|
|
|
1007
1487
|
const timestamp = headers.get("Vqs-Timestamp");
|
|
1008
1488
|
const contentType = headers.get("Content-Type") || "application/octet-stream";
|
|
1009
1489
|
const receiptHandle = headers.get("Vqs-Receipt-Handle");
|
|
1490
|
+
const expiresAtStr = headers.get("Vqs-Expires-At");
|
|
1010
1491
|
if (!messageId || !timestamp || !receiptHandle) {
|
|
1011
1492
|
return null;
|
|
1012
1493
|
}
|
|
@@ -1018,6 +1499,7 @@ function parseQueueHeaders(headers) {
|
|
|
1018
1499
|
messageId,
|
|
1019
1500
|
deliveryCount,
|
|
1020
1501
|
createdAt: new Date(timestamp),
|
|
1502
|
+
expiresAt: expiresAtStr ? new Date(expiresAtStr) : void 0,
|
|
1021
1503
|
contentType,
|
|
1022
1504
|
receiptHandle
|
|
1023
1505
|
};
|
|
@@ -1138,7 +1620,7 @@ var ApiClient = class _ApiClient {
|
|
|
1138
1620
|
}
|
|
1139
1621
|
console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
|
|
1140
1622
|
}
|
|
1141
|
-
init.headers.set("User-Agent", `@vercel/queue/${"0.1.
|
|
1623
|
+
init.headers.set("User-Agent", `@vercel/queue/${"0.1.2"}`);
|
|
1142
1624
|
init.headers.set("Vqs-Client-Ts", (/* @__PURE__ */ new Date()).toISOString());
|
|
1143
1625
|
const response = await fetch(url, init);
|
|
1144
1626
|
if (isDebugEnabled()) {
|
|
@@ -1474,12 +1956,36 @@ var ApiClient = class _ApiClient {
|
|
|
1474
1956
|
|
|
1475
1957
|
// src/client.ts
|
|
1476
1958
|
var apiClients = /* @__PURE__ */ new WeakMap();
|
|
1959
|
+
var API_CLIENT_KEY = Symbol.for("@vercel/queue.apiClient");
|
|
1960
|
+
function setApi(client, api) {
|
|
1961
|
+
apiClients.set(client, api);
|
|
1962
|
+
Object.defineProperty(client, API_CLIENT_KEY, {
|
|
1963
|
+
value: api,
|
|
1964
|
+
writable: false,
|
|
1965
|
+
enumerable: false,
|
|
1966
|
+
configurable: false
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1477
1969
|
function getApi(client) {
|
|
1478
1970
|
const api = apiClients.get(client);
|
|
1479
|
-
if (
|
|
1480
|
-
|
|
1971
|
+
if (api) {
|
|
1972
|
+
return api;
|
|
1481
1973
|
}
|
|
1482
|
-
|
|
1974
|
+
const apiFromSymbol = client[API_CLIENT_KEY];
|
|
1975
|
+
if (typeof apiFromSymbol === "object" && apiFromSymbol !== null) {
|
|
1976
|
+
const resolvedApi = apiFromSymbol;
|
|
1977
|
+
apiClients.set(client, resolvedApi);
|
|
1978
|
+
return resolvedApi;
|
|
1979
|
+
}
|
|
1980
|
+
throw new Error(
|
|
1981
|
+
"QueueClient not initialized. This may happen when multiple bundled copies of @vercel/queue are loaded in local dev."
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1984
|
+
function resolveCallbackRequest(input) {
|
|
1985
|
+
if ("request" in input) {
|
|
1986
|
+
return input.request;
|
|
1987
|
+
}
|
|
1988
|
+
return input;
|
|
1483
1989
|
}
|
|
1484
1990
|
function getApiClient(client) {
|
|
1485
1991
|
return getApi(client);
|
|
@@ -1489,15 +1995,17 @@ function resolveRegion(region) {
|
|
|
1489
1995
|
if (region) return region;
|
|
1490
1996
|
const fromEnv = process.env.VERCEL_REGION;
|
|
1491
1997
|
if (fromEnv) return fromEnv;
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1998
|
+
if (!isDevMode()) {
|
|
1999
|
+
console.warn(
|
|
2000
|
+
`[QueueClient] Region not detected \u2014 defaulting to "${DEFAULT_REGION}". On Vercel this is set automatically via VERCEL_REGION. To silence this warning, pass region explicitly: new QueueClient({ region: "iad1" })`
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
1495
2003
|
return DEFAULT_REGION;
|
|
1496
2004
|
}
|
|
1497
2005
|
var QueueClient = class {
|
|
1498
2006
|
constructor(options = {}) {
|
|
1499
2007
|
const region = resolveRegion(options.region);
|
|
1500
|
-
|
|
2008
|
+
setApi(this, new ApiClient({ ...options, region }));
|
|
1501
2009
|
}
|
|
1502
2010
|
/**
|
|
1503
2011
|
* Send a message to a topic.
|
|
@@ -1525,11 +2033,12 @@ var QueueClient = class {
|
|
|
1525
2033
|
headers: options?.headers
|
|
1526
2034
|
});
|
|
1527
2035
|
if (result.messageId && isDevMode()) {
|
|
1528
|
-
|
|
2036
|
+
invokeDevHandlers(
|
|
1529
2037
|
topicName,
|
|
1530
2038
|
result.messageId,
|
|
1531
2039
|
api.getRegion(),
|
|
1532
|
-
options?.delaySeconds
|
|
2040
|
+
options?.delaySeconds,
|
|
2041
|
+
options?.retentionSeconds
|
|
1533
2042
|
);
|
|
1534
2043
|
}
|
|
1535
2044
|
return { messageId: result.messageId };
|
|
@@ -1551,10 +2060,26 @@ var QueueClient = class {
|
|
|
1551
2060
|
* @param options.visibilityTimeoutSeconds - Message lock duration (default: 300, max: 3600)
|
|
1552
2061
|
* @param options.retry - Called when the handler throws. Return `{ afterSeconds: N }` to
|
|
1553
2062
|
* reschedule the message for redelivery after N seconds.
|
|
1554
|
-
* @returns A `
|
|
2063
|
+
* @returns A route handler that accepts either `Request` or `{ request: Request }`
|
|
1555
2064
|
*/
|
|
1556
2065
|
handleCallback = (handler, options) => {
|
|
1557
|
-
|
|
2066
|
+
if (isDevMode()) {
|
|
2067
|
+
registerDevHandler(handler, this, options);
|
|
2068
|
+
}
|
|
2069
|
+
return async (requestOrEvent) => {
|
|
2070
|
+
const request = resolveCallbackRequest(requestOrEvent);
|
|
2071
|
+
if (isDevMode() && request.headers.get("x-vercel-queue-prime") === "1") {
|
|
2072
|
+
const primeFile = request.headers.get("x-vercel-queue-prime-file");
|
|
2073
|
+
if (primeFile) {
|
|
2074
|
+
registerDevHandler(
|
|
2075
|
+
handler,
|
|
2076
|
+
this,
|
|
2077
|
+
options,
|
|
2078
|
+
primeFile
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
return Response.json({ status: "primed" });
|
|
2082
|
+
}
|
|
1558
2083
|
try {
|
|
1559
2084
|
const parsed = await parseCallback(request);
|
|
1560
2085
|
await handleCallback(handler, parsed, {
|
|
@@ -1596,11 +2121,29 @@ var QueueClient = class {
|
|
|
1596
2121
|
* @returns A `(req, res) => Promise<void>` route handler
|
|
1597
2122
|
*/
|
|
1598
2123
|
handleNodeCallback = (handler, options) => {
|
|
2124
|
+
if (isDevMode()) {
|
|
2125
|
+
registerDevHandler(handler, this, options);
|
|
2126
|
+
}
|
|
1599
2127
|
return async (req, res) => {
|
|
1600
2128
|
if (req.method !== "POST") {
|
|
1601
2129
|
res.status(200).end();
|
|
1602
2130
|
return;
|
|
1603
2131
|
}
|
|
2132
|
+
const primeHeader = req.headers["x-vercel-queue-prime"];
|
|
2133
|
+
if (isDevMode() && primeHeader === "1") {
|
|
2134
|
+
const primeFileHeader = req.headers["x-vercel-queue-prime-file"];
|
|
2135
|
+
const primeFile = Array.isArray(primeFileHeader) ? primeFileHeader[0] : primeFileHeader;
|
|
2136
|
+
if (primeFile) {
|
|
2137
|
+
registerDevHandler(
|
|
2138
|
+
handler,
|
|
2139
|
+
this,
|
|
2140
|
+
options,
|
|
2141
|
+
primeFile
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
res.status(200).json({ status: "primed" });
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
1604
2147
|
try {
|
|
1605
2148
|
const parsed = parseRawCallback(req.body, req.headers);
|
|
1606
2149
|
await handleCallback(handler, parsed, {
|
|
@@ -1622,7 +2165,7 @@ var QueueClient = class {
|
|
|
1622
2165
|
};
|
|
1623
2166
|
var PollingQueueClient = class {
|
|
1624
2167
|
constructor(options) {
|
|
1625
|
-
|
|
2168
|
+
setApi(this, new ApiClient(options));
|
|
1626
2169
|
}
|
|
1627
2170
|
/**
|
|
1628
2171
|
* Send a message to a topic.
|
|
@@ -1721,6 +2264,25 @@ var PollingQueueClient = class {
|
|
|
1721
2264
|
}
|
|
1722
2265
|
};
|
|
1723
2266
|
};
|
|
2267
|
+
|
|
2268
|
+
// src/default-client.ts
|
|
2269
|
+
var _defaultClient;
|
|
2270
|
+
function getDefaultClient() {
|
|
2271
|
+
if (!_defaultClient) {
|
|
2272
|
+
_defaultClient = new QueueClient();
|
|
2273
|
+
}
|
|
2274
|
+
return _defaultClient;
|
|
2275
|
+
}
|
|
2276
|
+
function resolveClient(region) {
|
|
2277
|
+
if (!region) return getDefaultClient();
|
|
2278
|
+
return new QueueClient({ region });
|
|
2279
|
+
}
|
|
2280
|
+
async function send(topicName, payload, options) {
|
|
2281
|
+
return resolveClient(options?.region).send(topicName, payload, options);
|
|
2282
|
+
}
|
|
2283
|
+
function handleCallback2(handler, options) {
|
|
2284
|
+
return getDefaultClient().handleCallback(handler, options);
|
|
2285
|
+
}
|
|
1724
2286
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1725
2287
|
0 && (module.exports = {
|
|
1726
2288
|
BadRequestError,
|
|
@@ -1744,7 +2306,9 @@ var PollingQueueClient = class {
|
|
|
1744
2306
|
QueueEmptyError,
|
|
1745
2307
|
StreamTransport,
|
|
1746
2308
|
UnauthorizedError,
|
|
2309
|
+
handleCallback,
|
|
1747
2310
|
parseCallback,
|
|
1748
|
-
parseRawCallback
|
|
2311
|
+
parseRawCallback,
|
|
2312
|
+
send
|
|
1749
2313
|
});
|
|
1750
2314
|
//# sourceMappingURL=index.js.map
|