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