@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.mjs
CHANGED
|
@@ -76,7 +76,9 @@ import { parseMultipartStream } from "mixpart";
|
|
|
76
76
|
|
|
77
77
|
// src/dev.ts
|
|
78
78
|
import * as fs from "fs";
|
|
79
|
+
import * as net from "net";
|
|
79
80
|
import * as path from "path";
|
|
81
|
+
import { minimatch } from "minimatch";
|
|
80
82
|
|
|
81
83
|
// src/types.ts
|
|
82
84
|
var MessageNotFoundError = class extends Error {
|
|
@@ -261,8 +263,8 @@ var ConsumerGroup = class {
|
|
|
261
263
|
firstDelayMs = 0;
|
|
262
264
|
}
|
|
263
265
|
}
|
|
264
|
-
const lifecyclePromise = new Promise((
|
|
265
|
-
resolveLifecycle =
|
|
266
|
+
const lifecyclePromise = new Promise((resolve2) => {
|
|
267
|
+
resolveLifecycle = resolve2;
|
|
266
268
|
});
|
|
267
269
|
const safeResolve = () => {
|
|
268
270
|
if (!isResolved) {
|
|
@@ -339,11 +341,12 @@ var ConsumerGroup = class {
|
|
|
339
341
|
message.receiptHandle,
|
|
340
342
|
options
|
|
341
343
|
);
|
|
344
|
+
const DEFAULT_RETENTION_MS = 864e5;
|
|
342
345
|
const metadata = {
|
|
343
346
|
messageId: message.messageId,
|
|
344
347
|
deliveryCount: message.deliveryCount,
|
|
345
348
|
createdAt: message.createdAt,
|
|
346
|
-
expiresAt: message.expiresAt,
|
|
349
|
+
expiresAt: message.expiresAt ?? new Date(message.createdAt.getTime() + DEFAULT_RETENTION_MS),
|
|
347
350
|
topicName: this.topicName,
|
|
348
351
|
consumerGroup: this.consumerGroupName,
|
|
349
352
|
region: this.client.getRegion()
|
|
@@ -494,10 +497,12 @@ var Topic = class {
|
|
|
494
497
|
headers: options?.headers
|
|
495
498
|
});
|
|
496
499
|
if (result.messageId && isDevMode()) {
|
|
497
|
-
|
|
500
|
+
invokeDevHandlers(
|
|
498
501
|
this.topicName,
|
|
499
502
|
result.messageId,
|
|
500
|
-
this.client.getRegion()
|
|
503
|
+
this.client.getRegion(),
|
|
504
|
+
options?.delaySeconds,
|
|
505
|
+
options?.retentionSeconds
|
|
501
506
|
);
|
|
502
507
|
}
|
|
503
508
|
return { messageId: result.messageId };
|
|
@@ -703,16 +708,26 @@ async function handleCallback(handler, request, options) {
|
|
|
703
708
|
}
|
|
704
709
|
|
|
705
710
|
// src/dev.ts
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
let urlPath = filePath.replace(/^app\//, "/").replace(/^pages\//, "/").replace(/\/route\.(ts|mts|js|mjs|tsx|jsx)$/, "").replace(/\.(ts|mts|js|mjs|tsx|jsx)$/, "");
|
|
709
|
-
if (!urlPath.startsWith("/")) {
|
|
710
|
-
urlPath = "/" + urlPath;
|
|
711
|
-
}
|
|
712
|
-
return urlPath;
|
|
711
|
+
function isDevMode() {
|
|
712
|
+
return process.env.NODE_ENV === "development";
|
|
713
713
|
}
|
|
714
|
+
var ROUTE_MAPPINGS_KEY = Symbol.for("@vercel/queue.devRouteMappings");
|
|
714
715
|
function filePathToConsumerGroup(filePath) {
|
|
715
|
-
|
|
716
|
+
let result = "";
|
|
717
|
+
for (const char of filePath) {
|
|
718
|
+
if (char === "_") {
|
|
719
|
+
result += "__";
|
|
720
|
+
} else if (char === "/") {
|
|
721
|
+
result += "_S";
|
|
722
|
+
} else if (char === ".") {
|
|
723
|
+
result += "_D";
|
|
724
|
+
} else if (/[A-Za-z0-9-]/.test(char)) {
|
|
725
|
+
result += char;
|
|
726
|
+
} else {
|
|
727
|
+
result += "_" + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0");
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return result;
|
|
716
731
|
}
|
|
717
732
|
function getDevRouteMappings() {
|
|
718
733
|
const g = globalThis;
|
|
@@ -734,13 +749,19 @@ function getDevRouteMappings() {
|
|
|
734
749
|
for (const [filePath, config] of Object.entries(vercelJson.functions)) {
|
|
735
750
|
if (!config.experimentalTriggers) continue;
|
|
736
751
|
for (const trigger of config.experimentalTriggers) {
|
|
737
|
-
if (trigger.type?.startsWith("queue/")
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
topic
|
|
741
|
-
|
|
742
|
-
|
|
752
|
+
if (!trigger.type?.startsWith("queue/") || !trigger.topic) continue;
|
|
753
|
+
if (trigger.type !== "queue/v2beta") {
|
|
754
|
+
console.warn(
|
|
755
|
+
`[Dev Mode] Unsupported trigger type "${trigger.type}" for topic "${trigger.topic}" in ${filePath}. Use "queue/v2beta" instead.`
|
|
756
|
+
);
|
|
757
|
+
continue;
|
|
743
758
|
}
|
|
759
|
+
mappings.push({
|
|
760
|
+
filePath,
|
|
761
|
+
topic: trigger.topic,
|
|
762
|
+
consumer: filePathToConsumerGroup(filePath),
|
|
763
|
+
retryAfterSeconds: trigger.retryAfterSeconds
|
|
764
|
+
});
|
|
744
765
|
}
|
|
745
766
|
}
|
|
746
767
|
g[ROUTE_MAPPINGS_KEY] = mappings.length > 0 ? mappings : null;
|
|
@@ -753,9 +774,7 @@ function getDevRouteMappings() {
|
|
|
753
774
|
}
|
|
754
775
|
function findMatchingRoutes(topicName) {
|
|
755
776
|
const mappings = getDevRouteMappings();
|
|
756
|
-
if (!mappings)
|
|
757
|
-
return [];
|
|
758
|
-
}
|
|
777
|
+
if (!mappings) return [];
|
|
759
778
|
return mappings.filter((mapping) => {
|
|
760
779
|
if (mapping.topic.includes("*")) {
|
|
761
780
|
return matchesWildcardPattern(topicName, mapping.topic);
|
|
@@ -763,149 +782,607 @@ function findMatchingRoutes(topicName) {
|
|
|
763
782
|
return mapping.topic === topicName;
|
|
764
783
|
});
|
|
765
784
|
}
|
|
766
|
-
function
|
|
767
|
-
|
|
785
|
+
function findRetryAfterSeconds(topicName, consumerGroup) {
|
|
786
|
+
const routes = findMatchingRoutes(topicName);
|
|
787
|
+
const route = routes.find((r) => r.consumer === consumerGroup);
|
|
788
|
+
return route?.retryAfterSeconds;
|
|
789
|
+
}
|
|
790
|
+
function stripSrcPrefix(filePath) {
|
|
791
|
+
if (/^src\/(app|pages|server)\//.test(filePath)) {
|
|
792
|
+
return filePath.slice(4);
|
|
793
|
+
}
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
function matchesFunctionsPattern(sourceFile, pattern) {
|
|
797
|
+
return sourceFile === pattern || minimatch(sourceFile, pattern);
|
|
798
|
+
}
|
|
799
|
+
function findMappingsForFile(absolutePath) {
|
|
800
|
+
const mappings = getDevRouteMappings();
|
|
801
|
+
if (!mappings) return [];
|
|
802
|
+
const cwd = process.cwd();
|
|
803
|
+
let relative2;
|
|
804
|
+
try {
|
|
805
|
+
relative2 = path.relative(cwd, absolutePath);
|
|
806
|
+
} catch {
|
|
807
|
+
return [];
|
|
808
|
+
}
|
|
809
|
+
const normalized = relative2.replace(/\\/g, "/");
|
|
810
|
+
const stripped = stripSrcPrefix(normalized);
|
|
811
|
+
return mappings.filter(
|
|
812
|
+
(m) => matchesFunctionsPattern(normalized, m.filePath) || stripped !== null && matchesFunctionsPattern(stripped, m.filePath)
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
function parseFrameFilePath(line) {
|
|
816
|
+
let match = line.match(/\((.+?):\d+:\d+\)/);
|
|
817
|
+
if (!match) match = line.match(/at\s+(.+?):\d+:\d+/);
|
|
818
|
+
if (!match) return null;
|
|
819
|
+
let filePath = match[1].trim();
|
|
820
|
+
if (filePath === "native" || filePath.startsWith("node:") || filePath.startsWith("internal")) {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
if (filePath.startsWith("file://")) {
|
|
824
|
+
try {
|
|
825
|
+
filePath = new URL(filePath).pathname;
|
|
826
|
+
} catch {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(filePath)) {
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
if (filePath.startsWith("./")) {
|
|
834
|
+
filePath = filePath.slice(2);
|
|
835
|
+
}
|
|
836
|
+
return filePath;
|
|
837
|
+
}
|
|
838
|
+
var _sdkPackageDir;
|
|
839
|
+
function getSdkPackageDir() {
|
|
840
|
+
if (_sdkPackageDir) return _sdkPackageDir;
|
|
841
|
+
try {
|
|
842
|
+
const thisDir = typeof __dirname !== "undefined" ? __dirname : path.dirname(new URL(import.meta.url).pathname);
|
|
843
|
+
_sdkPackageDir = path.resolve(thisDir, "..");
|
|
844
|
+
} catch {
|
|
845
|
+
_sdkPackageDir = "";
|
|
846
|
+
}
|
|
847
|
+
return _sdkPackageDir;
|
|
848
|
+
}
|
|
849
|
+
function extractCallerFilePath() {
|
|
850
|
+
const stack = new Error().stack;
|
|
851
|
+
if (!stack) return null;
|
|
852
|
+
const lines = stack.split("\n").slice(1);
|
|
853
|
+
const pkgDir = getSdkPackageDir();
|
|
854
|
+
for (const line of lines) {
|
|
855
|
+
const fp = parseFrameFilePath(line);
|
|
856
|
+
if (!fp) continue;
|
|
857
|
+
const absolute = path.isAbsolute(fp) ? fp : path.resolve(process.cwd(), fp);
|
|
858
|
+
let realFp;
|
|
859
|
+
try {
|
|
860
|
+
realFp = fs.realpathSync(absolute);
|
|
861
|
+
} catch {
|
|
862
|
+
realFp = absolute;
|
|
863
|
+
}
|
|
864
|
+
if (pkgDir && realFp.startsWith(pkgDir)) continue;
|
|
865
|
+
return realFp;
|
|
866
|
+
}
|
|
867
|
+
return null;
|
|
868
|
+
}
|
|
869
|
+
var HANDLER_REGISTRY_KEY = Symbol.for("@vercel/queue.devHandlerRegistry");
|
|
870
|
+
function getHandlerRegistry() {
|
|
871
|
+
const g = globalThis;
|
|
872
|
+
if (!g[HANDLER_REGISTRY_KEY]) {
|
|
873
|
+
g[HANDLER_REGISTRY_KEY] = /* @__PURE__ */ new Map();
|
|
874
|
+
}
|
|
875
|
+
return g[HANDLER_REGISTRY_KEY];
|
|
876
|
+
}
|
|
877
|
+
function registerHandlerForFile(filePath, handler, client, options) {
|
|
878
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
879
|
+
const fileMappings = findMappingsForFile(absolutePath);
|
|
880
|
+
if (fileMappings.length === 0) return false;
|
|
881
|
+
const registry = getHandlerRegistry();
|
|
882
|
+
for (const mapping of fileMappings) {
|
|
883
|
+
const key = mapping.topic;
|
|
884
|
+
const existing = registry.get(key) ?? [];
|
|
885
|
+
const nextEntry = {
|
|
886
|
+
consumerGroup: mapping.consumer,
|
|
887
|
+
handler,
|
|
888
|
+
client,
|
|
889
|
+
options
|
|
890
|
+
};
|
|
891
|
+
const existingIndex = existing.findIndex(
|
|
892
|
+
(e) => e.consumerGroup === mapping.consumer
|
|
893
|
+
);
|
|
894
|
+
if (existingIndex >= 0) {
|
|
895
|
+
existing[existingIndex] = nextEntry;
|
|
896
|
+
} else {
|
|
897
|
+
existing.push(nextEntry);
|
|
898
|
+
}
|
|
899
|
+
registry.set(key, existing);
|
|
900
|
+
}
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
903
|
+
function registerDevHandler(handler, client, options, _testCallerPath) {
|
|
904
|
+
const callerPath = _testCallerPath ?? extractCallerFilePath();
|
|
905
|
+
if (!callerPath) {
|
|
906
|
+
console.warn(
|
|
907
|
+
"[Dev Mode] Could not determine caller file path for handler registration."
|
|
908
|
+
);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
const registered = registerHandlerForFile(
|
|
912
|
+
callerPath,
|
|
913
|
+
handler,
|
|
914
|
+
client,
|
|
915
|
+
options
|
|
916
|
+
);
|
|
917
|
+
if (!registered) {
|
|
918
|
+
const allMappings = getDevRouteMappings();
|
|
919
|
+
if (allMappings && allMappings.length > 0) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
const cwd = process.cwd();
|
|
923
|
+
let relative2;
|
|
924
|
+
try {
|
|
925
|
+
relative2 = path.relative(cwd, callerPath).replace(/\\/g, "/");
|
|
926
|
+
} catch {
|
|
927
|
+
relative2 = callerPath;
|
|
928
|
+
}
|
|
929
|
+
console.warn(
|
|
930
|
+
`[Dev Mode] handleCallback() in ${relative2} has no matching experimentalTriggers in vercel.json. This handler won't receive messages.
|
|
931
|
+
|
|
932
|
+
Add a trigger to vercel.json:
|
|
933
|
+
"${relative2}": {
|
|
934
|
+
"experimentalTriggers": [{ "type": "queue/v2beta", "topic": "your-topic" }]
|
|
935
|
+
}`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
function lookupHandlers(topicName) {
|
|
940
|
+
const registry = getHandlerRegistry();
|
|
941
|
+
const result = [];
|
|
942
|
+
for (const [pattern, handlers] of registry) {
|
|
943
|
+
const matches = pattern.includes("*") ? matchesWildcardPattern(topicName, pattern) : pattern === topicName;
|
|
944
|
+
if (matches) {
|
|
945
|
+
result.push(...handlers);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return result;
|
|
949
|
+
}
|
|
950
|
+
var DEV_RETRY_INITIAL_DELAY_MS = 50;
|
|
951
|
+
var DEV_RETRY_MAX_WAIT_MS = 5e3;
|
|
952
|
+
var DEV_RETRY_BACKOFF = 2;
|
|
953
|
+
var PORT_CHECK_TIMEOUT_MS = 250;
|
|
954
|
+
var PRIME_PORT_ENV_KEYS = [
|
|
955
|
+
"PORT",
|
|
956
|
+
"NEXT_PORT",
|
|
957
|
+
"NEXTJS_PORT",
|
|
958
|
+
"NUXT_PORT",
|
|
959
|
+
"NITRO_PORT",
|
|
960
|
+
"SVELTEKIT_PORT",
|
|
961
|
+
"VITE_PORT",
|
|
962
|
+
"DEV_PORT",
|
|
963
|
+
"npm_config_port"
|
|
964
|
+
];
|
|
965
|
+
var PRIME_URL_ENV_KEYS = [
|
|
966
|
+
"__NEXT_PRIVATE_ORIGIN",
|
|
967
|
+
"NUXT_PUBLIC_SITE_URL",
|
|
968
|
+
"URL"
|
|
969
|
+
];
|
|
970
|
+
function formatErrorReason(error) {
|
|
971
|
+
if (error instanceof Error) {
|
|
972
|
+
return error.message;
|
|
973
|
+
}
|
|
974
|
+
return String(error);
|
|
975
|
+
}
|
|
976
|
+
function isMessageNotFoundError(error) {
|
|
977
|
+
if (error instanceof MessageNotFoundError) {
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
if (error instanceof Error && error.name === "MessageNotFoundError") {
|
|
981
|
+
return true;
|
|
982
|
+
}
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
function parsePort(value) {
|
|
986
|
+
if (!value) return null;
|
|
987
|
+
const parsed = Number.parseInt(value, 10);
|
|
988
|
+
if (!Number.isFinite(parsed) || parsed < 1 || parsed > 65535) return null;
|
|
989
|
+
return parsed;
|
|
990
|
+
}
|
|
991
|
+
function parsePortFromUrl(value) {
|
|
992
|
+
if (!value) return null;
|
|
993
|
+
try {
|
|
994
|
+
const parsed = new URL(value).port;
|
|
995
|
+
return parsePort(parsed);
|
|
996
|
+
} catch {
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
function collectPrimePorts() {
|
|
1001
|
+
const result = [];
|
|
1002
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1003
|
+
const add = (port) => {
|
|
1004
|
+
if (port && !seen.has(port)) {
|
|
1005
|
+
seen.add(port);
|
|
1006
|
+
result.push(port);
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
for (const key of PRIME_PORT_ENV_KEYS) {
|
|
1010
|
+
add(parsePort(process.env[key]));
|
|
1011
|
+
}
|
|
1012
|
+
for (const key of PRIME_URL_ENV_KEYS) {
|
|
1013
|
+
add(parsePortFromUrl(process.env[key]));
|
|
1014
|
+
}
|
|
1015
|
+
return result;
|
|
1016
|
+
}
|
|
1017
|
+
function isPortListening(port) {
|
|
1018
|
+
return new Promise((resolve2) => {
|
|
1019
|
+
const socket = net.connect({ host: "localhost", port });
|
|
1020
|
+
let settled = false;
|
|
1021
|
+
const finish = (listening) => {
|
|
1022
|
+
if (settled) return;
|
|
1023
|
+
settled = true;
|
|
1024
|
+
socket.destroy();
|
|
1025
|
+
resolve2(listening);
|
|
1026
|
+
};
|
|
1027
|
+
socket.once("connect", () => finish(true));
|
|
1028
|
+
socket.once("error", () => finish(false));
|
|
1029
|
+
socket.setTimeout(PORT_CHECK_TIMEOUT_MS, () => finish(false));
|
|
1030
|
+
});
|
|
768
1031
|
}
|
|
769
|
-
|
|
770
|
-
var DEV_VISIBILITY_MAX_WAIT = 5e3;
|
|
771
|
-
var DEV_VISIBILITY_BACKOFF_MULTIPLIER = 2;
|
|
772
|
-
async function waitForMessageVisibility(topicName, consumerGroup, messageId, region) {
|
|
773
|
-
const client = new ApiClient({ region });
|
|
1032
|
+
async function invokeWithRetry(handler, request, options) {
|
|
774
1033
|
let elapsed = 0;
|
|
775
|
-
let
|
|
776
|
-
while (
|
|
1034
|
+
let delay = DEV_RETRY_INITIAL_DELAY_MS;
|
|
1035
|
+
while (true) {
|
|
777
1036
|
try {
|
|
778
|
-
await
|
|
779
|
-
|
|
780
|
-
consumerGroup,
|
|
781
|
-
messageId,
|
|
782
|
-
visibilityTimeoutSeconds: 0
|
|
783
|
-
});
|
|
784
|
-
return true;
|
|
1037
|
+
await handleCallback(handler, request, options);
|
|
1038
|
+
return;
|
|
785
1039
|
} catch (error) {
|
|
786
|
-
if (error
|
|
787
|
-
await new Promise((
|
|
788
|
-
elapsed +=
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1040
|
+
if (isMessageNotFoundError(error) && elapsed < DEV_RETRY_MAX_WAIT_MS) {
|
|
1041
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1042
|
+
elapsed += delay;
|
|
1043
|
+
delay = Math.min(
|
|
1044
|
+
delay * DEV_RETRY_BACKOFF,
|
|
1045
|
+
DEV_RETRY_MAX_WAIT_MS - elapsed
|
|
792
1046
|
);
|
|
793
1047
|
continue;
|
|
794
1048
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1049
|
+
throw error;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
function filePathToUrlPath(filePath) {
|
|
1054
|
+
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)$/, "");
|
|
1055
|
+
if (!urlPath.startsWith("/")) {
|
|
1056
|
+
urlPath = "/" + urlPath;
|
|
1057
|
+
}
|
|
1058
|
+
return urlPath;
|
|
1059
|
+
}
|
|
1060
|
+
async function ensureHandlersLoaded(topicName, options = {}) {
|
|
1061
|
+
const diagnostics = {
|
|
1062
|
+
triedPorts: collectPrimePorts(),
|
|
1063
|
+
listeningPorts: [],
|
|
1064
|
+
unavailablePorts: [],
|
|
1065
|
+
importFailures: [],
|
|
1066
|
+
primeFailures: []
|
|
1067
|
+
};
|
|
1068
|
+
const matchingRoutes = findMatchingRoutes(topicName);
|
|
1069
|
+
if (matchingRoutes.length === 0) return diagnostics;
|
|
1070
|
+
const shouldRefreshRegistered = options.refreshRegistered === true;
|
|
1071
|
+
for (const port of diagnostics.triedPorts) {
|
|
1072
|
+
if (await isPortListening(port)) {
|
|
1073
|
+
diagnostics.listeningPorts.push(port);
|
|
1074
|
+
} else {
|
|
1075
|
+
diagnostics.unavailablePorts.push(port);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
for (const route of matchingRoutes) {
|
|
1079
|
+
const alreadyRegistered = isHandlerRegistered(topicName, route.consumer);
|
|
1080
|
+
if (alreadyRegistered && !shouldRefreshRegistered) {
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
if (!alreadyRegistered) {
|
|
1084
|
+
const absolutePath = path.resolve(process.cwd(), route.filePath);
|
|
1085
|
+
try {
|
|
1086
|
+
await import(absolutePath);
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
diagnostics.importFailures.push({
|
|
1089
|
+
filePath: route.filePath,
|
|
1090
|
+
reason: formatErrorReason(error)
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
if (isHandlerRegistered(topicName, route.consumer)) continue;
|
|
1094
|
+
}
|
|
1095
|
+
for (const port of diagnostics.listeningPorts) {
|
|
1096
|
+
const url = `http://localhost:${port}${filePathToUrlPath(route.filePath)}`;
|
|
1097
|
+
try {
|
|
1098
|
+
const response = await fetch(url, {
|
|
1099
|
+
method: "POST",
|
|
1100
|
+
headers: {
|
|
1101
|
+
"x-vercel-queue-prime": "1",
|
|
1102
|
+
"x-vercel-queue-prime-file": route.filePath
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
try {
|
|
1106
|
+
await response.text();
|
|
1107
|
+
} catch {
|
|
1108
|
+
}
|
|
1109
|
+
if (isHandlerRegistered(topicName, route.consumer)) {
|
|
1110
|
+
break;
|
|
1111
|
+
}
|
|
1112
|
+
diagnostics.primeFailures.push({
|
|
1113
|
+
filePath: route.filePath,
|
|
1114
|
+
url,
|
|
1115
|
+
reason: `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`.trim()
|
|
1116
|
+
});
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
diagnostics.primeFailures.push({
|
|
1119
|
+
filePath: route.filePath,
|
|
1120
|
+
url,
|
|
1121
|
+
reason: formatErrorReason(error)
|
|
1122
|
+
});
|
|
800
1123
|
}
|
|
801
|
-
console.error(
|
|
802
|
-
`[Dev Mode] Error polling for message visibility: topic="${topicName}" messageId="${messageId}"`,
|
|
803
|
-
error
|
|
804
|
-
);
|
|
805
|
-
return false;
|
|
806
1124
|
}
|
|
807
1125
|
}
|
|
808
|
-
|
|
809
|
-
|
|
1126
|
+
return diagnostics;
|
|
1127
|
+
}
|
|
1128
|
+
function buildNoHandlerWarning(topicName, routes, diagnostics) {
|
|
1129
|
+
const files = routes.map((r) => r.filePath);
|
|
1130
|
+
const suggestedPort = diagnostics.listeningPorts[0] ?? diagnostics.triedPorts[0];
|
|
1131
|
+
const suggestedUrls = suggestedPort ? routes.map(
|
|
1132
|
+
(r) => `http://localhost:${suggestedPort}${filePathToUrlPath(r.filePath)}`
|
|
1133
|
+
) : [];
|
|
1134
|
+
let portSummary;
|
|
1135
|
+
if (diagnostics.triedPorts.length === 0) {
|
|
1136
|
+
portSummary = "No local dev port detected from env. Set PORT (or NEXT_PORT/NUXT_PORT/VITE_PORT).";
|
|
1137
|
+
} else if (diagnostics.listeningPorts.length === 0) {
|
|
1138
|
+
portSummary = `Detected env ports: [${diagnostics.triedPorts.join(", ")}], but none are listening.`;
|
|
1139
|
+
} else {
|
|
1140
|
+
const unavailable = diagnostics.unavailablePorts.length > 0 ? ` Not listening: [${diagnostics.unavailablePorts.join(", ")}].` : "";
|
|
1141
|
+
portSummary = `Detected env ports: [${diagnostics.triedPorts.join(", ")}]. Listening: [${diagnostics.listeningPorts.join(", ")}].` + unavailable;
|
|
1142
|
+
}
|
|
1143
|
+
const importSummary = diagnostics.importFailures.length > 0 ? `
|
|
1144
|
+
Import failures: ` + diagnostics.importFailures.slice(0, 2).map((f) => `${f.filePath} (${f.reason})`).join("; ") : "";
|
|
1145
|
+
const primeSummary = diagnostics.primeFailures.length > 0 ? `
|
|
1146
|
+
Prime failures: ` + diagnostics.primeFailures.slice(0, 3).map((f) => `${f.url} (${f.reason})`).join("; ") : "";
|
|
1147
|
+
return `[Dev Mode] No registered handler for topic "${topicName}". vercel.json maps this topic to [${files.join(", ")}] but auto-loading failed.
|
|
1148
|
+
${portSummary}${importSummary}${primeSummary}
|
|
1149
|
+
Ensure your dev server is running, set PORT if needed, and confirm mapped route files call handleCallback()/handleNodeCallback() at module scope.
|
|
1150
|
+
` + (suggestedUrls.length > 0 ? `Try opening: ${suggestedUrls.join(" or ")}` : "Set PORT (or NEXT_PORT/NUXT_PORT/VITE_PORT) and try sending again.");
|
|
1151
|
+
}
|
|
1152
|
+
function isHandlerRegistered(topicName, consumerGroup) {
|
|
1153
|
+
return lookupHandlers(topicName).some(
|
|
1154
|
+
(h) => h.consumerGroup === consumerGroup
|
|
810
1155
|
);
|
|
811
|
-
return false;
|
|
812
1156
|
}
|
|
813
|
-
|
|
1157
|
+
var DEV_REDELIVERY_MAX_DELAY_S = 10;
|
|
1158
|
+
var DEV_REDELIVERY_DEFAULT_DELAY_S = 2;
|
|
1159
|
+
var DEV_REDELIVERY_MAX_ATTEMPTS = 10;
|
|
1160
|
+
var DEFAULT_RETENTION_S = 86400;
|
|
1161
|
+
function scheduleDevRedelivery(ctx, delayS) {
|
|
1162
|
+
const cappedDelay = Math.min(Math.max(delayS, 0), DEV_REDELIVERY_MAX_DELAY_S);
|
|
1163
|
+
console.log(
|
|
1164
|
+
`[Dev Mode] \u21BB Scheduling re-delivery in ${cappedDelay}s: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
|
|
1165
|
+
);
|
|
1166
|
+
setTimeout(async () => {
|
|
1167
|
+
const nextDeliveryCount = ctx.deliveryCount + 1;
|
|
1168
|
+
const expiresAt = new Date(
|
|
1169
|
+
ctx.createdAt.getTime() + ctx.retentionSeconds * 1e3
|
|
1170
|
+
);
|
|
1171
|
+
if (Date.now() >= expiresAt.getTime()) {
|
|
1172
|
+
console.log(
|
|
1173
|
+
`[Dev Mode] Message expired, stopping retries: topic="${ctx.topicName}" messageId="${ctx.messageId}"`
|
|
1174
|
+
);
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
if (nextDeliveryCount > DEV_REDELIVERY_MAX_ATTEMPTS) {
|
|
1178
|
+
console.log(
|
|
1179
|
+
`[Dev Mode] Max re-deliveries (${DEV_REDELIVERY_MAX_ATTEMPTS}) reached: topic="${ctx.topicName}" messageId="${ctx.messageId}"`
|
|
1180
|
+
);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
const metadata = {
|
|
1184
|
+
messageId: ctx.messageId,
|
|
1185
|
+
deliveryCount: nextDeliveryCount,
|
|
1186
|
+
createdAt: ctx.createdAt,
|
|
1187
|
+
expiresAt,
|
|
1188
|
+
topicName: ctx.topicName,
|
|
1189
|
+
consumerGroup: ctx.consumerGroup,
|
|
1190
|
+
region: ctx.region
|
|
1191
|
+
};
|
|
1192
|
+
console.log(
|
|
1193
|
+
`[Dev Mode] Re-delivering: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}" deliveryCount=${nextDeliveryCount}`
|
|
1194
|
+
);
|
|
1195
|
+
let succeeded = true;
|
|
1196
|
+
let nextRetryAfterS = null;
|
|
1197
|
+
let nextAcknowledged = false;
|
|
1198
|
+
try {
|
|
1199
|
+
await ctx.handler(ctx.payload, metadata);
|
|
1200
|
+
} catch (error) {
|
|
1201
|
+
succeeded = false;
|
|
1202
|
+
if (ctx.retry) {
|
|
1203
|
+
let directive;
|
|
1204
|
+
try {
|
|
1205
|
+
directive = ctx.retry(error, metadata);
|
|
1206
|
+
} catch (retryErr) {
|
|
1207
|
+
console.warn("[Dev Mode] retry handler threw:", retryErr);
|
|
1208
|
+
}
|
|
1209
|
+
if (directive && "afterSeconds" in directive) {
|
|
1210
|
+
nextRetryAfterS = directive.afterSeconds;
|
|
1211
|
+
} else if (directive && "acknowledge" in directive) {
|
|
1212
|
+
nextAcknowledged = true;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
if (!nextAcknowledged) {
|
|
1216
|
+
console.error(
|
|
1217
|
+
`[Dev Mode] \u2717 Handler error on re-delivery: topic="${ctx.topicName}" messageId="${ctx.messageId}"`,
|
|
1218
|
+
error
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (succeeded) {
|
|
1223
|
+
console.log(
|
|
1224
|
+
`[Dev Mode] \u2713 Message processed on re-delivery: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
|
|
1225
|
+
);
|
|
1226
|
+
} else if (nextAcknowledged) {
|
|
1227
|
+
console.log(
|
|
1228
|
+
`[Dev Mode] \u2713 Message acknowledged (will not retry): topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
|
|
1229
|
+
);
|
|
1230
|
+
} else {
|
|
1231
|
+
const nextDelay = nextRetryAfterS ?? ctx.defaultRetryDelayS;
|
|
1232
|
+
scheduleDevRedelivery(
|
|
1233
|
+
{ ...ctx, deliveryCount: nextDeliveryCount },
|
|
1234
|
+
nextDelay
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
}, cappedDelay * 1e3);
|
|
1238
|
+
}
|
|
1239
|
+
function invokeDevHandlers(topicName, messageId, region, delaySeconds, retentionSeconds) {
|
|
814
1240
|
if (delaySeconds && delaySeconds > 0) {
|
|
815
1241
|
console.log(
|
|
816
1242
|
`[Dev Mode] Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
|
|
817
1243
|
);
|
|
818
1244
|
setTimeout(() => {
|
|
819
|
-
|
|
1245
|
+
invokeDevHandlers(
|
|
1246
|
+
topicName,
|
|
1247
|
+
messageId,
|
|
1248
|
+
region,
|
|
1249
|
+
void 0,
|
|
1250
|
+
retentionSeconds
|
|
1251
|
+
);
|
|
820
1252
|
}, delaySeconds * 1e3);
|
|
821
1253
|
return;
|
|
822
1254
|
}
|
|
823
1255
|
console.log(
|
|
824
1256
|
`[Dev Mode] Message sent: topic="${topicName}" messageId="${messageId}"`
|
|
825
1257
|
);
|
|
826
|
-
const matchingRoutes = findMatchingRoutes(topicName);
|
|
827
|
-
if (matchingRoutes.length === 0) {
|
|
828
|
-
console.log(
|
|
829
|
-
`[Dev Mode] No matching routes in vercel.json for topic "${topicName}"`
|
|
830
|
-
);
|
|
831
|
-
return;
|
|
832
|
-
}
|
|
833
|
-
const consumerGroups = matchingRoutes.map((r) => r.consumer);
|
|
834
|
-
console.log(
|
|
835
|
-
`[Dev Mode] Scheduling callbacks for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
|
|
836
|
-
);
|
|
837
1258
|
(async () => {
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
);
|
|
1259
|
+
let handlers = lookupHandlers(topicName);
|
|
1260
|
+
let diagnostics = null;
|
|
1261
|
+
if (handlers.length > 0) {
|
|
1262
|
+
await ensureHandlersLoaded(topicName, { refreshRegistered: true });
|
|
1263
|
+
handlers = lookupHandlers(topicName);
|
|
1264
|
+
} else {
|
|
1265
|
+
diagnostics = await ensureHandlersLoaded(topicName);
|
|
1266
|
+
handlers = lookupHandlers(topicName);
|
|
1267
|
+
}
|
|
1268
|
+
if (handlers.length === 0) {
|
|
1269
|
+
const matchingRoutes = findMatchingRoutes(topicName);
|
|
1270
|
+
if (matchingRoutes.length > 0) {
|
|
1271
|
+
const safeDiagnostics = diagnostics ?? {
|
|
1272
|
+
triedPorts: collectPrimePorts(),
|
|
1273
|
+
listeningPorts: [],
|
|
1274
|
+
unavailablePorts: [],
|
|
1275
|
+
importFailures: [],
|
|
1276
|
+
primeFailures: []
|
|
1277
|
+
};
|
|
1278
|
+
console.warn(
|
|
1279
|
+
buildNoHandlerWarning(topicName, matchingRoutes, safeDiagnostics)
|
|
1280
|
+
);
|
|
1281
|
+
} else {
|
|
1282
|
+
console.warn(
|
|
1283
|
+
`[Dev Mode] No registered handler for topic "${topicName}".
|
|
1284
|
+
Ensure vercel.json has a matching experimentalTriggers entry and the route file calls handleCallback().`
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
849
1287
|
return;
|
|
850
1288
|
}
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1289
|
+
const consumerGroups = handlers.map((h) => h.consumerGroup);
|
|
1290
|
+
console.log(
|
|
1291
|
+
`[Dev Mode] Invoking handlers for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
|
|
1292
|
+
);
|
|
1293
|
+
const effectiveRetention = retentionSeconds ?? DEFAULT_RETENTION_S;
|
|
1294
|
+
for (const entry of handlers) {
|
|
1295
|
+
let capturedPayload;
|
|
1296
|
+
let capturedCreatedAt = /* @__PURE__ */ new Date();
|
|
1297
|
+
let capturedDeliveryCount = 1;
|
|
1298
|
+
let handlerSucceeded = true;
|
|
1299
|
+
let retryAfterS = null;
|
|
1300
|
+
let retryAcknowledged = false;
|
|
1301
|
+
const wrappedHandler = async (message, metadata) => {
|
|
1302
|
+
capturedPayload = message;
|
|
1303
|
+
capturedCreatedAt = metadata.createdAt;
|
|
1304
|
+
capturedDeliveryCount = metadata.deliveryCount;
|
|
1305
|
+
try {
|
|
1306
|
+
await entry.handler(message, metadata);
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
handlerSucceeded = false;
|
|
1309
|
+
throw error;
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
const wrappedRetry = entry.options?.retry ? (error, metadata) => {
|
|
1313
|
+
const directive = entry.options.retry(error, metadata);
|
|
1314
|
+
if (directive && "afterSeconds" in directive) {
|
|
1315
|
+
retryAfterS = directive.afterSeconds;
|
|
1316
|
+
} else if (directive && "acknowledge" in directive) {
|
|
1317
|
+
retryAcknowledged = true;
|
|
1318
|
+
}
|
|
1319
|
+
return directive;
|
|
1320
|
+
} : void 0;
|
|
1321
|
+
const request = {
|
|
1322
|
+
queueName: topicName,
|
|
1323
|
+
consumerGroup: entry.consumerGroup,
|
|
1324
|
+
messageId,
|
|
1325
|
+
region
|
|
1326
|
+
};
|
|
1327
|
+
const callbackOptions = {
|
|
1328
|
+
client: entry.client,
|
|
1329
|
+
visibilityTimeoutSeconds: entry.options?.visibilityTimeoutSeconds,
|
|
1330
|
+
retry: wrappedRetry
|
|
1331
|
+
};
|
|
1332
|
+
const consumerDefaultDelay = Math.min(
|
|
1333
|
+
findRetryAfterSeconds(topicName, entry.consumerGroup) ?? DEV_REDELIVERY_DEFAULT_DELAY_S,
|
|
1334
|
+
DEV_REDELIVERY_MAX_DELAY_S
|
|
857
1335
|
);
|
|
1336
|
+
const buildRedeliveryCtx = () => ({
|
|
1337
|
+
handler: entry.handler,
|
|
1338
|
+
retry: entry.options?.retry,
|
|
1339
|
+
payload: capturedPayload,
|
|
1340
|
+
topicName,
|
|
1341
|
+
consumerGroup: entry.consumerGroup,
|
|
1342
|
+
messageId,
|
|
1343
|
+
region,
|
|
1344
|
+
createdAt: capturedCreatedAt,
|
|
1345
|
+
retentionSeconds: effectiveRetention,
|
|
1346
|
+
deliveryCount: capturedDeliveryCount,
|
|
1347
|
+
defaultRetryDelayS: consumerDefaultDelay
|
|
1348
|
+
});
|
|
858
1349
|
try {
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
"
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
"
|
|
867
|
-
|
|
868
|
-
})
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
const responseData = await response.json();
|
|
872
|
-
if (responseData.status === "success") {
|
|
873
|
-
console.log(
|
|
874
|
-
`[Dev Mode] \u2713 Message processed successfully: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}"`
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
} catch {
|
|
878
|
-
console.warn(
|
|
879
|
-
`[Dev Mode] Handler returned OK but response was not JSON: topic="${topicName}" consumer="${route.consumer}"`
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
} else {
|
|
883
|
-
try {
|
|
884
|
-
const errorData = await response.json();
|
|
885
|
-
console.error(
|
|
886
|
-
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" error="${errorData.error || response.statusText}"`
|
|
887
|
-
);
|
|
888
|
-
} catch {
|
|
889
|
-
console.error(
|
|
890
|
-
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${route.consumer}" messageId="${messageId}" status=${response.status}`
|
|
891
|
-
);
|
|
892
|
-
}
|
|
1350
|
+
await invokeWithRetry(wrappedHandler, request, callbackOptions);
|
|
1351
|
+
if (handlerSucceeded) {
|
|
1352
|
+
console.log(
|
|
1353
|
+
`[Dev Mode] \u2713 Message processed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`
|
|
1354
|
+
);
|
|
1355
|
+
} else if (retryAcknowledged) {
|
|
1356
|
+
console.log(
|
|
1357
|
+
`[Dev Mode] \u2713 Message acknowledged (will not retry): topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`
|
|
1358
|
+
);
|
|
1359
|
+
} else if (retryAfterS !== null) {
|
|
1360
|
+
const devDelay = Math.min(retryAfterS, DEV_REDELIVERY_MAX_DELAY_S);
|
|
1361
|
+
scheduleDevRedelivery(buildRedeliveryCtx(), devDelay);
|
|
893
1362
|
}
|
|
894
1363
|
} catch (error) {
|
|
895
1364
|
console.error(
|
|
896
|
-
`[Dev Mode] \u2717
|
|
1365
|
+
`[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`,
|
|
897
1366
|
error
|
|
898
1367
|
);
|
|
1368
|
+
if (!handlerSucceeded) {
|
|
1369
|
+
scheduleDevRedelivery(buildRedeliveryCtx(), consumerDefaultDelay);
|
|
1370
|
+
}
|
|
899
1371
|
}
|
|
900
1372
|
}
|
|
901
1373
|
})();
|
|
902
1374
|
}
|
|
903
|
-
function
|
|
1375
|
+
function clearDevState() {
|
|
904
1376
|
const g = globalThis;
|
|
905
1377
|
delete g[ROUTE_MAPPINGS_KEY];
|
|
1378
|
+
delete g[HANDLER_REGISTRY_KEY];
|
|
906
1379
|
}
|
|
907
1380
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
908
|
-
globalThis.
|
|
1381
|
+
globalThis.__clearDevState = clearDevState;
|
|
1382
|
+
globalThis.__filePathToConsumerGroup = filePathToConsumerGroup;
|
|
1383
|
+
globalThis.__filePathToUrlPath = filePathToUrlPath;
|
|
1384
|
+
globalThis.__matchesFunctionsPattern = matchesFunctionsPattern;
|
|
1385
|
+
globalThis.__stripSrcPrefix = stripSrcPrefix;
|
|
909
1386
|
}
|
|
910
1387
|
|
|
911
1388
|
// src/oidc.ts
|
|
@@ -949,6 +1426,7 @@ function parseQueueHeaders(headers) {
|
|
|
949
1426
|
const timestamp = headers.get("Vqs-Timestamp");
|
|
950
1427
|
const contentType = headers.get("Content-Type") || "application/octet-stream";
|
|
951
1428
|
const receiptHandle = headers.get("Vqs-Receipt-Handle");
|
|
1429
|
+
const expiresAtStr = headers.get("Vqs-Expires-At");
|
|
952
1430
|
if (!messageId || !timestamp || !receiptHandle) {
|
|
953
1431
|
return null;
|
|
954
1432
|
}
|
|
@@ -960,6 +1438,7 @@ function parseQueueHeaders(headers) {
|
|
|
960
1438
|
messageId,
|
|
961
1439
|
deliveryCount,
|
|
962
1440
|
createdAt: new Date(timestamp),
|
|
1441
|
+
expiresAt: expiresAtStr ? new Date(expiresAtStr) : void 0,
|
|
963
1442
|
contentType,
|
|
964
1443
|
receiptHandle
|
|
965
1444
|
};
|
|
@@ -1080,7 +1559,7 @@ var ApiClient = class _ApiClient {
|
|
|
1080
1559
|
}
|
|
1081
1560
|
console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
|
|
1082
1561
|
}
|
|
1083
|
-
init.headers.set("User-Agent", `@vercel/queue/${"0.1.
|
|
1562
|
+
init.headers.set("User-Agent", `@vercel/queue/${"0.1.2"}`);
|
|
1084
1563
|
init.headers.set("Vqs-Client-Ts", (/* @__PURE__ */ new Date()).toISOString());
|
|
1085
1564
|
const response = await fetch(url, init);
|
|
1086
1565
|
if (isDebugEnabled()) {
|
|
@@ -1416,12 +1895,36 @@ var ApiClient = class _ApiClient {
|
|
|
1416
1895
|
|
|
1417
1896
|
// src/client.ts
|
|
1418
1897
|
var apiClients = /* @__PURE__ */ new WeakMap();
|
|
1898
|
+
var API_CLIENT_KEY = Symbol.for("@vercel/queue.apiClient");
|
|
1899
|
+
function setApi(client, api) {
|
|
1900
|
+
apiClients.set(client, api);
|
|
1901
|
+
Object.defineProperty(client, API_CLIENT_KEY, {
|
|
1902
|
+
value: api,
|
|
1903
|
+
writable: false,
|
|
1904
|
+
enumerable: false,
|
|
1905
|
+
configurable: false
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1419
1908
|
function getApi(client) {
|
|
1420
1909
|
const api = apiClients.get(client);
|
|
1421
|
-
if (
|
|
1422
|
-
|
|
1910
|
+
if (api) {
|
|
1911
|
+
return api;
|
|
1423
1912
|
}
|
|
1424
|
-
|
|
1913
|
+
const apiFromSymbol = client[API_CLIENT_KEY];
|
|
1914
|
+
if (typeof apiFromSymbol === "object" && apiFromSymbol !== null) {
|
|
1915
|
+
const resolvedApi = apiFromSymbol;
|
|
1916
|
+
apiClients.set(client, resolvedApi);
|
|
1917
|
+
return resolvedApi;
|
|
1918
|
+
}
|
|
1919
|
+
throw new Error(
|
|
1920
|
+
"QueueClient not initialized. This may happen when multiple bundled copies of @vercel/queue are loaded in local dev."
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
function resolveCallbackRequest(input) {
|
|
1924
|
+
if ("request" in input) {
|
|
1925
|
+
return input.request;
|
|
1926
|
+
}
|
|
1927
|
+
return input;
|
|
1425
1928
|
}
|
|
1426
1929
|
function getApiClient(client) {
|
|
1427
1930
|
return getApi(client);
|
|
@@ -1431,15 +1934,17 @@ function resolveRegion(region) {
|
|
|
1431
1934
|
if (region) return region;
|
|
1432
1935
|
const fromEnv = process.env.VERCEL_REGION;
|
|
1433
1936
|
if (fromEnv) return fromEnv;
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1937
|
+
if (!isDevMode()) {
|
|
1938
|
+
console.warn(
|
|
1939
|
+
`[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" })`
|
|
1940
|
+
);
|
|
1941
|
+
}
|
|
1437
1942
|
return DEFAULT_REGION;
|
|
1438
1943
|
}
|
|
1439
1944
|
var QueueClient = class {
|
|
1440
1945
|
constructor(options = {}) {
|
|
1441
1946
|
const region = resolveRegion(options.region);
|
|
1442
|
-
|
|
1947
|
+
setApi(this, new ApiClient({ ...options, region }));
|
|
1443
1948
|
}
|
|
1444
1949
|
/**
|
|
1445
1950
|
* Send a message to a topic.
|
|
@@ -1467,11 +1972,12 @@ var QueueClient = class {
|
|
|
1467
1972
|
headers: options?.headers
|
|
1468
1973
|
});
|
|
1469
1974
|
if (result.messageId && isDevMode()) {
|
|
1470
|
-
|
|
1975
|
+
invokeDevHandlers(
|
|
1471
1976
|
topicName,
|
|
1472
1977
|
result.messageId,
|
|
1473
1978
|
api.getRegion(),
|
|
1474
|
-
options?.delaySeconds
|
|
1979
|
+
options?.delaySeconds,
|
|
1980
|
+
options?.retentionSeconds
|
|
1475
1981
|
);
|
|
1476
1982
|
}
|
|
1477
1983
|
return { messageId: result.messageId };
|
|
@@ -1493,10 +1999,26 @@ var QueueClient = class {
|
|
|
1493
1999
|
* @param options.visibilityTimeoutSeconds - Message lock duration (default: 300, max: 3600)
|
|
1494
2000
|
* @param options.retry - Called when the handler throws. Return `{ afterSeconds: N }` to
|
|
1495
2001
|
* reschedule the message for redelivery after N seconds.
|
|
1496
|
-
* @returns A `
|
|
2002
|
+
* @returns A route handler that accepts either `Request` or `{ request: Request }`
|
|
1497
2003
|
*/
|
|
1498
2004
|
handleCallback = (handler, options) => {
|
|
1499
|
-
|
|
2005
|
+
if (isDevMode()) {
|
|
2006
|
+
registerDevHandler(handler, this, options);
|
|
2007
|
+
}
|
|
2008
|
+
return async (requestOrEvent) => {
|
|
2009
|
+
const request = resolveCallbackRequest(requestOrEvent);
|
|
2010
|
+
if (isDevMode() && request.headers.get("x-vercel-queue-prime") === "1") {
|
|
2011
|
+
const primeFile = request.headers.get("x-vercel-queue-prime-file");
|
|
2012
|
+
if (primeFile) {
|
|
2013
|
+
registerDevHandler(
|
|
2014
|
+
handler,
|
|
2015
|
+
this,
|
|
2016
|
+
options,
|
|
2017
|
+
primeFile
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
return Response.json({ status: "primed" });
|
|
2021
|
+
}
|
|
1500
2022
|
try {
|
|
1501
2023
|
const parsed = await parseCallback(request);
|
|
1502
2024
|
await handleCallback(handler, parsed, {
|
|
@@ -1538,11 +2060,29 @@ var QueueClient = class {
|
|
|
1538
2060
|
* @returns A `(req, res) => Promise<void>` route handler
|
|
1539
2061
|
*/
|
|
1540
2062
|
handleNodeCallback = (handler, options) => {
|
|
2063
|
+
if (isDevMode()) {
|
|
2064
|
+
registerDevHandler(handler, this, options);
|
|
2065
|
+
}
|
|
1541
2066
|
return async (req, res) => {
|
|
1542
2067
|
if (req.method !== "POST") {
|
|
1543
2068
|
res.status(200).end();
|
|
1544
2069
|
return;
|
|
1545
2070
|
}
|
|
2071
|
+
const primeHeader = req.headers["x-vercel-queue-prime"];
|
|
2072
|
+
if (isDevMode() && primeHeader === "1") {
|
|
2073
|
+
const primeFileHeader = req.headers["x-vercel-queue-prime-file"];
|
|
2074
|
+
const primeFile = Array.isArray(primeFileHeader) ? primeFileHeader[0] : primeFileHeader;
|
|
2075
|
+
if (primeFile) {
|
|
2076
|
+
registerDevHandler(
|
|
2077
|
+
handler,
|
|
2078
|
+
this,
|
|
2079
|
+
options,
|
|
2080
|
+
primeFile
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
res.status(200).json({ status: "primed" });
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
1546
2086
|
try {
|
|
1547
2087
|
const parsed = parseRawCallback(req.body, req.headers);
|
|
1548
2088
|
await handleCallback(handler, parsed, {
|
|
@@ -1564,7 +2104,7 @@ var QueueClient = class {
|
|
|
1564
2104
|
};
|
|
1565
2105
|
var PollingQueueClient = class {
|
|
1566
2106
|
constructor(options) {
|
|
1567
|
-
|
|
2107
|
+
setApi(this, new ApiClient(options));
|
|
1568
2108
|
}
|
|
1569
2109
|
/**
|
|
1570
2110
|
* Send a message to a topic.
|
|
@@ -1663,6 +2203,25 @@ var PollingQueueClient = class {
|
|
|
1663
2203
|
}
|
|
1664
2204
|
};
|
|
1665
2205
|
};
|
|
2206
|
+
|
|
2207
|
+
// src/default-client.ts
|
|
2208
|
+
var _defaultClient;
|
|
2209
|
+
function getDefaultClient() {
|
|
2210
|
+
if (!_defaultClient) {
|
|
2211
|
+
_defaultClient = new QueueClient();
|
|
2212
|
+
}
|
|
2213
|
+
return _defaultClient;
|
|
2214
|
+
}
|
|
2215
|
+
function resolveClient(region) {
|
|
2216
|
+
if (!region) return getDefaultClient();
|
|
2217
|
+
return new QueueClient({ region });
|
|
2218
|
+
}
|
|
2219
|
+
async function send(topicName, payload, options) {
|
|
2220
|
+
return resolveClient(options?.region).send(topicName, payload, options);
|
|
2221
|
+
}
|
|
2222
|
+
function handleCallback2(handler, options) {
|
|
2223
|
+
return getDefaultClient().handleCallback(handler, options);
|
|
2224
|
+
}
|
|
1666
2225
|
export {
|
|
1667
2226
|
BadRequestError,
|
|
1668
2227
|
BufferTransport,
|
|
@@ -1685,7 +2244,9 @@ export {
|
|
|
1685
2244
|
QueueEmptyError,
|
|
1686
2245
|
StreamTransport,
|
|
1687
2246
|
UnauthorizedError,
|
|
2247
|
+
handleCallback2 as handleCallback,
|
|
1688
2248
|
parseCallback,
|
|
1689
|
-
parseRawCallback
|
|
2249
|
+
parseRawCallback,
|
|
2250
|
+
send
|
|
1690
2251
|
};
|
|
1691
2252
|
//# sourceMappingURL=index.mjs.map
|