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