@upstash/workflow 0.1.4 → 0.2.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 +1 -1
- package/astro.d.ts +1 -1
- package/astro.js +348 -191
- package/astro.mjs +1 -1
- package/{chunk-HO2SB246.mjs → chunk-5R2BFC3N.mjs} +419 -194
- package/cloudflare.d.mts +1 -1
- package/cloudflare.d.ts +1 -1
- package/cloudflare.js +348 -191
- package/cloudflare.mjs +1 -1
- package/express.d.mts +1 -1
- package/express.d.ts +1 -1
- package/express.js +369 -189
- package/express.mjs +17 -4
- package/h3.d.mts +1 -1
- package/h3.d.ts +1 -1
- package/h3.js +348 -191
- package/h3.mjs +1 -1
- package/hono.d.mts +1 -1
- package/hono.d.ts +1 -1
- package/hono.js +348 -191
- package/hono.mjs +1 -1
- package/index.d.mts +74 -21
- package/index.d.ts +74 -21
- package/index.js +426 -211
- package/index.mjs +5 -5
- package/nextjs.d.mts +1 -1
- package/nextjs.d.ts +1 -1
- package/nextjs.js +348 -191
- package/nextjs.mjs +1 -1
- package/package.json +1 -1
- package/solidjs.d.mts +1 -1
- package/solidjs.d.ts +1 -1
- package/solidjs.js +348 -191
- package/solidjs.mjs +1 -1
- package/svelte.d.mts +1 -1
- package/svelte.d.ts +1 -1
- package/svelte.js +348 -191
- package/svelte.mjs +1 -1
- package/{types-CQuc-j8n.d.mts → types-Cki_MHrh.d.mts} +85 -31
- package/{types-CQuc-j8n.d.ts → types-Cki_MHrh.d.ts} +85 -31
package/astro.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// platforms/astro.ts
|
|
@@ -36,22 +26,33 @@ module.exports = __toCommonJS(astro_exports);
|
|
|
36
26
|
|
|
37
27
|
// src/error.ts
|
|
38
28
|
var import_qstash = require("@upstash/qstash");
|
|
39
|
-
var
|
|
29
|
+
var WorkflowError = class extends import_qstash.QstashError {
|
|
40
30
|
constructor(message) {
|
|
41
31
|
super(message);
|
|
42
|
-
this.name = "
|
|
32
|
+
this.name = "WorkflowError";
|
|
43
33
|
}
|
|
44
34
|
};
|
|
45
|
-
var
|
|
35
|
+
var WorkflowAbort = class extends Error {
|
|
46
36
|
stepInfo;
|
|
47
37
|
stepName;
|
|
48
|
-
|
|
38
|
+
/**
|
|
39
|
+
* whether workflow is to be canceled on abort
|
|
40
|
+
*/
|
|
41
|
+
cancelWorkflow;
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @param stepName name of the aborting step
|
|
45
|
+
* @param stepInfo step information
|
|
46
|
+
* @param cancelWorkflow
|
|
47
|
+
*/
|
|
48
|
+
constructor(stepName, stepInfo, cancelWorkflow = false) {
|
|
49
49
|
super(
|
|
50
50
|
`This is an Upstash Workflow error thrown after a step executes. It is expected to be raised. Make sure that you await for each step. Also, if you are using try/catch blocks, you should not wrap context.run/sleep/sleepUntil/call methods with try/catch. Aborting workflow after executing step '${stepName}'.`
|
|
51
51
|
);
|
|
52
|
-
this.name = "
|
|
52
|
+
this.name = "WorkflowAbort";
|
|
53
53
|
this.stepName = stepName;
|
|
54
54
|
this.stepInfo = stepInfo;
|
|
55
|
+
this.cancelWorkflow = cancelWorkflow;
|
|
55
56
|
}
|
|
56
57
|
};
|
|
57
58
|
var formatWorkflowError = (error) => {
|
|
@@ -73,6 +74,44 @@ var makeNotifyRequest = async (requester, eventId, eventData) => {
|
|
|
73
74
|
});
|
|
74
75
|
return result;
|
|
75
76
|
};
|
|
77
|
+
var makeCancelRequest = async (requester, workflowRunId) => {
|
|
78
|
+
await requester.request({
|
|
79
|
+
path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
|
|
80
|
+
method: "DELETE",
|
|
81
|
+
parseResponseAsJson: false
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
};
|
|
85
|
+
var getSteps = async (requester, workflowRunId, messageId, debug) => {
|
|
86
|
+
try {
|
|
87
|
+
const steps = await requester.request({
|
|
88
|
+
path: ["v2", "workflows", "runs", workflowRunId],
|
|
89
|
+
parseResponseAsJson: true
|
|
90
|
+
});
|
|
91
|
+
if (!messageId) {
|
|
92
|
+
await debug?.log("INFO", "ENDPOINT_START", {
|
|
93
|
+
message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
|
|
94
|
+
});
|
|
95
|
+
return steps;
|
|
96
|
+
} else {
|
|
97
|
+
const index = steps.findIndex((item) => item.messageId === messageId);
|
|
98
|
+
if (index === -1) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const filteredSteps = steps.slice(0, index + 1);
|
|
102
|
+
await debug?.log("INFO", "ENDPOINT_START", {
|
|
103
|
+
message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
|
|
104
|
+
});
|
|
105
|
+
return filteredSteps;
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
await debug?.log("ERROR", "ERROR", {
|
|
109
|
+
message: "failed while fetching steps.",
|
|
110
|
+
error
|
|
111
|
+
});
|
|
112
|
+
throw new WorkflowError(`Failed while pulling steps. ${error}`);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
76
115
|
|
|
77
116
|
// src/context/steps.ts
|
|
78
117
|
var BaseLazyStep = class {
|
|
@@ -687,6 +726,7 @@ var StepTypes = [
|
|
|
687
726
|
];
|
|
688
727
|
|
|
689
728
|
// src/workflow-requests.ts
|
|
729
|
+
var import_qstash2 = require("@upstash/qstash");
|
|
690
730
|
var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
691
731
|
const { headers } = getHeaders(
|
|
692
732
|
"true",
|
|
@@ -697,20 +737,32 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
697
737
|
workflowContext.failureUrl,
|
|
698
738
|
retries
|
|
699
739
|
);
|
|
700
|
-
await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
|
|
701
|
-
headers,
|
|
702
|
-
requestPayload: workflowContext.requestPayload,
|
|
703
|
-
url: workflowContext.url
|
|
704
|
-
});
|
|
705
740
|
try {
|
|
706
741
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
707
|
-
await workflowContext.qstashClient.publish({
|
|
742
|
+
const result = await workflowContext.qstashClient.publish({
|
|
708
743
|
headers,
|
|
709
744
|
method: "POST",
|
|
710
745
|
body,
|
|
711
746
|
url: workflowContext.url
|
|
712
747
|
});
|
|
713
|
-
|
|
748
|
+
if (result.deduplicated) {
|
|
749
|
+
await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
|
|
750
|
+
message: `Workflow run ${workflowContext.workflowRunId} already exists. A new one isn't created.`,
|
|
751
|
+
headers,
|
|
752
|
+
requestPayload: workflowContext.requestPayload,
|
|
753
|
+
url: workflowContext.url,
|
|
754
|
+
messageId: result.messageId
|
|
755
|
+
});
|
|
756
|
+
return ok("workflow-run-already-exists");
|
|
757
|
+
} else {
|
|
758
|
+
await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
|
|
759
|
+
headers,
|
|
760
|
+
requestPayload: workflowContext.requestPayload,
|
|
761
|
+
url: workflowContext.url,
|
|
762
|
+
messageId: result.messageId
|
|
763
|
+
});
|
|
764
|
+
return ok("success");
|
|
765
|
+
}
|
|
714
766
|
} catch (error) {
|
|
715
767
|
const error_ = error;
|
|
716
768
|
return err(error_);
|
|
@@ -718,7 +770,9 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
718
770
|
};
|
|
719
771
|
var triggerRouteFunction = async ({
|
|
720
772
|
onCleanup,
|
|
721
|
-
onStep
|
|
773
|
+
onStep,
|
|
774
|
+
onCancel,
|
|
775
|
+
debug
|
|
722
776
|
}) => {
|
|
723
777
|
try {
|
|
724
778
|
await onStep();
|
|
@@ -726,19 +780,50 @@ var triggerRouteFunction = async ({
|
|
|
726
780
|
return ok("workflow-finished");
|
|
727
781
|
} catch (error) {
|
|
728
782
|
const error_ = error;
|
|
729
|
-
|
|
783
|
+
if (error instanceof import_qstash2.QstashError && error.status === 400) {
|
|
784
|
+
await debug?.log("WARN", "RESPONSE_WORKFLOW", {
|
|
785
|
+
message: `tried to append to a cancelled workflow. exiting without publishing.`,
|
|
786
|
+
name: error.name,
|
|
787
|
+
errorMessage: error.message
|
|
788
|
+
});
|
|
789
|
+
return ok("workflow-was-finished");
|
|
790
|
+
} else if (!(error_ instanceof WorkflowAbort)) {
|
|
791
|
+
return err(error_);
|
|
792
|
+
} else if (error_.cancelWorkflow) {
|
|
793
|
+
await onCancel();
|
|
794
|
+
return ok("workflow-finished");
|
|
795
|
+
} else {
|
|
796
|
+
return ok("step-finished");
|
|
797
|
+
}
|
|
730
798
|
}
|
|
731
799
|
};
|
|
732
800
|
var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
|
|
733
801
|
await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
|
|
734
802
|
deletedWorkflowRunId: workflowContext.workflowRunId
|
|
735
803
|
});
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
804
|
+
try {
|
|
805
|
+
await workflowContext.qstashClient.http.request({
|
|
806
|
+
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
807
|
+
method: "DELETE",
|
|
808
|
+
parseResponseAsJson: false
|
|
809
|
+
});
|
|
810
|
+
await debug?.log(
|
|
811
|
+
"SUBMIT",
|
|
812
|
+
"SUBMIT_CLEANUP",
|
|
813
|
+
`workflow run ${workflowContext.workflowRunId} deleted.`
|
|
814
|
+
);
|
|
815
|
+
return { deleted: true };
|
|
816
|
+
} catch (error) {
|
|
817
|
+
if (error instanceof import_qstash2.QstashError && error.status === 404) {
|
|
818
|
+
await debug?.log("WARN", "SUBMIT_CLEANUP", {
|
|
819
|
+
message: `Failed to remove workflow run ${workflowContext.workflowRunId} as it doesn't exist.`,
|
|
820
|
+
name: error.name,
|
|
821
|
+
errorMessage: error.message
|
|
822
|
+
});
|
|
823
|
+
return { deleted: false };
|
|
824
|
+
}
|
|
825
|
+
throw error;
|
|
826
|
+
}
|
|
742
827
|
};
|
|
743
828
|
var recreateUserHeaders = (headers) => {
|
|
744
829
|
const filteredHeaders = new Headers();
|
|
@@ -754,15 +839,32 @@ var recreateUserHeaders = (headers) => {
|
|
|
754
839
|
var handleThirdPartyCallResult = async (request, requestPayload, client, workflowUrl, failureUrl, retries, debug) => {
|
|
755
840
|
try {
|
|
756
841
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
757
|
-
|
|
842
|
+
let callbackPayload;
|
|
843
|
+
if (requestPayload) {
|
|
844
|
+
callbackPayload = requestPayload;
|
|
845
|
+
} else {
|
|
846
|
+
const workflowRunId2 = request.headers.get("upstash-workflow-runid");
|
|
847
|
+
const messageId = request.headers.get("upstash-message-id");
|
|
848
|
+
if (!workflowRunId2)
|
|
849
|
+
throw new WorkflowError("workflow run id missing in context.call lazy fetch.");
|
|
850
|
+
if (!messageId) throw new WorkflowError("message id missing in context.call lazy fetch.");
|
|
851
|
+
const steps = await getSteps(client.http, workflowRunId2, messageId, debug);
|
|
852
|
+
const failingStep = steps.find((step) => step.messageId === messageId);
|
|
853
|
+
if (!failingStep)
|
|
854
|
+
throw new WorkflowError(
|
|
855
|
+
"Failed to submit the context.call." + (steps.length === 0 ? "No steps found." : `No step was found with matching messageId ${messageId} out of ${steps.length} steps.`)
|
|
856
|
+
);
|
|
857
|
+
callbackPayload = atob(failingStep.body);
|
|
858
|
+
}
|
|
859
|
+
const callbackMessage = JSON.parse(callbackPayload);
|
|
758
860
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
759
861
|
await debug?.log("WARN", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
760
862
|
status: callbackMessage.status,
|
|
761
|
-
body: atob(callbackMessage.body)
|
|
863
|
+
body: atob(callbackMessage.body ?? "")
|
|
762
864
|
});
|
|
763
865
|
console.warn(
|
|
764
866
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
765
|
-
${atob(callbackMessage.body)}`
|
|
867
|
+
${atob(callbackMessage.body ?? "")}`
|
|
766
868
|
);
|
|
767
869
|
return ok("call-will-retry");
|
|
768
870
|
}
|
|
@@ -796,7 +898,7 @@ ${atob(callbackMessage.body)}`
|
|
|
796
898
|
);
|
|
797
899
|
const callResponse = {
|
|
798
900
|
status: callbackMessage.status,
|
|
799
|
-
body: atob(callbackMessage.body),
|
|
901
|
+
body: atob(callbackMessage.body ?? ""),
|
|
800
902
|
header: callbackMessage.header
|
|
801
903
|
};
|
|
802
904
|
const callResultStep = {
|
|
@@ -827,9 +929,7 @@ ${atob(callbackMessage.body)}`
|
|
|
827
929
|
} catch (error) {
|
|
828
930
|
const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
|
|
829
931
|
return err(
|
|
830
|
-
new
|
|
831
|
-
`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`
|
|
832
|
-
)
|
|
932
|
+
new WorkflowError(`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`)
|
|
833
933
|
);
|
|
834
934
|
}
|
|
835
935
|
};
|
|
@@ -837,7 +937,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
837
937
|
const baseHeaders = {
|
|
838
938
|
[WORKFLOW_INIT_HEADER]: initHeaderValue,
|
|
839
939
|
[WORKFLOW_ID_HEADER]: workflowRunId,
|
|
840
|
-
[WORKFLOW_URL_HEADER]: workflowUrl
|
|
940
|
+
[WORKFLOW_URL_HEADER]: workflowUrl,
|
|
941
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody"
|
|
841
942
|
};
|
|
842
943
|
if (!step?.callUrl) {
|
|
843
944
|
baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
@@ -850,8 +951,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
850
951
|
}
|
|
851
952
|
if (step?.callUrl) {
|
|
852
953
|
baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
|
|
853
|
-
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete";
|
|
854
|
-
if (retries) {
|
|
954
|
+
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
|
|
955
|
+
if (retries !== void 0) {
|
|
855
956
|
baseHeaders["Upstash-Callback-Retries"] = retries.toString();
|
|
856
957
|
baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
|
|
857
958
|
}
|
|
@@ -886,6 +987,7 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
886
987
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
887
988
|
"Upstash-Callback-Workflow-Init": "false",
|
|
888
989
|
"Upstash-Callback-Workflow-Url": workflowUrl,
|
|
990
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
|
|
889
991
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
890
992
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
891
993
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
|
|
@@ -934,7 +1036,7 @@ var verifyRequest = async (body, signature, verifier) => {
|
|
|
934
1036
|
throw new Error("Signature in `Upstash-Signature` header is not valid");
|
|
935
1037
|
}
|
|
936
1038
|
} catch (error) {
|
|
937
|
-
throw new
|
|
1039
|
+
throw new WorkflowError(
|
|
938
1040
|
`Failed to verify that the Workflow request comes from QStash: ${error}
|
|
939
1041
|
|
|
940
1042
|
If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
|
|
@@ -974,14 +1076,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
974
1076
|
*
|
|
975
1077
|
* If a function is already executing (this.executingStep), this
|
|
976
1078
|
* means that there is a nested step which is not allowed. In this
|
|
977
|
-
* case, addStep throws
|
|
1079
|
+
* case, addStep throws WorkflowError.
|
|
978
1080
|
*
|
|
979
1081
|
* @param stepInfo step plan to add
|
|
980
1082
|
* @returns result of the step function
|
|
981
1083
|
*/
|
|
982
1084
|
async addStep(stepInfo) {
|
|
983
1085
|
if (this.executingStep) {
|
|
984
|
-
throw new
|
|
1086
|
+
throw new WorkflowError(
|
|
985
1087
|
`A step can not be run inside another step. Tried to run '${stepInfo.stepName}' inside '${this.executingStep}'`
|
|
986
1088
|
);
|
|
987
1089
|
}
|
|
@@ -1066,7 +1168,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1066
1168
|
const sortedSteps = sortSteps(this.steps);
|
|
1067
1169
|
const plannedParallelStepCount = sortedSteps[initialStepCount + this.planStepCount]?.concurrent;
|
|
1068
1170
|
if (parallelCallState !== "first" && plannedParallelStepCount !== parallelSteps.length) {
|
|
1069
|
-
throw new
|
|
1171
|
+
throw new WorkflowError(
|
|
1070
1172
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
1071
1173
|
);
|
|
1072
1174
|
}
|
|
@@ -1088,7 +1190,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1088
1190
|
case "partial": {
|
|
1089
1191
|
const planStep = this.steps.at(-1);
|
|
1090
1192
|
if (!planStep || planStep.targetStep === void 0) {
|
|
1091
|
-
throw new
|
|
1193
|
+
throw new WorkflowError(
|
|
1092
1194
|
`There must be a last step and it should have targetStep larger than 0.Received: ${JSON.stringify(planStep)}`
|
|
1093
1195
|
);
|
|
1094
1196
|
}
|
|
@@ -1102,17 +1204,17 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1102
1204
|
);
|
|
1103
1205
|
await this.submitStepsToQStash([resultStep], [parallelStep]);
|
|
1104
1206
|
} catch (error) {
|
|
1105
|
-
if (error instanceof
|
|
1207
|
+
if (error instanceof WorkflowAbort) {
|
|
1106
1208
|
throw error;
|
|
1107
1209
|
}
|
|
1108
|
-
throw new
|
|
1210
|
+
throw new WorkflowError(
|
|
1109
1211
|
`Error submitting steps to QStash in partial parallel step execution: ${error}`
|
|
1110
1212
|
);
|
|
1111
1213
|
}
|
|
1112
1214
|
break;
|
|
1113
1215
|
}
|
|
1114
1216
|
case "discard": {
|
|
1115
|
-
throw new
|
|
1217
|
+
throw new WorkflowAbort("discarded parallel");
|
|
1116
1218
|
}
|
|
1117
1219
|
case "last": {
|
|
1118
1220
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
@@ -1163,7 +1265,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1163
1265
|
*/
|
|
1164
1266
|
async submitStepsToQStash(steps, lazySteps) {
|
|
1165
1267
|
if (steps.length === 0) {
|
|
1166
|
-
throw new
|
|
1268
|
+
throw new WorkflowError(
|
|
1167
1269
|
`Unable to submit steps to QStash. Provided list is empty. Current step: ${this.stepCount}`
|
|
1168
1270
|
);
|
|
1169
1271
|
}
|
|
@@ -1203,7 +1305,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1203
1305
|
method: "POST",
|
|
1204
1306
|
parseResponseAsJson: false
|
|
1205
1307
|
});
|
|
1206
|
-
throw new
|
|
1308
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
1207
1309
|
}
|
|
1208
1310
|
const result = await this.context.qstashClient.batchJSON(
|
|
1209
1311
|
steps.map((singleStep, index) => {
|
|
@@ -1255,7 +1357,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1255
1357
|
};
|
|
1256
1358
|
})
|
|
1257
1359
|
});
|
|
1258
|
-
throw new
|
|
1360
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
1259
1361
|
}
|
|
1260
1362
|
/**
|
|
1261
1363
|
* Get the promise by executing the lazt steps list. If there is a single
|
|
@@ -1280,7 +1382,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1280
1382
|
} else if (Array.isArray(result) && lazyStepList.length === result.length && index < lazyStepList.length) {
|
|
1281
1383
|
return result[index];
|
|
1282
1384
|
} else {
|
|
1283
|
-
throw new
|
|
1385
|
+
throw new WorkflowError(
|
|
1284
1386
|
`Unexpected parallel call result while executing step ${index}: '${result}'. Expected ${lazyStepList.length} many items`
|
|
1285
1387
|
);
|
|
1286
1388
|
}
|
|
@@ -1292,12 +1394,12 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1292
1394
|
};
|
|
1293
1395
|
var validateStep = (lazyStep, stepFromRequest) => {
|
|
1294
1396
|
if (lazyStep.stepName !== stepFromRequest.stepName) {
|
|
1295
|
-
throw new
|
|
1397
|
+
throw new WorkflowError(
|
|
1296
1398
|
`Incompatible step name. Expected '${lazyStep.stepName}', got '${stepFromRequest.stepName}' from the request`
|
|
1297
1399
|
);
|
|
1298
1400
|
}
|
|
1299
1401
|
if (lazyStep.stepType !== stepFromRequest.stepType) {
|
|
1300
|
-
throw new
|
|
1402
|
+
throw new WorkflowError(
|
|
1301
1403
|
`Incompatible step type. Expected '${lazyStep.stepType}', got '${stepFromRequest.stepType}' from the request`
|
|
1302
1404
|
);
|
|
1303
1405
|
}
|
|
@@ -1308,12 +1410,12 @@ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
|
|
|
1308
1410
|
validateStep(lazySteps[index], stepFromRequest);
|
|
1309
1411
|
}
|
|
1310
1412
|
} catch (error) {
|
|
1311
|
-
if (error instanceof
|
|
1413
|
+
if (error instanceof WorkflowError) {
|
|
1312
1414
|
const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
|
|
1313
1415
|
const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
|
|
1314
1416
|
const requestStepNames = stepsFromRequest.map((step) => step.stepName);
|
|
1315
1417
|
const requestStepTypes = stepsFromRequest.map((step) => step.stepType);
|
|
1316
|
-
throw new
|
|
1418
|
+
throw new WorkflowError(
|
|
1317
1419
|
`Incompatible steps detected in parallel execution: ${error.message}
|
|
1318
1420
|
> Step Names from the request: ${JSON.stringify(requestStepNames)}
|
|
1319
1421
|
Step Types from the request: ${JSON.stringify(requestStepTypes)}
|
|
@@ -1426,10 +1528,6 @@ var WorkflowContext = class {
|
|
|
1426
1528
|
* headers of the initial request
|
|
1427
1529
|
*/
|
|
1428
1530
|
headers;
|
|
1429
|
-
/**
|
|
1430
|
-
* initial payload as a raw string
|
|
1431
|
-
*/
|
|
1432
|
-
rawInitialPayload;
|
|
1433
1531
|
/**
|
|
1434
1532
|
* Map of environment variables and their values.
|
|
1435
1533
|
*
|
|
@@ -1464,7 +1562,6 @@ var WorkflowContext = class {
|
|
|
1464
1562
|
failureUrl,
|
|
1465
1563
|
debug,
|
|
1466
1564
|
initialPayload,
|
|
1467
|
-
rawInitialPayload,
|
|
1468
1565
|
env,
|
|
1469
1566
|
retries
|
|
1470
1567
|
}) {
|
|
@@ -1475,7 +1572,6 @@ var WorkflowContext = class {
|
|
|
1475
1572
|
this.failureUrl = failureUrl;
|
|
1476
1573
|
this.headers = headers;
|
|
1477
1574
|
this.requestPayload = initialPayload;
|
|
1478
|
-
this.rawInitialPayload = rawInitialPayload ?? JSON.stringify(this.requestPayload);
|
|
1479
1575
|
this.env = env ?? {};
|
|
1480
1576
|
this.retries = retries ?? DEFAULT_RETRIES;
|
|
1481
1577
|
this.executor = new AutoExecutor(this, this.steps, debug);
|
|
@@ -1496,7 +1592,7 @@ var WorkflowContext = class {
|
|
|
1496
1592
|
* const [result1, result2] = await Promise.all([
|
|
1497
1593
|
* context.run("step 1", () => {
|
|
1498
1594
|
* return "result1"
|
|
1499
|
-
* })
|
|
1595
|
+
* }),
|
|
1500
1596
|
* context.run("step 2", async () => {
|
|
1501
1597
|
* return await fetchResults()
|
|
1502
1598
|
* })
|
|
@@ -1514,6 +1610,10 @@ var WorkflowContext = class {
|
|
|
1514
1610
|
/**
|
|
1515
1611
|
* Stops the execution for the duration provided.
|
|
1516
1612
|
*
|
|
1613
|
+
* ```typescript
|
|
1614
|
+
* await context.sleep('sleep1', 3) // wait for three seconds
|
|
1615
|
+
* ```
|
|
1616
|
+
*
|
|
1517
1617
|
* @param stepName
|
|
1518
1618
|
* @param duration sleep duration in seconds
|
|
1519
1619
|
* @returns undefined
|
|
@@ -1524,6 +1624,10 @@ var WorkflowContext = class {
|
|
|
1524
1624
|
/**
|
|
1525
1625
|
* Stops the execution until the date time provided.
|
|
1526
1626
|
*
|
|
1627
|
+
* ```typescript
|
|
1628
|
+
* await context.sleepUntil('sleep1', Date.now() / 1000 + 3) // wait for three seconds
|
|
1629
|
+
* ```
|
|
1630
|
+
*
|
|
1527
1631
|
* @param stepName
|
|
1528
1632
|
* @param datetime time to sleep until. Can be provided as a number (in unix seconds),
|
|
1529
1633
|
* as a Date object or a string (passed to `new Date(datetimeString)`)
|
|
@@ -1547,7 +1651,7 @@ var WorkflowContext = class {
|
|
|
1547
1651
|
* const { status, body } = await context.call<string>(
|
|
1548
1652
|
* "post call step",
|
|
1549
1653
|
* {
|
|
1550
|
-
* url:
|
|
1654
|
+
* url: "https://www.some-endpoint.com/api",
|
|
1551
1655
|
* method: "POST",
|
|
1552
1656
|
* body: "my-payload"
|
|
1553
1657
|
* }
|
|
@@ -1601,45 +1705,43 @@ var WorkflowContext = class {
|
|
|
1601
1705
|
}
|
|
1602
1706
|
}
|
|
1603
1707
|
/**
|
|
1604
|
-
*
|
|
1605
|
-
* timeout ends
|
|
1708
|
+
* Pauses workflow execution until a specific event occurs or a timeout is reached.
|
|
1606
1709
|
*
|
|
1607
|
-
|
|
1608
|
-
* const
|
|
1609
|
-
*
|
|
1610
|
-
*
|
|
1611
|
-
|
|
1612
|
-
* );
|
|
1613
|
-
* ```
|
|
1710
|
+
*```ts
|
|
1711
|
+
* const result = await workflow.waitForEvent("payment-confirmed", {
|
|
1712
|
+
* timeout: "5m"
|
|
1713
|
+
* });
|
|
1714
|
+
*```
|
|
1614
1715
|
*
|
|
1615
|
-
* To notify a waiting workflow
|
|
1716
|
+
* To notify a waiting workflow:
|
|
1616
1717
|
*
|
|
1617
1718
|
* ```ts
|
|
1618
1719
|
* import { Client } from "@upstash/workflow";
|
|
1619
1720
|
*
|
|
1620
|
-
* const client = new Client({ token: });
|
|
1721
|
+
* const client = new Client({ token: "<QSTASH_TOKEN>" });
|
|
1621
1722
|
*
|
|
1622
1723
|
* await client.notify({
|
|
1623
|
-
* eventId: "
|
|
1624
|
-
*
|
|
1724
|
+
* eventId: "payment.confirmed",
|
|
1725
|
+
* data: {
|
|
1726
|
+
* amount: 99.99,
|
|
1727
|
+
* currency: "USD"
|
|
1728
|
+
* }
|
|
1625
1729
|
* })
|
|
1626
1730
|
* ```
|
|
1627
1731
|
*
|
|
1732
|
+
* Alternatively, you can use the `context.notify` method.
|
|
1733
|
+
*
|
|
1628
1734
|
* @param stepName
|
|
1629
|
-
* @param eventId
|
|
1630
|
-
* @param
|
|
1631
|
-
* @returns
|
|
1632
|
-
* timeout
|
|
1633
|
-
* is the
|
|
1735
|
+
* @param eventId - Unique identifier for the event to wait for
|
|
1736
|
+
* @param options - Configuration options.
|
|
1737
|
+
* @returns `{ timeout: boolean, eventData: unknown }`.
|
|
1738
|
+
* The `timeout` property specifies if the workflow has timed out. The `eventData`
|
|
1739
|
+
* is the data passed when notifying this workflow of an event.
|
|
1634
1740
|
*/
|
|
1635
|
-
async waitForEvent(stepName, eventId,
|
|
1636
|
-
const
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
eventId,
|
|
1640
|
-
typeof timeout === "string" ? timeout : `${timeout}s`
|
|
1641
|
-
)
|
|
1642
|
-
);
|
|
1741
|
+
async waitForEvent(stepName, eventId, options = {}) {
|
|
1742
|
+
const { timeout = "7d" } = options;
|
|
1743
|
+
const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
|
|
1744
|
+
const result = await this.addStep(new LazyWaitForEventStep(stepName, eventId, timeoutStr));
|
|
1643
1745
|
try {
|
|
1644
1746
|
return {
|
|
1645
1747
|
...result,
|
|
@@ -1649,6 +1751,27 @@ var WorkflowContext = class {
|
|
|
1649
1751
|
return result;
|
|
1650
1752
|
}
|
|
1651
1753
|
}
|
|
1754
|
+
/**
|
|
1755
|
+
* Notify workflow runs waiting for an event
|
|
1756
|
+
*
|
|
1757
|
+
* ```ts
|
|
1758
|
+
* const { eventId, eventData, notifyResponse } = await context.notify(
|
|
1759
|
+
* "notify step", "event-id", "event-data"
|
|
1760
|
+
* );
|
|
1761
|
+
* ```
|
|
1762
|
+
*
|
|
1763
|
+
* Upon `context.notify`, the workflow runs waiting for the given eventId (context.waitForEvent)
|
|
1764
|
+
* will receive the given event data and resume execution.
|
|
1765
|
+
*
|
|
1766
|
+
* The response includes the same eventId and eventData. Additionally, there is
|
|
1767
|
+
* a notifyResponse field which contains a list of `Waiter` objects, each corresponding
|
|
1768
|
+
* to a notified workflow run.
|
|
1769
|
+
*
|
|
1770
|
+
* @param stepName
|
|
1771
|
+
* @param eventId event id to notify
|
|
1772
|
+
* @param eventData event data to notify with
|
|
1773
|
+
* @returns notify response which has event id, event data and list of waiters which were notified
|
|
1774
|
+
*/
|
|
1652
1775
|
async notify(stepName, eventId, eventData) {
|
|
1653
1776
|
const result = await this.addStep(
|
|
1654
1777
|
new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
|
|
@@ -1662,6 +1785,15 @@ var WorkflowContext = class {
|
|
|
1662
1785
|
return result;
|
|
1663
1786
|
}
|
|
1664
1787
|
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Cancel the current workflow run
|
|
1790
|
+
*
|
|
1791
|
+
* Will throw WorkflowAbort to stop workflow execution.
|
|
1792
|
+
* Shouldn't be inside try/catch.
|
|
1793
|
+
*/
|
|
1794
|
+
async cancel() {
|
|
1795
|
+
throw new WorkflowAbort("cancel", void 0, true);
|
|
1796
|
+
}
|
|
1665
1797
|
/**
|
|
1666
1798
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
1667
1799
|
* DisabledWorkflowContext.
|
|
@@ -1702,7 +1834,8 @@ var WorkflowLogger = class _WorkflowLogger {
|
|
|
1702
1834
|
}
|
|
1703
1835
|
writeToConsole(logEntry) {
|
|
1704
1836
|
const JSON_SPACING = 2;
|
|
1705
|
-
console.
|
|
1837
|
+
const logMethod = logEntry.logLevel === "ERROR" ? console.error : logEntry.logLevel === "WARN" ? console.warn : console.log;
|
|
1838
|
+
logMethod(JSON.stringify(logEntry, void 0, JSON_SPACING));
|
|
1706
1839
|
}
|
|
1707
1840
|
shouldLog(level) {
|
|
1708
1841
|
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
@@ -1720,11 +1853,13 @@ var WorkflowLogger = class _WorkflowLogger {
|
|
|
1720
1853
|
};
|
|
1721
1854
|
|
|
1722
1855
|
// src/utils.ts
|
|
1723
|
-
var import_node_crypto = __toESM(require("crypto"));
|
|
1724
1856
|
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
1725
1857
|
var NANOID_LENGTH = 21;
|
|
1858
|
+
function getRandomInt() {
|
|
1859
|
+
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
1860
|
+
}
|
|
1726
1861
|
function nanoid() {
|
|
1727
|
-
return
|
|
1862
|
+
return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
1728
1863
|
}
|
|
1729
1864
|
function getWorkflowRunId(id) {
|
|
1730
1865
|
return `wfr_${id ?? nanoid()}`;
|
|
@@ -1742,6 +1877,63 @@ function decodeBase64(base64) {
|
|
|
1742
1877
|
}
|
|
1743
1878
|
}
|
|
1744
1879
|
|
|
1880
|
+
// src/serve/authorization.ts
|
|
1881
|
+
var import_qstash3 = require("@upstash/qstash");
|
|
1882
|
+
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
1883
|
+
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
1884
|
+
/**
|
|
1885
|
+
* overwrite the WorkflowContext.addStep method to always raise WorkflowAbort
|
|
1886
|
+
* error in order to stop the execution whenever we encounter a step.
|
|
1887
|
+
*
|
|
1888
|
+
* @param _step
|
|
1889
|
+
*/
|
|
1890
|
+
async addStep(_step) {
|
|
1891
|
+
throw new WorkflowAbort(_DisabledWorkflowContext.disabledMessage);
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1894
|
+
* overwrite cancel method to do nothing
|
|
1895
|
+
*/
|
|
1896
|
+
async cancel() {
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
/**
|
|
1900
|
+
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
1901
|
+
* route function with the new context.
|
|
1902
|
+
*
|
|
1903
|
+
* - returns "run-ended" if there are no steps found or
|
|
1904
|
+
* if the auth failed and user called `return`
|
|
1905
|
+
* - returns "step-found" if DisabledWorkflowContext.addStep is called.
|
|
1906
|
+
* - if there is another error, returns the error.
|
|
1907
|
+
*
|
|
1908
|
+
* @param routeFunction
|
|
1909
|
+
*/
|
|
1910
|
+
static async tryAuthentication(routeFunction, context) {
|
|
1911
|
+
const disabledContext = new _DisabledWorkflowContext({
|
|
1912
|
+
qstashClient: new import_qstash3.Client({
|
|
1913
|
+
baseUrl: "disabled-client",
|
|
1914
|
+
token: "disabled-client"
|
|
1915
|
+
}),
|
|
1916
|
+
workflowRunId: context.workflowRunId,
|
|
1917
|
+
headers: context.headers,
|
|
1918
|
+
steps: [],
|
|
1919
|
+
url: context.url,
|
|
1920
|
+
failureUrl: context.failureUrl,
|
|
1921
|
+
initialPayload: context.requestPayload,
|
|
1922
|
+
env: context.env,
|
|
1923
|
+
retries: context.retries
|
|
1924
|
+
});
|
|
1925
|
+
try {
|
|
1926
|
+
await routeFunction(disabledContext);
|
|
1927
|
+
} catch (error) {
|
|
1928
|
+
if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage) {
|
|
1929
|
+
return ok("step-found");
|
|
1930
|
+
}
|
|
1931
|
+
return err(error);
|
|
1932
|
+
}
|
|
1933
|
+
return ok("run-ended");
|
|
1934
|
+
}
|
|
1935
|
+
};
|
|
1936
|
+
|
|
1745
1937
|
// src/workflow-parser.ts
|
|
1746
1938
|
var getPayload = async (request) => {
|
|
1747
1939
|
try {
|
|
@@ -1750,8 +1942,8 @@ var getPayload = async (request) => {
|
|
|
1750
1942
|
return;
|
|
1751
1943
|
}
|
|
1752
1944
|
};
|
|
1753
|
-
var
|
|
1754
|
-
const [encodedInitialPayload, ...encodedSteps] =
|
|
1945
|
+
var processRawSteps = (rawSteps) => {
|
|
1946
|
+
const [encodedInitialPayload, ...encodedSteps] = rawSteps;
|
|
1755
1947
|
const rawInitialPayload = decodeBase64(encodedInitialPayload.body);
|
|
1756
1948
|
const initialStep = {
|
|
1757
1949
|
stepId: 0,
|
|
@@ -1761,27 +1953,21 @@ var parsePayload = async (rawPayload, debug) => {
|
|
|
1761
1953
|
concurrent: NO_CONCURRENCY
|
|
1762
1954
|
};
|
|
1763
1955
|
const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
|
|
1764
|
-
const otherSteps =
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
};
|
|
1780
|
-
step.out = newOut;
|
|
1781
|
-
}
|
|
1782
|
-
return step;
|
|
1783
|
-
})
|
|
1784
|
-
);
|
|
1956
|
+
const otherSteps = stepsToDecode.map((rawStep) => {
|
|
1957
|
+
const step = JSON.parse(decodeBase64(rawStep.body));
|
|
1958
|
+
try {
|
|
1959
|
+
step.out = JSON.parse(step.out);
|
|
1960
|
+
} catch {
|
|
1961
|
+
}
|
|
1962
|
+
if (step.waitEventId) {
|
|
1963
|
+
const newOut = {
|
|
1964
|
+
eventData: step.out ? decodeBase64(step.out) : void 0,
|
|
1965
|
+
timeout: step.waitTimeout ?? false
|
|
1966
|
+
};
|
|
1967
|
+
step.out = newOut;
|
|
1968
|
+
}
|
|
1969
|
+
return step;
|
|
1970
|
+
});
|
|
1785
1971
|
const steps = [initialStep, ...otherSteps];
|
|
1786
1972
|
return {
|
|
1787
1973
|
rawInitialPayload,
|
|
@@ -1829,20 +2015,20 @@ var validateRequest = (request) => {
|
|
|
1829
2015
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
1830
2016
|
const isFirstInvocation = !versionHeader;
|
|
1831
2017
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
1832
|
-
throw new
|
|
2018
|
+
throw new WorkflowError(
|
|
1833
2019
|
`Incompatible workflow sdk protocol version. Expected ${WORKFLOW_PROTOCOL_VERSION}, got ${versionHeader} from the request.`
|
|
1834
2020
|
);
|
|
1835
2021
|
}
|
|
1836
2022
|
const workflowRunId = isFirstInvocation ? getWorkflowRunId() : request.headers.get(WORKFLOW_ID_HEADER) ?? "";
|
|
1837
2023
|
if (workflowRunId.length === 0) {
|
|
1838
|
-
throw new
|
|
2024
|
+
throw new WorkflowError("Couldn't get workflow id from header");
|
|
1839
2025
|
}
|
|
1840
2026
|
return {
|
|
1841
2027
|
isFirstInvocation,
|
|
1842
2028
|
workflowRunId
|
|
1843
2029
|
};
|
|
1844
2030
|
};
|
|
1845
|
-
var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
2031
|
+
var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requester, messageId, debug) => {
|
|
1846
2032
|
if (isFirstInvocation) {
|
|
1847
2033
|
return {
|
|
1848
2034
|
rawInitialPayload: requestPayload ?? "",
|
|
@@ -1850,10 +2036,18 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
1850
2036
|
isLastDuplicate: false
|
|
1851
2037
|
};
|
|
1852
2038
|
} else {
|
|
2039
|
+
let rawSteps;
|
|
1853
2040
|
if (!requestPayload) {
|
|
1854
|
-
|
|
2041
|
+
await debug?.log(
|
|
2042
|
+
"INFO",
|
|
2043
|
+
"ENDPOINT_START",
|
|
2044
|
+
"request payload is empty, steps will be fetched from QStash."
|
|
2045
|
+
);
|
|
2046
|
+
rawSteps = await getSteps(requester, workflowRunId, messageId, debug);
|
|
2047
|
+
} else {
|
|
2048
|
+
rawSteps = JSON.parse(requestPayload);
|
|
1855
2049
|
}
|
|
1856
|
-
const { rawInitialPayload, steps } =
|
|
2050
|
+
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
1857
2051
|
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, debug);
|
|
1858
2052
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
1859
2053
|
return {
|
|
@@ -1863,13 +2057,13 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
1863
2057
|
};
|
|
1864
2058
|
}
|
|
1865
2059
|
};
|
|
1866
|
-
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, failureFunction, debug) => {
|
|
2060
|
+
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, debug) => {
|
|
1867
2061
|
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
|
|
1868
2062
|
return ok("not-failure-callback");
|
|
1869
2063
|
}
|
|
1870
2064
|
if (!failureFunction) {
|
|
1871
2065
|
return err(
|
|
1872
|
-
new
|
|
2066
|
+
new WorkflowError(
|
|
1873
2067
|
"Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
|
|
1874
2068
|
)
|
|
1875
2069
|
);
|
|
@@ -1880,92 +2074,48 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
1880
2074
|
);
|
|
1881
2075
|
const decodedBody = body ? decodeBase64(body) : "{}";
|
|
1882
2076
|
const errorPayload = JSON.parse(decodedBody);
|
|
1883
|
-
const {
|
|
1884
|
-
rawInitialPayload,
|
|
1885
|
-
steps,
|
|
1886
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1887
|
-
isLastDuplicate: _isLastDuplicate
|
|
1888
|
-
} = await parseRequest(decodeBase64(sourceBody), false, debug);
|
|
1889
2077
|
const workflowContext = new WorkflowContext({
|
|
1890
2078
|
qstashClient,
|
|
1891
2079
|
workflowRunId,
|
|
1892
|
-
initialPayload: initialPayloadParser(
|
|
1893
|
-
rawInitialPayload,
|
|
2080
|
+
initialPayload: initialPayloadParser(decodeBase64(sourceBody)),
|
|
1894
2081
|
headers: recreateUserHeaders(new Headers(sourceHeader)),
|
|
1895
|
-
steps,
|
|
2082
|
+
steps: [],
|
|
1896
2083
|
url,
|
|
1897
2084
|
failureUrl: url,
|
|
1898
2085
|
debug
|
|
1899
2086
|
});
|
|
1900
|
-
|
|
2087
|
+
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
2088
|
+
routeFunction,
|
|
2089
|
+
workflowContext
|
|
2090
|
+
);
|
|
2091
|
+
if (authCheck.isErr()) {
|
|
2092
|
+
await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
|
|
2093
|
+
throw authCheck.error;
|
|
2094
|
+
} else if (authCheck.value === "run-ended") {
|
|
2095
|
+
return err(new WorkflowError("Not authorized to run the failure function."));
|
|
2096
|
+
}
|
|
2097
|
+
await failureFunction({
|
|
2098
|
+
context: workflowContext,
|
|
2099
|
+
failStatus: status,
|
|
2100
|
+
failResponse: errorPayload.message,
|
|
2101
|
+
failHeaders: header
|
|
2102
|
+
});
|
|
1901
2103
|
} catch (error) {
|
|
1902
2104
|
return err(error);
|
|
1903
2105
|
}
|
|
1904
2106
|
return ok("is-failure-callback");
|
|
1905
2107
|
};
|
|
1906
2108
|
|
|
1907
|
-
// src/serve/authorization.ts
|
|
1908
|
-
var import_qstash2 = require("@upstash/qstash");
|
|
1909
|
-
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
1910
|
-
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
1911
|
-
/**
|
|
1912
|
-
* overwrite the WorkflowContext.addStep method to always raise QStashWorkflowAbort
|
|
1913
|
-
* error in order to stop the execution whenever we encounter a step.
|
|
1914
|
-
*
|
|
1915
|
-
* @param _step
|
|
1916
|
-
*/
|
|
1917
|
-
async addStep(_step) {
|
|
1918
|
-
throw new QStashWorkflowAbort(_DisabledWorkflowContext.disabledMessage);
|
|
1919
|
-
}
|
|
1920
|
-
/**
|
|
1921
|
-
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
1922
|
-
* route function with the new context.
|
|
1923
|
-
*
|
|
1924
|
-
* - returns "run-ended" if there are no steps found or
|
|
1925
|
-
* if the auth failed and user called `return`
|
|
1926
|
-
* - returns "step-found" if DisabledWorkflowContext.addStep is called.
|
|
1927
|
-
* - if there is another error, returns the error.
|
|
1928
|
-
*
|
|
1929
|
-
* @param routeFunction
|
|
1930
|
-
*/
|
|
1931
|
-
static async tryAuthentication(routeFunction, context) {
|
|
1932
|
-
const disabledContext = new _DisabledWorkflowContext({
|
|
1933
|
-
qstashClient: new import_qstash2.Client({
|
|
1934
|
-
baseUrl: "disabled-client",
|
|
1935
|
-
token: "disabled-client"
|
|
1936
|
-
}),
|
|
1937
|
-
workflowRunId: context.workflowRunId,
|
|
1938
|
-
headers: context.headers,
|
|
1939
|
-
steps: [],
|
|
1940
|
-
url: context.url,
|
|
1941
|
-
failureUrl: context.failureUrl,
|
|
1942
|
-
initialPayload: context.requestPayload,
|
|
1943
|
-
rawInitialPayload: context.rawInitialPayload,
|
|
1944
|
-
env: context.env,
|
|
1945
|
-
retries: context.retries
|
|
1946
|
-
});
|
|
1947
|
-
try {
|
|
1948
|
-
await routeFunction(disabledContext);
|
|
1949
|
-
} catch (error) {
|
|
1950
|
-
if (error instanceof QStashWorkflowAbort && error.stepName === this.disabledMessage) {
|
|
1951
|
-
return ok("step-found");
|
|
1952
|
-
}
|
|
1953
|
-
return err(error);
|
|
1954
|
-
}
|
|
1955
|
-
return ok("run-ended");
|
|
1956
|
-
}
|
|
1957
|
-
};
|
|
1958
|
-
|
|
1959
2109
|
// src/serve/options.ts
|
|
1960
|
-
var import_qstash3 = require("@upstash/qstash");
|
|
1961
2110
|
var import_qstash4 = require("@upstash/qstash");
|
|
2111
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
1962
2112
|
var processOptions = (options) => {
|
|
1963
2113
|
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
1964
2114
|
const receiverEnvironmentVariablesSet = Boolean(
|
|
1965
2115
|
environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
|
|
1966
2116
|
);
|
|
1967
2117
|
return {
|
|
1968
|
-
qstashClient: new
|
|
2118
|
+
qstashClient: new import_qstash5.Client({
|
|
1969
2119
|
baseUrl: environment.QSTASH_URL,
|
|
1970
2120
|
token: environment.QSTASH_TOKEN
|
|
1971
2121
|
}),
|
|
@@ -1986,7 +2136,7 @@ var processOptions = (options) => {
|
|
|
1986
2136
|
throw error;
|
|
1987
2137
|
}
|
|
1988
2138
|
},
|
|
1989
|
-
receiver: receiverEnvironmentVariablesSet ? new
|
|
2139
|
+
receiver: receiverEnvironmentVariablesSet ? new import_qstash4.Receiver({
|
|
1990
2140
|
currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
|
|
1991
2141
|
nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
|
|
1992
2142
|
}) : void 0,
|
|
@@ -2048,6 +2198,9 @@ var serve = (routeFunction, options) => {
|
|
|
2048
2198
|
const { rawInitialPayload, steps, isLastDuplicate } = await parseRequest(
|
|
2049
2199
|
requestPayload,
|
|
2050
2200
|
isFirstInvocation,
|
|
2201
|
+
workflowRunId,
|
|
2202
|
+
qstashClient.http,
|
|
2203
|
+
request.headers.get("upstash-message-id"),
|
|
2051
2204
|
debug
|
|
2052
2205
|
);
|
|
2053
2206
|
if (isLastDuplicate) {
|
|
@@ -2058,6 +2211,7 @@ var serve = (routeFunction, options) => {
|
|
|
2058
2211
|
requestPayload,
|
|
2059
2212
|
qstashClient,
|
|
2060
2213
|
initialPayloadParser,
|
|
2214
|
+
routeFunction,
|
|
2061
2215
|
failureFunction
|
|
2062
2216
|
);
|
|
2063
2217
|
if (failureCheck.isErr()) {
|
|
@@ -2070,7 +2224,6 @@ var serve = (routeFunction, options) => {
|
|
|
2070
2224
|
qstashClient,
|
|
2071
2225
|
workflowRunId,
|
|
2072
2226
|
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
2073
|
-
rawInitialPayload,
|
|
2074
2227
|
headers: recreateUserHeaders(request.headers),
|
|
2075
2228
|
steps,
|
|
2076
2229
|
url: workflowUrl,
|
|
@@ -2108,7 +2261,11 @@ var serve = (routeFunction, options) => {
|
|
|
2108
2261
|
onStep: async () => routeFunction(workflowContext),
|
|
2109
2262
|
onCleanup: async () => {
|
|
2110
2263
|
await triggerWorkflowDelete(workflowContext, debug);
|
|
2111
|
-
}
|
|
2264
|
+
},
|
|
2265
|
+
onCancel: async () => {
|
|
2266
|
+
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
2267
|
+
},
|
|
2268
|
+
debug
|
|
2112
2269
|
});
|
|
2113
2270
|
if (result.isErr()) {
|
|
2114
2271
|
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
@@ -2134,7 +2291,7 @@ var serve = (routeFunction, options) => {
|
|
|
2134
2291
|
};
|
|
2135
2292
|
|
|
2136
2293
|
// src/client/index.ts
|
|
2137
|
-
var
|
|
2294
|
+
var import_qstash6 = require("@upstash/qstash");
|
|
2138
2295
|
|
|
2139
2296
|
// platforms/astro.ts
|
|
2140
2297
|
function serve2(routeFunction, options) {
|