@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/index.js
CHANGED
|
@@ -304,14 +304,6 @@ var WORKFLOW_CREATED_AT_HEADER = "Upstash-Workflow-CreatedAt";
|
|
|
304
304
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
305
305
|
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
306
306
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
307
|
-
var WORKFLOW_FEATURE_SET = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
308
|
-
var WORKFLOW_STEP_CONFIG_CALL_TYPE = "stepConfig";
|
|
309
|
-
var WORKFLOW_STEP_CONFIG_HEADER = "Upstash-Workflow-Step-Config";
|
|
310
|
-
var FLOW_CONTROL_KEY_HEADER = "Upstash-Flow-Control-Key";
|
|
311
|
-
var FLOW_CONTROL_VALUE_HEADER = "Upstash-Flow-Control-Value";
|
|
312
|
-
var RETRIES_HEADER = "Upstash-Retries";
|
|
313
|
-
var RETRY_DELAY_HEADER = "Upstash-Retry-Delay";
|
|
314
|
-
var WORKFLOW_STEP_CONFIG_FEATURE = "WF_StepConfig";
|
|
315
307
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
316
308
|
var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
317
309
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
@@ -329,6 +321,102 @@ var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
|
329
321
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
330
322
|
var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
|
|
331
323
|
|
|
324
|
+
// src/context/auto-executor.ts
|
|
325
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
326
|
+
|
|
327
|
+
// src/qstash/headers.ts
|
|
328
|
+
var import_qstash4 = require("@upstash/qstash");
|
|
329
|
+
|
|
330
|
+
// src/utils.ts
|
|
331
|
+
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
332
|
+
var NANOID_LENGTH = 21;
|
|
333
|
+
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
334
|
+
function validateLabel(label) {
|
|
335
|
+
if (label === void 0) return;
|
|
336
|
+
const labels = Array.isArray(label) ? label : [label];
|
|
337
|
+
if (labels.length === 0) {
|
|
338
|
+
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
339
|
+
}
|
|
340
|
+
for (const value of labels) {
|
|
341
|
+
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
342
|
+
throw new WorkflowNonRetryableError(
|
|
343
|
+
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function serializeLabel(label) {
|
|
349
|
+
return Array.isArray(label) ? label.join(",") : label;
|
|
350
|
+
}
|
|
351
|
+
function validateFlowControl(flowControl) {
|
|
352
|
+
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
353
|
+
throw new WorkflowNonRetryableError(
|
|
354
|
+
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function getRandomInt() {
|
|
359
|
+
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
360
|
+
}
|
|
361
|
+
function nanoid(length = NANOID_LENGTH) {
|
|
362
|
+
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
363
|
+
}
|
|
364
|
+
function getWorkflowRunId(id) {
|
|
365
|
+
return `wfr_${id ?? nanoid()}`;
|
|
366
|
+
}
|
|
367
|
+
function decodeBase64(base64) {
|
|
368
|
+
const binString = atob(base64);
|
|
369
|
+
try {
|
|
370
|
+
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
371
|
+
return new TextDecoder().decode(intArray);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
console.warn(
|
|
374
|
+
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
375
|
+
);
|
|
376
|
+
return binString;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function getUserIdFromToken(qstashClient) {
|
|
380
|
+
try {
|
|
381
|
+
const token = qstashClient.token;
|
|
382
|
+
const decodedToken = decodeBase64(token);
|
|
383
|
+
const tokenPayload = JSON.parse(decodedToken);
|
|
384
|
+
const userId = tokenPayload.UserID;
|
|
385
|
+
if (!userId) {
|
|
386
|
+
throw new WorkflowError("QStash token payload does not contain userId");
|
|
387
|
+
}
|
|
388
|
+
return userId;
|
|
389
|
+
} catch (error) {
|
|
390
|
+
throw new WorkflowError(
|
|
391
|
+
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function getQStashUrl(qstashClient) {
|
|
396
|
+
try {
|
|
397
|
+
const requester = qstashClient.http;
|
|
398
|
+
const baseUrl = requester.baseUrl;
|
|
399
|
+
if (!baseUrl) {
|
|
400
|
+
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
401
|
+
}
|
|
402
|
+
return baseUrl;
|
|
403
|
+
} catch (error) {
|
|
404
|
+
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function getEventId() {
|
|
408
|
+
return `evt_${nanoid(15)}`;
|
|
409
|
+
}
|
|
410
|
+
function stringifyBody(body) {
|
|
411
|
+
if (body === void 0) {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
if (typeof body === "string") {
|
|
415
|
+
return body;
|
|
416
|
+
}
|
|
417
|
+
return JSON.stringify(body);
|
|
418
|
+
}
|
|
419
|
+
|
|
332
420
|
// node_modules/neverthrow/dist/index.es.js
|
|
333
421
|
var defaultErrorConfig = {
|
|
334
422
|
withStackTrace: false
|
|
@@ -744,102 +832,6 @@ var Err = class {
|
|
|
744
832
|
};
|
|
745
833
|
var fromThrowable = Result.fromThrowable;
|
|
746
834
|
|
|
747
|
-
// src/context/auto-executor.ts
|
|
748
|
-
var import_qstash5 = require("@upstash/qstash");
|
|
749
|
-
|
|
750
|
-
// src/qstash/headers.ts
|
|
751
|
-
var import_qstash4 = require("@upstash/qstash");
|
|
752
|
-
|
|
753
|
-
// src/utils.ts
|
|
754
|
-
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
755
|
-
var NANOID_LENGTH = 21;
|
|
756
|
-
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
757
|
-
function validateLabel(label) {
|
|
758
|
-
if (label === void 0) return;
|
|
759
|
-
const labels = Array.isArray(label) ? label : [label];
|
|
760
|
-
if (labels.length === 0) {
|
|
761
|
-
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
762
|
-
}
|
|
763
|
-
for (const value of labels) {
|
|
764
|
-
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
765
|
-
throw new WorkflowNonRetryableError(
|
|
766
|
-
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
767
|
-
);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
function serializeLabel(label) {
|
|
772
|
-
return Array.isArray(label) ? label.join(",") : label;
|
|
773
|
-
}
|
|
774
|
-
function validateFlowControl(flowControl) {
|
|
775
|
-
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
776
|
-
throw new WorkflowNonRetryableError(
|
|
777
|
-
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
function getRandomInt() {
|
|
782
|
-
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
783
|
-
}
|
|
784
|
-
function nanoid(length = NANOID_LENGTH) {
|
|
785
|
-
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
786
|
-
}
|
|
787
|
-
function getWorkflowRunId(id) {
|
|
788
|
-
return `wfr_${id ?? nanoid()}`;
|
|
789
|
-
}
|
|
790
|
-
function decodeBase64(base64) {
|
|
791
|
-
const binString = atob(base64);
|
|
792
|
-
try {
|
|
793
|
-
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
794
|
-
return new TextDecoder().decode(intArray);
|
|
795
|
-
} catch (error) {
|
|
796
|
-
console.warn(
|
|
797
|
-
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
798
|
-
);
|
|
799
|
-
return binString;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
function getUserIdFromToken(qstashClient) {
|
|
803
|
-
try {
|
|
804
|
-
const token = qstashClient.token;
|
|
805
|
-
const decodedToken = decodeBase64(token);
|
|
806
|
-
const tokenPayload = JSON.parse(decodedToken);
|
|
807
|
-
const userId = tokenPayload.UserID;
|
|
808
|
-
if (!userId) {
|
|
809
|
-
throw new WorkflowError("QStash token payload does not contain userId");
|
|
810
|
-
}
|
|
811
|
-
return userId;
|
|
812
|
-
} catch (error) {
|
|
813
|
-
throw new WorkflowError(
|
|
814
|
-
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
815
|
-
);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
function getQStashUrl(qstashClient) {
|
|
819
|
-
try {
|
|
820
|
-
const requester = qstashClient.http;
|
|
821
|
-
const baseUrl = requester.baseUrl;
|
|
822
|
-
if (!baseUrl) {
|
|
823
|
-
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
824
|
-
}
|
|
825
|
-
return baseUrl;
|
|
826
|
-
} catch (error) {
|
|
827
|
-
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
function getEventId() {
|
|
831
|
-
return `evt_${nanoid(15)}`;
|
|
832
|
-
}
|
|
833
|
-
function stringifyBody(body) {
|
|
834
|
-
if (body === void 0) {
|
|
835
|
-
return void 0;
|
|
836
|
-
}
|
|
837
|
-
if (typeof body === "string") {
|
|
838
|
-
return body;
|
|
839
|
-
}
|
|
840
|
-
return JSON.stringify(body);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
835
|
// src/types.ts
|
|
844
836
|
var StepTypes = [
|
|
845
837
|
"Initial",
|
|
@@ -879,6 +871,10 @@ var triggerFirstInvocation = async (params) => {
|
|
|
879
871
|
workflowConfig: {
|
|
880
872
|
workflowRunId: workflowContext.workflowRunId,
|
|
881
873
|
workflowUrl: workflowContext.url,
|
|
874
|
+
// the run doesn't exist yet, so the trigger time is sent when the creation time
|
|
875
|
+
// is not known. serve and client.trigger set it on the context already, so a
|
|
876
|
+
// retry of this request carries the same value and is deduplicated.
|
|
877
|
+
workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
|
|
882
878
|
failureUrl,
|
|
883
879
|
retries,
|
|
884
880
|
retryDelay,
|
|
@@ -945,10 +941,6 @@ var triggerFirstInvocation = async (params) => {
|
|
|
945
941
|
return err(error_);
|
|
946
942
|
}
|
|
947
943
|
};
|
|
948
|
-
var flushPendingStep = async (workflowContext) => {
|
|
949
|
-
const { executor } = workflowContext;
|
|
950
|
-
return await executor.submitPendingStep();
|
|
951
|
-
};
|
|
952
944
|
var triggerRouteFunction = async ({
|
|
953
945
|
onCleanup,
|
|
954
946
|
onStep,
|
|
@@ -986,7 +978,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
|
|
|
986
978
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
987
979
|
method: "DELETE",
|
|
988
980
|
parseResponseAsJson: false,
|
|
989
|
-
body: JSON.stringify(result)
|
|
981
|
+
body: JSON.stringify(result),
|
|
982
|
+
// with the creation time, QStash deletes the run only if it is the same run.
|
|
983
|
+
// a late delete of a previous run can't delete a new run with the same id.
|
|
984
|
+
headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
|
|
990
985
|
});
|
|
991
986
|
await dispatchDebug?.("onInfo", {
|
|
992
987
|
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
@@ -1063,6 +1058,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
1063
1058
|
const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
|
|
1064
1059
|
const contentType = request.headers.get("Upstash-Workflow-ContentType");
|
|
1065
1060
|
const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
|
|
1061
|
+
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
1066
1062
|
if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
|
|
1067
1063
|
throw new Error(
|
|
1068
1064
|
`Missing info in callback message source header: ${JSON.stringify({
|
|
@@ -1080,6 +1076,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
1080
1076
|
workflowConfig: {
|
|
1081
1077
|
workflowRunId,
|
|
1082
1078
|
workflowUrl,
|
|
1079
|
+
workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
|
|
1083
1080
|
telemetry
|
|
1084
1081
|
},
|
|
1085
1082
|
invokeCount: Number(invokeCount)
|
|
@@ -1156,29 +1153,8 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
|
|
|
1156
1153
|
var BaseLazyStep = class _BaseLazyStep {
|
|
1157
1154
|
stepName;
|
|
1158
1155
|
context;
|
|
1159
|
-
|
|
1160
|
-
* step-level settings (flow control, retries etc.) which override the
|
|
1161
|
-
* settings the workflow run was triggered with, for this step only.
|
|
1162
|
-
*/
|
|
1163
|
-
stepSettings;
|
|
1164
|
-
/**
|
|
1165
|
-
* whether this step's result can be submitted after the route function
|
|
1166
|
-
* has moved on, instead of right away.
|
|
1167
|
-
*
|
|
1168
|
-
* Enabled for steps whose `getResultStep` produces the final `out` —
|
|
1169
|
-
* which is not the same as "needs no server round trip":
|
|
1170
|
-
* `LazyCreateWebhookStep` builds its result in `getBody` and returns
|
|
1171
|
-
* `out: undefined` from `getResultStep`, so deferring it would hand
|
|
1172
|
-
* `undefined` to the route function.
|
|
1173
|
-
*
|
|
1174
|
-
* Deferring lets the route function continue and reveal the next step,
|
|
1175
|
-
* so that step's settings can ride on this step's submission instead
|
|
1176
|
-
* of needing a step config request of their own.
|
|
1177
|
-
*/
|
|
1178
|
-
supportsDeferredSubmission = false;
|
|
1179
|
-
constructor(context, stepName, stepSettings) {
|
|
1156
|
+
constructor(context, stepName) {
|
|
1180
1157
|
this.context = context;
|
|
1181
|
-
this.stepSettings = stepSettings;
|
|
1182
1158
|
if (!stepName) {
|
|
1183
1159
|
throw new WorkflowError(
|
|
1184
1160
|
"A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
|
|
@@ -1242,18 +1218,13 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1242
1218
|
step.out = JSON.stringify(step.out);
|
|
1243
1219
|
return JSON.stringify(step);
|
|
1244
1220
|
}
|
|
1245
|
-
getHeaders({
|
|
1246
|
-
context,
|
|
1247
|
-
telemetry,
|
|
1248
|
-
invokeCount,
|
|
1249
|
-
step,
|
|
1250
|
-
stepSettings
|
|
1251
|
-
}) {
|
|
1221
|
+
getHeaders({ context, telemetry, invokeCount, step }) {
|
|
1252
1222
|
return getHeaders({
|
|
1253
1223
|
initHeaderValue: "false",
|
|
1254
1224
|
workflowConfig: {
|
|
1255
1225
|
workflowRunId: context.workflowRunId,
|
|
1256
1226
|
workflowUrl: context.url,
|
|
1227
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1257
1228
|
useJSONContent: false,
|
|
1258
1229
|
telemetry
|
|
1259
1230
|
},
|
|
@@ -1261,8 +1232,7 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1261
1232
|
stepInfo: {
|
|
1262
1233
|
step,
|
|
1263
1234
|
lazyStep: this
|
|
1264
|
-
}
|
|
1265
|
-
stepSettings
|
|
1235
|
+
}
|
|
1266
1236
|
});
|
|
1267
1237
|
}
|
|
1268
1238
|
async submitStep({ context, body, headers }) {
|
|
@@ -1280,9 +1250,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
|
|
|
1280
1250
|
stepFunction;
|
|
1281
1251
|
stepType = "Run";
|
|
1282
1252
|
allowUndefinedOut = true;
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
super(context, stepName, stepSettings);
|
|
1253
|
+
constructor(context, stepName, stepFunction) {
|
|
1254
|
+
super(context, stepName);
|
|
1286
1255
|
this.stepFunction = stepFunction;
|
|
1287
1256
|
}
|
|
1288
1257
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1312,7 +1281,6 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1312
1281
|
sleep;
|
|
1313
1282
|
stepType = "SleepFor";
|
|
1314
1283
|
allowUndefinedOut = true;
|
|
1315
|
-
supportsDeferredSubmission = true;
|
|
1316
1284
|
constructor(context, stepName, sleep) {
|
|
1317
1285
|
super(context, stepName);
|
|
1318
1286
|
this.sleep = sleep;
|
|
@@ -1352,7 +1320,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1352
1320
|
sleepUntil;
|
|
1353
1321
|
stepType = "SleepUntil";
|
|
1354
1322
|
allowUndefinedOut = true;
|
|
1355
|
-
supportsDeferredSubmission = true;
|
|
1356
1323
|
constructor(context, stepName, sleepUntil) {
|
|
1357
1324
|
super(context, stepName);
|
|
1358
1325
|
this.sleepUntil = sleepUntil;
|
|
@@ -1585,6 +1552,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
|
|
|
1585
1552
|
) : {},
|
|
1586
1553
|
// note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
|
|
1587
1554
|
"Upstash-Workflow-Runid": [context.workflowRunId],
|
|
1555
|
+
// same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
|
|
1556
|
+
...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
|
|
1588
1557
|
[WORKFLOW_INIT_HEADER]: ["false"],
|
|
1589
1558
|
[WORKFLOW_URL_HEADER]: [context.url],
|
|
1590
1559
|
"Upstash-Workflow-CallType": ["step"]
|
|
@@ -1687,6 +1656,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1687
1656
|
workflowConfig: {
|
|
1688
1657
|
workflowRunId: context.workflowRunId,
|
|
1689
1658
|
workflowUrl: context.url,
|
|
1659
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1690
1660
|
telemetry,
|
|
1691
1661
|
useJSONContent: false
|
|
1692
1662
|
},
|
|
@@ -1696,6 +1666,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1696
1666
|
invokerHeaders[key] = value;
|
|
1697
1667
|
});
|
|
1698
1668
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1669
|
+
if (context.workflowRunCreatedAt) {
|
|
1670
|
+
invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
|
|
1671
|
+
}
|
|
1699
1672
|
const request = {
|
|
1700
1673
|
body: stringifyBody(this.params.body),
|
|
1701
1674
|
headers: Object.fromEntries(
|
|
@@ -1724,6 +1697,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1724
1697
|
workflowConfig: {
|
|
1725
1698
|
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1726
1699
|
workflowUrl: newUrl,
|
|
1700
|
+
// the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
|
|
1701
|
+
// it separates the invocations made by different runs of the invoker.
|
|
1702
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1727
1703
|
retries,
|
|
1728
1704
|
retryDelay,
|
|
1729
1705
|
telemetry,
|
|
@@ -1845,7 +1821,6 @@ var WorkflowHeaders = class {
|
|
|
1845
1821
|
invokeCount;
|
|
1846
1822
|
initHeaderValue;
|
|
1847
1823
|
stepInfo;
|
|
1848
|
-
stepSettings;
|
|
1849
1824
|
headers;
|
|
1850
1825
|
/**
|
|
1851
1826
|
* @param params workflow header parameters
|
|
@@ -1855,20 +1830,17 @@ var WorkflowHeaders = class {
|
|
|
1855
1830
|
workflowConfig,
|
|
1856
1831
|
invokeCount,
|
|
1857
1832
|
initHeaderValue,
|
|
1858
|
-
stepInfo
|
|
1859
|
-
stepSettings
|
|
1833
|
+
stepInfo
|
|
1860
1834
|
}) {
|
|
1861
1835
|
this.userHeaders = userHeaders;
|
|
1862
1836
|
this.workflowConfig = workflowConfig;
|
|
1863
1837
|
this.invokeCount = invokeCount;
|
|
1864
1838
|
this.initHeaderValue = initHeaderValue;
|
|
1865
1839
|
this.stepInfo = stepInfo;
|
|
1866
|
-
this.stepSettings = stepSettings;
|
|
1867
1840
|
this.headers = {
|
|
1868
1841
|
rawHeaders: {},
|
|
1869
1842
|
workflowHeaders: {},
|
|
1870
|
-
failureHeaders: {}
|
|
1871
|
-
stepSettingsHeaders: {}
|
|
1843
|
+
failureHeaders: {}
|
|
1872
1844
|
};
|
|
1873
1845
|
}
|
|
1874
1846
|
getHeaders() {
|
|
@@ -1879,7 +1851,6 @@ var WorkflowHeaders = class {
|
|
|
1879
1851
|
this.addUserHeaders();
|
|
1880
1852
|
this.addInvokeCount();
|
|
1881
1853
|
this.addFailureUrl();
|
|
1882
|
-
this.addStepSettings();
|
|
1883
1854
|
const contentType = this.addContentType();
|
|
1884
1855
|
return this.prefixHeaders(contentType);
|
|
1885
1856
|
}
|
|
@@ -1889,10 +1860,13 @@ var WorkflowHeaders = class {
|
|
|
1889
1860
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1890
1861
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1891
1862
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1892
|
-
[WORKFLOW_FEATURE_HEADER]:
|
|
1863
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1893
1864
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1894
1865
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1895
1866
|
};
|
|
1867
|
+
if (this.workflowConfig.workflowRunCreatedAt) {
|
|
1868
|
+
this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
|
|
1869
|
+
}
|
|
1896
1870
|
if (this.stepInfo?.lazyStep.stepType !== "Call") {
|
|
1897
1871
|
this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
1898
1872
|
}
|
|
@@ -1967,7 +1941,7 @@ var WorkflowHeaders = class {
|
|
|
1967
1941
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1968
1942
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1969
1943
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1970
|
-
this.headers.failureHeaders["Feature-Set"] =
|
|
1944
|
+
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
1971
1945
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1972
1946
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1973
1947
|
}
|
|
@@ -1975,42 +1949,6 @@ var WorkflowHeaders = class {
|
|
|
1975
1949
|
this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
|
|
1976
1950
|
}
|
|
1977
1951
|
}
|
|
1978
|
-
/**
|
|
1979
|
-
* Applies the step-level settings of a step to the message.
|
|
1980
|
-
*
|
|
1981
|
-
* These headers configure the message itself, so that QStash uses them
|
|
1982
|
-
* instead of the settings the run was triggered with. The delivery of
|
|
1983
|
-
* the message is what executes the step, which is how a step's
|
|
1984
|
-
* settings reach it.
|
|
1985
|
-
*
|
|
1986
|
-
* When any setting is present the feature set is extended with
|
|
1987
|
-
* `WF_StepConfig`, which is what tells QStash to keep them. QStash
|
|
1988
|
-
* reports that back on the delivery as `Upstash-Workflow-Step-Config`.
|
|
1989
|
-
*/
|
|
1990
|
-
addStepSettings() {
|
|
1991
|
-
if (!this.stepSettings) {
|
|
1992
|
-
return;
|
|
1993
|
-
}
|
|
1994
|
-
const headers = {};
|
|
1995
|
-
if (this.stepSettings.flowControl) {
|
|
1996
|
-
const { flowControlKey, flowControlValue } = prepareFlowControl(
|
|
1997
|
-
this.stepSettings.flowControl
|
|
1998
|
-
);
|
|
1999
|
-
headers[FLOW_CONTROL_KEY_HEADER] = flowControlKey;
|
|
2000
|
-
headers[FLOW_CONTROL_VALUE_HEADER] = flowControlValue;
|
|
2001
|
-
}
|
|
2002
|
-
if (this.stepSettings.retries !== void 0) {
|
|
2003
|
-
headers[RETRIES_HEADER] = this.stepSettings.retries.toString();
|
|
2004
|
-
}
|
|
2005
|
-
if (this.stepSettings.retryDelay) {
|
|
2006
|
-
headers[RETRY_DELAY_HEADER] = this.stepSettings.retryDelay;
|
|
2007
|
-
}
|
|
2008
|
-
if (Object.keys(headers).length === 0) {
|
|
2009
|
-
return;
|
|
2010
|
-
}
|
|
2011
|
-
headers[WORKFLOW_FEATURE_HEADER] = `${WORKFLOW_FEATURE_SET},${WORKFLOW_STEP_CONFIG_FEATURE}`;
|
|
2012
|
-
this.headers.stepSettingsHeaders = headers;
|
|
2013
|
-
}
|
|
2014
1952
|
addContentType() {
|
|
2015
1953
|
if (this.workflowConfig.useJSONContent) {
|
|
2016
1954
|
this.headers.rawHeaders["content-type"] = "application/json";
|
|
@@ -2024,17 +1962,14 @@ var WorkflowHeaders = class {
|
|
|
2024
1962
|
return contentType;
|
|
2025
1963
|
}
|
|
2026
1964
|
prefixHeaders(contentType) {
|
|
2027
|
-
const { rawHeaders, workflowHeaders, failureHeaders
|
|
1965
|
+
const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
|
|
2028
1966
|
const isCall = this.stepInfo?.lazyStep.stepType === "Call";
|
|
2029
1967
|
return {
|
|
2030
1968
|
headers: {
|
|
2031
1969
|
...rawHeaders,
|
|
2032
1970
|
...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
|
|
2033
1971
|
...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
|
|
2034
|
-
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
|
|
2035
|
-
// last, so they override the run's settings above. Never
|
|
2036
|
-
// prefixed: they configure this message, not its callback.
|
|
2037
|
-
...stepSettingsHeaders
|
|
1972
|
+
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
|
|
2038
1973
|
},
|
|
2039
1974
|
contentType
|
|
2040
1975
|
};
|
|
@@ -2085,16 +2020,16 @@ var submitParallelSteps = async ({
|
|
|
2085
2020
|
info: `Submitting ${planSteps.length} parallel steps.`
|
|
2086
2021
|
});
|
|
2087
2022
|
const result = await context.qstashClient.batch(
|
|
2088
|
-
planSteps.map((planStep
|
|
2023
|
+
planSteps.map((planStep) => {
|
|
2089
2024
|
const { headers } = getHeaders({
|
|
2090
2025
|
initHeaderValue: "false",
|
|
2091
2026
|
workflowConfig: {
|
|
2092
2027
|
workflowRunId: context.workflowRunId,
|
|
2093
2028
|
workflowUrl: context.url,
|
|
2029
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
2094
2030
|
telemetry
|
|
2095
2031
|
},
|
|
2096
|
-
invokeCount
|
|
2097
|
-
stepSettings: steps[index].stepSettings
|
|
2032
|
+
invokeCount
|
|
2098
2033
|
});
|
|
2099
2034
|
return {
|
|
2100
2035
|
headers,
|
|
@@ -2113,38 +2048,31 @@ var submitParallelSteps = async ({
|
|
|
2113
2048
|
}
|
|
2114
2049
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
2115
2050
|
};
|
|
2116
|
-
var
|
|
2051
|
+
var submitSingleStep = async ({
|
|
2052
|
+
context,
|
|
2117
2053
|
lazyStep,
|
|
2118
2054
|
stepId,
|
|
2055
|
+
invokeCount,
|
|
2119
2056
|
concurrency,
|
|
2057
|
+
telemetry,
|
|
2058
|
+
dispatchDebug,
|
|
2120
2059
|
dispatchLifecycle
|
|
2121
2060
|
}) => {
|
|
2122
2061
|
await dispatchLifecycle("beforeExecution", {
|
|
2123
2062
|
stepName: lazyStep.stepName
|
|
2124
2063
|
});
|
|
2064
|
+
let resultStep;
|
|
2125
2065
|
try {
|
|
2126
|
-
|
|
2066
|
+
resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
2127
2067
|
} catch (error) {
|
|
2128
2068
|
attachStepNameToError(error, lazyStep.stepName);
|
|
2129
2069
|
throw error;
|
|
2130
2070
|
}
|
|
2131
|
-
};
|
|
2132
|
-
var submitStepResult = async ({
|
|
2133
|
-
context,
|
|
2134
|
-
lazyStep,
|
|
2135
|
-
resultStep,
|
|
2136
|
-
invokeCount,
|
|
2137
|
-
concurrency,
|
|
2138
|
-
telemetry,
|
|
2139
|
-
dispatchDebug,
|
|
2140
|
-
nextStepSettings
|
|
2141
|
-
}) => {
|
|
2142
2071
|
const { headers } = lazyStep.getHeaders({
|
|
2143
2072
|
context,
|
|
2144
2073
|
step: resultStep,
|
|
2145
2074
|
invokeCount,
|
|
2146
|
-
telemetry
|
|
2147
|
-
stepSettings: nextStepSettings
|
|
2075
|
+
telemetry
|
|
2148
2076
|
});
|
|
2149
2077
|
const body = lazyStep.getBody({
|
|
2150
2078
|
context,
|
|
@@ -2169,138 +2097,6 @@ var submitStepResult = async ({
|
|
|
2169
2097
|
}
|
|
2170
2098
|
return resultStep;
|
|
2171
2099
|
};
|
|
2172
|
-
var publishStepConfigRequest = async ({
|
|
2173
|
-
context,
|
|
2174
|
-
lazyStep,
|
|
2175
|
-
targetStep,
|
|
2176
|
-
invokeCount,
|
|
2177
|
-
telemetry,
|
|
2178
|
-
dispatchDebug
|
|
2179
|
-
}) => {
|
|
2180
|
-
const { headers } = getHeaders({
|
|
2181
|
-
initHeaderValue: "false",
|
|
2182
|
-
workflowConfig: {
|
|
2183
|
-
workflowRunId: context.workflowRunId,
|
|
2184
|
-
workflowUrl: context.url,
|
|
2185
|
-
telemetry
|
|
2186
|
-
},
|
|
2187
|
-
invokeCount,
|
|
2188
|
-
stepSettings: lazyStep.stepSettings
|
|
2189
|
-
});
|
|
2190
|
-
await dispatchDebug("onInfo", {
|
|
2191
|
-
info: `Step "${lazyStep.stepName}" (${targetStep}) has step-level settings which the current delivery does not carry. Requesting a delivery which does.`
|
|
2192
|
-
});
|
|
2193
|
-
const result = await context.qstashClient.publishJSON({
|
|
2194
|
-
headers: {
|
|
2195
|
-
...headers,
|
|
2196
|
-
"Upstash-Workflow-CallType": WORKFLOW_STEP_CONFIG_CALL_TYPE
|
|
2197
|
-
},
|
|
2198
|
-
method: "POST",
|
|
2199
|
-
body: { targetStep, invokeCount },
|
|
2200
|
-
url: context.url,
|
|
2201
|
-
contentBasedDeduplication: true
|
|
2202
|
-
});
|
|
2203
|
-
if (result?.deduplicated) {
|
|
2204
|
-
await dispatchDebug("onWarning", {
|
|
2205
|
-
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.`
|
|
2206
|
-
});
|
|
2207
|
-
} else if (result?.messageId) {
|
|
2208
|
-
await dispatchDebug("onInfo", {
|
|
2209
|
-
info: `Requested a delivery under the settings of step "${lazyStep.stepName}" with messageId: ${result.messageId}.`
|
|
2210
|
-
});
|
|
2211
|
-
}
|
|
2212
|
-
};
|
|
2213
|
-
|
|
2214
|
-
// src/qstash/step-config.ts
|
|
2215
|
-
var DEFAULT_PARALLELISM = 0;
|
|
2216
|
-
var DEFAULT_RATE = 0;
|
|
2217
|
-
var DEFAULT_PERIOD_SECONDS = 1;
|
|
2218
|
-
var SECONDS_PER_UNIT = {
|
|
2219
|
-
s: 1,
|
|
2220
|
-
m: 60,
|
|
2221
|
-
h: 60 * 60,
|
|
2222
|
-
d: 24 * 60 * 60
|
|
2223
|
-
};
|
|
2224
|
-
var durationToSeconds = (duration) => {
|
|
2225
|
-
if (typeof duration === "number") {
|
|
2226
|
-
return Number.isFinite(duration) ? duration : void 0;
|
|
2227
|
-
}
|
|
2228
|
-
const match = /^(\d+)([smhd])$/.exec(duration.trim());
|
|
2229
|
-
if (match) {
|
|
2230
|
-
return Number(match[1]) * SECONDS_PER_UNIT[match[2]];
|
|
2231
|
-
}
|
|
2232
|
-
const seconds = Number(duration.trim());
|
|
2233
|
-
return Number.isFinite(seconds) ? seconds : void 0;
|
|
2234
|
-
};
|
|
2235
|
-
var normalizeFlowControl = (flowControl) => {
|
|
2236
|
-
const period = flowControl.period === void 0 ? void 0 : durationToSeconds(flowControl.period);
|
|
2237
|
-
return {
|
|
2238
|
-
key: flowControl.key,
|
|
2239
|
-
parallelism: flowControl.parallelism ?? DEFAULT_PARALLELISM,
|
|
2240
|
-
rate: flowControl.rate ?? flowControl.ratePerSecond ?? DEFAULT_RATE,
|
|
2241
|
-
period: period ?? DEFAULT_PERIOD_SECONDS
|
|
2242
|
-
};
|
|
2243
|
-
};
|
|
2244
|
-
var parseFlowControlHeaders = (key, value) => {
|
|
2245
|
-
if (!key) {
|
|
2246
|
-
return void 0;
|
|
2247
|
-
}
|
|
2248
|
-
const flowControl = {
|
|
2249
|
-
key,
|
|
2250
|
-
parallelism: DEFAULT_PARALLELISM,
|
|
2251
|
-
rate: DEFAULT_RATE,
|
|
2252
|
-
period: DEFAULT_PERIOD_SECONDS
|
|
2253
|
-
};
|
|
2254
|
-
for (const entry of (value ?? "").split(",")) {
|
|
2255
|
-
const [name, rawValue] = entry.split("=").map((part) => part.trim());
|
|
2256
|
-
if (!name || rawValue === void 0) {
|
|
2257
|
-
continue;
|
|
2258
|
-
}
|
|
2259
|
-
switch (name) {
|
|
2260
|
-
case "parallelism": {
|
|
2261
|
-
flowControl.parallelism = Number(rawValue);
|
|
2262
|
-
break;
|
|
2263
|
-
}
|
|
2264
|
-
case "rate": {
|
|
2265
|
-
flowControl.rate = Number(rawValue);
|
|
2266
|
-
break;
|
|
2267
|
-
}
|
|
2268
|
-
case "period": {
|
|
2269
|
-
flowControl.period = durationToSeconds(rawValue) ?? DEFAULT_PERIOD_SECONDS;
|
|
2270
|
-
break;
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
return flowControl;
|
|
2275
|
-
};
|
|
2276
|
-
var getEffectiveConfig = (headers) => {
|
|
2277
|
-
const retriesHeader = headers.get(RETRIES_HEADER);
|
|
2278
|
-
return {
|
|
2279
|
-
flowControl: parseFlowControlHeaders(
|
|
2280
|
-
headers.get(FLOW_CONTROL_KEY_HEADER),
|
|
2281
|
-
headers.get(FLOW_CONTROL_VALUE_HEADER)
|
|
2282
|
-
),
|
|
2283
|
-
retries: retriesHeader === null ? void 0 : Number(retriesHeader),
|
|
2284
|
-
retryDelay: headers.get(RETRY_DELAY_HEADER) ?? void 0,
|
|
2285
|
-
hasStepConfig: headers.get(WORKFLOW_STEP_CONFIG_HEADER) === "true"
|
|
2286
|
-
};
|
|
2287
|
-
};
|
|
2288
|
-
var describeStepSettingsMismatch = (stepSettings, effectiveConfig) => {
|
|
2289
|
-
if (stepSettings.flowControl) {
|
|
2290
|
-
const wanted = normalizeFlowControl(stepSettings.flowControl);
|
|
2291
|
-
const applied = effectiveConfig.flowControl;
|
|
2292
|
-
if (!applied || applied.key !== wanted.key || applied.parallelism !== wanted.parallelism || applied.rate !== wanted.rate || applied.period !== wanted.period) {
|
|
2293
|
-
return `flow control: expected ${JSON.stringify(wanted)}, delivery has ${JSON.stringify(applied)}`;
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2296
|
-
if (stepSettings.retries !== void 0 && effectiveConfig.retries !== void 0 && stepSettings.retries !== effectiveConfig.retries) {
|
|
2297
|
-
return `retries: expected ${stepSettings.retries}, delivery has ${effectiveConfig.retries}`;
|
|
2298
|
-
}
|
|
2299
|
-
if (stepSettings.retryDelay !== void 0 && stepSettings.retryDelay !== effectiveConfig.retryDelay) {
|
|
2300
|
-
return `retry delay: expected '${stepSettings.retryDelay}', delivery has '${effectiveConfig.retryDelay ?? ""}'`;
|
|
2301
|
-
}
|
|
2302
|
-
return void 0;
|
|
2303
|
-
};
|
|
2304
2100
|
|
|
2305
2101
|
// src/context/auto-executor.ts
|
|
2306
2102
|
var AutoExecutor = class _AutoExecutor {
|
|
@@ -2317,25 +2113,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2317
2113
|
stepCount = 0;
|
|
2318
2114
|
planStepCount = 0;
|
|
2319
2115
|
executingStep = false;
|
|
2320
|
-
/**
|
|
2321
|
-
* an executed step whose result hasn't been submitted to QStash yet.
|
|
2322
|
-
*
|
|
2323
|
-
* Set when a step which can produce its result in-process executes. The
|
|
2324
|
-
* submission waits so that the route function can continue and reveal
|
|
2325
|
-
* what comes next: if that is a single step with step-level settings,
|
|
2326
|
-
* the settings ride on this submission and no step config request is
|
|
2327
|
-
* needed, since the delivery of the submission is what executes it.
|
|
2328
|
-
*
|
|
2329
|
-
* Flushed when the next step is reached (see `addStep`) or when the
|
|
2330
|
-
* route function ends (see `flushPendingStep` in `serve`).
|
|
2331
|
-
*/
|
|
2332
|
-
pendingStep;
|
|
2333
|
-
/**
|
|
2334
|
-
* configuration QStash applied to the delivery being handled. A step
|
|
2335
|
-
* with step-level settings is settled by comparing its settings against
|
|
2336
|
-
* this.
|
|
2337
|
-
*/
|
|
2338
|
-
effectiveConfig;
|
|
2339
2116
|
/**
|
|
2340
2117
|
* @param context workflow context
|
|
2341
2118
|
* @param steps list of steps
|
|
@@ -2343,17 +2120,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2343
2120
|
* @param dispatchLifecycle lifecycle event dispatcher
|
|
2344
2121
|
* @param telemetry optional telemetry information
|
|
2345
2122
|
* @param invokeCount optional invoke count
|
|
2346
|
-
* @param effectiveConfig configuration QStash applied to the delivery
|
|
2347
|
-
* being handled
|
|
2348
2123
|
*/
|
|
2349
|
-
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount
|
|
2124
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount) {
|
|
2350
2125
|
this.context = context;
|
|
2351
2126
|
this.steps = steps;
|
|
2352
2127
|
this.dispatchDebug = dispatchDebug;
|
|
2353
2128
|
this.dispatchLifecycle = dispatchLifecycle;
|
|
2354
2129
|
this.telemetry = telemetry;
|
|
2355
2130
|
this.invokeCount = invokeCount ?? 0;
|
|
2356
|
-
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
2357
2131
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
2358
2132
|
}
|
|
2359
2133
|
/**
|
|
@@ -2388,15 +2162,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2388
2162
|
lazyStepList.push(stepInfo);
|
|
2389
2163
|
const index = this.indexInCurrentList++;
|
|
2390
2164
|
const requestComplete = this.deferExecution().then(async () => {
|
|
2391
|
-
const submitted = await this.submitPendingStep(
|
|
2392
|
-
lazyStepList.length === 1 ? lazyStepList[0].stepSettings : void 0
|
|
2393
|
-
);
|
|
2394
|
-
if (submitted.isErr()) {
|
|
2395
|
-
throw submitted.error;
|
|
2396
|
-
}
|
|
2397
|
-
if (submitted.value.result === "submitted-step") {
|
|
2398
|
-
throw submitted.value.abort;
|
|
2399
|
-
}
|
|
2400
2165
|
if (!this.promises.has(lazyStepList)) {
|
|
2401
2166
|
const promise2 = this.getExecutionPromise(lazyStepList);
|
|
2402
2167
|
this.promises.set(lazyStepList, promise2);
|
|
@@ -2450,100 +2215,18 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2450
2215
|
}
|
|
2451
2216
|
return parsedOut;
|
|
2452
2217
|
}
|
|
2453
|
-
const
|
|
2454
|
-
if (mismatch) {
|
|
2455
|
-
if (!this.effectiveConfig.hasStepConfig) {
|
|
2456
|
-
await publishStepConfigRequest({
|
|
2457
|
-
context: this.context,
|
|
2458
|
-
lazyStep,
|
|
2459
|
-
targetStep: this.stepCount,
|
|
2460
|
-
invokeCount: this.invokeCount,
|
|
2461
|
-
telemetry: this.telemetry,
|
|
2462
|
-
dispatchDebug: this.dispatchDebug
|
|
2463
|
-
});
|
|
2464
|
-
throw new WorkflowAbort(lazyStep.stepName);
|
|
2465
|
-
} else {
|
|
2466
|
-
await this.dispatchDebug("onWarning", {
|
|
2467
|
-
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.`
|
|
2468
|
-
});
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
const resultStep = await executeStep({
|
|
2472
|
-
lazyStep,
|
|
2473
|
-
stepId: this.stepCount,
|
|
2474
|
-
concurrency: NO_CONCURRENCY,
|
|
2475
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2476
|
-
});
|
|
2477
|
-
if (lazyStep.supportsDeferredSubmission) {
|
|
2478
|
-
this.pendingStep = { status: "held", lazyStep, resultStep };
|
|
2479
|
-
return lazyStep.parseOut({
|
|
2480
|
-
...resultStep,
|
|
2481
|
-
out: resultStep.out === void 0 ? void 0 : JSON.stringify(resultStep.out)
|
|
2482
|
-
});
|
|
2483
|
-
}
|
|
2484
|
-
await submitStepResult({
|
|
2218
|
+
const resultStep = await submitSingleStep({
|
|
2485
2219
|
context: this.context,
|
|
2486
2220
|
lazyStep,
|
|
2487
|
-
|
|
2221
|
+
stepId: this.stepCount,
|
|
2488
2222
|
invokeCount: this.invokeCount,
|
|
2489
|
-
concurrency:
|
|
2223
|
+
concurrency: 1,
|
|
2490
2224
|
telemetry: this.telemetry,
|
|
2491
|
-
dispatchDebug: this.dispatchDebug
|
|
2225
|
+
dispatchDebug: this.dispatchDebug,
|
|
2226
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2492
2227
|
});
|
|
2493
2228
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2494
2229
|
}
|
|
2495
|
-
/**
|
|
2496
|
-
* Submits the result of the step being held, if there is one, and
|
|
2497
|
-
* returns the abort the caller has to throw to end the invocation.
|
|
2498
|
-
*
|
|
2499
|
-
* Returns undefined when no step is being held, which is the caller's
|
|
2500
|
-
* signal that there is nothing to end. Throws instead of returning
|
|
2501
|
-
* when the submission itself fails, so that error reaches the caller
|
|
2502
|
-
* rather than an abort claiming the step was submitted.
|
|
2503
|
-
*
|
|
2504
|
-
* Callers which arrive while a submission is already under way (the
|
|
2505
|
-
* steps of a parallel group, added together) wait for that one and get
|
|
2506
|
-
* the same abort, rather than submitting a second time.
|
|
2507
|
-
*
|
|
2508
|
-
* The next step's settings are attached whenever it has any, without
|
|
2509
|
-
* checking them against the current delivery: the executor only knows
|
|
2510
|
-
* the configuration of the delivery in hand, which inside a
|
|
2511
|
-
* delivery is the *previous* step's, so such a check would be wrong
|
|
2512
|
-
* exactly when two steps with settings follow each other.
|
|
2513
|
-
*
|
|
2514
|
-
* @param nextStepSettings step-level settings of the next step
|
|
2515
|
-
* @returns `submitted-step` with the abort which ends this invocation,
|
|
2516
|
-
* or `no-pending-step` when nothing was held and there is nothing to
|
|
2517
|
-
* end
|
|
2518
|
-
*/
|
|
2519
|
-
async submitPendingStep(nextStepSettings) {
|
|
2520
|
-
if (!this.pendingStep) {
|
|
2521
|
-
return ok({ result: "no-pending-step" });
|
|
2522
|
-
}
|
|
2523
|
-
try {
|
|
2524
|
-
if (this.pendingStep.status === "submitting") {
|
|
2525
|
-
return ok({ result: "submitted-step", abort: await this.pendingStep.submitted });
|
|
2526
|
-
}
|
|
2527
|
-
const { lazyStep, resultStep } = this.pendingStep;
|
|
2528
|
-
const submitted = (async () => {
|
|
2529
|
-
await submitStepResult({
|
|
2530
|
-
context: this.context,
|
|
2531
|
-
lazyStep,
|
|
2532
|
-
resultStep,
|
|
2533
|
-
invokeCount: this.invokeCount,
|
|
2534
|
-
concurrency: NO_CONCURRENCY,
|
|
2535
|
-
telemetry: this.telemetry,
|
|
2536
|
-
dispatchDebug: this.dispatchDebug,
|
|
2537
|
-
nextStepSettings
|
|
2538
|
-
});
|
|
2539
|
-
return new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2540
|
-
})();
|
|
2541
|
-
this.pendingStep = { status: "submitting", submitted };
|
|
2542
|
-
return ok({ result: "submitted-step", abort: await submitted });
|
|
2543
|
-
} catch (error) {
|
|
2544
|
-
return err(error);
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
2230
|
/**
|
|
2548
2231
|
* Runs steps in parallel.
|
|
2549
2232
|
*
|
|
@@ -2592,20 +2275,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2592
2275
|
validateStep(parallelSteps[stepIndex], planStep);
|
|
2593
2276
|
try {
|
|
2594
2277
|
const parallelStep = parallelSteps[stepIndex];
|
|
2595
|
-
const resultStep = await
|
|
2596
|
-
lazyStep: parallelStep,
|
|
2597
|
-
stepId: planStep.targetStep,
|
|
2598
|
-
concurrency: parallelSteps.length,
|
|
2599
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2600
|
-
});
|
|
2601
|
-
await submitStepResult({
|
|
2278
|
+
const resultStep = await submitSingleStep({
|
|
2602
2279
|
context: this.context,
|
|
2603
2280
|
lazyStep: parallelStep,
|
|
2604
|
-
|
|
2281
|
+
stepId: planStep.targetStep,
|
|
2605
2282
|
invokeCount: this.invokeCount,
|
|
2606
2283
|
concurrency: parallelSteps.length,
|
|
2607
2284
|
telemetry: this.telemetry,
|
|
2608
|
-
dispatchDebug: this.dispatchDebug
|
|
2285
|
+
dispatchDebug: this.dispatchDebug,
|
|
2286
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2609
2287
|
});
|
|
2610
2288
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2611
2289
|
} catch (error) {
|
|
@@ -3154,46 +2832,6 @@ var WorkflowContext = class {
|
|
|
3154
2832
|
* `1` on the first retry, `2` on the second, and so on.
|
|
3155
2833
|
*/
|
|
3156
2834
|
retried;
|
|
3157
|
-
/**
|
|
3158
|
-
* Configuration QStash applied to the request being handled.
|
|
3159
|
-
*
|
|
3160
|
-
* This is the configuration of *this delivery*, not of the run: it is
|
|
3161
|
-
* the configuration the run was triggered with, except inside the
|
|
3162
|
-
* delivery which executes a step that has step-level settings, where
|
|
3163
|
-
* it is that step's settings. The fields below expose it; the executor
|
|
3164
|
-
* gets it directly rather than reaching through the context.
|
|
3165
|
-
*/
|
|
3166
|
-
effectiveConfig;
|
|
3167
|
-
/**
|
|
3168
|
-
* Flow control QStash applied to the request being handled, if any.
|
|
3169
|
-
*
|
|
3170
|
-
* This is the flow control of the request in hand, which is the one the
|
|
3171
|
-
* run was triggered with except inside a step carrying its own.
|
|
3172
|
-
*/
|
|
3173
|
-
get flowControl() {
|
|
3174
|
-
return this.effectiveConfig.flowControl;
|
|
3175
|
-
}
|
|
3176
|
-
/**
|
|
3177
|
-
* Retry limit QStash applied to the request being handled, if it
|
|
3178
|
-
* reported one.
|
|
3179
|
-
*
|
|
3180
|
-
* Not to be confused with {@link retried}, which is how many retries
|
|
3181
|
-
* have already happened.
|
|
3182
|
-
*
|
|
3183
|
-
* @see {@link flowControl} for what "the request being handled" means
|
|
3184
|
-
*/
|
|
3185
|
-
get retries() {
|
|
3186
|
-
return this.effectiveConfig.retries;
|
|
3187
|
-
}
|
|
3188
|
-
/**
|
|
3189
|
-
* Retry delay expression QStash applied to the request being handled,
|
|
3190
|
-
* if any.
|
|
3191
|
-
*
|
|
3192
|
-
* @see {@link flowControl} for what "the request being handled" means
|
|
3193
|
-
*/
|
|
3194
|
-
get retryDelay() {
|
|
3195
|
-
return this.effectiveConfig.retryDelay;
|
|
3196
|
-
}
|
|
3197
2835
|
constructor({
|
|
3198
2836
|
qstashClient,
|
|
3199
2837
|
workflowRunId,
|
|
@@ -3207,8 +2845,7 @@ var WorkflowContext = class {
|
|
|
3207
2845
|
invokeCount,
|
|
3208
2846
|
label,
|
|
3209
2847
|
retried,
|
|
3210
|
-
middlewareManager
|
|
3211
|
-
effectiveConfig
|
|
2848
|
+
middlewareManager
|
|
3212
2849
|
}) {
|
|
3213
2850
|
this.qstashClient = qstashClient;
|
|
3214
2851
|
this.workflowRunId = workflowRunId;
|
|
@@ -3220,7 +2857,6 @@ var WorkflowContext = class {
|
|
|
3220
2857
|
this.env = env ?? {};
|
|
3221
2858
|
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
3222
2859
|
this.retried = retried ?? 0;
|
|
3223
|
-
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
3224
2860
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
3225
2861
|
middlewareManagerInstance.assignContext(this);
|
|
3226
2862
|
this.executor = new AutoExecutor(
|
|
@@ -3229,8 +2865,7 @@ var WorkflowContext = class {
|
|
|
3229
2865
|
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
3230
2866
|
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
3231
2867
|
telemetry,
|
|
3232
|
-
invokeCount
|
|
3233
|
-
this.effectiveConfig
|
|
2868
|
+
invokeCount
|
|
3234
2869
|
);
|
|
3235
2870
|
}
|
|
3236
2871
|
/**
|
|
@@ -3256,35 +2891,13 @@ var WorkflowContext = class {
|
|
|
3256
2891
|
* ])
|
|
3257
2892
|
* ```
|
|
3258
2893
|
*
|
|
3259
|
-
* Step-level settings can be passed as a third argument, overriding
|
|
3260
|
-
* the settings the workflow run was triggered with for this step only:
|
|
3261
|
-
*
|
|
3262
|
-
* ```typescript
|
|
3263
|
-
* const result = await context.run(
|
|
3264
|
-
* "step 1",
|
|
3265
|
-
* () => {
|
|
3266
|
-
* return "result"
|
|
3267
|
-
* },
|
|
3268
|
-
* {
|
|
3269
|
-
* flowControl: { key: "custom-key", parallelism: 3 },
|
|
3270
|
-
* retries: 5,
|
|
3271
|
-
* }
|
|
3272
|
-
* )
|
|
3273
|
-
* ```
|
|
3274
|
-
*
|
|
3275
2894
|
* @param stepName name of the step
|
|
3276
2895
|
* @param stepFunction step function to be executed
|
|
3277
|
-
* @param stepSettings step-level settings for this step
|
|
3278
2896
|
* @returns result of the step function
|
|
3279
2897
|
*/
|
|
3280
|
-
async run(stepName, stepFunction
|
|
3281
|
-
if (stepSettings) {
|
|
3282
|
-
validateFlowControl(stepSettings.flowControl);
|
|
3283
|
-
}
|
|
2898
|
+
async run(stepName, stepFunction) {
|
|
3284
2899
|
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
3285
|
-
return await this.addStep(
|
|
3286
|
-
new LazyFunctionStep(this, stepName, wrappedStepFunction, stepSettings)
|
|
3287
|
-
);
|
|
2900
|
+
return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
|
|
3288
2901
|
}
|
|
3289
2902
|
/**
|
|
3290
2903
|
* Stops the execution for the duration provided.
|
|
@@ -3507,7 +3120,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3507
3120
|
*
|
|
3508
3121
|
* @param routeFunction
|
|
3509
3122
|
*/
|
|
3510
|
-
static async tryAuthentication(routeFunction, context
|
|
3123
|
+
static async tryAuthentication(routeFunction, context) {
|
|
3511
3124
|
const disabledContext = new _DisabledWorkflowContext({
|
|
3512
3125
|
qstashClient: new import_qstash10.Client({
|
|
3513
3126
|
baseUrl: "disabled-client",
|
|
@@ -3522,10 +3135,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3522
3135
|
initialPayload: context.requestPayload,
|
|
3523
3136
|
env: context.env,
|
|
3524
3137
|
label: context.labels,
|
|
3525
|
-
retried: context.retried
|
|
3526
|
-
// the route function runs for real here until it reaches a step, so
|
|
3527
|
-
// it observes the same configuration as it will on the real context
|
|
3528
|
-
effectiveConfig
|
|
3138
|
+
retried: context.retried
|
|
3529
3139
|
});
|
|
3530
3140
|
try {
|
|
3531
3141
|
await routeFunction(disabledContext);
|
|
@@ -3742,7 +3352,6 @@ var handleFailure = async ({
|
|
|
3742
3352
|
}
|
|
3743
3353
|
const userHeaders = recreateUserHeaders(request.headers);
|
|
3744
3354
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3745
|
-
const effectiveConfig = getEffectiveConfig(request.headers);
|
|
3746
3355
|
const workflowContext = new WorkflowContext({
|
|
3747
3356
|
qstashClient,
|
|
3748
3357
|
workflowRunId,
|
|
@@ -3756,13 +3365,11 @@ var handleFailure = async ({
|
|
|
3756
3365
|
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3757
3366
|
retried,
|
|
3758
3367
|
workflowRunCreatedAt: workflowCreatedAt,
|
|
3759
|
-
middlewareManager: void 0
|
|
3760
|
-
effectiveConfig
|
|
3368
|
+
middlewareManager: void 0
|
|
3761
3369
|
});
|
|
3762
3370
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3763
3371
|
routeFunction,
|
|
3764
|
-
workflowContext
|
|
3765
|
-
effectiveConfig
|
|
3372
|
+
workflowContext
|
|
3766
3373
|
);
|
|
3767
3374
|
if (authCheck.isErr()) {
|
|
3768
3375
|
await dispatchDebug?.("onError", {
|
|
@@ -4251,28 +3858,26 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
4251
3858
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
4252
3859
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
4253
3860
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
4254
|
-
const
|
|
4255
|
-
const
|
|
3861
|
+
const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
3862
|
+
const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
|
|
4256
3863
|
const workflowContext = new WorkflowContext({
|
|
4257
3864
|
qstashClient: regionalClient,
|
|
4258
3865
|
workflowRunId,
|
|
4259
3866
|
initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
|
|
4260
3867
|
headers: recreateUserHeaders(request.headers),
|
|
4261
3868
|
steps,
|
|
4262
|
-
effectiveConfig,
|
|
4263
3869
|
url: workflowUrl,
|
|
4264
3870
|
env,
|
|
4265
3871
|
telemetry,
|
|
4266
3872
|
invokeCount,
|
|
4267
3873
|
label,
|
|
4268
3874
|
retried,
|
|
4269
|
-
workflowRunCreatedAt
|
|
3875
|
+
workflowRunCreatedAt,
|
|
4270
3876
|
middlewareManager
|
|
4271
3877
|
});
|
|
4272
3878
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
4273
3879
|
routeFunction,
|
|
4274
|
-
workflowContext
|
|
4275
|
-
effectiveConfig
|
|
3880
|
+
workflowContext
|
|
4276
3881
|
);
|
|
4277
3882
|
if (authCheck.isErr()) {
|
|
4278
3883
|
throw authCheck.error;
|
|
@@ -4309,23 +3914,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
4309
3914
|
if (steps.length === 1) {
|
|
4310
3915
|
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
4311
3916
|
}
|
|
4312
|
-
|
|
4313
|
-
try {
|
|
4314
|
-
outcome = { ran: true, result: await routeFunction(workflowContext) };
|
|
4315
|
-
} catch (error) {
|
|
4316
|
-
outcome = { ran: false, error };
|
|
4317
|
-
}
|
|
4318
|
-
const submitted = await flushPendingStep(workflowContext);
|
|
4319
|
-
if (submitted.isErr()) {
|
|
4320
|
-
throw submitted.error;
|
|
4321
|
-
}
|
|
4322
|
-
if (submitted.value.result === "submitted-step") {
|
|
4323
|
-
throw submitted.value.abort;
|
|
4324
|
-
}
|
|
4325
|
-
if (!outcome.ran) {
|
|
4326
|
-
throw outcome.error;
|
|
4327
|
-
}
|
|
4328
|
-
return outcome.result;
|
|
3917
|
+
return await routeFunction(workflowContext);
|
|
4329
3918
|
},
|
|
4330
3919
|
onCleanup: async (result2) => {
|
|
4331
3920
|
await middlewareManager.dispatchLifecycle("runCompleted", {
|