run402-mcp 3.7.2 → 3.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/sdk/README.md +1 -1
- package/sdk/dist/config.d.ts +15 -1
- package/sdk/dist/config.d.ts.map +1 -1
- package/sdk/dist/config.js +49 -0
- package/sdk/dist/config.js.map +1 -1
- package/sdk/dist/index.d.ts +1 -1
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +1 -1
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.js +153 -15
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +31 -2
- package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.types.js.map +1 -1
- package/sdk/dist/node/config.d.ts +2 -2
- package/sdk/dist/node/config.d.ts.map +1 -1
- package/sdk/dist/node/config.js +1 -1
- package/sdk/dist/node/config.js.map +1 -1
- package/sdk/dist/node/deploy-manifest.d.ts.map +1 -1
- package/sdk/dist/node/deploy-manifest.js +3 -0
- package/sdk/dist/node/deploy-manifest.js.map +1 -1
- package/sdk/dist/node/index.d.ts +2 -2
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +1 -1
- package/sdk/dist/node/index.js.map +1 -1
|
@@ -38,6 +38,7 @@ const SECRET_KEY_RE = /^[A-Z_][A-Z0-9_]{0,127}$/;
|
|
|
38
38
|
const APPLY_SAFE_RETRY_CODES = new Set([
|
|
39
39
|
"BASE_RELEASE_CONFLICT",
|
|
40
40
|
]);
|
|
41
|
+
const EMAIL_TRIGGER_EVENTS = new Set(["reply_received", "delivery", "bounced", "complained"]);
|
|
41
42
|
const STATIC_ACTIVATION_FAILURE_CODES = new Set([
|
|
42
43
|
"BAD_FIELD",
|
|
43
44
|
"INVALID_SPEC",
|
|
@@ -718,6 +719,7 @@ function functionToCoreSpec(fn) {
|
|
|
718
719
|
...(fn.entrypoint !== undefined ? { entrypoint: fn.entrypoint } : {}),
|
|
719
720
|
...(fn.config !== undefined ? { config: fn.config } : {}),
|
|
720
721
|
...(fn.deps !== undefined ? { deps: fn.deps } : {}),
|
|
722
|
+
...(fn.triggers !== undefined ? { triggers: fn.triggers } : {}),
|
|
721
723
|
...(fn.schedule !== undefined ? { schedule: fn.schedule } : {}),
|
|
722
724
|
...(fn.requireAuth !== undefined ? { requireAuth: fn.requireAuth } : {}),
|
|
723
725
|
...(fn.requireRole !== undefined ? { requireRole: fn.requireRole } : {}),
|
|
@@ -740,6 +742,7 @@ function functionToWire(fn) {
|
|
|
740
742
|
}
|
|
741
743
|
: {}),
|
|
742
744
|
...(fn.deps !== undefined ? { deps: fn.deps } : {}),
|
|
745
|
+
...(fn.triggers !== undefined ? { triggers: fn.triggers } : {}),
|
|
743
746
|
...(fn.schedule !== undefined ? { schedule: fn.schedule } : {}),
|
|
744
747
|
...(fn.requireAuth !== undefined ? { require_auth: fn.requireAuth } : {}),
|
|
745
748
|
...(fn.requireRole !== undefined
|
|
@@ -1016,11 +1019,14 @@ async function preflightTierFunctionLimits(client, spec, ciCredentials) {
|
|
|
1016
1019
|
max_function_memory_mb: limits.maxMemoryMb.value,
|
|
1017
1020
|
});
|
|
1018
1021
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1022
|
+
for (const trigger of scheduleTriggersForFunction(entry.fn)) {
|
|
1023
|
+
if (!limits.minCronIntervalMinutes)
|
|
1024
|
+
continue;
|
|
1025
|
+
const intervalMinutes = estimateCronMinimumIntervalMinutes(trigger.cron);
|
|
1021
1026
|
if (intervalMinutes !== null &&
|
|
1022
1027
|
intervalMinutes < limits.minCronIntervalMinutes.value) {
|
|
1023
|
-
|
|
1028
|
+
const triggerLabel = trigger.id === "__legacy__" ? "schedule" : `trigger ${trigger.id}`;
|
|
1029
|
+
throw tierLimitError(`Function ${entry.name} ${triggerLabel} runs every ${intervalMinutes} minute(s), below the ${limits.tier} tier minimum interval of ${limits.minCronIntervalMinutes.value} minutes.`, `${entry.fieldPrefix}.${trigger.fieldSuffix}`, trigger.cron, limits, limits.minCronIntervalMinutes, {
|
|
1024
1030
|
interval_minutes: intervalMinutes,
|
|
1025
1031
|
min_interval_minutes: limits.minCronIntervalMinutes.value,
|
|
1026
1032
|
min_cron_interval_minutes: limits.minCronIntervalMinutes.value,
|
|
@@ -1044,7 +1050,7 @@ function hasFunctionTierPreflightInputs(functions) {
|
|
|
1044
1050
|
return false;
|
|
1045
1051
|
return collectFunctionPreflightEntries(functions).some((entry) => (entry.fn.config?.timeoutSeconds !== undefined ||
|
|
1046
1052
|
entry.fn.config?.memoryMb !== undefined ||
|
|
1047
|
-
|
|
1053
|
+
scheduleTriggersForFunction(entry.fn).length > 0));
|
|
1048
1054
|
}
|
|
1049
1055
|
function collectFunctionPreflightEntries(functions) {
|
|
1050
1056
|
if (!functions)
|
|
@@ -1160,23 +1166,21 @@ async function computeDesiredScheduledFunctionCount(client, spec) {
|
|
|
1160
1166
|
function countScheduledFunctionsInSetEntries(functions) {
|
|
1161
1167
|
if (!functions)
|
|
1162
1168
|
return 0;
|
|
1163
|
-
|
|
1164
|
-
for (const
|
|
1165
|
-
|
|
1166
|
-
names.add(name);
|
|
1169
|
+
let count = 0;
|
|
1170
|
+
for (const fn of Object.values(functions.replace ?? {})) {
|
|
1171
|
+
count += scheduleTriggersForFunction(fn).length;
|
|
1167
1172
|
}
|
|
1168
|
-
for (const
|
|
1169
|
-
|
|
1170
|
-
names.add(name);
|
|
1173
|
+
for (const fn of Object.values(functions.patch?.set ?? {})) {
|
|
1174
|
+
count += scheduleTriggersForFunction(fn).length;
|
|
1171
1175
|
}
|
|
1172
|
-
return
|
|
1176
|
+
return count;
|
|
1173
1177
|
}
|
|
1174
1178
|
function scheduledFunctionNames(functions) {
|
|
1175
1179
|
if (!functions)
|
|
1176
1180
|
return null;
|
|
1177
1181
|
const scheduled = new Set();
|
|
1178
1182
|
for (const [name, fn] of Object.entries(functions)) {
|
|
1179
|
-
if (
|
|
1183
|
+
if (scheduleTriggersForFunction(fn).length > 0)
|
|
1180
1184
|
scheduled.add(name);
|
|
1181
1185
|
}
|
|
1182
1186
|
return scheduled;
|
|
@@ -1188,7 +1192,7 @@ function applyScheduledFunctionPatch(scheduled, patch) {
|
|
|
1188
1192
|
for (const [name, fn] of Object.entries(patch?.set ?? {})) {
|
|
1189
1193
|
if (fn.schedule === null)
|
|
1190
1194
|
scheduled.delete(name);
|
|
1191
|
-
else if (
|
|
1195
|
+
else if (scheduleTriggersForFunction(fn).length > 0)
|
|
1192
1196
|
scheduled.add(name);
|
|
1193
1197
|
}
|
|
1194
1198
|
}
|
|
@@ -1205,11 +1209,33 @@ async function readActiveScheduledFunctionNames(client, projectId) {
|
|
|
1205
1209
|
}
|
|
1206
1210
|
const scheduled = new Set();
|
|
1207
1211
|
for (const fn of inventory.functions ?? []) {
|
|
1208
|
-
if (isScheduledCron(fn.schedule))
|
|
1212
|
+
if (isScheduledCron(fn.schedule) || scheduleTriggersForFunction(fn).length > 0)
|
|
1209
1213
|
scheduled.add(fn.name);
|
|
1210
1214
|
}
|
|
1211
1215
|
return scheduled;
|
|
1212
1216
|
}
|
|
1217
|
+
function scheduleTriggersForFunction(fn) {
|
|
1218
|
+
if (fn.triggers && fn.triggers.length > 0) {
|
|
1219
|
+
return fn.triggers
|
|
1220
|
+
.filter((trigger) => trigger.type === "schedule")
|
|
1221
|
+
.map((trigger) => ({
|
|
1222
|
+
...trigger,
|
|
1223
|
+
fieldSuffix: `triggers.${trigger.id}.cron`,
|
|
1224
|
+
}));
|
|
1225
|
+
}
|
|
1226
|
+
return isScheduledCron(fn.schedule)
|
|
1227
|
+
? [{
|
|
1228
|
+
id: "__legacy__",
|
|
1229
|
+
type: "schedule",
|
|
1230
|
+
cron: fn.schedule,
|
|
1231
|
+
timezone: "UTC",
|
|
1232
|
+
misfire_policy: "skip",
|
|
1233
|
+
overlap_policy: "allow",
|
|
1234
|
+
run: { event_type: "legacy.schedule", payload: {} },
|
|
1235
|
+
fieldSuffix: "schedule",
|
|
1236
|
+
}]
|
|
1237
|
+
: [];
|
|
1238
|
+
}
|
|
1213
1239
|
function scheduledCountTierLimitError(count, limits, limit, countSource) {
|
|
1214
1240
|
return tierLimitError(`Deploy would have ${count} scheduled function(s), exceeding the ${limits.tier} tier maximum of ${limit.value}.`, "functions.scheduled_count", count, limits, limit, {
|
|
1215
1241
|
tier_max: limit.value,
|
|
@@ -1890,6 +1916,7 @@ const FUNCTION_SPEC_FIELDS = new Set([
|
|
|
1890
1916
|
"entrypoint",
|
|
1891
1917
|
"config",
|
|
1892
1918
|
"deps",
|
|
1919
|
+
"triggers",
|
|
1893
1920
|
"schedule",
|
|
1894
1921
|
"requireAuth",
|
|
1895
1922
|
"requireRole",
|
|
@@ -1897,6 +1924,8 @@ const FUNCTION_SPEC_FIELDS = new Set([
|
|
|
1897
1924
|
"capabilities",
|
|
1898
1925
|
]);
|
|
1899
1926
|
const FUNCTION_CONFIG_FIELDS = new Set(["timeoutSeconds", "memoryMb"]);
|
|
1927
|
+
const FUNCTION_TRIGGER_FIELDS = new Set(["id", "type", "cron", "timezone", "misfire_policy", "overlap_policy", "mailbox", "events", "run"]);
|
|
1928
|
+
const FUNCTION_TRIGGER_RUN_FIELDS = new Set(["event_type", "payload", "retry", "expires_after_seconds"]);
|
|
1900
1929
|
const SITE_SPEC_FIELDS = new Set(["replace", "patch", "public_paths"]);
|
|
1901
1930
|
const SITE_PATCH_FIELDS = new Set(["put", "delete"]);
|
|
1902
1931
|
const SITE_PUBLIC_PATHS_FIELDS = new Set(["mode", "replace"]);
|
|
@@ -2029,6 +2058,81 @@ function validateFunctionMap(value, resource) {
|
|
|
2029
2058
|
if (entry.files !== undefined) {
|
|
2030
2059
|
requireObject(entry.files, `${resource}.${name}.files`);
|
|
2031
2060
|
}
|
|
2061
|
+
validateFunctionTriggers(entry.triggers, `${resource}.${name}.triggers`);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
function validateFunctionTriggers(value, resource) {
|
|
2065
|
+
if (value === undefined)
|
|
2066
|
+
return;
|
|
2067
|
+
if (!Array.isArray(value))
|
|
2068
|
+
throw invalidSpec(`ReleaseSpec.${resource} must be an array`, resource);
|
|
2069
|
+
const seen = new Set();
|
|
2070
|
+
for (const [index, trigger] of value.entries()) {
|
|
2071
|
+
const path = `${resource}.${index}`;
|
|
2072
|
+
const obj = requireObject(trigger, path);
|
|
2073
|
+
validateKnownFields(obj, path, FUNCTION_TRIGGER_FIELDS, {
|
|
2074
|
+
delivery: "Remove delivery; schedule triggers always start durable function runs.",
|
|
2075
|
+
});
|
|
2076
|
+
if (typeof obj.id !== "string" || obj.id.trim() === "") {
|
|
2077
|
+
throw invalidSpec(`ReleaseSpec.${path}.id is required for function triggers`, `${path}.id`);
|
|
2078
|
+
}
|
|
2079
|
+
if (seen.has(obj.id))
|
|
2080
|
+
throw invalidSpec(`ReleaseSpec.${path}.id duplicates ${JSON.stringify(obj.id)}`, `${path}.id`);
|
|
2081
|
+
seen.add(obj.id);
|
|
2082
|
+
if (obj.type === "schedule") {
|
|
2083
|
+
if (obj.mailbox !== undefined || obj.events !== undefined) {
|
|
2084
|
+
throw invalidSpec(`ReleaseSpec.${path} schedule triggers cannot include mailbox or events`, path);
|
|
2085
|
+
}
|
|
2086
|
+
if (typeof obj.cron !== "string" || obj.cron.trim().split(/\s+/).length !== 5) {
|
|
2087
|
+
throw invalidSpec(`ReleaseSpec.${path}.cron must be a 5-field cron expression`, `${path}.cron`);
|
|
2088
|
+
}
|
|
2089
|
+
if (obj.timezone !== undefined && typeof obj.timezone !== "string") {
|
|
2090
|
+
throw invalidSpec(`ReleaseSpec.${path}.timezone must be a string`, `${path}.timezone`);
|
|
2091
|
+
}
|
|
2092
|
+
if (obj.misfire_policy !== undefined && obj.misfire_policy !== "skip") {
|
|
2093
|
+
throw invalidSpec(`ReleaseSpec.${path}.misfire_policy must be "skip"`, `${path}.misfire_policy`);
|
|
2094
|
+
}
|
|
2095
|
+
if (obj.overlap_policy !== undefined && obj.overlap_policy !== "allow") {
|
|
2096
|
+
throw invalidSpec(`ReleaseSpec.${path}.overlap_policy must be "allow"`, `${path}.overlap_policy`);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
else if (obj.type === "email") {
|
|
2100
|
+
if (obj.cron !== undefined ||
|
|
2101
|
+
obj.timezone !== undefined ||
|
|
2102
|
+
obj.misfire_policy !== undefined ||
|
|
2103
|
+
obj.overlap_policy !== undefined) {
|
|
2104
|
+
throw invalidSpec(`ReleaseSpec.${path} email triggers cannot include schedule fields`, path);
|
|
2105
|
+
}
|
|
2106
|
+
if (typeof obj.mailbox !== "string" || obj.mailbox.trim() === "") {
|
|
2107
|
+
throw invalidSpec(`ReleaseSpec.${path}.mailbox is required`, `${path}.mailbox`);
|
|
2108
|
+
}
|
|
2109
|
+
if (!Array.isArray(obj.events) || obj.events.length === 0) {
|
|
2110
|
+
throw invalidSpec(`ReleaseSpec.${path}.events must be a non-empty array`, `${path}.events`);
|
|
2111
|
+
}
|
|
2112
|
+
for (const [eventIndex, event] of obj.events.entries()) {
|
|
2113
|
+
if (typeof event !== "string" || !EMAIL_TRIGGER_EVENTS.has(event)) {
|
|
2114
|
+
throw invalidSpec(`ReleaseSpec.${path}.events.${eventIndex} must be one of reply_received, delivery, bounced, complained`, `${path}.events.${eventIndex}`);
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
throw invalidSpec(`ReleaseSpec.${path}.type must be "schedule" or "email"`, `${path}.type`);
|
|
2120
|
+
}
|
|
2121
|
+
const run = requireObject(obj.run, `${path}.run`);
|
|
2122
|
+
validateKnownFields(run, `${path}.run`, FUNCTION_TRIGGER_RUN_FIELDS);
|
|
2123
|
+
if (typeof run.event_type !== "string" || run.event_type.trim() === "") {
|
|
2124
|
+
throw invalidSpec(`ReleaseSpec.${path}.run.event_type is required`, `${path}.run.event_type`);
|
|
2125
|
+
}
|
|
2126
|
+
if (run.payload !== undefined)
|
|
2127
|
+
requireObject(run.payload, `${path}.run.payload`);
|
|
2128
|
+
if (run.retry !== undefined)
|
|
2129
|
+
requireObject(run.retry, `${path}.run.retry`);
|
|
2130
|
+
if (run.expires_after_seconds !== undefined &&
|
|
2131
|
+
(typeof run.expires_after_seconds !== "number" ||
|
|
2132
|
+
!Number.isSafeInteger(run.expires_after_seconds) ||
|
|
2133
|
+
run.expires_after_seconds <= 0)) {
|
|
2134
|
+
throw invalidSpec(`ReleaseSpec.${path}.run.expires_after_seconds must be a positive integer`, `${path}.run.expires_after_seconds`);
|
|
2135
|
+
}
|
|
2032
2136
|
}
|
|
2033
2137
|
}
|
|
2034
2138
|
/**
|
|
@@ -2934,6 +3038,8 @@ async function normalizeFunction(fn, remember) {
|
|
|
2934
3038
|
out.config = fn.config;
|
|
2935
3039
|
if (fn.deps !== undefined)
|
|
2936
3040
|
out.deps = fn.deps;
|
|
3041
|
+
if (fn.triggers !== undefined)
|
|
3042
|
+
out.triggers = normalizeFunctionTriggers(fn.triggers);
|
|
2937
3043
|
if (fn.schedule !== undefined)
|
|
2938
3044
|
out.schedule = fn.schedule;
|
|
2939
3045
|
if (fn.entrypoint)
|
|
@@ -2955,6 +3061,38 @@ async function normalizeFunction(fn, remember) {
|
|
|
2955
3061
|
}
|
|
2956
3062
|
return out;
|
|
2957
3063
|
}
|
|
3064
|
+
function normalizeFunctionTriggers(triggers) {
|
|
3065
|
+
return triggers
|
|
3066
|
+
.map((trigger) => {
|
|
3067
|
+
const run = {
|
|
3068
|
+
event_type: trigger.run.event_type,
|
|
3069
|
+
payload: trigger.run.payload ?? {},
|
|
3070
|
+
...(trigger.run.retry !== undefined ? { retry: { ...trigger.run.retry } } : {}),
|
|
3071
|
+
...(trigger.run.expires_after_seconds !== undefined
|
|
3072
|
+
? { expires_after_seconds: trigger.run.expires_after_seconds }
|
|
3073
|
+
: {}),
|
|
3074
|
+
};
|
|
3075
|
+
if (trigger.type === "email") {
|
|
3076
|
+
return {
|
|
3077
|
+
id: trigger.id,
|
|
3078
|
+
type: "email",
|
|
3079
|
+
mailbox: trigger.mailbox,
|
|
3080
|
+
events: [...trigger.events].sort(),
|
|
3081
|
+
run,
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
3084
|
+
return {
|
|
3085
|
+
id: trigger.id,
|
|
3086
|
+
type: "schedule",
|
|
3087
|
+
cron: trigger.cron,
|
|
3088
|
+
timezone: trigger.timezone ?? "UTC",
|
|
3089
|
+
misfire_policy: trigger.misfire_policy ?? "skip",
|
|
3090
|
+
overlap_policy: trigger.overlap_policy ?? "allow",
|
|
3091
|
+
run,
|
|
3092
|
+
};
|
|
3093
|
+
})
|
|
3094
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
3095
|
+
}
|
|
2958
3096
|
async function normalizeFileSet(set, remember) {
|
|
2959
3097
|
// `dir(path)` produces a LocalDirRef sentinel that is documented as a
|
|
2960
3098
|
// valid site.replace / site.patch.put input. Expand it to a plain FileSet
|