@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/solidjs.js
CHANGED
|
@@ -215,14 +215,6 @@ var WORKFLOW_CREATED_AT_HEADER = "Upstash-Workflow-CreatedAt";
|
|
|
215
215
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
216
216
|
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
217
217
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
218
|
-
var WORKFLOW_FEATURE_SET = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
219
|
-
var WORKFLOW_STEP_CONFIG_CALL_TYPE = "stepConfig";
|
|
220
|
-
var WORKFLOW_STEP_CONFIG_HEADER = "Upstash-Workflow-Step-Config";
|
|
221
|
-
var FLOW_CONTROL_KEY_HEADER = "Upstash-Flow-Control-Key";
|
|
222
|
-
var FLOW_CONTROL_VALUE_HEADER = "Upstash-Flow-Control-Value";
|
|
223
|
-
var RETRIES_HEADER = "Upstash-Retries";
|
|
224
|
-
var RETRY_DELAY_HEADER = "Upstash-Retry-Delay";
|
|
225
|
-
var WORKFLOW_STEP_CONFIG_FEATURE = "WF_StepConfig";
|
|
226
218
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
227
219
|
var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
228
220
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
@@ -240,6 +232,102 @@ var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
|
240
232
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
241
233
|
var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
|
|
242
234
|
|
|
235
|
+
// src/context/auto-executor.ts
|
|
236
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
237
|
+
|
|
238
|
+
// src/qstash/headers.ts
|
|
239
|
+
var import_qstash4 = require("@upstash/qstash");
|
|
240
|
+
|
|
241
|
+
// src/utils.ts
|
|
242
|
+
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
243
|
+
var NANOID_LENGTH = 21;
|
|
244
|
+
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
245
|
+
function validateLabel(label) {
|
|
246
|
+
if (label === void 0) return;
|
|
247
|
+
const labels = Array.isArray(label) ? label : [label];
|
|
248
|
+
if (labels.length === 0) {
|
|
249
|
+
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
250
|
+
}
|
|
251
|
+
for (const value of labels) {
|
|
252
|
+
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
253
|
+
throw new WorkflowNonRetryableError(
|
|
254
|
+
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function serializeLabel(label) {
|
|
260
|
+
return Array.isArray(label) ? label.join(",") : label;
|
|
261
|
+
}
|
|
262
|
+
function validateFlowControl(flowControl) {
|
|
263
|
+
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
264
|
+
throw new WorkflowNonRetryableError(
|
|
265
|
+
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function getRandomInt() {
|
|
270
|
+
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
271
|
+
}
|
|
272
|
+
function nanoid(length = NANOID_LENGTH) {
|
|
273
|
+
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
274
|
+
}
|
|
275
|
+
function getWorkflowRunId(id) {
|
|
276
|
+
return `wfr_${id ?? nanoid()}`;
|
|
277
|
+
}
|
|
278
|
+
function decodeBase64(base64) {
|
|
279
|
+
const binString = atob(base64);
|
|
280
|
+
try {
|
|
281
|
+
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
282
|
+
return new TextDecoder().decode(intArray);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.warn(
|
|
285
|
+
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
286
|
+
);
|
|
287
|
+
return binString;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function getUserIdFromToken(qstashClient) {
|
|
291
|
+
try {
|
|
292
|
+
const token = qstashClient.token;
|
|
293
|
+
const decodedToken = decodeBase64(token);
|
|
294
|
+
const tokenPayload = JSON.parse(decodedToken);
|
|
295
|
+
const userId = tokenPayload.UserID;
|
|
296
|
+
if (!userId) {
|
|
297
|
+
throw new WorkflowError("QStash token payload does not contain userId");
|
|
298
|
+
}
|
|
299
|
+
return userId;
|
|
300
|
+
} catch (error) {
|
|
301
|
+
throw new WorkflowError(
|
|
302
|
+
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function getQStashUrl(qstashClient) {
|
|
307
|
+
try {
|
|
308
|
+
const requester = qstashClient.http;
|
|
309
|
+
const baseUrl = requester.baseUrl;
|
|
310
|
+
if (!baseUrl) {
|
|
311
|
+
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
312
|
+
}
|
|
313
|
+
return baseUrl;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function getEventId() {
|
|
319
|
+
return `evt_${nanoid(15)}`;
|
|
320
|
+
}
|
|
321
|
+
function stringifyBody(body) {
|
|
322
|
+
if (body === void 0) {
|
|
323
|
+
return void 0;
|
|
324
|
+
}
|
|
325
|
+
if (typeof body === "string") {
|
|
326
|
+
return body;
|
|
327
|
+
}
|
|
328
|
+
return JSON.stringify(body);
|
|
329
|
+
}
|
|
330
|
+
|
|
243
331
|
// node_modules/neverthrow/dist/index.es.js
|
|
244
332
|
var defaultErrorConfig = {
|
|
245
333
|
withStackTrace: false
|
|
@@ -655,102 +743,6 @@ var Err = class {
|
|
|
655
743
|
};
|
|
656
744
|
var fromThrowable = Result.fromThrowable;
|
|
657
745
|
|
|
658
|
-
// src/context/auto-executor.ts
|
|
659
|
-
var import_qstash5 = require("@upstash/qstash");
|
|
660
|
-
|
|
661
|
-
// src/qstash/headers.ts
|
|
662
|
-
var import_qstash4 = require("@upstash/qstash");
|
|
663
|
-
|
|
664
|
-
// src/utils.ts
|
|
665
|
-
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
666
|
-
var NANOID_LENGTH = 21;
|
|
667
|
-
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
668
|
-
function validateLabel(label) {
|
|
669
|
-
if (label === void 0) return;
|
|
670
|
-
const labels = Array.isArray(label) ? label : [label];
|
|
671
|
-
if (labels.length === 0) {
|
|
672
|
-
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
673
|
-
}
|
|
674
|
-
for (const value of labels) {
|
|
675
|
-
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
676
|
-
throw new WorkflowNonRetryableError(
|
|
677
|
-
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
function serializeLabel(label) {
|
|
683
|
-
return Array.isArray(label) ? label.join(",") : label;
|
|
684
|
-
}
|
|
685
|
-
function validateFlowControl(flowControl) {
|
|
686
|
-
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
687
|
-
throw new WorkflowNonRetryableError(
|
|
688
|
-
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
689
|
-
);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
function getRandomInt() {
|
|
693
|
-
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
694
|
-
}
|
|
695
|
-
function nanoid(length = NANOID_LENGTH) {
|
|
696
|
-
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
697
|
-
}
|
|
698
|
-
function getWorkflowRunId(id) {
|
|
699
|
-
return `wfr_${id ?? nanoid()}`;
|
|
700
|
-
}
|
|
701
|
-
function decodeBase64(base64) {
|
|
702
|
-
const binString = atob(base64);
|
|
703
|
-
try {
|
|
704
|
-
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
705
|
-
return new TextDecoder().decode(intArray);
|
|
706
|
-
} catch (error) {
|
|
707
|
-
console.warn(
|
|
708
|
-
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
709
|
-
);
|
|
710
|
-
return binString;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
function getUserIdFromToken(qstashClient) {
|
|
714
|
-
try {
|
|
715
|
-
const token = qstashClient.token;
|
|
716
|
-
const decodedToken = decodeBase64(token);
|
|
717
|
-
const tokenPayload = JSON.parse(decodedToken);
|
|
718
|
-
const userId = tokenPayload.UserID;
|
|
719
|
-
if (!userId) {
|
|
720
|
-
throw new WorkflowError("QStash token payload does not contain userId");
|
|
721
|
-
}
|
|
722
|
-
return userId;
|
|
723
|
-
} catch (error) {
|
|
724
|
-
throw new WorkflowError(
|
|
725
|
-
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
726
|
-
);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
function getQStashUrl(qstashClient) {
|
|
730
|
-
try {
|
|
731
|
-
const requester = qstashClient.http;
|
|
732
|
-
const baseUrl = requester.baseUrl;
|
|
733
|
-
if (!baseUrl) {
|
|
734
|
-
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
735
|
-
}
|
|
736
|
-
return baseUrl;
|
|
737
|
-
} catch (error) {
|
|
738
|
-
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
function getEventId() {
|
|
742
|
-
return `evt_${nanoid(15)}`;
|
|
743
|
-
}
|
|
744
|
-
function stringifyBody(body) {
|
|
745
|
-
if (body === void 0) {
|
|
746
|
-
return void 0;
|
|
747
|
-
}
|
|
748
|
-
if (typeof body === "string") {
|
|
749
|
-
return body;
|
|
750
|
-
}
|
|
751
|
-
return JSON.stringify(body);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
746
|
// src/types.ts
|
|
755
747
|
var StepTypes = [
|
|
756
748
|
"Initial",
|
|
@@ -790,6 +782,10 @@ var triggerFirstInvocation = async (params) => {
|
|
|
790
782
|
workflowConfig: {
|
|
791
783
|
workflowRunId: workflowContext.workflowRunId,
|
|
792
784
|
workflowUrl: workflowContext.url,
|
|
785
|
+
// the run doesn't exist yet, so the trigger time is sent when the creation time
|
|
786
|
+
// is not known. serve and client.trigger set it on the context already, so a
|
|
787
|
+
// retry of this request carries the same value and is deduplicated.
|
|
788
|
+
workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
|
|
793
789
|
failureUrl,
|
|
794
790
|
retries,
|
|
795
791
|
retryDelay,
|
|
@@ -856,10 +852,6 @@ var triggerFirstInvocation = async (params) => {
|
|
|
856
852
|
return err(error_);
|
|
857
853
|
}
|
|
858
854
|
};
|
|
859
|
-
var flushPendingStep = async (workflowContext) => {
|
|
860
|
-
const { executor } = workflowContext;
|
|
861
|
-
return await executor.submitPendingStep();
|
|
862
|
-
};
|
|
863
855
|
var triggerRouteFunction = async ({
|
|
864
856
|
onCleanup,
|
|
865
857
|
onStep,
|
|
@@ -897,7 +889,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
|
|
|
897
889
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
898
890
|
method: "DELETE",
|
|
899
891
|
parseResponseAsJson: false,
|
|
900
|
-
body: JSON.stringify(result)
|
|
892
|
+
body: JSON.stringify(result),
|
|
893
|
+
// with the creation time, QStash deletes the run only if it is the same run.
|
|
894
|
+
// a late delete of a previous run can't delete a new run with the same id.
|
|
895
|
+
headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
|
|
901
896
|
});
|
|
902
897
|
await dispatchDebug?.("onInfo", {
|
|
903
898
|
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
@@ -974,6 +969,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
974
969
|
const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
|
|
975
970
|
const contentType = request.headers.get("Upstash-Workflow-ContentType");
|
|
976
971
|
const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
|
|
972
|
+
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
977
973
|
if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
|
|
978
974
|
throw new Error(
|
|
979
975
|
`Missing info in callback message source header: ${JSON.stringify({
|
|
@@ -991,6 +987,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
991
987
|
workflowConfig: {
|
|
992
988
|
workflowRunId,
|
|
993
989
|
workflowUrl,
|
|
990
|
+
workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
|
|
994
991
|
telemetry
|
|
995
992
|
},
|
|
996
993
|
invokeCount: Number(invokeCount)
|
|
@@ -1067,29 +1064,8 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
|
|
|
1067
1064
|
var BaseLazyStep = class _BaseLazyStep {
|
|
1068
1065
|
stepName;
|
|
1069
1066
|
context;
|
|
1070
|
-
|
|
1071
|
-
* step-level settings (flow control, retries etc.) which override the
|
|
1072
|
-
* settings the workflow run was triggered with, for this step only.
|
|
1073
|
-
*/
|
|
1074
|
-
stepSettings;
|
|
1075
|
-
/**
|
|
1076
|
-
* whether this step's result can be submitted after the route function
|
|
1077
|
-
* has moved on, instead of right away.
|
|
1078
|
-
*
|
|
1079
|
-
* Enabled for steps whose `getResultStep` produces the final `out` —
|
|
1080
|
-
* which is not the same as "needs no server round trip":
|
|
1081
|
-
* `LazyCreateWebhookStep` builds its result in `getBody` and returns
|
|
1082
|
-
* `out: undefined` from `getResultStep`, so deferring it would hand
|
|
1083
|
-
* `undefined` to the route function.
|
|
1084
|
-
*
|
|
1085
|
-
* Deferring lets the route function continue and reveal the next step,
|
|
1086
|
-
* so that step's settings can ride on this step's submission instead
|
|
1087
|
-
* of needing a step config request of their own.
|
|
1088
|
-
*/
|
|
1089
|
-
supportsDeferredSubmission = false;
|
|
1090
|
-
constructor(context, stepName, stepSettings) {
|
|
1067
|
+
constructor(context, stepName) {
|
|
1091
1068
|
this.context = context;
|
|
1092
|
-
this.stepSettings = stepSettings;
|
|
1093
1069
|
if (!stepName) {
|
|
1094
1070
|
throw new WorkflowError(
|
|
1095
1071
|
"A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
|
|
@@ -1153,18 +1129,13 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1153
1129
|
step.out = JSON.stringify(step.out);
|
|
1154
1130
|
return JSON.stringify(step);
|
|
1155
1131
|
}
|
|
1156
|
-
getHeaders({
|
|
1157
|
-
context,
|
|
1158
|
-
telemetry,
|
|
1159
|
-
invokeCount,
|
|
1160
|
-
step,
|
|
1161
|
-
stepSettings
|
|
1162
|
-
}) {
|
|
1132
|
+
getHeaders({ context, telemetry, invokeCount, step }) {
|
|
1163
1133
|
return getHeaders({
|
|
1164
1134
|
initHeaderValue: "false",
|
|
1165
1135
|
workflowConfig: {
|
|
1166
1136
|
workflowRunId: context.workflowRunId,
|
|
1167
1137
|
workflowUrl: context.url,
|
|
1138
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1168
1139
|
useJSONContent: false,
|
|
1169
1140
|
telemetry
|
|
1170
1141
|
},
|
|
@@ -1172,8 +1143,7 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1172
1143
|
stepInfo: {
|
|
1173
1144
|
step,
|
|
1174
1145
|
lazyStep: this
|
|
1175
|
-
}
|
|
1176
|
-
stepSettings
|
|
1146
|
+
}
|
|
1177
1147
|
});
|
|
1178
1148
|
}
|
|
1179
1149
|
async submitStep({ context, body, headers }) {
|
|
@@ -1191,9 +1161,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
|
|
|
1191
1161
|
stepFunction;
|
|
1192
1162
|
stepType = "Run";
|
|
1193
1163
|
allowUndefinedOut = true;
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
super(context, stepName, stepSettings);
|
|
1164
|
+
constructor(context, stepName, stepFunction) {
|
|
1165
|
+
super(context, stepName);
|
|
1197
1166
|
this.stepFunction = stepFunction;
|
|
1198
1167
|
}
|
|
1199
1168
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1223,7 +1192,6 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1223
1192
|
sleep;
|
|
1224
1193
|
stepType = "SleepFor";
|
|
1225
1194
|
allowUndefinedOut = true;
|
|
1226
|
-
supportsDeferredSubmission = true;
|
|
1227
1195
|
constructor(context, stepName, sleep) {
|
|
1228
1196
|
super(context, stepName);
|
|
1229
1197
|
this.sleep = sleep;
|
|
@@ -1263,7 +1231,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1263
1231
|
sleepUntil;
|
|
1264
1232
|
stepType = "SleepUntil";
|
|
1265
1233
|
allowUndefinedOut = true;
|
|
1266
|
-
supportsDeferredSubmission = true;
|
|
1267
1234
|
constructor(context, stepName, sleepUntil) {
|
|
1268
1235
|
super(context, stepName);
|
|
1269
1236
|
this.sleepUntil = sleepUntil;
|
|
@@ -1496,6 +1463,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
|
|
|
1496
1463
|
) : {},
|
|
1497
1464
|
// note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
|
|
1498
1465
|
"Upstash-Workflow-Runid": [context.workflowRunId],
|
|
1466
|
+
// same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
|
|
1467
|
+
...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
|
|
1499
1468
|
[WORKFLOW_INIT_HEADER]: ["false"],
|
|
1500
1469
|
[WORKFLOW_URL_HEADER]: [context.url],
|
|
1501
1470
|
"Upstash-Workflow-CallType": ["step"]
|
|
@@ -1598,6 +1567,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1598
1567
|
workflowConfig: {
|
|
1599
1568
|
workflowRunId: context.workflowRunId,
|
|
1600
1569
|
workflowUrl: context.url,
|
|
1570
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1601
1571
|
telemetry,
|
|
1602
1572
|
useJSONContent: false
|
|
1603
1573
|
},
|
|
@@ -1607,6 +1577,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1607
1577
|
invokerHeaders[key] = value;
|
|
1608
1578
|
});
|
|
1609
1579
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1580
|
+
if (context.workflowRunCreatedAt) {
|
|
1581
|
+
invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
|
|
1582
|
+
}
|
|
1610
1583
|
const request = {
|
|
1611
1584
|
body: stringifyBody(this.params.body),
|
|
1612
1585
|
headers: Object.fromEntries(
|
|
@@ -1635,6 +1608,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1635
1608
|
workflowConfig: {
|
|
1636
1609
|
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1637
1610
|
workflowUrl: newUrl,
|
|
1611
|
+
// the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
|
|
1612
|
+
// it separates the invocations made by different runs of the invoker.
|
|
1613
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1638
1614
|
retries,
|
|
1639
1615
|
retryDelay,
|
|
1640
1616
|
telemetry,
|
|
@@ -1756,7 +1732,6 @@ var WorkflowHeaders = class {
|
|
|
1756
1732
|
invokeCount;
|
|
1757
1733
|
initHeaderValue;
|
|
1758
1734
|
stepInfo;
|
|
1759
|
-
stepSettings;
|
|
1760
1735
|
headers;
|
|
1761
1736
|
/**
|
|
1762
1737
|
* @param params workflow header parameters
|
|
@@ -1766,20 +1741,17 @@ var WorkflowHeaders = class {
|
|
|
1766
1741
|
workflowConfig,
|
|
1767
1742
|
invokeCount,
|
|
1768
1743
|
initHeaderValue,
|
|
1769
|
-
stepInfo
|
|
1770
|
-
stepSettings
|
|
1744
|
+
stepInfo
|
|
1771
1745
|
}) {
|
|
1772
1746
|
this.userHeaders = userHeaders;
|
|
1773
1747
|
this.workflowConfig = workflowConfig;
|
|
1774
1748
|
this.invokeCount = invokeCount;
|
|
1775
1749
|
this.initHeaderValue = initHeaderValue;
|
|
1776
1750
|
this.stepInfo = stepInfo;
|
|
1777
|
-
this.stepSettings = stepSettings;
|
|
1778
1751
|
this.headers = {
|
|
1779
1752
|
rawHeaders: {},
|
|
1780
1753
|
workflowHeaders: {},
|
|
1781
|
-
failureHeaders: {}
|
|
1782
|
-
stepSettingsHeaders: {}
|
|
1754
|
+
failureHeaders: {}
|
|
1783
1755
|
};
|
|
1784
1756
|
}
|
|
1785
1757
|
getHeaders() {
|
|
@@ -1790,7 +1762,6 @@ var WorkflowHeaders = class {
|
|
|
1790
1762
|
this.addUserHeaders();
|
|
1791
1763
|
this.addInvokeCount();
|
|
1792
1764
|
this.addFailureUrl();
|
|
1793
|
-
this.addStepSettings();
|
|
1794
1765
|
const contentType = this.addContentType();
|
|
1795
1766
|
return this.prefixHeaders(contentType);
|
|
1796
1767
|
}
|
|
@@ -1800,10 +1771,13 @@ var WorkflowHeaders = class {
|
|
|
1800
1771
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1801
1772
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1802
1773
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1803
|
-
[WORKFLOW_FEATURE_HEADER]:
|
|
1774
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1804
1775
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1805
1776
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1806
1777
|
};
|
|
1778
|
+
if (this.workflowConfig.workflowRunCreatedAt) {
|
|
1779
|
+
this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
|
|
1780
|
+
}
|
|
1807
1781
|
if (this.stepInfo?.lazyStep.stepType !== "Call") {
|
|
1808
1782
|
this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
1809
1783
|
}
|
|
@@ -1878,7 +1852,7 @@ var WorkflowHeaders = class {
|
|
|
1878
1852
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1879
1853
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1880
1854
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1881
|
-
this.headers.failureHeaders["Feature-Set"] =
|
|
1855
|
+
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
1882
1856
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1883
1857
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1884
1858
|
}
|
|
@@ -1886,42 +1860,6 @@ var WorkflowHeaders = class {
|
|
|
1886
1860
|
this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
|
|
1887
1861
|
}
|
|
1888
1862
|
}
|
|
1889
|
-
/**
|
|
1890
|
-
* Applies the step-level settings of a step to the message.
|
|
1891
|
-
*
|
|
1892
|
-
* These headers configure the message itself, so that QStash uses them
|
|
1893
|
-
* instead of the settings the run was triggered with. The delivery of
|
|
1894
|
-
* the message is what executes the step, which is how a step's
|
|
1895
|
-
* settings reach it.
|
|
1896
|
-
*
|
|
1897
|
-
* When any setting is present the feature set is extended with
|
|
1898
|
-
* `WF_StepConfig`, which is what tells QStash to keep them. QStash
|
|
1899
|
-
* reports that back on the delivery as `Upstash-Workflow-Step-Config`.
|
|
1900
|
-
*/
|
|
1901
|
-
addStepSettings() {
|
|
1902
|
-
if (!this.stepSettings) {
|
|
1903
|
-
return;
|
|
1904
|
-
}
|
|
1905
|
-
const headers = {};
|
|
1906
|
-
if (this.stepSettings.flowControl) {
|
|
1907
|
-
const { flowControlKey, flowControlValue } = prepareFlowControl(
|
|
1908
|
-
this.stepSettings.flowControl
|
|
1909
|
-
);
|
|
1910
|
-
headers[FLOW_CONTROL_KEY_HEADER] = flowControlKey;
|
|
1911
|
-
headers[FLOW_CONTROL_VALUE_HEADER] = flowControlValue;
|
|
1912
|
-
}
|
|
1913
|
-
if (this.stepSettings.retries !== void 0) {
|
|
1914
|
-
headers[RETRIES_HEADER] = this.stepSettings.retries.toString();
|
|
1915
|
-
}
|
|
1916
|
-
if (this.stepSettings.retryDelay) {
|
|
1917
|
-
headers[RETRY_DELAY_HEADER] = this.stepSettings.retryDelay;
|
|
1918
|
-
}
|
|
1919
|
-
if (Object.keys(headers).length === 0) {
|
|
1920
|
-
return;
|
|
1921
|
-
}
|
|
1922
|
-
headers[WORKFLOW_FEATURE_HEADER] = `${WORKFLOW_FEATURE_SET},${WORKFLOW_STEP_CONFIG_FEATURE}`;
|
|
1923
|
-
this.headers.stepSettingsHeaders = headers;
|
|
1924
|
-
}
|
|
1925
1863
|
addContentType() {
|
|
1926
1864
|
if (this.workflowConfig.useJSONContent) {
|
|
1927
1865
|
this.headers.rawHeaders["content-type"] = "application/json";
|
|
@@ -1935,17 +1873,14 @@ var WorkflowHeaders = class {
|
|
|
1935
1873
|
return contentType;
|
|
1936
1874
|
}
|
|
1937
1875
|
prefixHeaders(contentType) {
|
|
1938
|
-
const { rawHeaders, workflowHeaders, failureHeaders
|
|
1876
|
+
const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
|
|
1939
1877
|
const isCall = this.stepInfo?.lazyStep.stepType === "Call";
|
|
1940
1878
|
return {
|
|
1941
1879
|
headers: {
|
|
1942
1880
|
...rawHeaders,
|
|
1943
1881
|
...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
|
|
1944
1882
|
...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
|
|
1945
|
-
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
|
|
1946
|
-
// last, so they override the run's settings above. Never
|
|
1947
|
-
// prefixed: they configure this message, not its callback.
|
|
1948
|
-
...stepSettingsHeaders
|
|
1883
|
+
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
|
|
1949
1884
|
},
|
|
1950
1885
|
contentType
|
|
1951
1886
|
};
|
|
@@ -1996,16 +1931,16 @@ var submitParallelSteps = async ({
|
|
|
1996
1931
|
info: `Submitting ${planSteps.length} parallel steps.`
|
|
1997
1932
|
});
|
|
1998
1933
|
const result = await context.qstashClient.batch(
|
|
1999
|
-
planSteps.map((planStep
|
|
1934
|
+
planSteps.map((planStep) => {
|
|
2000
1935
|
const { headers } = getHeaders({
|
|
2001
1936
|
initHeaderValue: "false",
|
|
2002
1937
|
workflowConfig: {
|
|
2003
1938
|
workflowRunId: context.workflowRunId,
|
|
2004
1939
|
workflowUrl: context.url,
|
|
1940
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
2005
1941
|
telemetry
|
|
2006
1942
|
},
|
|
2007
|
-
invokeCount
|
|
2008
|
-
stepSettings: steps[index].stepSettings
|
|
1943
|
+
invokeCount
|
|
2009
1944
|
});
|
|
2010
1945
|
return {
|
|
2011
1946
|
headers,
|
|
@@ -2024,38 +1959,31 @@ var submitParallelSteps = async ({
|
|
|
2024
1959
|
}
|
|
2025
1960
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
2026
1961
|
};
|
|
2027
|
-
var
|
|
1962
|
+
var submitSingleStep = async ({
|
|
1963
|
+
context,
|
|
2028
1964
|
lazyStep,
|
|
2029
1965
|
stepId,
|
|
1966
|
+
invokeCount,
|
|
2030
1967
|
concurrency,
|
|
1968
|
+
telemetry,
|
|
1969
|
+
dispatchDebug,
|
|
2031
1970
|
dispatchLifecycle
|
|
2032
1971
|
}) => {
|
|
2033
1972
|
await dispatchLifecycle("beforeExecution", {
|
|
2034
1973
|
stepName: lazyStep.stepName
|
|
2035
1974
|
});
|
|
1975
|
+
let resultStep;
|
|
2036
1976
|
try {
|
|
2037
|
-
|
|
1977
|
+
resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
2038
1978
|
} catch (error) {
|
|
2039
1979
|
attachStepNameToError(error, lazyStep.stepName);
|
|
2040
1980
|
throw error;
|
|
2041
1981
|
}
|
|
2042
|
-
};
|
|
2043
|
-
var submitStepResult = async ({
|
|
2044
|
-
context,
|
|
2045
|
-
lazyStep,
|
|
2046
|
-
resultStep,
|
|
2047
|
-
invokeCount,
|
|
2048
|
-
concurrency,
|
|
2049
|
-
telemetry,
|
|
2050
|
-
dispatchDebug,
|
|
2051
|
-
nextStepSettings
|
|
2052
|
-
}) => {
|
|
2053
1982
|
const { headers } = lazyStep.getHeaders({
|
|
2054
1983
|
context,
|
|
2055
1984
|
step: resultStep,
|
|
2056
1985
|
invokeCount,
|
|
2057
|
-
telemetry
|
|
2058
|
-
stepSettings: nextStepSettings
|
|
1986
|
+
telemetry
|
|
2059
1987
|
});
|
|
2060
1988
|
const body = lazyStep.getBody({
|
|
2061
1989
|
context,
|
|
@@ -2080,138 +2008,6 @@ var submitStepResult = async ({
|
|
|
2080
2008
|
}
|
|
2081
2009
|
return resultStep;
|
|
2082
2010
|
};
|
|
2083
|
-
var publishStepConfigRequest = async ({
|
|
2084
|
-
context,
|
|
2085
|
-
lazyStep,
|
|
2086
|
-
targetStep,
|
|
2087
|
-
invokeCount,
|
|
2088
|
-
telemetry,
|
|
2089
|
-
dispatchDebug
|
|
2090
|
-
}) => {
|
|
2091
|
-
const { headers } = getHeaders({
|
|
2092
|
-
initHeaderValue: "false",
|
|
2093
|
-
workflowConfig: {
|
|
2094
|
-
workflowRunId: context.workflowRunId,
|
|
2095
|
-
workflowUrl: context.url,
|
|
2096
|
-
telemetry
|
|
2097
|
-
},
|
|
2098
|
-
invokeCount,
|
|
2099
|
-
stepSettings: lazyStep.stepSettings
|
|
2100
|
-
});
|
|
2101
|
-
await dispatchDebug("onInfo", {
|
|
2102
|
-
info: `Step "${lazyStep.stepName}" (${targetStep}) has step-level settings which the current delivery does not carry. Requesting a delivery which does.`
|
|
2103
|
-
});
|
|
2104
|
-
const result = await context.qstashClient.publishJSON({
|
|
2105
|
-
headers: {
|
|
2106
|
-
...headers,
|
|
2107
|
-
"Upstash-Workflow-CallType": WORKFLOW_STEP_CONFIG_CALL_TYPE
|
|
2108
|
-
},
|
|
2109
|
-
method: "POST",
|
|
2110
|
-
body: { targetStep, invokeCount },
|
|
2111
|
-
url: context.url,
|
|
2112
|
-
contentBasedDeduplication: true
|
|
2113
|
-
});
|
|
2114
|
-
if (result?.deduplicated) {
|
|
2115
|
-
await dispatchDebug("onWarning", {
|
|
2116
|
-
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.`
|
|
2117
|
-
});
|
|
2118
|
-
} else if (result?.messageId) {
|
|
2119
|
-
await dispatchDebug("onInfo", {
|
|
2120
|
-
info: `Requested a delivery under the settings of step "${lazyStep.stepName}" with messageId: ${result.messageId}.`
|
|
2121
|
-
});
|
|
2122
|
-
}
|
|
2123
|
-
};
|
|
2124
|
-
|
|
2125
|
-
// src/qstash/step-config.ts
|
|
2126
|
-
var DEFAULT_PARALLELISM = 0;
|
|
2127
|
-
var DEFAULT_RATE = 0;
|
|
2128
|
-
var DEFAULT_PERIOD_SECONDS = 1;
|
|
2129
|
-
var SECONDS_PER_UNIT = {
|
|
2130
|
-
s: 1,
|
|
2131
|
-
m: 60,
|
|
2132
|
-
h: 60 * 60,
|
|
2133
|
-
d: 24 * 60 * 60
|
|
2134
|
-
};
|
|
2135
|
-
var durationToSeconds = (duration) => {
|
|
2136
|
-
if (typeof duration === "number") {
|
|
2137
|
-
return Number.isFinite(duration) ? duration : void 0;
|
|
2138
|
-
}
|
|
2139
|
-
const match = /^(\d+)([smhd])$/.exec(duration.trim());
|
|
2140
|
-
if (match) {
|
|
2141
|
-
return Number(match[1]) * SECONDS_PER_UNIT[match[2]];
|
|
2142
|
-
}
|
|
2143
|
-
const seconds = Number(duration.trim());
|
|
2144
|
-
return Number.isFinite(seconds) ? seconds : void 0;
|
|
2145
|
-
};
|
|
2146
|
-
var normalizeFlowControl = (flowControl) => {
|
|
2147
|
-
const period = flowControl.period === void 0 ? void 0 : durationToSeconds(flowControl.period);
|
|
2148
|
-
return {
|
|
2149
|
-
key: flowControl.key,
|
|
2150
|
-
parallelism: flowControl.parallelism ?? DEFAULT_PARALLELISM,
|
|
2151
|
-
rate: flowControl.rate ?? flowControl.ratePerSecond ?? DEFAULT_RATE,
|
|
2152
|
-
period: period ?? DEFAULT_PERIOD_SECONDS
|
|
2153
|
-
};
|
|
2154
|
-
};
|
|
2155
|
-
var parseFlowControlHeaders = (key, value) => {
|
|
2156
|
-
if (!key) {
|
|
2157
|
-
return void 0;
|
|
2158
|
-
}
|
|
2159
|
-
const flowControl = {
|
|
2160
|
-
key,
|
|
2161
|
-
parallelism: DEFAULT_PARALLELISM,
|
|
2162
|
-
rate: DEFAULT_RATE,
|
|
2163
|
-
period: DEFAULT_PERIOD_SECONDS
|
|
2164
|
-
};
|
|
2165
|
-
for (const entry of (value ?? "").split(",")) {
|
|
2166
|
-
const [name, rawValue] = entry.split("=").map((part) => part.trim());
|
|
2167
|
-
if (!name || rawValue === void 0) {
|
|
2168
|
-
continue;
|
|
2169
|
-
}
|
|
2170
|
-
switch (name) {
|
|
2171
|
-
case "parallelism": {
|
|
2172
|
-
flowControl.parallelism = Number(rawValue);
|
|
2173
|
-
break;
|
|
2174
|
-
}
|
|
2175
|
-
case "rate": {
|
|
2176
|
-
flowControl.rate = Number(rawValue);
|
|
2177
|
-
break;
|
|
2178
|
-
}
|
|
2179
|
-
case "period": {
|
|
2180
|
-
flowControl.period = durationToSeconds(rawValue) ?? DEFAULT_PERIOD_SECONDS;
|
|
2181
|
-
break;
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2185
|
-
return flowControl;
|
|
2186
|
-
};
|
|
2187
|
-
var getEffectiveConfig = (headers) => {
|
|
2188
|
-
const retriesHeader = headers.get(RETRIES_HEADER);
|
|
2189
|
-
return {
|
|
2190
|
-
flowControl: parseFlowControlHeaders(
|
|
2191
|
-
headers.get(FLOW_CONTROL_KEY_HEADER),
|
|
2192
|
-
headers.get(FLOW_CONTROL_VALUE_HEADER)
|
|
2193
|
-
),
|
|
2194
|
-
retries: retriesHeader === null ? void 0 : Number(retriesHeader),
|
|
2195
|
-
retryDelay: headers.get(RETRY_DELAY_HEADER) ?? void 0,
|
|
2196
|
-
hasStepConfig: headers.get(WORKFLOW_STEP_CONFIG_HEADER) === "true"
|
|
2197
|
-
};
|
|
2198
|
-
};
|
|
2199
|
-
var describeStepSettingsMismatch = (stepSettings, effectiveConfig) => {
|
|
2200
|
-
if (stepSettings.flowControl) {
|
|
2201
|
-
const wanted = normalizeFlowControl(stepSettings.flowControl);
|
|
2202
|
-
const applied = effectiveConfig.flowControl;
|
|
2203
|
-
if (!applied || applied.key !== wanted.key || applied.parallelism !== wanted.parallelism || applied.rate !== wanted.rate || applied.period !== wanted.period) {
|
|
2204
|
-
return `flow control: expected ${JSON.stringify(wanted)}, delivery has ${JSON.stringify(applied)}`;
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
if (stepSettings.retries !== void 0 && effectiveConfig.retries !== void 0 && stepSettings.retries !== effectiveConfig.retries) {
|
|
2208
|
-
return `retries: expected ${stepSettings.retries}, delivery has ${effectiveConfig.retries}`;
|
|
2209
|
-
}
|
|
2210
|
-
if (stepSettings.retryDelay !== void 0 && stepSettings.retryDelay !== effectiveConfig.retryDelay) {
|
|
2211
|
-
return `retry delay: expected '${stepSettings.retryDelay}', delivery has '${effectiveConfig.retryDelay ?? ""}'`;
|
|
2212
|
-
}
|
|
2213
|
-
return void 0;
|
|
2214
|
-
};
|
|
2215
2011
|
|
|
2216
2012
|
// src/context/auto-executor.ts
|
|
2217
2013
|
var AutoExecutor = class _AutoExecutor {
|
|
@@ -2228,25 +2024,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2228
2024
|
stepCount = 0;
|
|
2229
2025
|
planStepCount = 0;
|
|
2230
2026
|
executingStep = false;
|
|
2231
|
-
/**
|
|
2232
|
-
* an executed step whose result hasn't been submitted to QStash yet.
|
|
2233
|
-
*
|
|
2234
|
-
* Set when a step which can produce its result in-process executes. The
|
|
2235
|
-
* submission waits so that the route function can continue and reveal
|
|
2236
|
-
* what comes next: if that is a single step with step-level settings,
|
|
2237
|
-
* the settings ride on this submission and no step config request is
|
|
2238
|
-
* needed, since the delivery of the submission is what executes it.
|
|
2239
|
-
*
|
|
2240
|
-
* Flushed when the next step is reached (see `addStep`) or when the
|
|
2241
|
-
* route function ends (see `flushPendingStep` in `serve`).
|
|
2242
|
-
*/
|
|
2243
|
-
pendingStep;
|
|
2244
|
-
/**
|
|
2245
|
-
* configuration QStash applied to the delivery being handled. A step
|
|
2246
|
-
* with step-level settings is settled by comparing its settings against
|
|
2247
|
-
* this.
|
|
2248
|
-
*/
|
|
2249
|
-
effectiveConfig;
|
|
2250
2027
|
/**
|
|
2251
2028
|
* @param context workflow context
|
|
2252
2029
|
* @param steps list of steps
|
|
@@ -2254,17 +2031,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2254
2031
|
* @param dispatchLifecycle lifecycle event dispatcher
|
|
2255
2032
|
* @param telemetry optional telemetry information
|
|
2256
2033
|
* @param invokeCount optional invoke count
|
|
2257
|
-
* @param effectiveConfig configuration QStash applied to the delivery
|
|
2258
|
-
* being handled
|
|
2259
2034
|
*/
|
|
2260
|
-
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount
|
|
2035
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount) {
|
|
2261
2036
|
this.context = context;
|
|
2262
2037
|
this.steps = steps;
|
|
2263
2038
|
this.dispatchDebug = dispatchDebug;
|
|
2264
2039
|
this.dispatchLifecycle = dispatchLifecycle;
|
|
2265
2040
|
this.telemetry = telemetry;
|
|
2266
2041
|
this.invokeCount = invokeCount ?? 0;
|
|
2267
|
-
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
2268
2042
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
2269
2043
|
}
|
|
2270
2044
|
/**
|
|
@@ -2299,15 +2073,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2299
2073
|
lazyStepList.push(stepInfo);
|
|
2300
2074
|
const index = this.indexInCurrentList++;
|
|
2301
2075
|
const requestComplete = this.deferExecution().then(async () => {
|
|
2302
|
-
const submitted = await this.submitPendingStep(
|
|
2303
|
-
lazyStepList.length === 1 ? lazyStepList[0].stepSettings : void 0
|
|
2304
|
-
);
|
|
2305
|
-
if (submitted.isErr()) {
|
|
2306
|
-
throw submitted.error;
|
|
2307
|
-
}
|
|
2308
|
-
if (submitted.value.result === "submitted-step") {
|
|
2309
|
-
throw submitted.value.abort;
|
|
2310
|
-
}
|
|
2311
2076
|
if (!this.promises.has(lazyStepList)) {
|
|
2312
2077
|
const promise2 = this.getExecutionPromise(lazyStepList);
|
|
2313
2078
|
this.promises.set(lazyStepList, promise2);
|
|
@@ -2361,100 +2126,18 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2361
2126
|
}
|
|
2362
2127
|
return parsedOut;
|
|
2363
2128
|
}
|
|
2364
|
-
const
|
|
2365
|
-
if (mismatch) {
|
|
2366
|
-
if (!this.effectiveConfig.hasStepConfig) {
|
|
2367
|
-
await publishStepConfigRequest({
|
|
2368
|
-
context: this.context,
|
|
2369
|
-
lazyStep,
|
|
2370
|
-
targetStep: this.stepCount,
|
|
2371
|
-
invokeCount: this.invokeCount,
|
|
2372
|
-
telemetry: this.telemetry,
|
|
2373
|
-
dispatchDebug: this.dispatchDebug
|
|
2374
|
-
});
|
|
2375
|
-
throw new WorkflowAbort(lazyStep.stepName);
|
|
2376
|
-
} else {
|
|
2377
|
-
await this.dispatchDebug("onWarning", {
|
|
2378
|
-
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.`
|
|
2379
|
-
});
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
|
-
const resultStep = await executeStep({
|
|
2383
|
-
lazyStep,
|
|
2384
|
-
stepId: this.stepCount,
|
|
2385
|
-
concurrency: NO_CONCURRENCY,
|
|
2386
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2387
|
-
});
|
|
2388
|
-
if (lazyStep.supportsDeferredSubmission) {
|
|
2389
|
-
this.pendingStep = { status: "held", lazyStep, resultStep };
|
|
2390
|
-
return lazyStep.parseOut({
|
|
2391
|
-
...resultStep,
|
|
2392
|
-
out: resultStep.out === void 0 ? void 0 : JSON.stringify(resultStep.out)
|
|
2393
|
-
});
|
|
2394
|
-
}
|
|
2395
|
-
await submitStepResult({
|
|
2129
|
+
const resultStep = await submitSingleStep({
|
|
2396
2130
|
context: this.context,
|
|
2397
2131
|
lazyStep,
|
|
2398
|
-
|
|
2132
|
+
stepId: this.stepCount,
|
|
2399
2133
|
invokeCount: this.invokeCount,
|
|
2400
|
-
concurrency:
|
|
2134
|
+
concurrency: 1,
|
|
2401
2135
|
telemetry: this.telemetry,
|
|
2402
|
-
dispatchDebug: this.dispatchDebug
|
|
2136
|
+
dispatchDebug: this.dispatchDebug,
|
|
2137
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2403
2138
|
});
|
|
2404
2139
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2405
2140
|
}
|
|
2406
|
-
/**
|
|
2407
|
-
* Submits the result of the step being held, if there is one, and
|
|
2408
|
-
* returns the abort the caller has to throw to end the invocation.
|
|
2409
|
-
*
|
|
2410
|
-
* Returns undefined when no step is being held, which is the caller's
|
|
2411
|
-
* signal that there is nothing to end. Throws instead of returning
|
|
2412
|
-
* when the submission itself fails, so that error reaches the caller
|
|
2413
|
-
* rather than an abort claiming the step was submitted.
|
|
2414
|
-
*
|
|
2415
|
-
* Callers which arrive while a submission is already under way (the
|
|
2416
|
-
* steps of a parallel group, added together) wait for that one and get
|
|
2417
|
-
* the same abort, rather than submitting a second time.
|
|
2418
|
-
*
|
|
2419
|
-
* The next step's settings are attached whenever it has any, without
|
|
2420
|
-
* checking them against the current delivery: the executor only knows
|
|
2421
|
-
* the configuration of the delivery in hand, which inside a
|
|
2422
|
-
* delivery is the *previous* step's, so such a check would be wrong
|
|
2423
|
-
* exactly when two steps with settings follow each other.
|
|
2424
|
-
*
|
|
2425
|
-
* @param nextStepSettings step-level settings of the next step
|
|
2426
|
-
* @returns `submitted-step` with the abort which ends this invocation,
|
|
2427
|
-
* or `no-pending-step` when nothing was held and there is nothing to
|
|
2428
|
-
* end
|
|
2429
|
-
*/
|
|
2430
|
-
async submitPendingStep(nextStepSettings) {
|
|
2431
|
-
if (!this.pendingStep) {
|
|
2432
|
-
return ok({ result: "no-pending-step" });
|
|
2433
|
-
}
|
|
2434
|
-
try {
|
|
2435
|
-
if (this.pendingStep.status === "submitting") {
|
|
2436
|
-
return ok({ result: "submitted-step", abort: await this.pendingStep.submitted });
|
|
2437
|
-
}
|
|
2438
|
-
const { lazyStep, resultStep } = this.pendingStep;
|
|
2439
|
-
const submitted = (async () => {
|
|
2440
|
-
await submitStepResult({
|
|
2441
|
-
context: this.context,
|
|
2442
|
-
lazyStep,
|
|
2443
|
-
resultStep,
|
|
2444
|
-
invokeCount: this.invokeCount,
|
|
2445
|
-
concurrency: NO_CONCURRENCY,
|
|
2446
|
-
telemetry: this.telemetry,
|
|
2447
|
-
dispatchDebug: this.dispatchDebug,
|
|
2448
|
-
nextStepSettings
|
|
2449
|
-
});
|
|
2450
|
-
return new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2451
|
-
})();
|
|
2452
|
-
this.pendingStep = { status: "submitting", submitted };
|
|
2453
|
-
return ok({ result: "submitted-step", abort: await submitted });
|
|
2454
|
-
} catch (error) {
|
|
2455
|
-
return err(error);
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
2141
|
/**
|
|
2459
2142
|
* Runs steps in parallel.
|
|
2460
2143
|
*
|
|
@@ -2503,20 +2186,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2503
2186
|
validateStep(parallelSteps[stepIndex], planStep);
|
|
2504
2187
|
try {
|
|
2505
2188
|
const parallelStep = parallelSteps[stepIndex];
|
|
2506
|
-
const resultStep = await
|
|
2507
|
-
lazyStep: parallelStep,
|
|
2508
|
-
stepId: planStep.targetStep,
|
|
2509
|
-
concurrency: parallelSteps.length,
|
|
2510
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2511
|
-
});
|
|
2512
|
-
await submitStepResult({
|
|
2189
|
+
const resultStep = await submitSingleStep({
|
|
2513
2190
|
context: this.context,
|
|
2514
2191
|
lazyStep: parallelStep,
|
|
2515
|
-
|
|
2192
|
+
stepId: planStep.targetStep,
|
|
2516
2193
|
invokeCount: this.invokeCount,
|
|
2517
2194
|
concurrency: parallelSteps.length,
|
|
2518
2195
|
telemetry: this.telemetry,
|
|
2519
|
-
dispatchDebug: this.dispatchDebug
|
|
2196
|
+
dispatchDebug: this.dispatchDebug,
|
|
2197
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2520
2198
|
});
|
|
2521
2199
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2522
2200
|
} catch (error) {
|
|
@@ -3065,46 +2743,6 @@ var WorkflowContext = class {
|
|
|
3065
2743
|
* `1` on the first retry, `2` on the second, and so on.
|
|
3066
2744
|
*/
|
|
3067
2745
|
retried;
|
|
3068
|
-
/**
|
|
3069
|
-
* Configuration QStash applied to the request being handled.
|
|
3070
|
-
*
|
|
3071
|
-
* This is the configuration of *this delivery*, not of the run: it is
|
|
3072
|
-
* the configuration the run was triggered with, except inside the
|
|
3073
|
-
* delivery which executes a step that has step-level settings, where
|
|
3074
|
-
* it is that step's settings. The fields below expose it; the executor
|
|
3075
|
-
* gets it directly rather than reaching through the context.
|
|
3076
|
-
*/
|
|
3077
|
-
effectiveConfig;
|
|
3078
|
-
/**
|
|
3079
|
-
* Flow control QStash applied to the request being handled, if any.
|
|
3080
|
-
*
|
|
3081
|
-
* This is the flow control of the request in hand, which is the one the
|
|
3082
|
-
* run was triggered with except inside a step carrying its own.
|
|
3083
|
-
*/
|
|
3084
|
-
get flowControl() {
|
|
3085
|
-
return this.effectiveConfig.flowControl;
|
|
3086
|
-
}
|
|
3087
|
-
/**
|
|
3088
|
-
* Retry limit QStash applied to the request being handled, if it
|
|
3089
|
-
* reported one.
|
|
3090
|
-
*
|
|
3091
|
-
* Not to be confused with {@link retried}, which is how many retries
|
|
3092
|
-
* have already happened.
|
|
3093
|
-
*
|
|
3094
|
-
* @see {@link flowControl} for what "the request being handled" means
|
|
3095
|
-
*/
|
|
3096
|
-
get retries() {
|
|
3097
|
-
return this.effectiveConfig.retries;
|
|
3098
|
-
}
|
|
3099
|
-
/**
|
|
3100
|
-
* Retry delay expression QStash applied to the request being handled,
|
|
3101
|
-
* if any.
|
|
3102
|
-
*
|
|
3103
|
-
* @see {@link flowControl} for what "the request being handled" means
|
|
3104
|
-
*/
|
|
3105
|
-
get retryDelay() {
|
|
3106
|
-
return this.effectiveConfig.retryDelay;
|
|
3107
|
-
}
|
|
3108
2746
|
constructor({
|
|
3109
2747
|
qstashClient,
|
|
3110
2748
|
workflowRunId,
|
|
@@ -3118,8 +2756,7 @@ var WorkflowContext = class {
|
|
|
3118
2756
|
invokeCount,
|
|
3119
2757
|
label,
|
|
3120
2758
|
retried,
|
|
3121
|
-
middlewareManager
|
|
3122
|
-
effectiveConfig
|
|
2759
|
+
middlewareManager
|
|
3123
2760
|
}) {
|
|
3124
2761
|
this.qstashClient = qstashClient;
|
|
3125
2762
|
this.workflowRunId = workflowRunId;
|
|
@@ -3131,7 +2768,6 @@ var WorkflowContext = class {
|
|
|
3131
2768
|
this.env = env ?? {};
|
|
3132
2769
|
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
3133
2770
|
this.retried = retried ?? 0;
|
|
3134
|
-
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
3135
2771
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
3136
2772
|
middlewareManagerInstance.assignContext(this);
|
|
3137
2773
|
this.executor = new AutoExecutor(
|
|
@@ -3140,8 +2776,7 @@ var WorkflowContext = class {
|
|
|
3140
2776
|
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
3141
2777
|
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
3142
2778
|
telemetry,
|
|
3143
|
-
invokeCount
|
|
3144
|
-
this.effectiveConfig
|
|
2779
|
+
invokeCount
|
|
3145
2780
|
);
|
|
3146
2781
|
}
|
|
3147
2782
|
/**
|
|
@@ -3167,35 +2802,13 @@ var WorkflowContext = class {
|
|
|
3167
2802
|
* ])
|
|
3168
2803
|
* ```
|
|
3169
2804
|
*
|
|
3170
|
-
* Step-level settings can be passed as a third argument, overriding
|
|
3171
|
-
* the settings the workflow run was triggered with for this step only:
|
|
3172
|
-
*
|
|
3173
|
-
* ```typescript
|
|
3174
|
-
* const result = await context.run(
|
|
3175
|
-
* "step 1",
|
|
3176
|
-
* () => {
|
|
3177
|
-
* return "result"
|
|
3178
|
-
* },
|
|
3179
|
-
* {
|
|
3180
|
-
* flowControl: { key: "custom-key", parallelism: 3 },
|
|
3181
|
-
* retries: 5,
|
|
3182
|
-
* }
|
|
3183
|
-
* )
|
|
3184
|
-
* ```
|
|
3185
|
-
*
|
|
3186
2805
|
* @param stepName name of the step
|
|
3187
2806
|
* @param stepFunction step function to be executed
|
|
3188
|
-
* @param stepSettings step-level settings for this step
|
|
3189
2807
|
* @returns result of the step function
|
|
3190
2808
|
*/
|
|
3191
|
-
async run(stepName, stepFunction
|
|
3192
|
-
if (stepSettings) {
|
|
3193
|
-
validateFlowControl(stepSettings.flowControl);
|
|
3194
|
-
}
|
|
2809
|
+
async run(stepName, stepFunction) {
|
|
3195
2810
|
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
3196
|
-
return await this.addStep(
|
|
3197
|
-
new LazyFunctionStep(this, stepName, wrappedStepFunction, stepSettings)
|
|
3198
|
-
);
|
|
2811
|
+
return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
|
|
3199
2812
|
}
|
|
3200
2813
|
/**
|
|
3201
2814
|
* Stops the execution for the duration provided.
|
|
@@ -3418,7 +3031,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3418
3031
|
*
|
|
3419
3032
|
* @param routeFunction
|
|
3420
3033
|
*/
|
|
3421
|
-
static async tryAuthentication(routeFunction, context
|
|
3034
|
+
static async tryAuthentication(routeFunction, context) {
|
|
3422
3035
|
const disabledContext = new _DisabledWorkflowContext({
|
|
3423
3036
|
qstashClient: new import_qstash10.Client({
|
|
3424
3037
|
baseUrl: "disabled-client",
|
|
@@ -3433,10 +3046,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3433
3046
|
initialPayload: context.requestPayload,
|
|
3434
3047
|
env: context.env,
|
|
3435
3048
|
label: context.labels,
|
|
3436
|
-
retried: context.retried
|
|
3437
|
-
// the route function runs for real here until it reaches a step, so
|
|
3438
|
-
// it observes the same configuration as it will on the real context
|
|
3439
|
-
effectiveConfig
|
|
3049
|
+
retried: context.retried
|
|
3440
3050
|
});
|
|
3441
3051
|
try {
|
|
3442
3052
|
await routeFunction(disabledContext);
|
|
@@ -3653,7 +3263,6 @@ var handleFailure = async ({
|
|
|
3653
3263
|
}
|
|
3654
3264
|
const userHeaders = recreateUserHeaders(request.headers);
|
|
3655
3265
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3656
|
-
const effectiveConfig = getEffectiveConfig(request.headers);
|
|
3657
3266
|
const workflowContext = new WorkflowContext({
|
|
3658
3267
|
qstashClient,
|
|
3659
3268
|
workflowRunId,
|
|
@@ -3667,13 +3276,11 @@ var handleFailure = async ({
|
|
|
3667
3276
|
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3668
3277
|
retried,
|
|
3669
3278
|
workflowRunCreatedAt: workflowCreatedAt,
|
|
3670
|
-
middlewareManager: void 0
|
|
3671
|
-
effectiveConfig
|
|
3279
|
+
middlewareManager: void 0
|
|
3672
3280
|
});
|
|
3673
3281
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3674
3282
|
routeFunction,
|
|
3675
|
-
workflowContext
|
|
3676
|
-
effectiveConfig
|
|
3283
|
+
workflowContext
|
|
3677
3284
|
);
|
|
3678
3285
|
if (authCheck.isErr()) {
|
|
3679
3286
|
await dispatchDebug?.("onError", {
|
|
@@ -4162,28 +3769,26 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
4162
3769
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
4163
3770
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
4164
3771
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
4165
|
-
const
|
|
4166
|
-
const
|
|
3772
|
+
const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
3773
|
+
const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
|
|
4167
3774
|
const workflowContext = new WorkflowContext({
|
|
4168
3775
|
qstashClient: regionalClient,
|
|
4169
3776
|
workflowRunId,
|
|
4170
3777
|
initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
|
|
4171
3778
|
headers: recreateUserHeaders(request.headers),
|
|
4172
3779
|
steps,
|
|
4173
|
-
effectiveConfig,
|
|
4174
3780
|
url: workflowUrl,
|
|
4175
3781
|
env,
|
|
4176
3782
|
telemetry,
|
|
4177
3783
|
invokeCount,
|
|
4178
3784
|
label,
|
|
4179
3785
|
retried,
|
|
4180
|
-
workflowRunCreatedAt
|
|
3786
|
+
workflowRunCreatedAt,
|
|
4181
3787
|
middlewareManager
|
|
4182
3788
|
});
|
|
4183
3789
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
4184
3790
|
routeFunction,
|
|
4185
|
-
workflowContext
|
|
4186
|
-
effectiveConfig
|
|
3791
|
+
workflowContext
|
|
4187
3792
|
);
|
|
4188
3793
|
if (authCheck.isErr()) {
|
|
4189
3794
|
throw authCheck.error;
|
|
@@ -4220,23 +3825,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
4220
3825
|
if (steps.length === 1) {
|
|
4221
3826
|
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
4222
3827
|
}
|
|
4223
|
-
|
|
4224
|
-
try {
|
|
4225
|
-
outcome = { ran: true, result: await routeFunction(workflowContext) };
|
|
4226
|
-
} catch (error) {
|
|
4227
|
-
outcome = { ran: false, error };
|
|
4228
|
-
}
|
|
4229
|
-
const submitted = await flushPendingStep(workflowContext);
|
|
4230
|
-
if (submitted.isErr()) {
|
|
4231
|
-
throw submitted.error;
|
|
4232
|
-
}
|
|
4233
|
-
if (submitted.value.result === "submitted-step") {
|
|
4234
|
-
throw submitted.value.abort;
|
|
4235
|
-
}
|
|
4236
|
-
if (!outcome.ran) {
|
|
4237
|
-
throw outcome.error;
|
|
4238
|
-
}
|
|
4239
|
-
return outcome.result;
|
|
3828
|
+
return await routeFunction(workflowContext);
|
|
4240
3829
|
},
|
|
4241
3830
|
onCleanup: async (result2) => {
|
|
4242
3831
|
await middlewareManager.dispatchLifecycle("runCompleted", {
|