@upstash/workflow 0.1.5 → 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 +344 -179
- package/astro.mjs +1 -1
- package/{chunk-OJZEI7LO.mjs → chunk-5R2BFC3N.mjs} +415 -192
- package/cloudflare.d.mts +1 -1
- package/cloudflare.d.ts +1 -1
- package/cloudflare.js +344 -179
- package/cloudflare.mjs +1 -1
- package/express.d.mts +1 -1
- package/express.d.ts +1 -1
- package/express.js +360 -182
- package/express.mjs +17 -4
- package/h3.d.mts +1 -1
- package/h3.d.ts +1 -1
- package/h3.js +344 -179
- package/h3.mjs +1 -1
- package/hono.d.mts +1 -1
- package/hono.d.ts +1 -1
- package/hono.js +344 -179
- package/hono.mjs +1 -1
- package/index.d.mts +74 -21
- package/index.d.ts +74 -21
- package/index.js +422 -199
- package/index.mjs +5 -5
- package/nextjs.d.mts +1 -1
- package/nextjs.d.ts +1 -1
- package/nextjs.js +344 -179
- 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 +344 -179
- package/solidjs.mjs +1 -1
- package/svelte.d.mts +1 -1
- package/svelte.d.ts +1 -1
- package/svelte.js +344 -179
- 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
|
@@ -27,22 +27,33 @@ module.exports = __toCommonJS(nextjs_exports);
|
|
|
27
27
|
|
|
28
28
|
// src/error.ts
|
|
29
29
|
var import_qstash = require("@upstash/qstash");
|
|
30
|
-
var
|
|
30
|
+
var WorkflowError = class extends import_qstash.QstashError {
|
|
31
31
|
constructor(message) {
|
|
32
32
|
super(message);
|
|
33
|
-
this.name = "
|
|
33
|
+
this.name = "WorkflowError";
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
-
var
|
|
36
|
+
var WorkflowAbort = class extends Error {
|
|
37
37
|
stepInfo;
|
|
38
38
|
stepName;
|
|
39
|
-
|
|
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) {
|
|
40
50
|
super(
|
|
41
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}'.`
|
|
42
52
|
);
|
|
43
|
-
this.name = "
|
|
53
|
+
this.name = "WorkflowAbort";
|
|
44
54
|
this.stepName = stepName;
|
|
45
55
|
this.stepInfo = stepInfo;
|
|
56
|
+
this.cancelWorkflow = cancelWorkflow;
|
|
46
57
|
}
|
|
47
58
|
};
|
|
48
59
|
var formatWorkflowError = (error) => {
|
|
@@ -64,6 +75,44 @@ var makeNotifyRequest = async (requester, eventId, eventData) => {
|
|
|
64
75
|
});
|
|
65
76
|
return result;
|
|
66
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
|
+
};
|
|
67
116
|
|
|
68
117
|
// src/context/steps.ts
|
|
69
118
|
var BaseLazyStep = class {
|
|
@@ -678,6 +727,7 @@ var StepTypes = [
|
|
|
678
727
|
];
|
|
679
728
|
|
|
680
729
|
// src/workflow-requests.ts
|
|
730
|
+
var import_qstash2 = require("@upstash/qstash");
|
|
681
731
|
var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
682
732
|
const { headers } = getHeaders(
|
|
683
733
|
"true",
|
|
@@ -688,20 +738,32 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
688
738
|
workflowContext.failureUrl,
|
|
689
739
|
retries
|
|
690
740
|
);
|
|
691
|
-
await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
|
|
692
|
-
headers,
|
|
693
|
-
requestPayload: workflowContext.requestPayload,
|
|
694
|
-
url: workflowContext.url
|
|
695
|
-
});
|
|
696
741
|
try {
|
|
697
742
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
698
|
-
await workflowContext.qstashClient.publish({
|
|
743
|
+
const result = await workflowContext.qstashClient.publish({
|
|
699
744
|
headers,
|
|
700
745
|
method: "POST",
|
|
701
746
|
body,
|
|
702
747
|
url: workflowContext.url
|
|
703
748
|
});
|
|
704
|
-
|
|
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
|
+
}
|
|
705
767
|
} catch (error) {
|
|
706
768
|
const error_ = error;
|
|
707
769
|
return err(error_);
|
|
@@ -709,7 +771,9 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
709
771
|
};
|
|
710
772
|
var triggerRouteFunction = async ({
|
|
711
773
|
onCleanup,
|
|
712
|
-
onStep
|
|
774
|
+
onStep,
|
|
775
|
+
onCancel,
|
|
776
|
+
debug
|
|
713
777
|
}) => {
|
|
714
778
|
try {
|
|
715
779
|
await onStep();
|
|
@@ -717,19 +781,50 @@ var triggerRouteFunction = async ({
|
|
|
717
781
|
return ok("workflow-finished");
|
|
718
782
|
} catch (error) {
|
|
719
783
|
const error_ = error;
|
|
720
|
-
|
|
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
|
+
}
|
|
721
799
|
}
|
|
722
800
|
};
|
|
723
801
|
var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
|
|
724
802
|
await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
|
|
725
803
|
deletedWorkflowRunId: workflowContext.workflowRunId
|
|
726
804
|
});
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
+
}
|
|
733
828
|
};
|
|
734
829
|
var recreateUserHeaders = (headers) => {
|
|
735
830
|
const filteredHeaders = new Headers();
|
|
@@ -745,15 +840,32 @@ var recreateUserHeaders = (headers) => {
|
|
|
745
840
|
var handleThirdPartyCallResult = async (request, requestPayload, client, workflowUrl, failureUrl, retries, debug) => {
|
|
746
841
|
try {
|
|
747
842
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
748
|
-
|
|
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);
|
|
749
861
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
750
862
|
await debug?.log("WARN", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
751
863
|
status: callbackMessage.status,
|
|
752
|
-
body: atob(callbackMessage.body)
|
|
864
|
+
body: atob(callbackMessage.body ?? "")
|
|
753
865
|
});
|
|
754
866
|
console.warn(
|
|
755
867
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
756
|
-
${atob(callbackMessage.body)}`
|
|
868
|
+
${atob(callbackMessage.body ?? "")}`
|
|
757
869
|
);
|
|
758
870
|
return ok("call-will-retry");
|
|
759
871
|
}
|
|
@@ -787,7 +899,7 @@ ${atob(callbackMessage.body)}`
|
|
|
787
899
|
);
|
|
788
900
|
const callResponse = {
|
|
789
901
|
status: callbackMessage.status,
|
|
790
|
-
body: atob(callbackMessage.body),
|
|
902
|
+
body: atob(callbackMessage.body ?? ""),
|
|
791
903
|
header: callbackMessage.header
|
|
792
904
|
};
|
|
793
905
|
const callResultStep = {
|
|
@@ -818,9 +930,7 @@ ${atob(callbackMessage.body)}`
|
|
|
818
930
|
} catch (error) {
|
|
819
931
|
const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
|
|
820
932
|
return err(
|
|
821
|
-
new
|
|
822
|
-
`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`
|
|
823
|
-
)
|
|
933
|
+
new WorkflowError(`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`)
|
|
824
934
|
);
|
|
825
935
|
}
|
|
826
936
|
};
|
|
@@ -828,7 +938,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
828
938
|
const baseHeaders = {
|
|
829
939
|
[WORKFLOW_INIT_HEADER]: initHeaderValue,
|
|
830
940
|
[WORKFLOW_ID_HEADER]: workflowRunId,
|
|
831
|
-
[WORKFLOW_URL_HEADER]: workflowUrl
|
|
941
|
+
[WORKFLOW_URL_HEADER]: workflowUrl,
|
|
942
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody"
|
|
832
943
|
};
|
|
833
944
|
if (!step?.callUrl) {
|
|
834
945
|
baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
@@ -841,8 +952,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
841
952
|
}
|
|
842
953
|
if (step?.callUrl) {
|
|
843
954
|
baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
|
|
844
|
-
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete";
|
|
845
|
-
if (retries) {
|
|
955
|
+
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
|
|
956
|
+
if (retries !== void 0) {
|
|
846
957
|
baseHeaders["Upstash-Callback-Retries"] = retries.toString();
|
|
847
958
|
baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
|
|
848
959
|
}
|
|
@@ -877,6 +988,7 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
877
988
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
878
989
|
"Upstash-Callback-Workflow-Init": "false",
|
|
879
990
|
"Upstash-Callback-Workflow-Url": workflowUrl,
|
|
991
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
|
|
880
992
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
881
993
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
882
994
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
|
|
@@ -925,7 +1037,7 @@ var verifyRequest = async (body, signature, verifier) => {
|
|
|
925
1037
|
throw new Error("Signature in `Upstash-Signature` header is not valid");
|
|
926
1038
|
}
|
|
927
1039
|
} catch (error) {
|
|
928
|
-
throw new
|
|
1040
|
+
throw new WorkflowError(
|
|
929
1041
|
`Failed to verify that the Workflow request comes from QStash: ${error}
|
|
930
1042
|
|
|
931
1043
|
If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
|
|
@@ -965,14 +1077,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
965
1077
|
*
|
|
966
1078
|
* If a function is already executing (this.executingStep), this
|
|
967
1079
|
* means that there is a nested step which is not allowed. In this
|
|
968
|
-
* case, addStep throws
|
|
1080
|
+
* case, addStep throws WorkflowError.
|
|
969
1081
|
*
|
|
970
1082
|
* @param stepInfo step plan to add
|
|
971
1083
|
* @returns result of the step function
|
|
972
1084
|
*/
|
|
973
1085
|
async addStep(stepInfo) {
|
|
974
1086
|
if (this.executingStep) {
|
|
975
|
-
throw new
|
|
1087
|
+
throw new WorkflowError(
|
|
976
1088
|
`A step can not be run inside another step. Tried to run '${stepInfo.stepName}' inside '${this.executingStep}'`
|
|
977
1089
|
);
|
|
978
1090
|
}
|
|
@@ -1057,7 +1169,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1057
1169
|
const sortedSteps = sortSteps(this.steps);
|
|
1058
1170
|
const plannedParallelStepCount = sortedSteps[initialStepCount + this.planStepCount]?.concurrent;
|
|
1059
1171
|
if (parallelCallState !== "first" && plannedParallelStepCount !== parallelSteps.length) {
|
|
1060
|
-
throw new
|
|
1172
|
+
throw new WorkflowError(
|
|
1061
1173
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
1062
1174
|
);
|
|
1063
1175
|
}
|
|
@@ -1079,7 +1191,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1079
1191
|
case "partial": {
|
|
1080
1192
|
const planStep = this.steps.at(-1);
|
|
1081
1193
|
if (!planStep || planStep.targetStep === void 0) {
|
|
1082
|
-
throw new
|
|
1194
|
+
throw new WorkflowError(
|
|
1083
1195
|
`There must be a last step and it should have targetStep larger than 0.Received: ${JSON.stringify(planStep)}`
|
|
1084
1196
|
);
|
|
1085
1197
|
}
|
|
@@ -1093,17 +1205,17 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1093
1205
|
);
|
|
1094
1206
|
await this.submitStepsToQStash([resultStep], [parallelStep]);
|
|
1095
1207
|
} catch (error) {
|
|
1096
|
-
if (error instanceof
|
|
1208
|
+
if (error instanceof WorkflowAbort) {
|
|
1097
1209
|
throw error;
|
|
1098
1210
|
}
|
|
1099
|
-
throw new
|
|
1211
|
+
throw new WorkflowError(
|
|
1100
1212
|
`Error submitting steps to QStash in partial parallel step execution: ${error}`
|
|
1101
1213
|
);
|
|
1102
1214
|
}
|
|
1103
1215
|
break;
|
|
1104
1216
|
}
|
|
1105
1217
|
case "discard": {
|
|
1106
|
-
throw new
|
|
1218
|
+
throw new WorkflowAbort("discarded parallel");
|
|
1107
1219
|
}
|
|
1108
1220
|
case "last": {
|
|
1109
1221
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
@@ -1154,7 +1266,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1154
1266
|
*/
|
|
1155
1267
|
async submitStepsToQStash(steps, lazySteps) {
|
|
1156
1268
|
if (steps.length === 0) {
|
|
1157
|
-
throw new
|
|
1269
|
+
throw new WorkflowError(
|
|
1158
1270
|
`Unable to submit steps to QStash. Provided list is empty. Current step: ${this.stepCount}`
|
|
1159
1271
|
);
|
|
1160
1272
|
}
|
|
@@ -1194,7 +1306,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1194
1306
|
method: "POST",
|
|
1195
1307
|
parseResponseAsJson: false
|
|
1196
1308
|
});
|
|
1197
|
-
throw new
|
|
1309
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
1198
1310
|
}
|
|
1199
1311
|
const result = await this.context.qstashClient.batchJSON(
|
|
1200
1312
|
steps.map((singleStep, index) => {
|
|
@@ -1246,7 +1358,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1246
1358
|
};
|
|
1247
1359
|
})
|
|
1248
1360
|
});
|
|
1249
|
-
throw new
|
|
1361
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
1250
1362
|
}
|
|
1251
1363
|
/**
|
|
1252
1364
|
* Get the promise by executing the lazt steps list. If there is a single
|
|
@@ -1271,7 +1383,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1271
1383
|
} else if (Array.isArray(result) && lazyStepList.length === result.length && index < lazyStepList.length) {
|
|
1272
1384
|
return result[index];
|
|
1273
1385
|
} else {
|
|
1274
|
-
throw new
|
|
1386
|
+
throw new WorkflowError(
|
|
1275
1387
|
`Unexpected parallel call result while executing step ${index}: '${result}'. Expected ${lazyStepList.length} many items`
|
|
1276
1388
|
);
|
|
1277
1389
|
}
|
|
@@ -1283,12 +1395,12 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1283
1395
|
};
|
|
1284
1396
|
var validateStep = (lazyStep, stepFromRequest) => {
|
|
1285
1397
|
if (lazyStep.stepName !== stepFromRequest.stepName) {
|
|
1286
|
-
throw new
|
|
1398
|
+
throw new WorkflowError(
|
|
1287
1399
|
`Incompatible step name. Expected '${lazyStep.stepName}', got '${stepFromRequest.stepName}' from the request`
|
|
1288
1400
|
);
|
|
1289
1401
|
}
|
|
1290
1402
|
if (lazyStep.stepType !== stepFromRequest.stepType) {
|
|
1291
|
-
throw new
|
|
1403
|
+
throw new WorkflowError(
|
|
1292
1404
|
`Incompatible step type. Expected '${lazyStep.stepType}', got '${stepFromRequest.stepType}' from the request`
|
|
1293
1405
|
);
|
|
1294
1406
|
}
|
|
@@ -1299,12 +1411,12 @@ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
|
|
|
1299
1411
|
validateStep(lazySteps[index], stepFromRequest);
|
|
1300
1412
|
}
|
|
1301
1413
|
} catch (error) {
|
|
1302
|
-
if (error instanceof
|
|
1414
|
+
if (error instanceof WorkflowError) {
|
|
1303
1415
|
const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
|
|
1304
1416
|
const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
|
|
1305
1417
|
const requestStepNames = stepsFromRequest.map((step) => step.stepName);
|
|
1306
1418
|
const requestStepTypes = stepsFromRequest.map((step) => step.stepType);
|
|
1307
|
-
throw new
|
|
1419
|
+
throw new WorkflowError(
|
|
1308
1420
|
`Incompatible steps detected in parallel execution: ${error.message}
|
|
1309
1421
|
> Step Names from the request: ${JSON.stringify(requestStepNames)}
|
|
1310
1422
|
Step Types from the request: ${JSON.stringify(requestStepTypes)}
|
|
@@ -1417,10 +1529,6 @@ var WorkflowContext = class {
|
|
|
1417
1529
|
* headers of the initial request
|
|
1418
1530
|
*/
|
|
1419
1531
|
headers;
|
|
1420
|
-
/**
|
|
1421
|
-
* initial payload as a raw string
|
|
1422
|
-
*/
|
|
1423
|
-
rawInitialPayload;
|
|
1424
1532
|
/**
|
|
1425
1533
|
* Map of environment variables and their values.
|
|
1426
1534
|
*
|
|
@@ -1455,7 +1563,6 @@ var WorkflowContext = class {
|
|
|
1455
1563
|
failureUrl,
|
|
1456
1564
|
debug,
|
|
1457
1565
|
initialPayload,
|
|
1458
|
-
rawInitialPayload,
|
|
1459
1566
|
env,
|
|
1460
1567
|
retries
|
|
1461
1568
|
}) {
|
|
@@ -1466,7 +1573,6 @@ var WorkflowContext = class {
|
|
|
1466
1573
|
this.failureUrl = failureUrl;
|
|
1467
1574
|
this.headers = headers;
|
|
1468
1575
|
this.requestPayload = initialPayload;
|
|
1469
|
-
this.rawInitialPayload = rawInitialPayload ?? JSON.stringify(this.requestPayload);
|
|
1470
1576
|
this.env = env ?? {};
|
|
1471
1577
|
this.retries = retries ?? DEFAULT_RETRIES;
|
|
1472
1578
|
this.executor = new AutoExecutor(this, this.steps, debug);
|
|
@@ -1487,7 +1593,7 @@ var WorkflowContext = class {
|
|
|
1487
1593
|
* const [result1, result2] = await Promise.all([
|
|
1488
1594
|
* context.run("step 1", () => {
|
|
1489
1595
|
* return "result1"
|
|
1490
|
-
* })
|
|
1596
|
+
* }),
|
|
1491
1597
|
* context.run("step 2", async () => {
|
|
1492
1598
|
* return await fetchResults()
|
|
1493
1599
|
* })
|
|
@@ -1505,6 +1611,10 @@ var WorkflowContext = class {
|
|
|
1505
1611
|
/**
|
|
1506
1612
|
* Stops the execution for the duration provided.
|
|
1507
1613
|
*
|
|
1614
|
+
* ```typescript
|
|
1615
|
+
* await context.sleep('sleep1', 3) // wait for three seconds
|
|
1616
|
+
* ```
|
|
1617
|
+
*
|
|
1508
1618
|
* @param stepName
|
|
1509
1619
|
* @param duration sleep duration in seconds
|
|
1510
1620
|
* @returns undefined
|
|
@@ -1515,6 +1625,10 @@ var WorkflowContext = class {
|
|
|
1515
1625
|
/**
|
|
1516
1626
|
* Stops the execution until the date time provided.
|
|
1517
1627
|
*
|
|
1628
|
+
* ```typescript
|
|
1629
|
+
* await context.sleepUntil('sleep1', Date.now() / 1000 + 3) // wait for three seconds
|
|
1630
|
+
* ```
|
|
1631
|
+
*
|
|
1518
1632
|
* @param stepName
|
|
1519
1633
|
* @param datetime time to sleep until. Can be provided as a number (in unix seconds),
|
|
1520
1634
|
* as a Date object or a string (passed to `new Date(datetimeString)`)
|
|
@@ -1538,7 +1652,7 @@ var WorkflowContext = class {
|
|
|
1538
1652
|
* const { status, body } = await context.call<string>(
|
|
1539
1653
|
* "post call step",
|
|
1540
1654
|
* {
|
|
1541
|
-
* url:
|
|
1655
|
+
* url: "https://www.some-endpoint.com/api",
|
|
1542
1656
|
* method: "POST",
|
|
1543
1657
|
* body: "my-payload"
|
|
1544
1658
|
* }
|
|
@@ -1592,45 +1706,43 @@ var WorkflowContext = class {
|
|
|
1592
1706
|
}
|
|
1593
1707
|
}
|
|
1594
1708
|
/**
|
|
1595
|
-
*
|
|
1596
|
-
* timeout ends
|
|
1709
|
+
* Pauses workflow execution until a specific event occurs or a timeout is reached.
|
|
1597
1710
|
*
|
|
1598
|
-
|
|
1599
|
-
* const
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1602
|
-
|
|
1603
|
-
* );
|
|
1604
|
-
* ```
|
|
1711
|
+
*```ts
|
|
1712
|
+
* const result = await workflow.waitForEvent("payment-confirmed", {
|
|
1713
|
+
* timeout: "5m"
|
|
1714
|
+
* });
|
|
1715
|
+
*```
|
|
1605
1716
|
*
|
|
1606
|
-
* To notify a waiting workflow
|
|
1717
|
+
* To notify a waiting workflow:
|
|
1607
1718
|
*
|
|
1608
1719
|
* ```ts
|
|
1609
1720
|
* import { Client } from "@upstash/workflow";
|
|
1610
1721
|
*
|
|
1611
|
-
* const client = new Client({ token: });
|
|
1722
|
+
* const client = new Client({ token: "<QSTASH_TOKEN>" });
|
|
1612
1723
|
*
|
|
1613
1724
|
* await client.notify({
|
|
1614
|
-
* eventId: "
|
|
1615
|
-
*
|
|
1725
|
+
* eventId: "payment.confirmed",
|
|
1726
|
+
* data: {
|
|
1727
|
+
* amount: 99.99,
|
|
1728
|
+
* currency: "USD"
|
|
1729
|
+
* }
|
|
1616
1730
|
* })
|
|
1617
1731
|
* ```
|
|
1618
1732
|
*
|
|
1733
|
+
* Alternatively, you can use the `context.notify` method.
|
|
1734
|
+
*
|
|
1619
1735
|
* @param stepName
|
|
1620
|
-
* @param eventId
|
|
1621
|
-
* @param
|
|
1622
|
-
* @returns
|
|
1623
|
-
* timeout
|
|
1624
|
-
* 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.
|
|
1625
1741
|
*/
|
|
1626
|
-
async waitForEvent(stepName, eventId,
|
|
1627
|
-
const
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
eventId,
|
|
1631
|
-
typeof timeout === "string" ? timeout : `${timeout}s`
|
|
1632
|
-
)
|
|
1633
|
-
);
|
|
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));
|
|
1634
1746
|
try {
|
|
1635
1747
|
return {
|
|
1636
1748
|
...result,
|
|
@@ -1640,6 +1752,27 @@ var WorkflowContext = class {
|
|
|
1640
1752
|
return result;
|
|
1641
1753
|
}
|
|
1642
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
|
+
*/
|
|
1643
1776
|
async notify(stepName, eventId, eventData) {
|
|
1644
1777
|
const result = await this.addStep(
|
|
1645
1778
|
new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
|
|
@@ -1653,6 +1786,15 @@ var WorkflowContext = class {
|
|
|
1653
1786
|
return result;
|
|
1654
1787
|
}
|
|
1655
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
|
+
}
|
|
1656
1798
|
/**
|
|
1657
1799
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
1658
1800
|
* DisabledWorkflowContext.
|
|
@@ -1693,7 +1835,8 @@ var WorkflowLogger = class _WorkflowLogger {
|
|
|
1693
1835
|
}
|
|
1694
1836
|
writeToConsole(logEntry) {
|
|
1695
1837
|
const JSON_SPACING = 2;
|
|
1696
|
-
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));
|
|
1697
1840
|
}
|
|
1698
1841
|
shouldLog(level) {
|
|
1699
1842
|
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
@@ -1735,6 +1878,63 @@ function decodeBase64(base64) {
|
|
|
1735
1878
|
}
|
|
1736
1879
|
}
|
|
1737
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
|
+
|
|
1738
1938
|
// src/workflow-parser.ts
|
|
1739
1939
|
var getPayload = async (request) => {
|
|
1740
1940
|
try {
|
|
@@ -1743,8 +1943,8 @@ var getPayload = async (request) => {
|
|
|
1743
1943
|
return;
|
|
1744
1944
|
}
|
|
1745
1945
|
};
|
|
1746
|
-
var
|
|
1747
|
-
const [encodedInitialPayload, ...encodedSteps] =
|
|
1946
|
+
var processRawSteps = (rawSteps) => {
|
|
1947
|
+
const [encodedInitialPayload, ...encodedSteps] = rawSteps;
|
|
1748
1948
|
const rawInitialPayload = decodeBase64(encodedInitialPayload.body);
|
|
1749
1949
|
const initialStep = {
|
|
1750
1950
|
stepId: 0,
|
|
@@ -1754,27 +1954,21 @@ var parsePayload = async (rawPayload, debug) => {
|
|
|
1754
1954
|
concurrent: NO_CONCURRENCY
|
|
1755
1955
|
};
|
|
1756
1956
|
const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
|
|
1757
|
-
const otherSteps =
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
};
|
|
1773
|
-
step.out = newOut;
|
|
1774
|
-
}
|
|
1775
|
-
return step;
|
|
1776
|
-
})
|
|
1777
|
-
);
|
|
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
|
+
});
|
|
1778
1972
|
const steps = [initialStep, ...otherSteps];
|
|
1779
1973
|
return {
|
|
1780
1974
|
rawInitialPayload,
|
|
@@ -1822,20 +2016,20 @@ var validateRequest = (request) => {
|
|
|
1822
2016
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
1823
2017
|
const isFirstInvocation = !versionHeader;
|
|
1824
2018
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
1825
|
-
throw new
|
|
2019
|
+
throw new WorkflowError(
|
|
1826
2020
|
`Incompatible workflow sdk protocol version. Expected ${WORKFLOW_PROTOCOL_VERSION}, got ${versionHeader} from the request.`
|
|
1827
2021
|
);
|
|
1828
2022
|
}
|
|
1829
2023
|
const workflowRunId = isFirstInvocation ? getWorkflowRunId() : request.headers.get(WORKFLOW_ID_HEADER) ?? "";
|
|
1830
2024
|
if (workflowRunId.length === 0) {
|
|
1831
|
-
throw new
|
|
2025
|
+
throw new WorkflowError("Couldn't get workflow id from header");
|
|
1832
2026
|
}
|
|
1833
2027
|
return {
|
|
1834
2028
|
isFirstInvocation,
|
|
1835
2029
|
workflowRunId
|
|
1836
2030
|
};
|
|
1837
2031
|
};
|
|
1838
|
-
var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
2032
|
+
var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requester, messageId, debug) => {
|
|
1839
2033
|
if (isFirstInvocation) {
|
|
1840
2034
|
return {
|
|
1841
2035
|
rawInitialPayload: requestPayload ?? "",
|
|
@@ -1843,10 +2037,18 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
1843
2037
|
isLastDuplicate: false
|
|
1844
2038
|
};
|
|
1845
2039
|
} else {
|
|
2040
|
+
let rawSteps;
|
|
1846
2041
|
if (!requestPayload) {
|
|
1847
|
-
|
|
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);
|
|
1848
2050
|
}
|
|
1849
|
-
const { rawInitialPayload, steps } =
|
|
2051
|
+
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
1850
2052
|
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, debug);
|
|
1851
2053
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
1852
2054
|
return {
|
|
@@ -1856,13 +2058,13 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
1856
2058
|
};
|
|
1857
2059
|
}
|
|
1858
2060
|
};
|
|
1859
|
-
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, failureFunction, debug) => {
|
|
2061
|
+
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, debug) => {
|
|
1860
2062
|
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
|
|
1861
2063
|
return ok("not-failure-callback");
|
|
1862
2064
|
}
|
|
1863
2065
|
if (!failureFunction) {
|
|
1864
2066
|
return err(
|
|
1865
|
-
new
|
|
2067
|
+
new WorkflowError(
|
|
1866
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."
|
|
1867
2069
|
)
|
|
1868
2070
|
);
|
|
@@ -1873,92 +2075,48 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
1873
2075
|
);
|
|
1874
2076
|
const decodedBody = body ? decodeBase64(body) : "{}";
|
|
1875
2077
|
const errorPayload = JSON.parse(decodedBody);
|
|
1876
|
-
const {
|
|
1877
|
-
rawInitialPayload,
|
|
1878
|
-
steps,
|
|
1879
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1880
|
-
isLastDuplicate: _isLastDuplicate
|
|
1881
|
-
} = await parseRequest(decodeBase64(sourceBody), false, debug);
|
|
1882
2078
|
const workflowContext = new WorkflowContext({
|
|
1883
2079
|
qstashClient,
|
|
1884
2080
|
workflowRunId,
|
|
1885
|
-
initialPayload: initialPayloadParser(
|
|
1886
|
-
rawInitialPayload,
|
|
2081
|
+
initialPayload: initialPayloadParser(decodeBase64(sourceBody)),
|
|
1887
2082
|
headers: recreateUserHeaders(new Headers(sourceHeader)),
|
|
1888
|
-
steps,
|
|
2083
|
+
steps: [],
|
|
1889
2084
|
url,
|
|
1890
2085
|
failureUrl: url,
|
|
1891
2086
|
debug
|
|
1892
2087
|
});
|
|
1893
|
-
|
|
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
|
+
});
|
|
1894
2104
|
} catch (error) {
|
|
1895
2105
|
return err(error);
|
|
1896
2106
|
}
|
|
1897
2107
|
return ok("is-failure-callback");
|
|
1898
2108
|
};
|
|
1899
2109
|
|
|
1900
|
-
// src/serve/authorization.ts
|
|
1901
|
-
var import_qstash2 = require("@upstash/qstash");
|
|
1902
|
-
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
1903
|
-
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
1904
|
-
/**
|
|
1905
|
-
* overwrite the WorkflowContext.addStep method to always raise QStashWorkflowAbort
|
|
1906
|
-
* error in order to stop the execution whenever we encounter a step.
|
|
1907
|
-
*
|
|
1908
|
-
* @param _step
|
|
1909
|
-
*/
|
|
1910
|
-
async addStep(_step) {
|
|
1911
|
-
throw new QStashWorkflowAbort(_DisabledWorkflowContext.disabledMessage);
|
|
1912
|
-
}
|
|
1913
|
-
/**
|
|
1914
|
-
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
1915
|
-
* route function with the new context.
|
|
1916
|
-
*
|
|
1917
|
-
* - returns "run-ended" if there are no steps found or
|
|
1918
|
-
* if the auth failed and user called `return`
|
|
1919
|
-
* - returns "step-found" if DisabledWorkflowContext.addStep is called.
|
|
1920
|
-
* - if there is another error, returns the error.
|
|
1921
|
-
*
|
|
1922
|
-
* @param routeFunction
|
|
1923
|
-
*/
|
|
1924
|
-
static async tryAuthentication(routeFunction, context) {
|
|
1925
|
-
const disabledContext = new _DisabledWorkflowContext({
|
|
1926
|
-
qstashClient: new import_qstash2.Client({
|
|
1927
|
-
baseUrl: "disabled-client",
|
|
1928
|
-
token: "disabled-client"
|
|
1929
|
-
}),
|
|
1930
|
-
workflowRunId: context.workflowRunId,
|
|
1931
|
-
headers: context.headers,
|
|
1932
|
-
steps: [],
|
|
1933
|
-
url: context.url,
|
|
1934
|
-
failureUrl: context.failureUrl,
|
|
1935
|
-
initialPayload: context.requestPayload,
|
|
1936
|
-
rawInitialPayload: context.rawInitialPayload,
|
|
1937
|
-
env: context.env,
|
|
1938
|
-
retries: context.retries
|
|
1939
|
-
});
|
|
1940
|
-
try {
|
|
1941
|
-
await routeFunction(disabledContext);
|
|
1942
|
-
} catch (error) {
|
|
1943
|
-
if (error instanceof QStashWorkflowAbort && error.stepName === this.disabledMessage) {
|
|
1944
|
-
return ok("step-found");
|
|
1945
|
-
}
|
|
1946
|
-
return err(error);
|
|
1947
|
-
}
|
|
1948
|
-
return ok("run-ended");
|
|
1949
|
-
}
|
|
1950
|
-
};
|
|
1951
|
-
|
|
1952
2110
|
// src/serve/options.ts
|
|
1953
|
-
var import_qstash3 = require("@upstash/qstash");
|
|
1954
2111
|
var import_qstash4 = require("@upstash/qstash");
|
|
2112
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
1955
2113
|
var processOptions = (options) => {
|
|
1956
2114
|
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
1957
2115
|
const receiverEnvironmentVariablesSet = Boolean(
|
|
1958
2116
|
environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
|
|
1959
2117
|
);
|
|
1960
2118
|
return {
|
|
1961
|
-
qstashClient: new
|
|
2119
|
+
qstashClient: new import_qstash5.Client({
|
|
1962
2120
|
baseUrl: environment.QSTASH_URL,
|
|
1963
2121
|
token: environment.QSTASH_TOKEN
|
|
1964
2122
|
}),
|
|
@@ -1979,7 +2137,7 @@ var processOptions = (options) => {
|
|
|
1979
2137
|
throw error;
|
|
1980
2138
|
}
|
|
1981
2139
|
},
|
|
1982
|
-
receiver: receiverEnvironmentVariablesSet ? new
|
|
2140
|
+
receiver: receiverEnvironmentVariablesSet ? new import_qstash4.Receiver({
|
|
1983
2141
|
currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
|
|
1984
2142
|
nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
|
|
1985
2143
|
}) : void 0,
|
|
@@ -2041,6 +2199,9 @@ var serve = (routeFunction, options) => {
|
|
|
2041
2199
|
const { rawInitialPayload, steps, isLastDuplicate } = await parseRequest(
|
|
2042
2200
|
requestPayload,
|
|
2043
2201
|
isFirstInvocation,
|
|
2202
|
+
workflowRunId,
|
|
2203
|
+
qstashClient.http,
|
|
2204
|
+
request.headers.get("upstash-message-id"),
|
|
2044
2205
|
debug
|
|
2045
2206
|
);
|
|
2046
2207
|
if (isLastDuplicate) {
|
|
@@ -2051,6 +2212,7 @@ var serve = (routeFunction, options) => {
|
|
|
2051
2212
|
requestPayload,
|
|
2052
2213
|
qstashClient,
|
|
2053
2214
|
initialPayloadParser,
|
|
2215
|
+
routeFunction,
|
|
2054
2216
|
failureFunction
|
|
2055
2217
|
);
|
|
2056
2218
|
if (failureCheck.isErr()) {
|
|
@@ -2063,7 +2225,6 @@ var serve = (routeFunction, options) => {
|
|
|
2063
2225
|
qstashClient,
|
|
2064
2226
|
workflowRunId,
|
|
2065
2227
|
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
2066
|
-
rawInitialPayload,
|
|
2067
2228
|
headers: recreateUserHeaders(request.headers),
|
|
2068
2229
|
steps,
|
|
2069
2230
|
url: workflowUrl,
|
|
@@ -2101,7 +2262,11 @@ var serve = (routeFunction, options) => {
|
|
|
2101
2262
|
onStep: async () => routeFunction(workflowContext),
|
|
2102
2263
|
onCleanup: async () => {
|
|
2103
2264
|
await triggerWorkflowDelete(workflowContext, debug);
|
|
2104
|
-
}
|
|
2265
|
+
},
|
|
2266
|
+
onCancel: async () => {
|
|
2267
|
+
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
2268
|
+
},
|
|
2269
|
+
debug
|
|
2105
2270
|
});
|
|
2106
2271
|
if (result.isErr()) {
|
|
2107
2272
|
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
@@ -2127,7 +2292,7 @@ var serve = (routeFunction, options) => {
|
|
|
2127
2292
|
};
|
|
2128
2293
|
|
|
2129
2294
|
// src/client/index.ts
|
|
2130
|
-
var
|
|
2295
|
+
var import_qstash6 = require("@upstash/qstash");
|
|
2131
2296
|
|
|
2132
2297
|
// platforms/nextjs.ts
|
|
2133
2298
|
var serve2 = (routeFunction, options) => {
|