@upstash/workflow 1.4.0-rc → 1.4.0
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/astro.d.mts +2 -3
- package/astro.d.ts +2 -3
- package/astro.js +164 -575
- package/astro.mjs +1 -1
- package/{chunk-UH6O5IJY.mjs → chunk-DRHEHWMS.mjs} +326 -737
- package/cloudflare.d.mts +2 -3
- package/cloudflare.d.ts +2 -3
- package/cloudflare.js +164 -575
- package/cloudflare.mjs +1 -1
- package/express.d.mts +2 -3
- package/express.d.ts +2 -3
- package/express.js +164 -575
- package/express.mjs +1 -1
- package/h3.d.mts +2 -3
- package/h3.d.ts +2 -3
- package/h3.js +164 -575
- package/h3.mjs +1 -1
- package/hono.d.mts +2 -3
- package/hono.d.ts +2 -3
- package/hono.js +164 -575
- package/hono.mjs +1 -1
- package/index.d.mts +2 -3
- package/index.d.ts +2 -3
- package/index.js +164 -575
- package/index.mjs +1 -1
- package/nextjs.d.mts +2 -3
- package/nextjs.d.ts +2 -3
- package/nextjs.js +164 -575
- package/nextjs.mjs +1 -1
- package/package.json +1 -1
- package/react-router.d.mts +2 -3
- package/react-router.d.ts +2 -3
- package/react-router.js +164 -575
- package/react-router.mjs +1 -1
- package/{serve-many-CbIV7145.d.mts → serve-many-CG3BFvO3.d.mts} +1 -1
- package/{serve-many-BCtZg31b.d.ts → serve-many-iJF1IUXk.d.ts} +1 -1
- package/solidjs.d.mts +1 -2
- package/solidjs.d.ts +1 -2
- package/solidjs.js +164 -575
- package/solidjs.mjs +1 -1
- package/svelte.d.mts +2 -3
- package/svelte.d.ts +2 -3
- package/svelte.js +164 -575
- package/svelte.mjs +1 -1
- package/tanstack.d.mts +2 -3
- package/tanstack.d.ts +2 -3
- package/tanstack.js +164 -575
- package/tanstack.mjs +1 -1
- package/{types-ffLPVG5_.d.ts → types-CekOpKvz.d.mts} +8 -248
- package/{types-ffLPVG5_.d.mts → types-CekOpKvz.d.ts} +8 -248
package/astro.js
CHANGED
|
@@ -217,14 +217,6 @@ var WORKFLOW_CREATED_AT_HEADER = "Upstash-Workflow-CreatedAt";
|
|
|
217
217
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
218
218
|
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
219
219
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
220
|
-
var WORKFLOW_FEATURE_SET = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
221
|
-
var WORKFLOW_STEP_CONFIG_CALL_TYPE = "stepConfig";
|
|
222
|
-
var WORKFLOW_STEP_CONFIG_HEADER = "Upstash-Workflow-Step-Config";
|
|
223
|
-
var FLOW_CONTROL_KEY_HEADER = "Upstash-Flow-Control-Key";
|
|
224
|
-
var FLOW_CONTROL_VALUE_HEADER = "Upstash-Flow-Control-Value";
|
|
225
|
-
var RETRIES_HEADER = "Upstash-Retries";
|
|
226
|
-
var RETRY_DELAY_HEADER = "Upstash-Retry-Delay";
|
|
227
|
-
var WORKFLOW_STEP_CONFIG_FEATURE = "WF_StepConfig";
|
|
228
220
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
229
221
|
var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
230
222
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
@@ -242,6 +234,102 @@ var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
|
242
234
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
243
235
|
var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
|
|
244
236
|
|
|
237
|
+
// src/context/auto-executor.ts
|
|
238
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
239
|
+
|
|
240
|
+
// src/qstash/headers.ts
|
|
241
|
+
var import_qstash4 = require("@upstash/qstash");
|
|
242
|
+
|
|
243
|
+
// src/utils.ts
|
|
244
|
+
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
245
|
+
var NANOID_LENGTH = 21;
|
|
246
|
+
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
247
|
+
function validateLabel(label) {
|
|
248
|
+
if (label === void 0) return;
|
|
249
|
+
const labels = Array.isArray(label) ? label : [label];
|
|
250
|
+
if (labels.length === 0) {
|
|
251
|
+
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
252
|
+
}
|
|
253
|
+
for (const value of labels) {
|
|
254
|
+
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
255
|
+
throw new WorkflowNonRetryableError(
|
|
256
|
+
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function serializeLabel(label) {
|
|
262
|
+
return Array.isArray(label) ? label.join(",") : label;
|
|
263
|
+
}
|
|
264
|
+
function validateFlowControl(flowControl) {
|
|
265
|
+
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
266
|
+
throw new WorkflowNonRetryableError(
|
|
267
|
+
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function getRandomInt() {
|
|
272
|
+
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
273
|
+
}
|
|
274
|
+
function nanoid(length = NANOID_LENGTH) {
|
|
275
|
+
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
276
|
+
}
|
|
277
|
+
function getWorkflowRunId(id) {
|
|
278
|
+
return `wfr_${id ?? nanoid()}`;
|
|
279
|
+
}
|
|
280
|
+
function decodeBase64(base64) {
|
|
281
|
+
const binString = atob(base64);
|
|
282
|
+
try {
|
|
283
|
+
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
284
|
+
return new TextDecoder().decode(intArray);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.warn(
|
|
287
|
+
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
288
|
+
);
|
|
289
|
+
return binString;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function getUserIdFromToken(qstashClient) {
|
|
293
|
+
try {
|
|
294
|
+
const token = qstashClient.token;
|
|
295
|
+
const decodedToken = decodeBase64(token);
|
|
296
|
+
const tokenPayload = JSON.parse(decodedToken);
|
|
297
|
+
const userId = tokenPayload.UserID;
|
|
298
|
+
if (!userId) {
|
|
299
|
+
throw new WorkflowError("QStash token payload does not contain userId");
|
|
300
|
+
}
|
|
301
|
+
return userId;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
throw new WorkflowError(
|
|
304
|
+
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function getQStashUrl(qstashClient) {
|
|
309
|
+
try {
|
|
310
|
+
const requester = qstashClient.http;
|
|
311
|
+
const baseUrl = requester.baseUrl;
|
|
312
|
+
if (!baseUrl) {
|
|
313
|
+
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
314
|
+
}
|
|
315
|
+
return baseUrl;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function getEventId() {
|
|
321
|
+
return `evt_${nanoid(15)}`;
|
|
322
|
+
}
|
|
323
|
+
function stringifyBody(body) {
|
|
324
|
+
if (body === void 0) {
|
|
325
|
+
return void 0;
|
|
326
|
+
}
|
|
327
|
+
if (typeof body === "string") {
|
|
328
|
+
return body;
|
|
329
|
+
}
|
|
330
|
+
return JSON.stringify(body);
|
|
331
|
+
}
|
|
332
|
+
|
|
245
333
|
// node_modules/neverthrow/dist/index.es.js
|
|
246
334
|
var defaultErrorConfig = {
|
|
247
335
|
withStackTrace: false
|
|
@@ -657,102 +745,6 @@ var Err = class {
|
|
|
657
745
|
};
|
|
658
746
|
var fromThrowable = Result.fromThrowable;
|
|
659
747
|
|
|
660
|
-
// src/context/auto-executor.ts
|
|
661
|
-
var import_qstash5 = require("@upstash/qstash");
|
|
662
|
-
|
|
663
|
-
// src/qstash/headers.ts
|
|
664
|
-
var import_qstash4 = require("@upstash/qstash");
|
|
665
|
-
|
|
666
|
-
// src/utils.ts
|
|
667
|
-
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
668
|
-
var NANOID_LENGTH = 21;
|
|
669
|
-
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
670
|
-
function validateLabel(label) {
|
|
671
|
-
if (label === void 0) return;
|
|
672
|
-
const labels = Array.isArray(label) ? label : [label];
|
|
673
|
-
if (labels.length === 0) {
|
|
674
|
-
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
675
|
-
}
|
|
676
|
-
for (const value of labels) {
|
|
677
|
-
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
678
|
-
throw new WorkflowNonRetryableError(
|
|
679
|
-
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
function serializeLabel(label) {
|
|
685
|
-
return Array.isArray(label) ? label.join(",") : label;
|
|
686
|
-
}
|
|
687
|
-
function validateFlowControl(flowControl) {
|
|
688
|
-
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
689
|
-
throw new WorkflowNonRetryableError(
|
|
690
|
-
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
function getRandomInt() {
|
|
695
|
-
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
696
|
-
}
|
|
697
|
-
function nanoid(length = NANOID_LENGTH) {
|
|
698
|
-
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
699
|
-
}
|
|
700
|
-
function getWorkflowRunId(id) {
|
|
701
|
-
return `wfr_${id ?? nanoid()}`;
|
|
702
|
-
}
|
|
703
|
-
function decodeBase64(base64) {
|
|
704
|
-
const binString = atob(base64);
|
|
705
|
-
try {
|
|
706
|
-
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
707
|
-
return new TextDecoder().decode(intArray);
|
|
708
|
-
} catch (error) {
|
|
709
|
-
console.warn(
|
|
710
|
-
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
711
|
-
);
|
|
712
|
-
return binString;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
function getUserIdFromToken(qstashClient) {
|
|
716
|
-
try {
|
|
717
|
-
const token = qstashClient.token;
|
|
718
|
-
const decodedToken = decodeBase64(token);
|
|
719
|
-
const tokenPayload = JSON.parse(decodedToken);
|
|
720
|
-
const userId = tokenPayload.UserID;
|
|
721
|
-
if (!userId) {
|
|
722
|
-
throw new WorkflowError("QStash token payload does not contain userId");
|
|
723
|
-
}
|
|
724
|
-
return userId;
|
|
725
|
-
} catch (error) {
|
|
726
|
-
throw new WorkflowError(
|
|
727
|
-
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
function getQStashUrl(qstashClient) {
|
|
732
|
-
try {
|
|
733
|
-
const requester = qstashClient.http;
|
|
734
|
-
const baseUrl = requester.baseUrl;
|
|
735
|
-
if (!baseUrl) {
|
|
736
|
-
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
737
|
-
}
|
|
738
|
-
return baseUrl;
|
|
739
|
-
} catch (error) {
|
|
740
|
-
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
function getEventId() {
|
|
744
|
-
return `evt_${nanoid(15)}`;
|
|
745
|
-
}
|
|
746
|
-
function stringifyBody(body) {
|
|
747
|
-
if (body === void 0) {
|
|
748
|
-
return void 0;
|
|
749
|
-
}
|
|
750
|
-
if (typeof body === "string") {
|
|
751
|
-
return body;
|
|
752
|
-
}
|
|
753
|
-
return JSON.stringify(body);
|
|
754
|
-
}
|
|
755
|
-
|
|
756
748
|
// src/types.ts
|
|
757
749
|
var StepTypes = [
|
|
758
750
|
"Initial",
|
|
@@ -792,6 +784,10 @@ var triggerFirstInvocation = async (params) => {
|
|
|
792
784
|
workflowConfig: {
|
|
793
785
|
workflowRunId: workflowContext.workflowRunId,
|
|
794
786
|
workflowUrl: workflowContext.url,
|
|
787
|
+
// the run doesn't exist yet, so the trigger time is sent when the creation time
|
|
788
|
+
// is not known. serve and client.trigger set it on the context already, so a
|
|
789
|
+
// retry of this request carries the same value and is deduplicated.
|
|
790
|
+
workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
|
|
795
791
|
failureUrl,
|
|
796
792
|
retries,
|
|
797
793
|
retryDelay,
|
|
@@ -858,10 +854,6 @@ var triggerFirstInvocation = async (params) => {
|
|
|
858
854
|
return err(error_);
|
|
859
855
|
}
|
|
860
856
|
};
|
|
861
|
-
var flushPendingStep = async (workflowContext) => {
|
|
862
|
-
const { executor } = workflowContext;
|
|
863
|
-
return await executor.submitPendingStep();
|
|
864
|
-
};
|
|
865
857
|
var triggerRouteFunction = async ({
|
|
866
858
|
onCleanup,
|
|
867
859
|
onStep,
|
|
@@ -899,7 +891,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
|
|
|
899
891
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
900
892
|
method: "DELETE",
|
|
901
893
|
parseResponseAsJson: false,
|
|
902
|
-
body: JSON.stringify(result)
|
|
894
|
+
body: JSON.stringify(result),
|
|
895
|
+
// with the creation time, QStash deletes the run only if it is the same run.
|
|
896
|
+
// a late delete of a previous run can't delete a new run with the same id.
|
|
897
|
+
headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
|
|
903
898
|
});
|
|
904
899
|
await dispatchDebug?.("onInfo", {
|
|
905
900
|
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
@@ -976,6 +971,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
976
971
|
const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
|
|
977
972
|
const contentType = request.headers.get("Upstash-Workflow-ContentType");
|
|
978
973
|
const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
|
|
974
|
+
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
979
975
|
if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
|
|
980
976
|
throw new Error(
|
|
981
977
|
`Missing info in callback message source header: ${JSON.stringify({
|
|
@@ -993,6 +989,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
993
989
|
workflowConfig: {
|
|
994
990
|
workflowRunId,
|
|
995
991
|
workflowUrl,
|
|
992
|
+
workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
|
|
996
993
|
telemetry: telemetry2
|
|
997
994
|
},
|
|
998
995
|
invokeCount: Number(invokeCount)
|
|
@@ -1069,29 +1066,8 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
|
|
|
1069
1066
|
var BaseLazyStep = class _BaseLazyStep {
|
|
1070
1067
|
stepName;
|
|
1071
1068
|
context;
|
|
1072
|
-
|
|
1073
|
-
* step-level settings (flow control, retries etc.) which override the
|
|
1074
|
-
* settings the workflow run was triggered with, for this step only.
|
|
1075
|
-
*/
|
|
1076
|
-
stepSettings;
|
|
1077
|
-
/**
|
|
1078
|
-
* whether this step's result can be submitted after the route function
|
|
1079
|
-
* has moved on, instead of right away.
|
|
1080
|
-
*
|
|
1081
|
-
* Enabled for steps whose `getResultStep` produces the final `out` —
|
|
1082
|
-
* which is not the same as "needs no server round trip":
|
|
1083
|
-
* `LazyCreateWebhookStep` builds its result in `getBody` and returns
|
|
1084
|
-
* `out: undefined` from `getResultStep`, so deferring it would hand
|
|
1085
|
-
* `undefined` to the route function.
|
|
1086
|
-
*
|
|
1087
|
-
* Deferring lets the route function continue and reveal the next step,
|
|
1088
|
-
* so that step's settings can ride on this step's submission instead
|
|
1089
|
-
* of needing a step config request of their own.
|
|
1090
|
-
*/
|
|
1091
|
-
supportsDeferredSubmission = false;
|
|
1092
|
-
constructor(context, stepName, stepSettings) {
|
|
1069
|
+
constructor(context, stepName) {
|
|
1093
1070
|
this.context = context;
|
|
1094
|
-
this.stepSettings = stepSettings;
|
|
1095
1071
|
if (!stepName) {
|
|
1096
1072
|
throw new WorkflowError(
|
|
1097
1073
|
"A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
|
|
@@ -1155,18 +1131,13 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1155
1131
|
step.out = JSON.stringify(step.out);
|
|
1156
1132
|
return JSON.stringify(step);
|
|
1157
1133
|
}
|
|
1158
|
-
getHeaders({
|
|
1159
|
-
context,
|
|
1160
|
-
telemetry: telemetry2,
|
|
1161
|
-
invokeCount,
|
|
1162
|
-
step,
|
|
1163
|
-
stepSettings
|
|
1164
|
-
}) {
|
|
1134
|
+
getHeaders({ context, telemetry: telemetry2, invokeCount, step }) {
|
|
1165
1135
|
return getHeaders({
|
|
1166
1136
|
initHeaderValue: "false",
|
|
1167
1137
|
workflowConfig: {
|
|
1168
1138
|
workflowRunId: context.workflowRunId,
|
|
1169
1139
|
workflowUrl: context.url,
|
|
1140
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1170
1141
|
useJSONContent: false,
|
|
1171
1142
|
telemetry: telemetry2
|
|
1172
1143
|
},
|
|
@@ -1174,8 +1145,7 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1174
1145
|
stepInfo: {
|
|
1175
1146
|
step,
|
|
1176
1147
|
lazyStep: this
|
|
1177
|
-
}
|
|
1178
|
-
stepSettings
|
|
1148
|
+
}
|
|
1179
1149
|
});
|
|
1180
1150
|
}
|
|
1181
1151
|
async submitStep({ context, body, headers }) {
|
|
@@ -1193,9 +1163,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
|
|
|
1193
1163
|
stepFunction;
|
|
1194
1164
|
stepType = "Run";
|
|
1195
1165
|
allowUndefinedOut = true;
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
super(context, stepName, stepSettings);
|
|
1166
|
+
constructor(context, stepName, stepFunction) {
|
|
1167
|
+
super(context, stepName);
|
|
1199
1168
|
this.stepFunction = stepFunction;
|
|
1200
1169
|
}
|
|
1201
1170
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1225,7 +1194,6 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1225
1194
|
sleep;
|
|
1226
1195
|
stepType = "SleepFor";
|
|
1227
1196
|
allowUndefinedOut = true;
|
|
1228
|
-
supportsDeferredSubmission = true;
|
|
1229
1197
|
constructor(context, stepName, sleep) {
|
|
1230
1198
|
super(context, stepName);
|
|
1231
1199
|
this.sleep = sleep;
|
|
@@ -1265,7 +1233,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1265
1233
|
sleepUntil;
|
|
1266
1234
|
stepType = "SleepUntil";
|
|
1267
1235
|
allowUndefinedOut = true;
|
|
1268
|
-
supportsDeferredSubmission = true;
|
|
1269
1236
|
constructor(context, stepName, sleepUntil) {
|
|
1270
1237
|
super(context, stepName);
|
|
1271
1238
|
this.sleepUntil = sleepUntil;
|
|
@@ -1498,6 +1465,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
|
|
|
1498
1465
|
) : {},
|
|
1499
1466
|
// note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
|
|
1500
1467
|
"Upstash-Workflow-Runid": [context.workflowRunId],
|
|
1468
|
+
// same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
|
|
1469
|
+
...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
|
|
1501
1470
|
[WORKFLOW_INIT_HEADER]: ["false"],
|
|
1502
1471
|
[WORKFLOW_URL_HEADER]: [context.url],
|
|
1503
1472
|
"Upstash-Workflow-CallType": ["step"]
|
|
@@ -1600,6 +1569,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1600
1569
|
workflowConfig: {
|
|
1601
1570
|
workflowRunId: context.workflowRunId,
|
|
1602
1571
|
workflowUrl: context.url,
|
|
1572
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1603
1573
|
telemetry: telemetry2,
|
|
1604
1574
|
useJSONContent: false
|
|
1605
1575
|
},
|
|
@@ -1609,6 +1579,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1609
1579
|
invokerHeaders[key] = value;
|
|
1610
1580
|
});
|
|
1611
1581
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1582
|
+
if (context.workflowRunCreatedAt) {
|
|
1583
|
+
invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
|
|
1584
|
+
}
|
|
1612
1585
|
const request = {
|
|
1613
1586
|
body: stringifyBody(this.params.body),
|
|
1614
1587
|
headers: Object.fromEntries(
|
|
@@ -1637,6 +1610,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1637
1610
|
workflowConfig: {
|
|
1638
1611
|
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1639
1612
|
workflowUrl: newUrl,
|
|
1613
|
+
// the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
|
|
1614
|
+
// it separates the invocations made by different runs of the invoker.
|
|
1615
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1640
1616
|
retries,
|
|
1641
1617
|
retryDelay,
|
|
1642
1618
|
telemetry: telemetry2,
|
|
@@ -1758,7 +1734,6 @@ var WorkflowHeaders = class {
|
|
|
1758
1734
|
invokeCount;
|
|
1759
1735
|
initHeaderValue;
|
|
1760
1736
|
stepInfo;
|
|
1761
|
-
stepSettings;
|
|
1762
1737
|
headers;
|
|
1763
1738
|
/**
|
|
1764
1739
|
* @param params workflow header parameters
|
|
@@ -1768,20 +1743,17 @@ var WorkflowHeaders = class {
|
|
|
1768
1743
|
workflowConfig,
|
|
1769
1744
|
invokeCount,
|
|
1770
1745
|
initHeaderValue,
|
|
1771
|
-
stepInfo
|
|
1772
|
-
stepSettings
|
|
1746
|
+
stepInfo
|
|
1773
1747
|
}) {
|
|
1774
1748
|
this.userHeaders = userHeaders;
|
|
1775
1749
|
this.workflowConfig = workflowConfig;
|
|
1776
1750
|
this.invokeCount = invokeCount;
|
|
1777
1751
|
this.initHeaderValue = initHeaderValue;
|
|
1778
1752
|
this.stepInfo = stepInfo;
|
|
1779
|
-
this.stepSettings = stepSettings;
|
|
1780
1753
|
this.headers = {
|
|
1781
1754
|
rawHeaders: {},
|
|
1782
1755
|
workflowHeaders: {},
|
|
1783
|
-
failureHeaders: {}
|
|
1784
|
-
stepSettingsHeaders: {}
|
|
1756
|
+
failureHeaders: {}
|
|
1785
1757
|
};
|
|
1786
1758
|
}
|
|
1787
1759
|
getHeaders() {
|
|
@@ -1792,7 +1764,6 @@ var WorkflowHeaders = class {
|
|
|
1792
1764
|
this.addUserHeaders();
|
|
1793
1765
|
this.addInvokeCount();
|
|
1794
1766
|
this.addFailureUrl();
|
|
1795
|
-
this.addStepSettings();
|
|
1796
1767
|
const contentType = this.addContentType();
|
|
1797
1768
|
return this.prefixHeaders(contentType);
|
|
1798
1769
|
}
|
|
@@ -1802,10 +1773,13 @@ var WorkflowHeaders = class {
|
|
|
1802
1773
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1803
1774
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1804
1775
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1805
|
-
[WORKFLOW_FEATURE_HEADER]:
|
|
1776
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1806
1777
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1807
1778
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1808
1779
|
};
|
|
1780
|
+
if (this.workflowConfig.workflowRunCreatedAt) {
|
|
1781
|
+
this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
|
|
1782
|
+
}
|
|
1809
1783
|
if (this.stepInfo?.lazyStep.stepType !== "Call") {
|
|
1810
1784
|
this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
1811
1785
|
}
|
|
@@ -1880,7 +1854,7 @@ var WorkflowHeaders = class {
|
|
|
1880
1854
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1881
1855
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1882
1856
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1883
|
-
this.headers.failureHeaders["Feature-Set"] =
|
|
1857
|
+
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
1884
1858
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1885
1859
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1886
1860
|
}
|
|
@@ -1888,42 +1862,6 @@ var WorkflowHeaders = class {
|
|
|
1888
1862
|
this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
|
|
1889
1863
|
}
|
|
1890
1864
|
}
|
|
1891
|
-
/**
|
|
1892
|
-
* Applies the step-level settings of a step to the message.
|
|
1893
|
-
*
|
|
1894
|
-
* These headers configure the message itself, so that QStash uses them
|
|
1895
|
-
* instead of the settings the run was triggered with. The delivery of
|
|
1896
|
-
* the message is what executes the step, which is how a step's
|
|
1897
|
-
* settings reach it.
|
|
1898
|
-
*
|
|
1899
|
-
* When any setting is present the feature set is extended with
|
|
1900
|
-
* `WF_StepConfig`, which is what tells QStash to keep them. QStash
|
|
1901
|
-
* reports that back on the delivery as `Upstash-Workflow-Step-Config`.
|
|
1902
|
-
*/
|
|
1903
|
-
addStepSettings() {
|
|
1904
|
-
if (!this.stepSettings) {
|
|
1905
|
-
return;
|
|
1906
|
-
}
|
|
1907
|
-
const headers = {};
|
|
1908
|
-
if (this.stepSettings.flowControl) {
|
|
1909
|
-
const { flowControlKey, flowControlValue } = prepareFlowControl(
|
|
1910
|
-
this.stepSettings.flowControl
|
|
1911
|
-
);
|
|
1912
|
-
headers[FLOW_CONTROL_KEY_HEADER] = flowControlKey;
|
|
1913
|
-
headers[FLOW_CONTROL_VALUE_HEADER] = flowControlValue;
|
|
1914
|
-
}
|
|
1915
|
-
if (this.stepSettings.retries !== void 0) {
|
|
1916
|
-
headers[RETRIES_HEADER] = this.stepSettings.retries.toString();
|
|
1917
|
-
}
|
|
1918
|
-
if (this.stepSettings.retryDelay) {
|
|
1919
|
-
headers[RETRY_DELAY_HEADER] = this.stepSettings.retryDelay;
|
|
1920
|
-
}
|
|
1921
|
-
if (Object.keys(headers).length === 0) {
|
|
1922
|
-
return;
|
|
1923
|
-
}
|
|
1924
|
-
headers[WORKFLOW_FEATURE_HEADER] = `${WORKFLOW_FEATURE_SET},${WORKFLOW_STEP_CONFIG_FEATURE}`;
|
|
1925
|
-
this.headers.stepSettingsHeaders = headers;
|
|
1926
|
-
}
|
|
1927
1865
|
addContentType() {
|
|
1928
1866
|
if (this.workflowConfig.useJSONContent) {
|
|
1929
1867
|
this.headers.rawHeaders["content-type"] = "application/json";
|
|
@@ -1937,17 +1875,14 @@ var WorkflowHeaders = class {
|
|
|
1937
1875
|
return contentType;
|
|
1938
1876
|
}
|
|
1939
1877
|
prefixHeaders(contentType) {
|
|
1940
|
-
const { rawHeaders, workflowHeaders, failureHeaders
|
|
1878
|
+
const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
|
|
1941
1879
|
const isCall = this.stepInfo?.lazyStep.stepType === "Call";
|
|
1942
1880
|
return {
|
|
1943
1881
|
headers: {
|
|
1944
1882
|
...rawHeaders,
|
|
1945
1883
|
...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
|
|
1946
1884
|
...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
|
|
1947
|
-
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
|
|
1948
|
-
// last, so they override the run's settings above. Never
|
|
1949
|
-
// prefixed: they configure this message, not its callback.
|
|
1950
|
-
...stepSettingsHeaders
|
|
1885
|
+
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
|
|
1951
1886
|
},
|
|
1952
1887
|
contentType
|
|
1953
1888
|
};
|
|
@@ -1998,16 +1933,16 @@ var submitParallelSteps = async ({
|
|
|
1998
1933
|
info: `Submitting ${planSteps.length} parallel steps.`
|
|
1999
1934
|
});
|
|
2000
1935
|
const result = await context.qstashClient.batch(
|
|
2001
|
-
planSteps.map((planStep
|
|
1936
|
+
planSteps.map((planStep) => {
|
|
2002
1937
|
const { headers } = getHeaders({
|
|
2003
1938
|
initHeaderValue: "false",
|
|
2004
1939
|
workflowConfig: {
|
|
2005
1940
|
workflowRunId: context.workflowRunId,
|
|
2006
1941
|
workflowUrl: context.url,
|
|
1942
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
2007
1943
|
telemetry: telemetry2
|
|
2008
1944
|
},
|
|
2009
|
-
invokeCount
|
|
2010
|
-
stepSettings: steps[index].stepSettings
|
|
1945
|
+
invokeCount
|
|
2011
1946
|
});
|
|
2012
1947
|
return {
|
|
2013
1948
|
headers,
|
|
@@ -2026,38 +1961,31 @@ var submitParallelSteps = async ({
|
|
|
2026
1961
|
}
|
|
2027
1962
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
2028
1963
|
};
|
|
2029
|
-
var
|
|
1964
|
+
var submitSingleStep = async ({
|
|
1965
|
+
context,
|
|
2030
1966
|
lazyStep,
|
|
2031
1967
|
stepId,
|
|
1968
|
+
invokeCount,
|
|
2032
1969
|
concurrency,
|
|
1970
|
+
telemetry: telemetry2,
|
|
1971
|
+
dispatchDebug,
|
|
2033
1972
|
dispatchLifecycle
|
|
2034
1973
|
}) => {
|
|
2035
1974
|
await dispatchLifecycle("beforeExecution", {
|
|
2036
1975
|
stepName: lazyStep.stepName
|
|
2037
1976
|
});
|
|
1977
|
+
let resultStep;
|
|
2038
1978
|
try {
|
|
2039
|
-
|
|
1979
|
+
resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
2040
1980
|
} catch (error) {
|
|
2041
1981
|
attachStepNameToError(error, lazyStep.stepName);
|
|
2042
1982
|
throw error;
|
|
2043
1983
|
}
|
|
2044
|
-
};
|
|
2045
|
-
var submitStepResult = async ({
|
|
2046
|
-
context,
|
|
2047
|
-
lazyStep,
|
|
2048
|
-
resultStep,
|
|
2049
|
-
invokeCount,
|
|
2050
|
-
concurrency,
|
|
2051
|
-
telemetry: telemetry2,
|
|
2052
|
-
dispatchDebug,
|
|
2053
|
-
nextStepSettings
|
|
2054
|
-
}) => {
|
|
2055
1984
|
const { headers } = lazyStep.getHeaders({
|
|
2056
1985
|
context,
|
|
2057
1986
|
step: resultStep,
|
|
2058
1987
|
invokeCount,
|
|
2059
|
-
telemetry: telemetry2
|
|
2060
|
-
stepSettings: nextStepSettings
|
|
1988
|
+
telemetry: telemetry2
|
|
2061
1989
|
});
|
|
2062
1990
|
const body = lazyStep.getBody({
|
|
2063
1991
|
context,
|
|
@@ -2082,138 +2010,6 @@ var submitStepResult = async ({
|
|
|
2082
2010
|
}
|
|
2083
2011
|
return resultStep;
|
|
2084
2012
|
};
|
|
2085
|
-
var publishStepConfigRequest = async ({
|
|
2086
|
-
context,
|
|
2087
|
-
lazyStep,
|
|
2088
|
-
targetStep,
|
|
2089
|
-
invokeCount,
|
|
2090
|
-
telemetry: telemetry2,
|
|
2091
|
-
dispatchDebug
|
|
2092
|
-
}) => {
|
|
2093
|
-
const { headers } = getHeaders({
|
|
2094
|
-
initHeaderValue: "false",
|
|
2095
|
-
workflowConfig: {
|
|
2096
|
-
workflowRunId: context.workflowRunId,
|
|
2097
|
-
workflowUrl: context.url,
|
|
2098
|
-
telemetry: telemetry2
|
|
2099
|
-
},
|
|
2100
|
-
invokeCount,
|
|
2101
|
-
stepSettings: lazyStep.stepSettings
|
|
2102
|
-
});
|
|
2103
|
-
await dispatchDebug("onInfo", {
|
|
2104
|
-
info: `Step "${lazyStep.stepName}" (${targetStep}) has step-level settings which the current delivery does not carry. Requesting a delivery which does.`
|
|
2105
|
-
});
|
|
2106
|
-
const result = await context.qstashClient.publishJSON({
|
|
2107
|
-
headers: {
|
|
2108
|
-
...headers,
|
|
2109
|
-
"Upstash-Workflow-CallType": WORKFLOW_STEP_CONFIG_CALL_TYPE
|
|
2110
|
-
},
|
|
2111
|
-
method: "POST",
|
|
2112
|
-
body: { targetStep, invokeCount },
|
|
2113
|
-
url: context.url,
|
|
2114
|
-
contentBasedDeduplication: true
|
|
2115
|
-
});
|
|
2116
|
-
if (result?.deduplicated) {
|
|
2117
|
-
await dispatchDebug("onWarning", {
|
|
2118
|
-
warning: `A step config request for step "${lazyStep.stepName}" (${targetStep}) was already published and this one was deduplicated. Expected when the delivery which published it is being retried; otherwise the run stops here.`
|
|
2119
|
-
});
|
|
2120
|
-
} else if (result?.messageId) {
|
|
2121
|
-
await dispatchDebug("onInfo", {
|
|
2122
|
-
info: `Requested a delivery under the settings of step "${lazyStep.stepName}" with messageId: ${result.messageId}.`
|
|
2123
|
-
});
|
|
2124
|
-
}
|
|
2125
|
-
};
|
|
2126
|
-
|
|
2127
|
-
// src/qstash/step-config.ts
|
|
2128
|
-
var DEFAULT_PARALLELISM = 0;
|
|
2129
|
-
var DEFAULT_RATE = 0;
|
|
2130
|
-
var DEFAULT_PERIOD_SECONDS = 1;
|
|
2131
|
-
var SECONDS_PER_UNIT = {
|
|
2132
|
-
s: 1,
|
|
2133
|
-
m: 60,
|
|
2134
|
-
h: 60 * 60,
|
|
2135
|
-
d: 24 * 60 * 60
|
|
2136
|
-
};
|
|
2137
|
-
var durationToSeconds = (duration) => {
|
|
2138
|
-
if (typeof duration === "number") {
|
|
2139
|
-
return Number.isFinite(duration) ? duration : void 0;
|
|
2140
|
-
}
|
|
2141
|
-
const match = /^(\d+)([smhd])$/.exec(duration.trim());
|
|
2142
|
-
if (match) {
|
|
2143
|
-
return Number(match[1]) * SECONDS_PER_UNIT[match[2]];
|
|
2144
|
-
}
|
|
2145
|
-
const seconds = Number(duration.trim());
|
|
2146
|
-
return Number.isFinite(seconds) ? seconds : void 0;
|
|
2147
|
-
};
|
|
2148
|
-
var normalizeFlowControl = (flowControl) => {
|
|
2149
|
-
const period = flowControl.period === void 0 ? void 0 : durationToSeconds(flowControl.period);
|
|
2150
|
-
return {
|
|
2151
|
-
key: flowControl.key,
|
|
2152
|
-
parallelism: flowControl.parallelism ?? DEFAULT_PARALLELISM,
|
|
2153
|
-
rate: flowControl.rate ?? flowControl.ratePerSecond ?? DEFAULT_RATE,
|
|
2154
|
-
period: period ?? DEFAULT_PERIOD_SECONDS
|
|
2155
|
-
};
|
|
2156
|
-
};
|
|
2157
|
-
var parseFlowControlHeaders = (key, value) => {
|
|
2158
|
-
if (!key) {
|
|
2159
|
-
return void 0;
|
|
2160
|
-
}
|
|
2161
|
-
const flowControl = {
|
|
2162
|
-
key,
|
|
2163
|
-
parallelism: DEFAULT_PARALLELISM,
|
|
2164
|
-
rate: DEFAULT_RATE,
|
|
2165
|
-
period: DEFAULT_PERIOD_SECONDS
|
|
2166
|
-
};
|
|
2167
|
-
for (const entry of (value ?? "").split(",")) {
|
|
2168
|
-
const [name, rawValue] = entry.split("=").map((part) => part.trim());
|
|
2169
|
-
if (!name || rawValue === void 0) {
|
|
2170
|
-
continue;
|
|
2171
|
-
}
|
|
2172
|
-
switch (name) {
|
|
2173
|
-
case "parallelism": {
|
|
2174
|
-
flowControl.parallelism = Number(rawValue);
|
|
2175
|
-
break;
|
|
2176
|
-
}
|
|
2177
|
-
case "rate": {
|
|
2178
|
-
flowControl.rate = Number(rawValue);
|
|
2179
|
-
break;
|
|
2180
|
-
}
|
|
2181
|
-
case "period": {
|
|
2182
|
-
flowControl.period = durationToSeconds(rawValue) ?? DEFAULT_PERIOD_SECONDS;
|
|
2183
|
-
break;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
return flowControl;
|
|
2188
|
-
};
|
|
2189
|
-
var getEffectiveConfig = (headers) => {
|
|
2190
|
-
const retriesHeader = headers.get(RETRIES_HEADER);
|
|
2191
|
-
return {
|
|
2192
|
-
flowControl: parseFlowControlHeaders(
|
|
2193
|
-
headers.get(FLOW_CONTROL_KEY_HEADER),
|
|
2194
|
-
headers.get(FLOW_CONTROL_VALUE_HEADER)
|
|
2195
|
-
),
|
|
2196
|
-
retries: retriesHeader === null ? void 0 : Number(retriesHeader),
|
|
2197
|
-
retryDelay: headers.get(RETRY_DELAY_HEADER) ?? void 0,
|
|
2198
|
-
hasStepConfig: headers.get(WORKFLOW_STEP_CONFIG_HEADER) === "true"
|
|
2199
|
-
};
|
|
2200
|
-
};
|
|
2201
|
-
var describeStepSettingsMismatch = (stepSettings, effectiveConfig) => {
|
|
2202
|
-
if (stepSettings.flowControl) {
|
|
2203
|
-
const wanted = normalizeFlowControl(stepSettings.flowControl);
|
|
2204
|
-
const applied = effectiveConfig.flowControl;
|
|
2205
|
-
if (!applied || applied.key !== wanted.key || applied.parallelism !== wanted.parallelism || applied.rate !== wanted.rate || applied.period !== wanted.period) {
|
|
2206
|
-
return `flow control: expected ${JSON.stringify(wanted)}, delivery has ${JSON.stringify(applied)}`;
|
|
2207
|
-
}
|
|
2208
|
-
}
|
|
2209
|
-
if (stepSettings.retries !== void 0 && effectiveConfig.retries !== void 0 && stepSettings.retries !== effectiveConfig.retries) {
|
|
2210
|
-
return `retries: expected ${stepSettings.retries}, delivery has ${effectiveConfig.retries}`;
|
|
2211
|
-
}
|
|
2212
|
-
if (stepSettings.retryDelay !== void 0 && stepSettings.retryDelay !== effectiveConfig.retryDelay) {
|
|
2213
|
-
return `retry delay: expected '${stepSettings.retryDelay}', delivery has '${effectiveConfig.retryDelay ?? ""}'`;
|
|
2214
|
-
}
|
|
2215
|
-
return void 0;
|
|
2216
|
-
};
|
|
2217
2013
|
|
|
2218
2014
|
// src/context/auto-executor.ts
|
|
2219
2015
|
var AutoExecutor = class _AutoExecutor {
|
|
@@ -2230,25 +2026,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2230
2026
|
stepCount = 0;
|
|
2231
2027
|
planStepCount = 0;
|
|
2232
2028
|
executingStep = false;
|
|
2233
|
-
/**
|
|
2234
|
-
* an executed step whose result hasn't been submitted to QStash yet.
|
|
2235
|
-
*
|
|
2236
|
-
* Set when a step which can produce its result in-process executes. The
|
|
2237
|
-
* submission waits so that the route function can continue and reveal
|
|
2238
|
-
* what comes next: if that is a single step with step-level settings,
|
|
2239
|
-
* the settings ride on this submission and no step config request is
|
|
2240
|
-
* needed, since the delivery of the submission is what executes it.
|
|
2241
|
-
*
|
|
2242
|
-
* Flushed when the next step is reached (see `addStep`) or when the
|
|
2243
|
-
* route function ends (see `flushPendingStep` in `serve`).
|
|
2244
|
-
*/
|
|
2245
|
-
pendingStep;
|
|
2246
|
-
/**
|
|
2247
|
-
* configuration QStash applied to the delivery being handled. A step
|
|
2248
|
-
* with step-level settings is settled by comparing its settings against
|
|
2249
|
-
* this.
|
|
2250
|
-
*/
|
|
2251
|
-
effectiveConfig;
|
|
2252
2029
|
/**
|
|
2253
2030
|
* @param context workflow context
|
|
2254
2031
|
* @param steps list of steps
|
|
@@ -2256,17 +2033,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2256
2033
|
* @param dispatchLifecycle lifecycle event dispatcher
|
|
2257
2034
|
* @param telemetry optional telemetry information
|
|
2258
2035
|
* @param invokeCount optional invoke count
|
|
2259
|
-
* @param effectiveConfig configuration QStash applied to the delivery
|
|
2260
|
-
* being handled
|
|
2261
2036
|
*/
|
|
2262
|
-
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry2, invokeCount
|
|
2037
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry2, invokeCount) {
|
|
2263
2038
|
this.context = context;
|
|
2264
2039
|
this.steps = steps;
|
|
2265
2040
|
this.dispatchDebug = dispatchDebug;
|
|
2266
2041
|
this.dispatchLifecycle = dispatchLifecycle;
|
|
2267
2042
|
this.telemetry = telemetry2;
|
|
2268
2043
|
this.invokeCount = invokeCount ?? 0;
|
|
2269
|
-
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
2270
2044
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
2271
2045
|
}
|
|
2272
2046
|
/**
|
|
@@ -2301,15 +2075,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2301
2075
|
lazyStepList.push(stepInfo);
|
|
2302
2076
|
const index = this.indexInCurrentList++;
|
|
2303
2077
|
const requestComplete = this.deferExecution().then(async () => {
|
|
2304
|
-
const submitted = await this.submitPendingStep(
|
|
2305
|
-
lazyStepList.length === 1 ? lazyStepList[0].stepSettings : void 0
|
|
2306
|
-
);
|
|
2307
|
-
if (submitted.isErr()) {
|
|
2308
|
-
throw submitted.error;
|
|
2309
|
-
}
|
|
2310
|
-
if (submitted.value.result === "submitted-step") {
|
|
2311
|
-
throw submitted.value.abort;
|
|
2312
|
-
}
|
|
2313
2078
|
if (!this.promises.has(lazyStepList)) {
|
|
2314
2079
|
const promise2 = this.getExecutionPromise(lazyStepList);
|
|
2315
2080
|
this.promises.set(lazyStepList, promise2);
|
|
@@ -2363,100 +2128,18 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2363
2128
|
}
|
|
2364
2129
|
return parsedOut;
|
|
2365
2130
|
}
|
|
2366
|
-
const
|
|
2367
|
-
if (mismatch) {
|
|
2368
|
-
if (!this.effectiveConfig.hasStepConfig) {
|
|
2369
|
-
await publishStepConfigRequest({
|
|
2370
|
-
context: this.context,
|
|
2371
|
-
lazyStep,
|
|
2372
|
-
targetStep: this.stepCount,
|
|
2373
|
-
invokeCount: this.invokeCount,
|
|
2374
|
-
telemetry: this.telemetry,
|
|
2375
|
-
dispatchDebug: this.dispatchDebug
|
|
2376
|
-
});
|
|
2377
|
-
throw new WorkflowAbort(lazyStep.stepName);
|
|
2378
|
-
} else {
|
|
2379
|
-
await this.dispatchDebug("onWarning", {
|
|
2380
|
-
warning: `The step-level settings of step '${lazyStep.stepName}' were published but are not recognized on the delivery which carries them (${mismatch}). Executing the step with the settings of the delivery instead. This is a bug in @upstash/workflow, please report it.`
|
|
2381
|
-
});
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
const resultStep = await executeStep({
|
|
2385
|
-
lazyStep,
|
|
2386
|
-
stepId: this.stepCount,
|
|
2387
|
-
concurrency: NO_CONCURRENCY,
|
|
2388
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2389
|
-
});
|
|
2390
|
-
if (lazyStep.supportsDeferredSubmission) {
|
|
2391
|
-
this.pendingStep = { status: "held", lazyStep, resultStep };
|
|
2392
|
-
return lazyStep.parseOut({
|
|
2393
|
-
...resultStep,
|
|
2394
|
-
out: resultStep.out === void 0 ? void 0 : JSON.stringify(resultStep.out)
|
|
2395
|
-
});
|
|
2396
|
-
}
|
|
2397
|
-
await submitStepResult({
|
|
2131
|
+
const resultStep = await submitSingleStep({
|
|
2398
2132
|
context: this.context,
|
|
2399
2133
|
lazyStep,
|
|
2400
|
-
|
|
2134
|
+
stepId: this.stepCount,
|
|
2401
2135
|
invokeCount: this.invokeCount,
|
|
2402
|
-
concurrency:
|
|
2136
|
+
concurrency: 1,
|
|
2403
2137
|
telemetry: this.telemetry,
|
|
2404
|
-
dispatchDebug: this.dispatchDebug
|
|
2138
|
+
dispatchDebug: this.dispatchDebug,
|
|
2139
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2405
2140
|
});
|
|
2406
2141
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2407
2142
|
}
|
|
2408
|
-
/**
|
|
2409
|
-
* Submits the result of the step being held, if there is one, and
|
|
2410
|
-
* returns the abort the caller has to throw to end the invocation.
|
|
2411
|
-
*
|
|
2412
|
-
* Returns undefined when no step is being held, which is the caller's
|
|
2413
|
-
* signal that there is nothing to end. Throws instead of returning
|
|
2414
|
-
* when the submission itself fails, so that error reaches the caller
|
|
2415
|
-
* rather than an abort claiming the step was submitted.
|
|
2416
|
-
*
|
|
2417
|
-
* Callers which arrive while a submission is already under way (the
|
|
2418
|
-
* steps of a parallel group, added together) wait for that one and get
|
|
2419
|
-
* the same abort, rather than submitting a second time.
|
|
2420
|
-
*
|
|
2421
|
-
* The next step's settings are attached whenever it has any, without
|
|
2422
|
-
* checking them against the current delivery: the executor only knows
|
|
2423
|
-
* the configuration of the delivery in hand, which inside a
|
|
2424
|
-
* delivery is the *previous* step's, so such a check would be wrong
|
|
2425
|
-
* exactly when two steps with settings follow each other.
|
|
2426
|
-
*
|
|
2427
|
-
* @param nextStepSettings step-level settings of the next step
|
|
2428
|
-
* @returns `submitted-step` with the abort which ends this invocation,
|
|
2429
|
-
* or `no-pending-step` when nothing was held and there is nothing to
|
|
2430
|
-
* end
|
|
2431
|
-
*/
|
|
2432
|
-
async submitPendingStep(nextStepSettings) {
|
|
2433
|
-
if (!this.pendingStep) {
|
|
2434
|
-
return ok({ result: "no-pending-step" });
|
|
2435
|
-
}
|
|
2436
|
-
try {
|
|
2437
|
-
if (this.pendingStep.status === "submitting") {
|
|
2438
|
-
return ok({ result: "submitted-step", abort: await this.pendingStep.submitted });
|
|
2439
|
-
}
|
|
2440
|
-
const { lazyStep, resultStep } = this.pendingStep;
|
|
2441
|
-
const submitted = (async () => {
|
|
2442
|
-
await submitStepResult({
|
|
2443
|
-
context: this.context,
|
|
2444
|
-
lazyStep,
|
|
2445
|
-
resultStep,
|
|
2446
|
-
invokeCount: this.invokeCount,
|
|
2447
|
-
concurrency: NO_CONCURRENCY,
|
|
2448
|
-
telemetry: this.telemetry,
|
|
2449
|
-
dispatchDebug: this.dispatchDebug,
|
|
2450
|
-
nextStepSettings
|
|
2451
|
-
});
|
|
2452
|
-
return new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2453
|
-
})();
|
|
2454
|
-
this.pendingStep = { status: "submitting", submitted };
|
|
2455
|
-
return ok({ result: "submitted-step", abort: await submitted });
|
|
2456
|
-
} catch (error) {
|
|
2457
|
-
return err(error);
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
2143
|
/**
|
|
2461
2144
|
* Runs steps in parallel.
|
|
2462
2145
|
*
|
|
@@ -2505,20 +2188,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2505
2188
|
validateStep(parallelSteps[stepIndex], planStep);
|
|
2506
2189
|
try {
|
|
2507
2190
|
const parallelStep = parallelSteps[stepIndex];
|
|
2508
|
-
const resultStep = await
|
|
2509
|
-
lazyStep: parallelStep,
|
|
2510
|
-
stepId: planStep.targetStep,
|
|
2511
|
-
concurrency: parallelSteps.length,
|
|
2512
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2513
|
-
});
|
|
2514
|
-
await submitStepResult({
|
|
2191
|
+
const resultStep = await submitSingleStep({
|
|
2515
2192
|
context: this.context,
|
|
2516
2193
|
lazyStep: parallelStep,
|
|
2517
|
-
|
|
2194
|
+
stepId: planStep.targetStep,
|
|
2518
2195
|
invokeCount: this.invokeCount,
|
|
2519
2196
|
concurrency: parallelSteps.length,
|
|
2520
2197
|
telemetry: this.telemetry,
|
|
2521
|
-
dispatchDebug: this.dispatchDebug
|
|
2198
|
+
dispatchDebug: this.dispatchDebug,
|
|
2199
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2522
2200
|
});
|
|
2523
2201
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2524
2202
|
} catch (error) {
|
|
@@ -3134,46 +2812,6 @@ var WorkflowContext = class {
|
|
|
3134
2812
|
* `1` on the first retry, `2` on the second, and so on.
|
|
3135
2813
|
*/
|
|
3136
2814
|
retried;
|
|
3137
|
-
/**
|
|
3138
|
-
* Configuration QStash applied to the request being handled.
|
|
3139
|
-
*
|
|
3140
|
-
* This is the configuration of *this delivery*, not of the run: it is
|
|
3141
|
-
* the configuration the run was triggered with, except inside the
|
|
3142
|
-
* delivery which executes a step that has step-level settings, where
|
|
3143
|
-
* it is that step's settings. The fields below expose it; the executor
|
|
3144
|
-
* gets it directly rather than reaching through the context.
|
|
3145
|
-
*/
|
|
3146
|
-
effectiveConfig;
|
|
3147
|
-
/**
|
|
3148
|
-
* Flow control QStash applied to the request being handled, if any.
|
|
3149
|
-
*
|
|
3150
|
-
* This is the flow control of the request in hand, which is the one the
|
|
3151
|
-
* run was triggered with except inside a step carrying its own.
|
|
3152
|
-
*/
|
|
3153
|
-
get flowControl() {
|
|
3154
|
-
return this.effectiveConfig.flowControl;
|
|
3155
|
-
}
|
|
3156
|
-
/**
|
|
3157
|
-
* Retry limit QStash applied to the request being handled, if it
|
|
3158
|
-
* reported one.
|
|
3159
|
-
*
|
|
3160
|
-
* Not to be confused with {@link retried}, which is how many retries
|
|
3161
|
-
* have already happened.
|
|
3162
|
-
*
|
|
3163
|
-
* @see {@link flowControl} for what "the request being handled" means
|
|
3164
|
-
*/
|
|
3165
|
-
get retries() {
|
|
3166
|
-
return this.effectiveConfig.retries;
|
|
3167
|
-
}
|
|
3168
|
-
/**
|
|
3169
|
-
* Retry delay expression QStash applied to the request being handled,
|
|
3170
|
-
* if any.
|
|
3171
|
-
*
|
|
3172
|
-
* @see {@link flowControl} for what "the request being handled" means
|
|
3173
|
-
*/
|
|
3174
|
-
get retryDelay() {
|
|
3175
|
-
return this.effectiveConfig.retryDelay;
|
|
3176
|
-
}
|
|
3177
2815
|
constructor({
|
|
3178
2816
|
qstashClient,
|
|
3179
2817
|
workflowRunId,
|
|
@@ -3187,8 +2825,7 @@ var WorkflowContext = class {
|
|
|
3187
2825
|
invokeCount,
|
|
3188
2826
|
label,
|
|
3189
2827
|
retried,
|
|
3190
|
-
middlewareManager
|
|
3191
|
-
effectiveConfig
|
|
2828
|
+
middlewareManager
|
|
3192
2829
|
}) {
|
|
3193
2830
|
this.qstashClient = qstashClient;
|
|
3194
2831
|
this.workflowRunId = workflowRunId;
|
|
@@ -3200,7 +2837,6 @@ var WorkflowContext = class {
|
|
|
3200
2837
|
this.env = env ?? {};
|
|
3201
2838
|
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
3202
2839
|
this.retried = retried ?? 0;
|
|
3203
|
-
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
3204
2840
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
3205
2841
|
middlewareManagerInstance.assignContext(this);
|
|
3206
2842
|
this.executor = new AutoExecutor(
|
|
@@ -3209,8 +2845,7 @@ var WorkflowContext = class {
|
|
|
3209
2845
|
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
3210
2846
|
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
3211
2847
|
telemetry2,
|
|
3212
|
-
invokeCount
|
|
3213
|
-
this.effectiveConfig
|
|
2848
|
+
invokeCount
|
|
3214
2849
|
);
|
|
3215
2850
|
}
|
|
3216
2851
|
/**
|
|
@@ -3236,35 +2871,13 @@ var WorkflowContext = class {
|
|
|
3236
2871
|
* ])
|
|
3237
2872
|
* ```
|
|
3238
2873
|
*
|
|
3239
|
-
* Step-level settings can be passed as a third argument, overriding
|
|
3240
|
-
* the settings the workflow run was triggered with for this step only:
|
|
3241
|
-
*
|
|
3242
|
-
* ```typescript
|
|
3243
|
-
* const result = await context.run(
|
|
3244
|
-
* "step 1",
|
|
3245
|
-
* () => {
|
|
3246
|
-
* return "result"
|
|
3247
|
-
* },
|
|
3248
|
-
* {
|
|
3249
|
-
* flowControl: { key: "custom-key", parallelism: 3 },
|
|
3250
|
-
* retries: 5,
|
|
3251
|
-
* }
|
|
3252
|
-
* )
|
|
3253
|
-
* ```
|
|
3254
|
-
*
|
|
3255
2874
|
* @param stepName name of the step
|
|
3256
2875
|
* @param stepFunction step function to be executed
|
|
3257
|
-
* @param stepSettings step-level settings for this step
|
|
3258
2876
|
* @returns result of the step function
|
|
3259
2877
|
*/
|
|
3260
|
-
async run(stepName, stepFunction
|
|
3261
|
-
if (stepSettings) {
|
|
3262
|
-
validateFlowControl(stepSettings.flowControl);
|
|
3263
|
-
}
|
|
2878
|
+
async run(stepName, stepFunction) {
|
|
3264
2879
|
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
3265
|
-
return await this.addStep(
|
|
3266
|
-
new LazyFunctionStep(this, stepName, wrappedStepFunction, stepSettings)
|
|
3267
|
-
);
|
|
2880
|
+
return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
|
|
3268
2881
|
}
|
|
3269
2882
|
/**
|
|
3270
2883
|
* Stops the execution for the duration provided.
|
|
@@ -3487,7 +3100,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3487
3100
|
*
|
|
3488
3101
|
* @param routeFunction
|
|
3489
3102
|
*/
|
|
3490
|
-
static async tryAuthentication(routeFunction, context
|
|
3103
|
+
static async tryAuthentication(routeFunction, context) {
|
|
3491
3104
|
const disabledContext = new _DisabledWorkflowContext({
|
|
3492
3105
|
qstashClient: new import_qstash10.Client({
|
|
3493
3106
|
baseUrl: "disabled-client",
|
|
@@ -3502,10 +3115,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3502
3115
|
initialPayload: context.requestPayload,
|
|
3503
3116
|
env: context.env,
|
|
3504
3117
|
label: context.labels,
|
|
3505
|
-
retried: context.retried
|
|
3506
|
-
// the route function runs for real here until it reaches a step, so
|
|
3507
|
-
// it observes the same configuration as it will on the real context
|
|
3508
|
-
effectiveConfig
|
|
3118
|
+
retried: context.retried
|
|
3509
3119
|
});
|
|
3510
3120
|
try {
|
|
3511
3121
|
await routeFunction(disabledContext);
|
|
@@ -3722,7 +3332,6 @@ var handleFailure = async ({
|
|
|
3722
3332
|
}
|
|
3723
3333
|
const userHeaders = recreateUserHeaders(request.headers);
|
|
3724
3334
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3725
|
-
const effectiveConfig = getEffectiveConfig(request.headers);
|
|
3726
3335
|
const workflowContext = new WorkflowContext({
|
|
3727
3336
|
qstashClient,
|
|
3728
3337
|
workflowRunId,
|
|
@@ -3736,13 +3345,11 @@ var handleFailure = async ({
|
|
|
3736
3345
|
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3737
3346
|
retried,
|
|
3738
3347
|
workflowRunCreatedAt: workflowCreatedAt,
|
|
3739
|
-
middlewareManager: void 0
|
|
3740
|
-
effectiveConfig
|
|
3348
|
+
middlewareManager: void 0
|
|
3741
3349
|
});
|
|
3742
3350
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3743
3351
|
routeFunction,
|
|
3744
|
-
workflowContext
|
|
3745
|
-
effectiveConfig
|
|
3352
|
+
workflowContext
|
|
3746
3353
|
);
|
|
3747
3354
|
if (authCheck.isErr()) {
|
|
3748
3355
|
await dispatchDebug?.("onError", {
|
|
@@ -4231,28 +3838,26 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
|
|
|
4231
3838
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
4232
3839
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
4233
3840
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
4234
|
-
const
|
|
4235
|
-
const
|
|
3841
|
+
const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
3842
|
+
const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
|
|
4236
3843
|
const workflowContext = new WorkflowContext({
|
|
4237
3844
|
qstashClient: regionalClient,
|
|
4238
3845
|
workflowRunId,
|
|
4239
3846
|
initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
|
|
4240
3847
|
headers: recreateUserHeaders(request.headers),
|
|
4241
3848
|
steps,
|
|
4242
|
-
effectiveConfig,
|
|
4243
3849
|
url: workflowUrl,
|
|
4244
3850
|
env,
|
|
4245
3851
|
telemetry: telemetry2,
|
|
4246
3852
|
invokeCount,
|
|
4247
3853
|
label,
|
|
4248
3854
|
retried,
|
|
4249
|
-
workflowRunCreatedAt
|
|
3855
|
+
workflowRunCreatedAt,
|
|
4250
3856
|
middlewareManager
|
|
4251
3857
|
});
|
|
4252
3858
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
4253
3859
|
routeFunction,
|
|
4254
|
-
workflowContext
|
|
4255
|
-
effectiveConfig
|
|
3860
|
+
workflowContext
|
|
4256
3861
|
);
|
|
4257
3862
|
if (authCheck.isErr()) {
|
|
4258
3863
|
throw authCheck.error;
|
|
@@ -4289,23 +3894,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
|
|
|
4289
3894
|
if (steps.length === 1) {
|
|
4290
3895
|
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
4291
3896
|
}
|
|
4292
|
-
|
|
4293
|
-
try {
|
|
4294
|
-
outcome = { ran: true, result: await routeFunction(workflowContext) };
|
|
4295
|
-
} catch (error) {
|
|
4296
|
-
outcome = { ran: false, error };
|
|
4297
|
-
}
|
|
4298
|
-
const submitted = await flushPendingStep(workflowContext);
|
|
4299
|
-
if (submitted.isErr()) {
|
|
4300
|
-
throw submitted.error;
|
|
4301
|
-
}
|
|
4302
|
-
if (submitted.value.result === "submitted-step") {
|
|
4303
|
-
throw submitted.value.abort;
|
|
4304
|
-
}
|
|
4305
|
-
if (!outcome.ran) {
|
|
4306
|
-
throw outcome.error;
|
|
4307
|
-
}
|
|
4308
|
-
return outcome.result;
|
|
3897
|
+
return await routeFunction(workflowContext);
|
|
4309
3898
|
},
|
|
4310
3899
|
onCleanup: async (result2) => {
|
|
4311
3900
|
await middlewareManager.dispatchLifecycle("runCompleted", {
|