@upstash/workflow 0.3.0-rc → 0.3.0-rc1
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 +3 -3
- package/astro.d.ts +3 -3
- package/astro.js +1048 -676
- package/astro.mjs +1 -1
- package/{chunk-AGYYZKP7.mjs → chunk-2Z32SOYM.mjs} +1047 -674
- package/cloudflare.d.mts +3 -3
- package/cloudflare.d.ts +3 -3
- package/cloudflare.js +1048 -676
- package/cloudflare.mjs +1 -1
- package/express.d.mts +3 -3
- package/express.d.ts +3 -3
- package/express.js +1058 -681
- package/express.mjs +11 -6
- package/h3.d.mts +3 -3
- package/h3.d.ts +3 -3
- package/h3.js +1053 -677
- package/h3.mjs +6 -2
- package/hono.d.mts +3 -3
- package/hono.d.ts +3 -3
- package/hono.js +1048 -676
- package/hono.mjs +1 -1
- package/index.d.mts +98 -64
- package/index.d.ts +98 -64
- package/index.js +1079 -697
- package/index.mjs +30 -20
- package/nextjs.d.mts +4 -4
- package/nextjs.d.ts +4 -4
- package/nextjs.js +1049 -677
- package/nextjs.mjs +2 -2
- package/package.json +1 -1
- package/{serve-many-DVtHRxeg.d.ts → serve-many-DhB8-zPD.d.mts} +2 -2
- package/{serve-many-DEwKPF6H.d.mts → serve-many-qnfynN1x.d.ts} +2 -2
- package/solidjs.d.mts +2 -2
- package/solidjs.d.ts +2 -2
- package/solidjs.js +1053 -677
- package/solidjs.mjs +6 -2
- package/svelte.d.mts +5 -6
- package/svelte.d.ts +5 -6
- package/svelte.js +1061 -682
- package/svelte.mjs +14 -7
- package/tanstack.d.mts +3 -3
- package/tanstack.d.ts +3 -3
- package/tanstack.js +1048 -676
- package/tanstack.mjs +1 -1
- package/{types-DESkn7K9.d.ts → types-pEje3VEB.d.mts} +212 -239
- package/{types-DESkn7K9.d.mts → types-pEje3VEB.d.ts} +212 -239
package/express.js
CHANGED
|
@@ -31,15 +31,18 @@ var WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId";
|
|
|
31
31
|
var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
|
|
32
32
|
var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
|
|
33
33
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
34
|
+
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
34
35
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
35
36
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
36
37
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
38
|
+
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
39
|
+
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
37
40
|
var WORKFLOW_PROTOCOL_VERSION = "1";
|
|
38
41
|
var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
39
42
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
40
43
|
var NO_CONCURRENCY = 1;
|
|
41
44
|
var DEFAULT_RETRIES = 3;
|
|
42
|
-
var VERSION = "
|
|
45
|
+
var VERSION = "v1.0.0";
|
|
43
46
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
44
47
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
45
48
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
@@ -57,26 +60,36 @@ var WorkflowError = class extends import_qstash.QstashError {
|
|
|
57
60
|
}
|
|
58
61
|
};
|
|
59
62
|
var WorkflowAbort = class extends Error {
|
|
60
|
-
stepInfo;
|
|
61
63
|
stepName;
|
|
64
|
+
stepInfo;
|
|
62
65
|
/**
|
|
63
|
-
* whether workflow is to be canceled on abort
|
|
64
|
-
*/
|
|
65
|
-
cancelWorkflow;
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
68
66
|
* @param stepName name of the aborting step
|
|
69
67
|
* @param stepInfo step information
|
|
70
|
-
* @param cancelWorkflow
|
|
71
68
|
*/
|
|
72
|
-
constructor(stepName, stepInfo
|
|
69
|
+
constructor(stepName, stepInfo) {
|
|
73
70
|
super(
|
|
74
71
|
`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}'.`
|
|
75
72
|
);
|
|
76
73
|
this.name = "WorkflowAbort";
|
|
77
74
|
this.stepName = stepName;
|
|
78
75
|
this.stepInfo = stepInfo;
|
|
79
|
-
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var WorkflowAuthError = class extends WorkflowAbort {
|
|
79
|
+
/**
|
|
80
|
+
* @param stepName name of the step found during authorization
|
|
81
|
+
*/
|
|
82
|
+
constructor(stepName) {
|
|
83
|
+
super(stepName);
|
|
84
|
+
this.name = "WorkflowAuthError";
|
|
85
|
+
this.message = `This is an Upstash Workflow error thrown during authorization check. Found step '${stepName}' during dry-run.`;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var WorkflowCancelAbort = class extends WorkflowAbort {
|
|
89
|
+
constructor() {
|
|
90
|
+
super("cancel");
|
|
91
|
+
this.name = "WorkflowCancelAbort";
|
|
92
|
+
this.message = "Workflow has been canceled by user via context.cancel().";
|
|
80
93
|
}
|
|
81
94
|
};
|
|
82
95
|
var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
@@ -84,22 +97,22 @@ var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
|
84
97
|
* @param message error message to be displayed
|
|
85
98
|
*/
|
|
86
99
|
constructor(message) {
|
|
87
|
-
super("
|
|
100
|
+
super("non-retryable-error");
|
|
88
101
|
this.name = "WorkflowNonRetryableError";
|
|
89
|
-
|
|
102
|
+
this.message = message ?? "Workflow failed with non-retryable error.";
|
|
90
103
|
}
|
|
91
104
|
};
|
|
92
105
|
var WorkflowRetryAfterError = class extends WorkflowAbort {
|
|
93
106
|
retryAfter;
|
|
94
107
|
/**
|
|
95
|
-
* @param retryAfter time in seconds after which the workflow should be retried
|
|
96
108
|
* @param message error message to be displayed
|
|
109
|
+
* @param retryAfter time in seconds after which the workflow should be retried
|
|
97
110
|
*/
|
|
98
111
|
constructor(message, retryAfter) {
|
|
99
|
-
super("retry"
|
|
112
|
+
super("retry-after-error");
|
|
100
113
|
this.name = "WorkflowRetryAfterError";
|
|
114
|
+
this.message = message;
|
|
101
115
|
this.retryAfter = retryAfter;
|
|
102
|
-
if (message) this.message = message;
|
|
103
116
|
}
|
|
104
117
|
};
|
|
105
118
|
var formatWorkflowError = (error) => {
|
|
@@ -151,15 +164,21 @@ var makeCancelRequest = async (requester, workflowRunId) => {
|
|
|
151
164
|
});
|
|
152
165
|
return true;
|
|
153
166
|
};
|
|
154
|
-
var getSteps = async (requester, workflowRunId, messageId,
|
|
167
|
+
var getSteps = async (requester, workflowRunId, messageId, dispatchDebug) => {
|
|
155
168
|
try {
|
|
156
169
|
const steps = await requester.request({
|
|
157
170
|
path: ["v2", "workflows", "runs", workflowRunId],
|
|
158
171
|
parseResponseAsJson: true
|
|
159
172
|
});
|
|
173
|
+
if (steps.length === 1) {
|
|
174
|
+
return {
|
|
175
|
+
steps,
|
|
176
|
+
workflowRunEnded: false
|
|
177
|
+
};
|
|
178
|
+
}
|
|
160
179
|
if (!messageId) {
|
|
161
|
-
await
|
|
162
|
-
|
|
180
|
+
await dispatchDebug?.("onInfo", {
|
|
181
|
+
info: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
|
|
163
182
|
});
|
|
164
183
|
return { steps, workflowRunEnded: false };
|
|
165
184
|
} else {
|
|
@@ -168,16 +187,15 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
|
|
|
168
187
|
return { steps: [], workflowRunEnded: false };
|
|
169
188
|
}
|
|
170
189
|
const filteredSteps = steps.slice(0, index + 1);
|
|
171
|
-
await
|
|
172
|
-
|
|
190
|
+
await dispatchDebug?.("onInfo", {
|
|
191
|
+
info: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
|
|
173
192
|
});
|
|
174
193
|
return { steps: filteredSteps, workflowRunEnded: false };
|
|
175
194
|
}
|
|
176
195
|
} catch (error) {
|
|
177
196
|
if (isInstanceOf(error, import_qstash2.QstashError) && error.status === 404) {
|
|
178
|
-
await
|
|
179
|
-
|
|
180
|
-
error
|
|
197
|
+
await dispatchDebug?.("onWarning", {
|
|
198
|
+
warning: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed."
|
|
181
199
|
});
|
|
182
200
|
return { steps: void 0, workflowRunEnded: true };
|
|
183
201
|
} else {
|
|
@@ -198,8 +216,8 @@ var NANOID_LENGTH = 21;
|
|
|
198
216
|
function getRandomInt() {
|
|
199
217
|
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
200
218
|
}
|
|
201
|
-
function nanoid() {
|
|
202
|
-
return Array.from({ length
|
|
219
|
+
function nanoid(length = NANOID_LENGTH) {
|
|
220
|
+
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
203
221
|
}
|
|
204
222
|
function getWorkflowRunId(id) {
|
|
205
223
|
return `wfr_${id ?? nanoid()}`;
|
|
@@ -216,6 +234,46 @@ function decodeBase64(base64) {
|
|
|
216
234
|
return binString;
|
|
217
235
|
}
|
|
218
236
|
}
|
|
237
|
+
function getUserIdFromToken(qstashClient) {
|
|
238
|
+
try {
|
|
239
|
+
const token = qstashClient.token;
|
|
240
|
+
const decodedToken = decodeBase64(token);
|
|
241
|
+
const tokenPayload = JSON.parse(decodedToken);
|
|
242
|
+
const userId = tokenPayload.UserID;
|
|
243
|
+
if (!userId) {
|
|
244
|
+
throw new WorkflowError("QStash token payload does not contain userId");
|
|
245
|
+
}
|
|
246
|
+
return userId;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
throw new WorkflowError(
|
|
249
|
+
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function getQStashUrl(qstashClient) {
|
|
254
|
+
try {
|
|
255
|
+
const requester = qstashClient.http;
|
|
256
|
+
const baseUrl = requester.baseUrl;
|
|
257
|
+
if (!baseUrl) {
|
|
258
|
+
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
259
|
+
}
|
|
260
|
+
return baseUrl;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function getEventId() {
|
|
266
|
+
return `evt_${nanoid(15)}`;
|
|
267
|
+
}
|
|
268
|
+
function stringifyBody(body) {
|
|
269
|
+
if (body === void 0) {
|
|
270
|
+
return void 0;
|
|
271
|
+
}
|
|
272
|
+
if (typeof body === "string") {
|
|
273
|
+
return body;
|
|
274
|
+
}
|
|
275
|
+
return JSON.stringify(body);
|
|
276
|
+
}
|
|
219
277
|
|
|
220
278
|
// node_modules/neverthrow/dist/index.es.js
|
|
221
279
|
var defaultErrorConfig = {
|
|
@@ -641,7 +699,9 @@ var StepTypes = [
|
|
|
641
699
|
"Call",
|
|
642
700
|
"Wait",
|
|
643
701
|
"Notify",
|
|
644
|
-
"Invoke"
|
|
702
|
+
"Invoke",
|
|
703
|
+
"CreateWebhook",
|
|
704
|
+
"WaitForWebhook"
|
|
645
705
|
];
|
|
646
706
|
|
|
647
707
|
// src/workflow-requests.ts
|
|
@@ -657,23 +717,26 @@ var triggerFirstInvocation = async (params) => {
|
|
|
657
717
|
invokeCount,
|
|
658
718
|
delay,
|
|
659
719
|
notBefore,
|
|
660
|
-
|
|
720
|
+
failureUrl,
|
|
721
|
+
retries,
|
|
722
|
+
retryDelay,
|
|
723
|
+
flowControl,
|
|
724
|
+
unknownSdk
|
|
661
725
|
}) => {
|
|
662
726
|
const { headers } = getHeaders({
|
|
663
727
|
initHeaderValue: "true",
|
|
664
728
|
workflowConfig: {
|
|
665
729
|
workflowRunId: workflowContext.workflowRunId,
|
|
666
730
|
workflowUrl: workflowContext.url,
|
|
667
|
-
failureUrl
|
|
668
|
-
retries
|
|
669
|
-
retryDelay
|
|
731
|
+
failureUrl,
|
|
732
|
+
retries,
|
|
733
|
+
retryDelay,
|
|
670
734
|
telemetry: telemetry2,
|
|
671
|
-
flowControl
|
|
735
|
+
flowControl,
|
|
672
736
|
useJSONContent: useJSONContent ?? false
|
|
673
737
|
},
|
|
674
738
|
invokeCount: invokeCount ?? 0,
|
|
675
|
-
userHeaders: workflowContext.headers
|
|
676
|
-
keepTriggerConfig
|
|
739
|
+
userHeaders: workflowContext.headers
|
|
677
740
|
});
|
|
678
741
|
if (workflowContext.headers.get("content-type")) {
|
|
679
742
|
headers["content-type"] = workflowContext.headers.get("content-type");
|
|
@@ -681,6 +744,9 @@ var triggerFirstInvocation = async (params) => {
|
|
|
681
744
|
if (useJSONContent) {
|
|
682
745
|
headers["content-type"] = "application/json";
|
|
683
746
|
}
|
|
747
|
+
if (unknownSdk) {
|
|
748
|
+
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
749
|
+
}
|
|
684
750
|
if (workflowContext.label) {
|
|
685
751
|
headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
|
|
686
752
|
}
|
|
@@ -701,21 +767,15 @@ var triggerFirstInvocation = async (params) => {
|
|
|
701
767
|
for (let i = 0; i < results.length; i++) {
|
|
702
768
|
const result = results[i];
|
|
703
769
|
const invocationParams = firstInvocationParams[i];
|
|
770
|
+
invocationParams.middlewareManager?.assignContext(invocationParams.workflowContext);
|
|
704
771
|
if (result.deduplicated) {
|
|
705
|
-
await invocationParams.
|
|
706
|
-
|
|
707
|
-
headers: invocationBatch[i].headers,
|
|
708
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
709
|
-
url: invocationParams.workflowContext.url,
|
|
710
|
-
messageId: result.messageId
|
|
772
|
+
await invocationParams.middlewareManager?.dispatchDebug("onWarning", {
|
|
773
|
+
warning: `Workflow run ${invocationParams.workflowContext.workflowRunId} already exists. A new one isn't created.`
|
|
711
774
|
});
|
|
712
775
|
invocationStatuses.push("workflow-run-already-exists");
|
|
713
776
|
} else {
|
|
714
|
-
await invocationParams.
|
|
715
|
-
|
|
716
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
717
|
-
url: invocationParams.workflowContext.url,
|
|
718
|
-
messageId: result.messageId
|
|
777
|
+
await invocationParams.middlewareManager?.dispatchDebug("onInfo", {
|
|
778
|
+
info: `Workflow run started successfully with URL ${invocationParams.workflowContext.url}.`
|
|
719
779
|
});
|
|
720
780
|
invocationStatuses.push("success");
|
|
721
781
|
}
|
|
@@ -737,7 +797,7 @@ var triggerRouteFunction = async ({
|
|
|
737
797
|
onCleanup,
|
|
738
798
|
onStep,
|
|
739
799
|
onCancel,
|
|
740
|
-
|
|
800
|
+
middlewareManager
|
|
741
801
|
}) => {
|
|
742
802
|
try {
|
|
743
803
|
const result = await onStep();
|
|
@@ -746,27 +806,25 @@ var triggerRouteFunction = async ({
|
|
|
746
806
|
} catch (error) {
|
|
747
807
|
const error_ = error;
|
|
748
808
|
if (isInstanceOf(error, import_qstash3.QstashError) && error.status === 400) {
|
|
749
|
-
await
|
|
750
|
-
|
|
751
|
-
name: error.name,
|
|
752
|
-
errorMessage: error.message
|
|
809
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
810
|
+
warning: `Tried to append to a cancelled workflow. Exiting without publishing. Error: ${error.message}`
|
|
753
811
|
});
|
|
754
812
|
return ok("workflow-was-finished");
|
|
755
813
|
} else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
|
|
756
814
|
return ok(error_);
|
|
757
|
-
} else if (
|
|
758
|
-
return err(error_);
|
|
759
|
-
} else if (error_.cancelWorkflow) {
|
|
815
|
+
} else if (isInstanceOf(error_, WorkflowCancelAbort)) {
|
|
760
816
|
await onCancel();
|
|
761
817
|
return ok("workflow-finished");
|
|
762
|
-
} else {
|
|
818
|
+
} else if (isInstanceOf(error_, WorkflowAbort)) {
|
|
763
819
|
return ok("step-finished");
|
|
820
|
+
} else {
|
|
821
|
+
return err(error_);
|
|
764
822
|
}
|
|
765
823
|
}
|
|
766
824
|
};
|
|
767
|
-
var triggerWorkflowDelete = async (workflowContext, result,
|
|
768
|
-
await
|
|
769
|
-
|
|
825
|
+
var triggerWorkflowDelete = async (workflowContext, result, cancel = false, dispatchDebug) => {
|
|
826
|
+
await dispatchDebug?.("onInfo", {
|
|
827
|
+
info: `Deleting workflow run ${workflowContext.workflowRunId} from QStash` + (cancel ? " with cancel=true." : ".")
|
|
770
828
|
});
|
|
771
829
|
await workflowContext.qstashClient.http.request({
|
|
772
830
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
@@ -774,18 +832,16 @@ var triggerWorkflowDelete = async (workflowContext, result, debug, cancel = fals
|
|
|
774
832
|
parseResponseAsJson: false,
|
|
775
833
|
body: JSON.stringify(result)
|
|
776
834
|
});
|
|
777
|
-
await
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
`workflow run ${workflowContext.workflowRunId} deleted.`
|
|
781
|
-
);
|
|
835
|
+
await dispatchDebug?.("onInfo", {
|
|
836
|
+
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
837
|
+
});
|
|
782
838
|
};
|
|
783
839
|
var recreateUserHeaders = (headers) => {
|
|
784
840
|
const filteredHeaders = new Headers();
|
|
785
841
|
const pairs = headers.entries();
|
|
786
842
|
for (const [header, value] of pairs) {
|
|
787
843
|
const headerLowerCase = header.toLowerCase();
|
|
788
|
-
const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
|
|
844
|
+
const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
|
|
789
845
|
!headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
|
|
790
846
|
headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
|
|
791
847
|
headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
|
|
@@ -800,12 +856,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
800
856
|
requestPayload,
|
|
801
857
|
client,
|
|
802
858
|
workflowUrl,
|
|
803
|
-
failureUrl,
|
|
804
|
-
retries,
|
|
805
|
-
retryDelay,
|
|
806
859
|
telemetry: telemetry2,
|
|
807
|
-
|
|
808
|
-
debug
|
|
860
|
+
middlewareManager
|
|
809
861
|
}) => {
|
|
810
862
|
try {
|
|
811
863
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
@@ -822,7 +874,7 @@ var handleThirdPartyCallResult = async ({
|
|
|
822
874
|
client.http,
|
|
823
875
|
workflowRunId2,
|
|
824
876
|
messageId,
|
|
825
|
-
|
|
877
|
+
middlewareManager?.dispatchDebug.bind(middlewareManager)
|
|
826
878
|
);
|
|
827
879
|
if (workflowRunEnded) {
|
|
828
880
|
return ok("workflow-ended");
|
|
@@ -836,9 +888,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
836
888
|
}
|
|
837
889
|
const callbackMessage = JSON.parse(callbackPayload);
|
|
838
890
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
839
|
-
await
|
|
840
|
-
|
|
841
|
-
body: atob(callbackMessage.body ?? "")
|
|
891
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
892
|
+
warning: `Third party call returned status ${callbackMessage.status}. Retrying (${callbackMessage.retried} out of ${callbackMessage.maxRetries}).`
|
|
842
893
|
});
|
|
843
894
|
console.warn(
|
|
844
895
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
@@ -871,11 +922,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
871
922
|
workflowConfig: {
|
|
872
923
|
workflowRunId,
|
|
873
924
|
workflowUrl,
|
|
874
|
-
|
|
875
|
-
retries,
|
|
876
|
-
retryDelay,
|
|
877
|
-
telemetry: telemetry2,
|
|
878
|
-
flowControl
|
|
925
|
+
telemetry: telemetry2
|
|
879
926
|
},
|
|
880
927
|
userHeaders,
|
|
881
928
|
invokeCount: Number(invokeCount)
|
|
@@ -892,19 +939,17 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
892
939
|
out: JSON.stringify(callResponse),
|
|
893
940
|
concurrent: Number(concurrentString)
|
|
894
941
|
};
|
|
895
|
-
await
|
|
896
|
-
|
|
897
|
-
headers: requestHeaders,
|
|
898
|
-
url: workflowUrl
|
|
942
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
943
|
+
info: `Submitting third party call result, step ${stepName} (${stepIdString}).`
|
|
899
944
|
});
|
|
900
|
-
|
|
945
|
+
await client.publishJSON({
|
|
901
946
|
headers: requestHeaders,
|
|
902
947
|
method: "POST",
|
|
903
948
|
body: callResultStep,
|
|
904
949
|
url: workflowUrl
|
|
905
950
|
});
|
|
906
|
-
await
|
|
907
|
-
|
|
951
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
952
|
+
info: `Third party call result submitted successfully, step ${stepName} (${stepIdString}).`
|
|
908
953
|
});
|
|
909
954
|
return ok("is-call-return");
|
|
910
955
|
} else {
|
|
@@ -953,15 +998,17 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
|
|
|
953
998
|
// src/context/steps.ts
|
|
954
999
|
var BaseLazyStep = class _BaseLazyStep {
|
|
955
1000
|
stepName;
|
|
956
|
-
|
|
1001
|
+
context;
|
|
1002
|
+
constructor(context, stepName) {
|
|
1003
|
+
this.context = context;
|
|
957
1004
|
if (!stepName) {
|
|
958
1005
|
throw new WorkflowError(
|
|
959
1006
|
"A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
|
|
960
1007
|
);
|
|
961
1008
|
}
|
|
962
1009
|
if (typeof stepName !== "string") {
|
|
963
|
-
|
|
964
|
-
|
|
1010
|
+
throw new WorkflowError(
|
|
1011
|
+
`A workflow step name must be a string. Received "${stepName}" (${typeof stepName}).`
|
|
965
1012
|
);
|
|
966
1013
|
}
|
|
967
1014
|
this.stepName = stepName;
|
|
@@ -971,13 +1018,14 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
971
1018
|
*
|
|
972
1019
|
* will be called when returning the steps to the context from auto executor
|
|
973
1020
|
*
|
|
974
|
-
* @param
|
|
1021
|
+
* @param step step
|
|
975
1022
|
* @returns parsed out field
|
|
976
1023
|
*/
|
|
977
|
-
parseOut(
|
|
1024
|
+
parseOut(step) {
|
|
1025
|
+
const out = step.out;
|
|
978
1026
|
if (out === void 0) {
|
|
979
1027
|
if (this.allowUndefinedOut) {
|
|
980
|
-
return
|
|
1028
|
+
return this.handleUndefinedOut(step);
|
|
981
1029
|
} else {
|
|
982
1030
|
throw new WorkflowError(
|
|
983
1031
|
`Error while parsing output of ${this.stepType} step. Expected a string, but got: undefined`
|
|
@@ -985,27 +1033,26 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
985
1033
|
}
|
|
986
1034
|
}
|
|
987
1035
|
if (typeof out === "object") {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
return out;
|
|
993
|
-
}
|
|
994
|
-
return {
|
|
995
|
-
...out,
|
|
996
|
-
eventData: _BaseLazyStep.tryParsing(out.eventData)
|
|
997
|
-
};
|
|
1036
|
+
console.warn(
|
|
1037
|
+
`Error while parsing ${this.stepType} step output. Expected a string, but got object. Please reach out to Upstash Support.`
|
|
1038
|
+
);
|
|
1039
|
+
return out;
|
|
998
1040
|
}
|
|
999
1041
|
if (typeof out !== "string") {
|
|
1000
1042
|
throw new WorkflowError(
|
|
1001
1043
|
`Error while parsing output of ${this.stepType} step. Expected a string or undefined, but got: ${typeof out}`
|
|
1002
1044
|
);
|
|
1003
1045
|
}
|
|
1004
|
-
return this.safeParseOut(out);
|
|
1046
|
+
return this.safeParseOut(out, step);
|
|
1005
1047
|
}
|
|
1006
|
-
|
|
1048
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1049
|
+
safeParseOut(out, step) {
|
|
1007
1050
|
return _BaseLazyStep.tryParsing(out);
|
|
1008
1051
|
}
|
|
1052
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1053
|
+
handleUndefinedOut(step) {
|
|
1054
|
+
return void 0;
|
|
1055
|
+
}
|
|
1009
1056
|
static tryParsing(stepOut) {
|
|
1010
1057
|
try {
|
|
1011
1058
|
return JSON.parse(stepOut);
|
|
@@ -1023,12 +1070,8 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1023
1070
|
workflowConfig: {
|
|
1024
1071
|
workflowRunId: context.workflowRunId,
|
|
1025
1072
|
workflowUrl: context.url,
|
|
1026
|
-
failureUrl: context.failureUrl,
|
|
1027
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1028
|
-
retryDelay: context.retryDelay,
|
|
1029
1073
|
useJSONContent: false,
|
|
1030
|
-
telemetry: telemetry2
|
|
1031
|
-
flowControl: context.flowControl
|
|
1074
|
+
telemetry: telemetry2
|
|
1032
1075
|
},
|
|
1033
1076
|
userHeaders: context.headers,
|
|
1034
1077
|
invokeCount,
|
|
@@ -1044,9 +1087,6 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1044
1087
|
body,
|
|
1045
1088
|
headers,
|
|
1046
1089
|
method: "POST",
|
|
1047
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1048
|
-
retryDelay: context.retryDelay,
|
|
1049
|
-
flowControl: context.flowControl,
|
|
1050
1090
|
url: context.url
|
|
1051
1091
|
}
|
|
1052
1092
|
]);
|
|
@@ -1056,8 +1096,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
|
|
|
1056
1096
|
stepFunction;
|
|
1057
1097
|
stepType = "Run";
|
|
1058
1098
|
allowUndefinedOut = true;
|
|
1059
|
-
constructor(stepName, stepFunction) {
|
|
1060
|
-
super(stepName);
|
|
1099
|
+
constructor(context, stepName, stepFunction) {
|
|
1100
|
+
super(context, stepName);
|
|
1061
1101
|
this.stepFunction = stepFunction;
|
|
1062
1102
|
}
|
|
1063
1103
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1087,8 +1127,8 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1087
1127
|
sleep;
|
|
1088
1128
|
stepType = "SleepFor";
|
|
1089
1129
|
allowUndefinedOut = true;
|
|
1090
|
-
constructor(stepName, sleep) {
|
|
1091
|
-
super(stepName);
|
|
1130
|
+
constructor(context, stepName, sleep) {
|
|
1131
|
+
super(context, stepName);
|
|
1092
1132
|
this.sleep = sleep;
|
|
1093
1133
|
}
|
|
1094
1134
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1117,9 +1157,6 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1117
1157
|
headers,
|
|
1118
1158
|
method: "POST",
|
|
1119
1159
|
url: context.url,
|
|
1120
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1121
|
-
retryDelay: context.retryDelay,
|
|
1122
|
-
flowControl: context.flowControl,
|
|
1123
1160
|
delay: isParallel ? void 0 : this.sleep
|
|
1124
1161
|
}
|
|
1125
1162
|
]);
|
|
@@ -1129,8 +1166,8 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1129
1166
|
sleepUntil;
|
|
1130
1167
|
stepType = "SleepUntil";
|
|
1131
1168
|
allowUndefinedOut = true;
|
|
1132
|
-
constructor(stepName, sleepUntil) {
|
|
1133
|
-
super(stepName);
|
|
1169
|
+
constructor(context, stepName, sleepUntil) {
|
|
1170
|
+
super(context, stepName);
|
|
1134
1171
|
this.sleepUntil = sleepUntil;
|
|
1135
1172
|
}
|
|
1136
1173
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1162,9 +1199,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1162
1199
|
headers,
|
|
1163
1200
|
method: "POST",
|
|
1164
1201
|
url: context.url,
|
|
1165
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1166
|
-
retryDelay: context.retryDelay,
|
|
1167
|
-
flowControl: context.flowControl,
|
|
1168
1202
|
notBefore: isParallel ? void 0 : this.sleepUntil
|
|
1169
1203
|
}
|
|
1170
1204
|
]);
|
|
@@ -1179,20 +1213,18 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1179
1213
|
retryDelay;
|
|
1180
1214
|
timeout;
|
|
1181
1215
|
flowControl;
|
|
1182
|
-
stringifyBody;
|
|
1183
1216
|
stepType = "Call";
|
|
1184
1217
|
allowUndefinedOut = false;
|
|
1185
|
-
constructor(
|
|
1186
|
-
super(stepName);
|
|
1187
|
-
this.url = url;
|
|
1188
|
-
this.method = method;
|
|
1189
|
-
this.body = body;
|
|
1190
|
-
this.headers = headers;
|
|
1191
|
-
this.retries = retries;
|
|
1192
|
-
this.retryDelay = retryDelay;
|
|
1193
|
-
this.timeout = timeout;
|
|
1194
|
-
this.flowControl = flowControl;
|
|
1195
|
-
this.stringifyBody = stringifyBody;
|
|
1218
|
+
constructor(params) {
|
|
1219
|
+
super(params.context, params.stepName);
|
|
1220
|
+
this.url = params.url;
|
|
1221
|
+
this.method = params.method ?? "GET";
|
|
1222
|
+
this.body = params.body;
|
|
1223
|
+
this.headers = params.headers ?? {};
|
|
1224
|
+
this.retries = params.retries ?? 0;
|
|
1225
|
+
this.retryDelay = params.retryDelay;
|
|
1226
|
+
this.timeout = params.timeout;
|
|
1227
|
+
this.flowControl = params.flowControl;
|
|
1196
1228
|
}
|
|
1197
1229
|
getPlanStep(concurrent, targetStep) {
|
|
1198
1230
|
return {
|
|
@@ -1289,7 +1321,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1289
1321
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
1290
1322
|
"Upstash-Callback-Workflow-Init": "false",
|
|
1291
1323
|
"Upstash-Callback-Workflow-Url": context.url,
|
|
1292
|
-
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger",
|
|
1324
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1293
1325
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
1294
1326
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
1295
1327
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
|
|
@@ -1302,22 +1334,10 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1302
1334
|
};
|
|
1303
1335
|
}
|
|
1304
1336
|
async submitStep({ context, headers }) {
|
|
1305
|
-
let callBody;
|
|
1306
|
-
if (this.stringifyBody) {
|
|
1307
|
-
callBody = JSON.stringify(this.body);
|
|
1308
|
-
} else {
|
|
1309
|
-
if (typeof this.body === "string") {
|
|
1310
|
-
callBody = this.body;
|
|
1311
|
-
} else {
|
|
1312
|
-
throw new WorkflowError(
|
|
1313
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your call step."
|
|
1314
|
-
);
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
1337
|
return await context.qstashClient.batch([
|
|
1318
1338
|
{
|
|
1319
1339
|
headers,
|
|
1320
|
-
body:
|
|
1340
|
+
body: this.body,
|
|
1321
1341
|
method: this.method,
|
|
1322
1342
|
url: this.url,
|
|
1323
1343
|
retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
|
|
@@ -1327,13 +1347,12 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1327
1347
|
]);
|
|
1328
1348
|
}
|
|
1329
1349
|
};
|
|
1330
|
-
var
|
|
1350
|
+
var LazyWaitEventStep = class extends BaseLazyStep {
|
|
1331
1351
|
eventId;
|
|
1332
1352
|
timeout;
|
|
1333
|
-
stepType = "Wait";
|
|
1334
1353
|
allowUndefinedOut = false;
|
|
1335
|
-
constructor(stepName, eventId, timeout) {
|
|
1336
|
-
super(stepName);
|
|
1354
|
+
constructor(context, stepName, eventId, timeout) {
|
|
1355
|
+
super(context, stepName);
|
|
1337
1356
|
this.eventId = eventId;
|
|
1338
1357
|
this.timeout = timeout;
|
|
1339
1358
|
}
|
|
@@ -1358,13 +1377,6 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1358
1377
|
concurrent
|
|
1359
1378
|
});
|
|
1360
1379
|
}
|
|
1361
|
-
safeParseOut(out) {
|
|
1362
|
-
const result = JSON.parse(out);
|
|
1363
|
-
return {
|
|
1364
|
-
...result,
|
|
1365
|
-
eventData: BaseLazyStep.tryParsing(result.eventData)
|
|
1366
|
-
};
|
|
1367
|
-
}
|
|
1368
1380
|
getHeaders({ context, telemetry: telemetry2, invokeCount, step }) {
|
|
1369
1381
|
const headers = super.getHeaders({ context, telemetry: telemetry2, invokeCount, step });
|
|
1370
1382
|
headers.headers["Upstash-Workflow-CallType"] = "step";
|
|
@@ -1398,7 +1410,7 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1398
1410
|
timeoutHeaders,
|
|
1399
1411
|
step: {
|
|
1400
1412
|
stepId: step.stepId,
|
|
1401
|
-
stepType:
|
|
1413
|
+
stepType: this.stepType,
|
|
1402
1414
|
stepName: step.stepName,
|
|
1403
1415
|
concurrent: step.concurrent,
|
|
1404
1416
|
targetStep: step.targetStep
|
|
@@ -1419,8 +1431,8 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1419
1431
|
};
|
|
1420
1432
|
var LazyNotifyStep = class extends LazyFunctionStep {
|
|
1421
1433
|
stepType = "Notify";
|
|
1422
|
-
constructor(stepName, eventId, eventData, requester) {
|
|
1423
|
-
super(stepName, async () => {
|
|
1434
|
+
constructor(context, stepName, eventId, eventData, requester) {
|
|
1435
|
+
super(context, stepName, async () => {
|
|
1424
1436
|
const notifyResponse = await makeNotifyRequest(requester, eventId, eventData);
|
|
1425
1437
|
return {
|
|
1426
1438
|
eventId,
|
|
@@ -1445,28 +1457,10 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1445
1457
|
* workflow id of the invoked workflow
|
|
1446
1458
|
*/
|
|
1447
1459
|
workflowId;
|
|
1448
|
-
constructor(stepName, {
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
workflowRunId,
|
|
1453
|
-
retries,
|
|
1454
|
-
retryDelay,
|
|
1455
|
-
flowControl,
|
|
1456
|
-
stringifyBody = true
|
|
1457
|
-
}) {
|
|
1458
|
-
super(stepName);
|
|
1459
|
-
this.params = {
|
|
1460
|
-
workflow,
|
|
1461
|
-
body,
|
|
1462
|
-
headers,
|
|
1463
|
-
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1464
|
-
retries,
|
|
1465
|
-
retryDelay,
|
|
1466
|
-
flowControl,
|
|
1467
|
-
stringifyBody
|
|
1468
|
-
};
|
|
1469
|
-
const { workflowId } = workflow;
|
|
1460
|
+
constructor(context, stepName, params) {
|
|
1461
|
+
super(context, stepName);
|
|
1462
|
+
this.params = params;
|
|
1463
|
+
const { workflowId } = params.workflow;
|
|
1470
1464
|
if (!workflowId) {
|
|
1471
1465
|
throw new WorkflowError("You can only invoke workflow which has a workflowId");
|
|
1472
1466
|
}
|
|
@@ -1506,31 +1500,18 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1506
1500
|
workflowConfig: {
|
|
1507
1501
|
workflowRunId: context.workflowRunId,
|
|
1508
1502
|
workflowUrl: context.url,
|
|
1509
|
-
failureUrl: context.failureUrl,
|
|
1510
|
-
retries: context.retries,
|
|
1511
|
-
retryDelay: context.retryDelay,
|
|
1512
1503
|
telemetry: telemetry2,
|
|
1513
|
-
flowControl: context.flowControl,
|
|
1514
1504
|
useJSONContent: false
|
|
1515
1505
|
},
|
|
1516
1506
|
userHeaders: context.headers,
|
|
1517
1507
|
invokeCount
|
|
1518
1508
|
});
|
|
1509
|
+
context.qstashClient.http.headers?.forEach((value, key) => {
|
|
1510
|
+
invokerHeaders[key] = value;
|
|
1511
|
+
});
|
|
1519
1512
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1520
|
-
let invokeBody;
|
|
1521
|
-
if (this.params.stringifyBody) {
|
|
1522
|
-
invokeBody = JSON.stringify(this.params.body);
|
|
1523
|
-
} else {
|
|
1524
|
-
if (typeof this.params.body === "string") {
|
|
1525
|
-
invokeBody = this.params.body;
|
|
1526
|
-
} else {
|
|
1527
|
-
throw new WorkflowError(
|
|
1528
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your invoke step."
|
|
1529
|
-
);
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
1513
|
const request = {
|
|
1533
|
-
body:
|
|
1514
|
+
body: stringifyBody(this.params.body),
|
|
1534
1515
|
headers: Object.fromEntries(
|
|
1535
1516
|
Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
|
|
1536
1517
|
),
|
|
@@ -1541,34 +1522,19 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1541
1522
|
return JSON.stringify(request);
|
|
1542
1523
|
}
|
|
1543
1524
|
getHeaders({ context, telemetry: telemetry2, invokeCount }) {
|
|
1544
|
-
const {
|
|
1545
|
-
workflow,
|
|
1546
|
-
headers = {},
|
|
1547
|
-
workflowRunId = getWorkflowRunId(),
|
|
1548
|
-
retries,
|
|
1549
|
-
retryDelay,
|
|
1550
|
-
flowControl
|
|
1551
|
-
} = this.params;
|
|
1525
|
+
const { workflow, headers = {}, workflowRunId, retries, retryDelay, flowControl } = this.params;
|
|
1552
1526
|
const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
|
|
1553
|
-
const {
|
|
1554
|
-
retries: workflowRetries,
|
|
1555
|
-
retryDelay: workflowRetryDelay,
|
|
1556
|
-
failureFunction,
|
|
1557
|
-
failureUrl,
|
|
1558
|
-
useJSONContent,
|
|
1559
|
-
flowControl: workflowFlowControl
|
|
1560
|
-
} = workflow.options;
|
|
1561
1527
|
const { headers: triggerHeaders, contentType } = getHeaders({
|
|
1562
1528
|
initHeaderValue: "true",
|
|
1563
1529
|
workflowConfig: {
|
|
1564
|
-
workflowRunId,
|
|
1530
|
+
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1565
1531
|
workflowUrl: newUrl,
|
|
1566
|
-
retries
|
|
1567
|
-
retryDelay
|
|
1532
|
+
retries,
|
|
1533
|
+
retryDelay,
|
|
1568
1534
|
telemetry: telemetry2,
|
|
1569
|
-
failureUrl:
|
|
1570
|
-
flowControl
|
|
1571
|
-
useJSONContent: useJSONContent ?? false
|
|
1535
|
+
failureUrl: newUrl,
|
|
1536
|
+
flowControl,
|
|
1537
|
+
useJSONContent: workflow.useJSONContent ?? false
|
|
1572
1538
|
},
|
|
1573
1539
|
invokeCount: invokeCount + 1,
|
|
1574
1540
|
userHeaders: new Headers(headers)
|
|
@@ -1587,6 +1553,88 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1587
1553
|
return [result];
|
|
1588
1554
|
}
|
|
1589
1555
|
};
|
|
1556
|
+
var LazyCreateWebhookStep = class extends BaseLazyStep {
|
|
1557
|
+
stepType = "CreateWebhook";
|
|
1558
|
+
allowUndefinedOut = false;
|
|
1559
|
+
getPlanStep(concurrent, targetStep) {
|
|
1560
|
+
return {
|
|
1561
|
+
stepId: 0,
|
|
1562
|
+
stepName: this.stepName,
|
|
1563
|
+
stepType: this.stepType,
|
|
1564
|
+
concurrent,
|
|
1565
|
+
targetStep
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
async getResultStep(concurrent, stepId) {
|
|
1569
|
+
return {
|
|
1570
|
+
stepId,
|
|
1571
|
+
stepName: this.stepName,
|
|
1572
|
+
stepType: this.stepType,
|
|
1573
|
+
out: void 0,
|
|
1574
|
+
concurrent
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
getBody({ step, context }) {
|
|
1578
|
+
const userId = getUserIdFromToken(context.qstashClient);
|
|
1579
|
+
const workflowRunId = context.workflowRunId;
|
|
1580
|
+
const eventId = getEventId();
|
|
1581
|
+
const qstashUrl = getQStashUrl(this.context.qstashClient);
|
|
1582
|
+
return JSON.stringify({
|
|
1583
|
+
...step,
|
|
1584
|
+
out: JSON.stringify({
|
|
1585
|
+
webhookUrl: `${qstashUrl}/v2/workflows/hooks/${userId}/${workflowRunId}/${eventId}`,
|
|
1586
|
+
eventId
|
|
1587
|
+
})
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
var LazyWaitForWebhookStep = class extends LazyWaitEventStep {
|
|
1592
|
+
stepType = "WaitForWebhook";
|
|
1593
|
+
allowUndefinedOut = true;
|
|
1594
|
+
constructor(context, stepName, webhook, timeout) {
|
|
1595
|
+
super(context, stepName, webhook.eventId, timeout);
|
|
1596
|
+
}
|
|
1597
|
+
safeParseOut(out) {
|
|
1598
|
+
const eventData = decodeBase64(out);
|
|
1599
|
+
const parsedEventData = BaseLazyStep.tryParsing(eventData);
|
|
1600
|
+
const body = parsedEventData.body;
|
|
1601
|
+
const parsedBody = typeof body === "string" ? decodeBase64(body) : void 0;
|
|
1602
|
+
const request = new Request(
|
|
1603
|
+
`${parsedEventData.proto}://${parsedEventData.host}${parsedEventData.url}`,
|
|
1604
|
+
{
|
|
1605
|
+
method: parsedEventData.method,
|
|
1606
|
+
headers: parsedEventData.header,
|
|
1607
|
+
body: parsedBody
|
|
1608
|
+
}
|
|
1609
|
+
);
|
|
1610
|
+
return {
|
|
1611
|
+
request,
|
|
1612
|
+
timeout: false
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
handleUndefinedOut() {
|
|
1616
|
+
return {
|
|
1617
|
+
timeout: true,
|
|
1618
|
+
request: void 0
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
var LazyWaitForEventStep = class extends LazyWaitEventStep {
|
|
1623
|
+
stepType = "Wait";
|
|
1624
|
+
allowUndefinedOut = true;
|
|
1625
|
+
parseWaitForEventOut(out, waitTimeout) {
|
|
1626
|
+
return {
|
|
1627
|
+
eventData: out ? BaseLazyStep.tryParsing(decodeBase64(out)) : void 0,
|
|
1628
|
+
timeout: waitTimeout ?? false
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
safeParseOut(out, step) {
|
|
1632
|
+
return this.parseWaitForEventOut(out, step.waitTimeout);
|
|
1633
|
+
}
|
|
1634
|
+
handleUndefinedOut(step) {
|
|
1635
|
+
return this.parseWaitForEventOut(void 0, step.waitTimeout);
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1590
1638
|
|
|
1591
1639
|
// src/qstash/headers.ts
|
|
1592
1640
|
var WorkflowHeaders = class {
|
|
@@ -1596,14 +1644,15 @@ var WorkflowHeaders = class {
|
|
|
1596
1644
|
initHeaderValue;
|
|
1597
1645
|
stepInfo;
|
|
1598
1646
|
headers;
|
|
1599
|
-
|
|
1647
|
+
/**
|
|
1648
|
+
* @param params workflow header parameters
|
|
1649
|
+
*/
|
|
1600
1650
|
constructor({
|
|
1601
1651
|
userHeaders,
|
|
1602
1652
|
workflowConfig,
|
|
1603
1653
|
invokeCount,
|
|
1604
1654
|
initHeaderValue,
|
|
1605
|
-
stepInfo
|
|
1606
|
-
keepTriggerConfig
|
|
1655
|
+
stepInfo
|
|
1607
1656
|
}) {
|
|
1608
1657
|
this.userHeaders = userHeaders;
|
|
1609
1658
|
this.workflowConfig = workflowConfig;
|
|
@@ -1615,7 +1664,6 @@ var WorkflowHeaders = class {
|
|
|
1615
1664
|
workflowHeaders: {},
|
|
1616
1665
|
failureHeaders: {}
|
|
1617
1666
|
};
|
|
1618
|
-
this.keepTriggerConfig = keepTriggerConfig;
|
|
1619
1667
|
}
|
|
1620
1668
|
getHeaders() {
|
|
1621
1669
|
this.addBaseHeaders();
|
|
@@ -1634,7 +1682,7 @@ var WorkflowHeaders = class {
|
|
|
1634
1682
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1635
1683
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1636
1684
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1637
|
-
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger
|
|
1685
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1638
1686
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1639
1687
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1640
1688
|
};
|
|
@@ -1704,12 +1752,12 @@ var WorkflowHeaders = class {
|
|
|
1704
1752
|
}
|
|
1705
1753
|
this.headers.workflowHeaders["Failure-Callback"] = this.workflowConfig.failureUrl;
|
|
1706
1754
|
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
|
|
1707
|
-
this.headers.failureHeaders[`Forward
|
|
1755
|
+
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_CALLBACK_HEADER}`] = "true";
|
|
1708
1756
|
this.headers.failureHeaders["Workflow-Runid"] = this.workflowConfig.workflowRunId;
|
|
1709
1757
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1710
1758
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1711
1759
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1712
|
-
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger";
|
|
1760
|
+
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
1713
1761
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1714
1762
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1715
1763
|
}
|
|
@@ -1779,14 +1827,13 @@ var submitParallelSteps = async ({
|
|
|
1779
1827
|
initialStepCount,
|
|
1780
1828
|
invokeCount,
|
|
1781
1829
|
telemetry: telemetry2,
|
|
1782
|
-
|
|
1830
|
+
dispatchDebug
|
|
1783
1831
|
}) => {
|
|
1784
1832
|
const planSteps = steps.map(
|
|
1785
1833
|
(step, index) => step.getPlanStep(steps.length, initialStepCount + index)
|
|
1786
1834
|
);
|
|
1787
|
-
await
|
|
1788
|
-
|
|
1789
|
-
steps: planSteps
|
|
1835
|
+
await dispatchDebug("onInfo", {
|
|
1836
|
+
info: `Submitting ${planSteps.length} parallel steps.`
|
|
1790
1837
|
});
|
|
1791
1838
|
const result = await context.qstashClient.batch(
|
|
1792
1839
|
planSteps.map((planStep) => {
|
|
@@ -1795,10 +1842,6 @@ var submitParallelSteps = async ({
|
|
|
1795
1842
|
workflowConfig: {
|
|
1796
1843
|
workflowRunId: context.workflowRunId,
|
|
1797
1844
|
workflowUrl: context.url,
|
|
1798
|
-
failureUrl: context.failureUrl,
|
|
1799
|
-
retries: context.retries,
|
|
1800
|
-
retryDelay: context.retryDelay,
|
|
1801
|
-
flowControl: context.flowControl,
|
|
1802
1845
|
telemetry: telemetry2
|
|
1803
1846
|
},
|
|
1804
1847
|
userHeaders: context.headers,
|
|
@@ -1814,13 +1857,11 @@ var submitParallelSteps = async ({
|
|
|
1814
1857
|
};
|
|
1815
1858
|
})
|
|
1816
1859
|
);
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
})
|
|
1823
|
-
});
|
|
1860
|
+
if (result && result.length > 0) {
|
|
1861
|
+
await dispatchDebug("onInfo", {
|
|
1862
|
+
info: `Submitted ${planSteps.length} parallel steps. messageIds: ${result.filter((r) => r).map((r) => r.messageId).join(", ")}.`
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1824
1865
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
1825
1866
|
};
|
|
1826
1867
|
var submitSingleStep = async ({
|
|
@@ -1830,14 +1871,13 @@ var submitSingleStep = async ({
|
|
|
1830
1871
|
invokeCount,
|
|
1831
1872
|
concurrency,
|
|
1832
1873
|
telemetry: telemetry2,
|
|
1833
|
-
|
|
1874
|
+
dispatchDebug,
|
|
1875
|
+
dispatchLifecycle
|
|
1834
1876
|
}) => {
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
fromRequest: false,
|
|
1838
|
-
step: resultStep,
|
|
1839
|
-
stepCount: stepId
|
|
1877
|
+
await dispatchLifecycle("beforeExecution", {
|
|
1878
|
+
stepName: lazyStep.stepName
|
|
1840
1879
|
});
|
|
1880
|
+
const resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
1841
1881
|
const { headers } = lazyStep.getHeaders({
|
|
1842
1882
|
context,
|
|
1843
1883
|
step: resultStep,
|
|
@@ -1851,10 +1891,6 @@ var submitSingleStep = async ({
|
|
|
1851
1891
|
invokeCount,
|
|
1852
1892
|
telemetry: telemetry2
|
|
1853
1893
|
});
|
|
1854
|
-
await debug?.log("SUBMIT", "SUBMIT_STEP", {
|
|
1855
|
-
length: 1,
|
|
1856
|
-
steps: [resultStep]
|
|
1857
|
-
});
|
|
1858
1894
|
const submitResult = await lazyStep.submitStep({
|
|
1859
1895
|
context,
|
|
1860
1896
|
body,
|
|
@@ -1864,13 +1900,11 @@ var submitSingleStep = async ({
|
|
|
1864
1900
|
step: resultStep,
|
|
1865
1901
|
telemetry: telemetry2
|
|
1866
1902
|
});
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
})
|
|
1873
|
-
});
|
|
1903
|
+
if (submitResult && submitResult[0]) {
|
|
1904
|
+
await dispatchDebug("onInfo", {
|
|
1905
|
+
info: `Submitted step "${resultStep.stepName}" with messageId: ${submitResult[0].messageId}.`
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1874
1908
|
return resultStep;
|
|
1875
1909
|
};
|
|
1876
1910
|
|
|
@@ -1879,21 +1913,31 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1879
1913
|
context;
|
|
1880
1914
|
promises = /* @__PURE__ */ new WeakMap();
|
|
1881
1915
|
activeLazyStepList;
|
|
1882
|
-
debug;
|
|
1883
1916
|
nonPlanStepCount;
|
|
1884
1917
|
steps;
|
|
1885
1918
|
indexInCurrentList = 0;
|
|
1886
1919
|
invokeCount;
|
|
1887
1920
|
telemetry;
|
|
1921
|
+
dispatchDebug;
|
|
1922
|
+
dispatchLifecycle;
|
|
1888
1923
|
stepCount = 0;
|
|
1889
1924
|
planStepCount = 0;
|
|
1890
1925
|
executingStep = false;
|
|
1891
|
-
|
|
1926
|
+
/**
|
|
1927
|
+
* @param context workflow context
|
|
1928
|
+
* @param steps list of steps
|
|
1929
|
+
* @param dispatchDebug debug event dispatcher
|
|
1930
|
+
* @param dispatchLifecycle lifecycle event dispatcher
|
|
1931
|
+
* @param telemetry optional telemetry information
|
|
1932
|
+
* @param invokeCount optional invoke count
|
|
1933
|
+
*/
|
|
1934
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry2, invokeCount) {
|
|
1892
1935
|
this.context = context;
|
|
1893
1936
|
this.steps = steps;
|
|
1937
|
+
this.dispatchDebug = dispatchDebug;
|
|
1938
|
+
this.dispatchLifecycle = dispatchLifecycle;
|
|
1894
1939
|
this.telemetry = telemetry2;
|
|
1895
1940
|
this.invokeCount = invokeCount ?? 0;
|
|
1896
|
-
this.debug = debug;
|
|
1897
1941
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
1898
1942
|
}
|
|
1899
1943
|
/**
|
|
@@ -1961,7 +2005,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1961
2005
|
/**
|
|
1962
2006
|
* Executes a step:
|
|
1963
2007
|
* - If the step result is available in the steps, returns the result
|
|
1964
|
-
* - If the result is not
|
|
2008
|
+
* - If the result is not available, runs the function
|
|
1965
2009
|
* - Sends the result to QStash
|
|
1966
2010
|
*
|
|
1967
2011
|
* @param lazyStep lazy step to execute
|
|
@@ -1971,12 +2015,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1971
2015
|
if (this.stepCount < this.nonPlanStepCount) {
|
|
1972
2016
|
const step = this.steps[this.stepCount + this.planStepCount];
|
|
1973
2017
|
validateStep(lazyStep, step);
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
2018
|
+
const parsedOut = lazyStep.parseOut(step);
|
|
2019
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2020
|
+
if (isLastMemoized) {
|
|
2021
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2022
|
+
stepName: lazyStep.stepName,
|
|
2023
|
+
result: parsedOut
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
return parsedOut;
|
|
1980
2027
|
}
|
|
1981
2028
|
const resultStep = await submitSingleStep({
|
|
1982
2029
|
context: this.context,
|
|
@@ -1985,15 +2032,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1985
2032
|
invokeCount: this.invokeCount,
|
|
1986
2033
|
concurrency: 1,
|
|
1987
2034
|
telemetry: this.telemetry,
|
|
1988
|
-
|
|
2035
|
+
dispatchDebug: this.dispatchDebug,
|
|
2036
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
1989
2037
|
});
|
|
1990
2038
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
1991
2039
|
}
|
|
1992
2040
|
/**
|
|
1993
2041
|
* Runs steps in parallel.
|
|
1994
2042
|
*
|
|
1995
|
-
* @param
|
|
1996
|
-
* @param stepFunctions list of async functions to run in parallel
|
|
2043
|
+
* @param parallelSteps list of lazy steps to run in parallel
|
|
1997
2044
|
* @returns results of the functions run in parallel
|
|
1998
2045
|
*/
|
|
1999
2046
|
async runParallel(parallelSteps) {
|
|
@@ -2006,12 +2053,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2006
2053
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
2007
2054
|
);
|
|
2008
2055
|
}
|
|
2009
|
-
await this.
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2056
|
+
await this.dispatchDebug("onInfo", {
|
|
2057
|
+
info: `Executing parallel steps with: ` + JSON.stringify({
|
|
2058
|
+
parallelCallState,
|
|
2059
|
+
initialStepCount,
|
|
2060
|
+
plannedParallelStepCount,
|
|
2061
|
+
stepCount: this.stepCount,
|
|
2062
|
+
planStepCount: this.planStepCount
|
|
2063
|
+
})
|
|
2015
2064
|
});
|
|
2016
2065
|
switch (parallelCallState) {
|
|
2017
2066
|
case "first": {
|
|
@@ -2021,7 +2070,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2021
2070
|
initialStepCount,
|
|
2022
2071
|
invokeCount: this.invokeCount,
|
|
2023
2072
|
telemetry: this.telemetry,
|
|
2024
|
-
|
|
2073
|
+
dispatchDebug: this.dispatchDebug
|
|
2025
2074
|
});
|
|
2026
2075
|
break;
|
|
2027
2076
|
}
|
|
@@ -2043,7 +2092,8 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2043
2092
|
invokeCount: this.invokeCount,
|
|
2044
2093
|
concurrency: parallelSteps.length,
|
|
2045
2094
|
telemetry: this.telemetry,
|
|
2046
|
-
|
|
2095
|
+
dispatchDebug: this.dispatchDebug,
|
|
2096
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2047
2097
|
});
|
|
2048
2098
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2049
2099
|
} catch (error) {
|
|
@@ -2057,13 +2107,34 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2057
2107
|
break;
|
|
2058
2108
|
}
|
|
2059
2109
|
case "discard": {
|
|
2110
|
+
const resultStep = this.steps.at(-1);
|
|
2111
|
+
const lazyStep = parallelSteps.find(
|
|
2112
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2113
|
+
);
|
|
2114
|
+
if (lazyStep) {
|
|
2115
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2116
|
+
stepName: lazyStep.stepName,
|
|
2117
|
+
result: lazyStep.parseOut(resultStep)
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2060
2120
|
throw new WorkflowAbort("discarded parallel");
|
|
2061
2121
|
}
|
|
2062
2122
|
case "last": {
|
|
2063
2123
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
2064
2124
|
validateParallelSteps(parallelSteps, parallelResultSteps);
|
|
2125
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2126
|
+
if (isLastMemoized) {
|
|
2127
|
+
const resultStep = this.steps.at(-1);
|
|
2128
|
+
const lazyStep = parallelSteps.find(
|
|
2129
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2130
|
+
);
|
|
2131
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2132
|
+
stepName: lazyStep.stepName,
|
|
2133
|
+
result: lazyStep.parseOut(resultStep)
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2065
2136
|
return parallelResultSteps.map(
|
|
2066
|
-
(step, index) => parallelSteps[index].parseOut(step
|
|
2137
|
+
(step, index) => parallelSteps[index].parseOut(step)
|
|
2067
2138
|
);
|
|
2068
2139
|
}
|
|
2069
2140
|
}
|
|
@@ -2119,7 +2190,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2119
2190
|
* @param index index of the current step
|
|
2120
2191
|
* @returns result[index] if lazyStepList > 1, otherwise result
|
|
2121
2192
|
*/
|
|
2122
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
2123
2193
|
static getResult(lazyStepList, result, index) {
|
|
2124
2194
|
if (lazyStepList.length === 1) {
|
|
2125
2195
|
return result;
|
|
@@ -2206,15 +2276,18 @@ var getProviderInfo = (api) => {
|
|
|
2206
2276
|
// src/context/api/base.ts
|
|
2207
2277
|
var BaseWorkflowApi = class {
|
|
2208
2278
|
context;
|
|
2279
|
+
/**
|
|
2280
|
+
* @param context workflow context
|
|
2281
|
+
*/
|
|
2209
2282
|
constructor({ context }) {
|
|
2210
2283
|
this.context = context;
|
|
2211
2284
|
}
|
|
2212
2285
|
/**
|
|
2213
2286
|
* context.call which uses a QStash API
|
|
2214
2287
|
*
|
|
2215
|
-
* @param stepName
|
|
2216
|
-
* @param settings
|
|
2217
|
-
* @returns
|
|
2288
|
+
* @param stepName name of the step
|
|
2289
|
+
* @param settings call settings including api configuration
|
|
2290
|
+
* @returns call response
|
|
2218
2291
|
*/
|
|
2219
2292
|
async callApi(stepName, settings) {
|
|
2220
2293
|
const { url, appendHeaders, method } = getProviderInfo(settings.api);
|
|
@@ -2222,7 +2295,7 @@ var BaseWorkflowApi = class {
|
|
|
2222
2295
|
return await this.context.call(stepName, {
|
|
2223
2296
|
url,
|
|
2224
2297
|
method: userMethod ?? method,
|
|
2225
|
-
body,
|
|
2298
|
+
body: typeof body === "string" ? body : JSON.stringify(body),
|
|
2226
2299
|
headers: {
|
|
2227
2300
|
...appendHeaders,
|
|
2228
2301
|
...headers
|
|
@@ -2374,6 +2447,129 @@ var getNewUrlFromWorkflowId = (url, workflowId) => {
|
|
|
2374
2447
|
return url.replace(/[^/]+$/, workflowId);
|
|
2375
2448
|
};
|
|
2376
2449
|
|
|
2450
|
+
// src/middleware/default-callbacks.ts
|
|
2451
|
+
var onErrorWithConsole = async ({ workflowRunId, error }) => {
|
|
2452
|
+
console.error(` [Upstash Workflow]: Error in workflow run ${workflowRunId}: ` + error);
|
|
2453
|
+
};
|
|
2454
|
+
var onWarningWithConsole = async ({ workflowRunId, warning }) => {
|
|
2455
|
+
console.warn(` [Upstash Workflow]: Warning in workflow run ${workflowRunId}: ` + warning);
|
|
2456
|
+
};
|
|
2457
|
+
var onInfoWithConsole = async ({
|
|
2458
|
+
workflowRunId,
|
|
2459
|
+
info
|
|
2460
|
+
}) => {
|
|
2461
|
+
console.info(` [Upstash Workflow]: Info in workflow run ${workflowRunId}: ` + info);
|
|
2462
|
+
};
|
|
2463
|
+
|
|
2464
|
+
// src/middleware/manager.ts
|
|
2465
|
+
var MiddlewareManager = class {
|
|
2466
|
+
middlewares;
|
|
2467
|
+
workflowRunId;
|
|
2468
|
+
context;
|
|
2469
|
+
/**
|
|
2470
|
+
* @param middlewares list of workflow middlewares
|
|
2471
|
+
*/
|
|
2472
|
+
constructor(middlewares = []) {
|
|
2473
|
+
this.middlewares = middlewares;
|
|
2474
|
+
}
|
|
2475
|
+
/**
|
|
2476
|
+
* Assign workflow run ID - will be passed to debug events
|
|
2477
|
+
*
|
|
2478
|
+
* @param workflowRunId workflow run id to assign
|
|
2479
|
+
*/
|
|
2480
|
+
assignWorkflowRunId(workflowRunId) {
|
|
2481
|
+
this.workflowRunId = workflowRunId;
|
|
2482
|
+
}
|
|
2483
|
+
/**
|
|
2484
|
+
* Assign context - required for lifecycle events
|
|
2485
|
+
*
|
|
2486
|
+
* also assigns workflowRunId from context
|
|
2487
|
+
*
|
|
2488
|
+
* @param context workflow context to assign
|
|
2489
|
+
*/
|
|
2490
|
+
assignContext(context) {
|
|
2491
|
+
this.context = context;
|
|
2492
|
+
this.workflowRunId = context.workflowRunId;
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* Internal method to execute middlewares with common error handling logic
|
|
2496
|
+
*
|
|
2497
|
+
* @param event event name to dispatch
|
|
2498
|
+
* @param params event parameters
|
|
2499
|
+
*/
|
|
2500
|
+
async executeMiddlewares(event, params) {
|
|
2501
|
+
await Promise.all(this.middlewares.map((m) => m.ensureInit()));
|
|
2502
|
+
await Promise.all(
|
|
2503
|
+
this.middlewares.map(async (middleware) => {
|
|
2504
|
+
const callback = middleware.getCallback(event);
|
|
2505
|
+
if (callback) {
|
|
2506
|
+
try {
|
|
2507
|
+
await callback(params);
|
|
2508
|
+
} catch (error) {
|
|
2509
|
+
try {
|
|
2510
|
+
const onErrorCallback = middleware.getCallback("onError") ?? onErrorWithConsole;
|
|
2511
|
+
await onErrorCallback({
|
|
2512
|
+
workflowRunId: this.workflowRunId,
|
|
2513
|
+
error
|
|
2514
|
+
});
|
|
2515
|
+
} catch (onErrorError) {
|
|
2516
|
+
console.error(
|
|
2517
|
+
`Failed while executing "onError" of middleware "${middleware.name}", falling back to logging the error to console. Error: ${onErrorError}`
|
|
2518
|
+
);
|
|
2519
|
+
onErrorWithConsole({
|
|
2520
|
+
workflowRunId: this.workflowRunId,
|
|
2521
|
+
error
|
|
2522
|
+
});
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
})
|
|
2527
|
+
);
|
|
2528
|
+
if (event === "onError") {
|
|
2529
|
+
onErrorWithConsole({
|
|
2530
|
+
workflowRunId: this.workflowRunId,
|
|
2531
|
+
...params
|
|
2532
|
+
});
|
|
2533
|
+
} else if (event === "onWarning") {
|
|
2534
|
+
onWarningWithConsole({
|
|
2535
|
+
workflowRunId: this.workflowRunId,
|
|
2536
|
+
...params
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Dispatch a debug event (onError, onWarning, onInfo)
|
|
2542
|
+
*
|
|
2543
|
+
* @param event debug event name
|
|
2544
|
+
* @param params event parameters
|
|
2545
|
+
*/
|
|
2546
|
+
async dispatchDebug(event, params) {
|
|
2547
|
+
const paramsWithRunId = {
|
|
2548
|
+
...params,
|
|
2549
|
+
workflowRunId: this.workflowRunId
|
|
2550
|
+
};
|
|
2551
|
+
await this.executeMiddlewares(event, paramsWithRunId);
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* Dispatch a lifecycle event (beforeExecution, afterExecution, runStarted, runCompleted)
|
|
2555
|
+
*
|
|
2556
|
+
* @param event lifecycle event name
|
|
2557
|
+
* @param params event parameters
|
|
2558
|
+
*/
|
|
2559
|
+
async dispatchLifecycle(event, params) {
|
|
2560
|
+
if (!this.context) {
|
|
2561
|
+
throw new WorkflowError(
|
|
2562
|
+
`Something went wrong while calling middlewares. Lifecycle event "${event}" was called before assignContext.`
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2565
|
+
const paramsWithContext = {
|
|
2566
|
+
...params,
|
|
2567
|
+
context: this.context
|
|
2568
|
+
};
|
|
2569
|
+
await this.executeMiddlewares(event, paramsWithContext);
|
|
2570
|
+
}
|
|
2571
|
+
};
|
|
2572
|
+
|
|
2377
2573
|
// src/context/context.ts
|
|
2378
2574
|
var WorkflowContext = class {
|
|
2379
2575
|
executor;
|
|
@@ -2419,32 +2615,13 @@ var WorkflowContext = class {
|
|
|
2419
2615
|
*/
|
|
2420
2616
|
url;
|
|
2421
2617
|
/**
|
|
2422
|
-
*
|
|
2423
|
-
*
|
|
2424
|
-
* https://upstash.com/docs/qstash/features/callbacks#what-is-a-failure-callback
|
|
2618
|
+
* Payload of the request which started the workflow.
|
|
2425
2619
|
*
|
|
2426
|
-
*
|
|
2620
|
+
* To specify its type, you can define `serve` as follows:
|
|
2427
2621
|
*
|
|
2428
2622
|
* ```ts
|
|
2429
|
-
*
|
|
2430
|
-
*
|
|
2431
|
-
* ...
|
|
2432
|
-
* },
|
|
2433
|
-
* {
|
|
2434
|
-
* failureUrl: "new-url-value"
|
|
2435
|
-
* }
|
|
2436
|
-
* )
|
|
2437
|
-
* ```
|
|
2438
|
-
*/
|
|
2439
|
-
failureUrl;
|
|
2440
|
-
/**
|
|
2441
|
-
* Payload of the request which started the workflow.
|
|
2442
|
-
*
|
|
2443
|
-
* To specify its type, you can define `serve` as follows:
|
|
2444
|
-
*
|
|
2445
|
-
* ```ts
|
|
2446
|
-
* // set requestPayload type to MyPayload:
|
|
2447
|
-
* export const POST = serve<MyPayload>(
|
|
2623
|
+
* // set requestPayload type to MyPayload:
|
|
2624
|
+
* export const POST = serve<MyPayload>(
|
|
2448
2625
|
* async (context) => {
|
|
2449
2626
|
* ...
|
|
2450
2627
|
* }
|
|
@@ -2492,46 +2669,6 @@ var WorkflowContext = class {
|
|
|
2492
2669
|
* Default value is set to `process.env`.
|
|
2493
2670
|
*/
|
|
2494
2671
|
env;
|
|
2495
|
-
/**
|
|
2496
|
-
* Number of retries
|
|
2497
|
-
*/
|
|
2498
|
-
retries;
|
|
2499
|
-
/**
|
|
2500
|
-
* Delay between retries.
|
|
2501
|
-
*
|
|
2502
|
-
* By default, the `retryDelay` is exponential backoff.
|
|
2503
|
-
* More details can be found in: https://upstash.com/docs/qstash/features/retry.
|
|
2504
|
-
*
|
|
2505
|
-
* The `retryDelay` option allows you to customize the delay (in milliseconds) between retry attempts when message delivery fails.
|
|
2506
|
-
*
|
|
2507
|
-
* You can use mathematical expressions and the following built-in functions to calculate the delay dynamically.
|
|
2508
|
-
* The special variable `retried` represents the current retry attempt count (starting from 0).
|
|
2509
|
-
*
|
|
2510
|
-
* Supported functions:
|
|
2511
|
-
* - `pow`
|
|
2512
|
-
* - `sqrt`
|
|
2513
|
-
* - `abs`
|
|
2514
|
-
* - `exp`
|
|
2515
|
-
* - `floor`
|
|
2516
|
-
* - `ceil`
|
|
2517
|
-
* - `round`
|
|
2518
|
-
* - `min`
|
|
2519
|
-
* - `max`
|
|
2520
|
-
*
|
|
2521
|
-
* Examples of valid `retryDelay` values:
|
|
2522
|
-
* ```ts
|
|
2523
|
-
* 1000 // 1 second
|
|
2524
|
-
* 1000 * (1 + retried) // 1 second multiplied by the current retry attempt
|
|
2525
|
-
* pow(2, retried) // 2 to the power of the current retry attempt
|
|
2526
|
-
* max(10, pow(2, retried)) // The greater of 10 or 2^retried
|
|
2527
|
-
* ```
|
|
2528
|
-
*/
|
|
2529
|
-
retryDelay;
|
|
2530
|
-
/**
|
|
2531
|
-
* Settings for controlling the number of active requests
|
|
2532
|
-
* and number of requests per second with the same key.
|
|
2533
|
-
*/
|
|
2534
|
-
flowControl;
|
|
2535
2672
|
/**
|
|
2536
2673
|
* Label to apply to the workflow run.
|
|
2537
2674
|
*
|
|
@@ -2554,30 +2691,31 @@ var WorkflowContext = class {
|
|
|
2554
2691
|
headers,
|
|
2555
2692
|
steps,
|
|
2556
2693
|
url,
|
|
2557
|
-
failureUrl,
|
|
2558
|
-
debug,
|
|
2559
2694
|
initialPayload,
|
|
2560
2695
|
env,
|
|
2561
|
-
retries,
|
|
2562
|
-
retryDelay,
|
|
2563
2696
|
telemetry: telemetry2,
|
|
2564
2697
|
invokeCount,
|
|
2565
|
-
|
|
2566
|
-
|
|
2698
|
+
label,
|
|
2699
|
+
middlewareManager
|
|
2567
2700
|
}) {
|
|
2568
2701
|
this.qstashClient = qstashClient;
|
|
2569
2702
|
this.workflowRunId = workflowRunId;
|
|
2570
2703
|
this.steps = steps;
|
|
2571
2704
|
this.url = url;
|
|
2572
|
-
this.failureUrl = failureUrl;
|
|
2573
2705
|
this.headers = headers;
|
|
2574
2706
|
this.requestPayload = initialPayload;
|
|
2575
2707
|
this.env = env ?? {};
|
|
2576
|
-
this.retries = retries ?? DEFAULT_RETRIES;
|
|
2577
|
-
this.retryDelay = retryDelay;
|
|
2578
|
-
this.flowControl = flowControl;
|
|
2579
2708
|
this.label = label;
|
|
2580
|
-
|
|
2709
|
+
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2710
|
+
middlewareManagerInstance.assignContext(this);
|
|
2711
|
+
this.executor = new AutoExecutor(
|
|
2712
|
+
this,
|
|
2713
|
+
this.steps,
|
|
2714
|
+
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
2715
|
+
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
2716
|
+
telemetry2,
|
|
2717
|
+
invokeCount
|
|
2718
|
+
);
|
|
2581
2719
|
}
|
|
2582
2720
|
/**
|
|
2583
2721
|
* Executes a workflow step
|
|
@@ -2608,7 +2746,7 @@ var WorkflowContext = class {
|
|
|
2608
2746
|
*/
|
|
2609
2747
|
async run(stepName, stepFunction) {
|
|
2610
2748
|
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
2611
|
-
return await this.addStep(new LazyFunctionStep(stepName, wrappedStepFunction));
|
|
2749
|
+
return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
|
|
2612
2750
|
}
|
|
2613
2751
|
/**
|
|
2614
2752
|
* Stops the execution for the duration provided.
|
|
@@ -2622,7 +2760,7 @@ var WorkflowContext = class {
|
|
|
2622
2760
|
* @returns undefined
|
|
2623
2761
|
*/
|
|
2624
2762
|
async sleep(stepName, duration) {
|
|
2625
|
-
await this.addStep(new LazySleepStep(stepName, duration));
|
|
2763
|
+
await this.addStep(new LazySleepStep(this, stepName, duration));
|
|
2626
2764
|
}
|
|
2627
2765
|
/**
|
|
2628
2766
|
* Stops the execution until the date time provided.
|
|
@@ -2644,48 +2782,38 @@ var WorkflowContext = class {
|
|
|
2644
2782
|
datetime = typeof datetime === "string" ? new Date(datetime) : datetime;
|
|
2645
2783
|
time = Math.round(datetime.getTime() / 1e3);
|
|
2646
2784
|
}
|
|
2647
|
-
await this.addStep(new LazySleepUntilStep(stepName, time));
|
|
2785
|
+
await this.addStep(new LazySleepUntilStep(this, stepName, time));
|
|
2648
2786
|
}
|
|
2649
2787
|
async call(stepName, settings) {
|
|
2650
2788
|
let callStep;
|
|
2651
2789
|
if ("workflow" in settings) {
|
|
2652
2790
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
2653
|
-
|
|
2791
|
+
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2792
|
+
callStep = new LazyCallStep({
|
|
2793
|
+
context: this,
|
|
2654
2794
|
stepName,
|
|
2655
2795
|
url,
|
|
2656
|
-
"POST",
|
|
2657
|
-
|
|
2658
|
-
settings.headers || {},
|
|
2659
|
-
settings.retries || 0,
|
|
2660
|
-
settings.retryDelay,
|
|
2661
|
-
settings.timeout,
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
);
|
|
2796
|
+
method: "POST",
|
|
2797
|
+
body: stringBody,
|
|
2798
|
+
headers: settings.headers || {},
|
|
2799
|
+
retries: settings.retries || 0,
|
|
2800
|
+
retryDelay: settings.retryDelay,
|
|
2801
|
+
timeout: settings.timeout,
|
|
2802
|
+
flowControl: settings.flowControl
|
|
2803
|
+
});
|
|
2665
2804
|
} else {
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
method = "GET",
|
|
2669
|
-
body,
|
|
2670
|
-
headers = {},
|
|
2671
|
-
retries = 0,
|
|
2672
|
-
retryDelay,
|
|
2673
|
-
timeout,
|
|
2674
|
-
flowControl,
|
|
2675
|
-
stringifyBody = true
|
|
2676
|
-
} = settings;
|
|
2677
|
-
callStep = new LazyCallStep(
|
|
2805
|
+
callStep = new LazyCallStep({
|
|
2806
|
+
context: this,
|
|
2678
2807
|
stepName,
|
|
2679
|
-
url,
|
|
2680
|
-
method,
|
|
2681
|
-
body,
|
|
2682
|
-
headers,
|
|
2683
|
-
retries,
|
|
2684
|
-
retryDelay,
|
|
2685
|
-
timeout,
|
|
2686
|
-
flowControl
|
|
2687
|
-
|
|
2688
|
-
);
|
|
2808
|
+
url: settings.url,
|
|
2809
|
+
method: settings.method ?? "GET",
|
|
2810
|
+
body: settings.body,
|
|
2811
|
+
headers: settings.headers ?? {},
|
|
2812
|
+
retries: settings.retries ?? 0,
|
|
2813
|
+
retryDelay: settings.retryDelay,
|
|
2814
|
+
timeout: settings.timeout,
|
|
2815
|
+
flowControl: settings.flowControl
|
|
2816
|
+
});
|
|
2689
2817
|
}
|
|
2690
2818
|
return await this.addStep(callStep);
|
|
2691
2819
|
}
|
|
@@ -2726,7 +2854,9 @@ var WorkflowContext = class {
|
|
|
2726
2854
|
async waitForEvent(stepName, eventId, options = {}) {
|
|
2727
2855
|
const { timeout = "7d" } = options;
|
|
2728
2856
|
const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
|
|
2729
|
-
return await this.addStep(
|
|
2857
|
+
return await this.addStep(
|
|
2858
|
+
new LazyWaitForEventStep(this, stepName, eventId, timeoutStr)
|
|
2859
|
+
);
|
|
2730
2860
|
}
|
|
2731
2861
|
/**
|
|
2732
2862
|
* Notify workflow runs waiting for an event
|
|
@@ -2751,20 +2881,28 @@ var WorkflowContext = class {
|
|
|
2751
2881
|
*/
|
|
2752
2882
|
async notify(stepName, eventId, eventData) {
|
|
2753
2883
|
return await this.addStep(
|
|
2754
|
-
new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
|
|
2884
|
+
new LazyNotifyStep(this, stepName, eventId, eventData, this.qstashClient.http)
|
|
2755
2885
|
);
|
|
2756
2886
|
}
|
|
2757
2887
|
async invoke(stepName, settings) {
|
|
2758
|
-
return await this.addStep(
|
|
2888
|
+
return await this.addStep(
|
|
2889
|
+
new LazyInvokeStep(this, stepName, settings)
|
|
2890
|
+
);
|
|
2891
|
+
}
|
|
2892
|
+
async createWebhook(stepName) {
|
|
2893
|
+
return await this.addStep(new LazyCreateWebhookStep(this, stepName));
|
|
2894
|
+
}
|
|
2895
|
+
async waitForWebhook(stepName, webhook, timeout) {
|
|
2896
|
+
return await this.addStep(new LazyWaitForWebhookStep(this, stepName, webhook, timeout));
|
|
2759
2897
|
}
|
|
2760
2898
|
/**
|
|
2761
2899
|
* Cancel the current workflow run
|
|
2762
2900
|
*
|
|
2763
|
-
* Will throw
|
|
2901
|
+
* Will throw WorkflowCancelAbort to stop workflow execution.
|
|
2764
2902
|
* Shouldn't be inside try/catch.
|
|
2765
2903
|
*/
|
|
2766
2904
|
async cancel() {
|
|
2767
|
-
throw new
|
|
2905
|
+
throw new WorkflowCancelAbort();
|
|
2768
2906
|
}
|
|
2769
2907
|
/**
|
|
2770
2908
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
@@ -2780,74 +2918,25 @@ var WorkflowContext = class {
|
|
|
2780
2918
|
}
|
|
2781
2919
|
};
|
|
2782
2920
|
|
|
2783
|
-
// src/logger.ts
|
|
2784
|
-
var LOG_LEVELS = ["DEBUG", "INFO", "SUBMIT", "WARN", "ERROR"];
|
|
2785
|
-
var WorkflowLogger = class _WorkflowLogger {
|
|
2786
|
-
logs = [];
|
|
2787
|
-
options;
|
|
2788
|
-
workflowRunId = void 0;
|
|
2789
|
-
constructor(options) {
|
|
2790
|
-
this.options = options;
|
|
2791
|
-
}
|
|
2792
|
-
async log(level, eventType, details) {
|
|
2793
|
-
if (this.shouldLog(level)) {
|
|
2794
|
-
const timestamp = Date.now();
|
|
2795
|
-
const logEntry = {
|
|
2796
|
-
timestamp,
|
|
2797
|
-
workflowRunId: this.workflowRunId ?? "",
|
|
2798
|
-
logLevel: level,
|
|
2799
|
-
eventType,
|
|
2800
|
-
details
|
|
2801
|
-
};
|
|
2802
|
-
this.logs.push(logEntry);
|
|
2803
|
-
if (this.options.logOutput === "console") {
|
|
2804
|
-
this.writeToConsole(logEntry);
|
|
2805
|
-
}
|
|
2806
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
setWorkflowRunId(workflowRunId) {
|
|
2810
|
-
this.workflowRunId = workflowRunId;
|
|
2811
|
-
}
|
|
2812
|
-
writeToConsole(logEntry) {
|
|
2813
|
-
const JSON_SPACING = 2;
|
|
2814
|
-
const logMethod = logEntry.logLevel === "ERROR" ? console.error : logEntry.logLevel === "WARN" ? console.warn : console.log;
|
|
2815
|
-
logMethod(JSON.stringify(logEntry, void 0, JSON_SPACING));
|
|
2816
|
-
}
|
|
2817
|
-
shouldLog(level) {
|
|
2818
|
-
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
2819
|
-
}
|
|
2820
|
-
static getLogger(verbose) {
|
|
2821
|
-
if (typeof verbose === "object") {
|
|
2822
|
-
return verbose;
|
|
2823
|
-
} else {
|
|
2824
|
-
return verbose ? new _WorkflowLogger({
|
|
2825
|
-
logLevel: "INFO",
|
|
2826
|
-
logOutput: "console"
|
|
2827
|
-
}) : void 0;
|
|
2828
|
-
}
|
|
2829
|
-
}
|
|
2830
|
-
};
|
|
2831
|
-
|
|
2832
2921
|
// src/serve/authorization.ts
|
|
2833
2922
|
var import_qstash9 = require("@upstash/qstash");
|
|
2834
2923
|
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
2835
2924
|
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
2836
2925
|
disabled = true;
|
|
2837
2926
|
/**
|
|
2838
|
-
* overwrite the WorkflowContext.addStep method to always raise
|
|
2927
|
+
* overwrite the WorkflowContext.addStep method to always raise WorkflowAuthError
|
|
2839
2928
|
* error in order to stop the execution whenever we encounter a step.
|
|
2840
2929
|
*
|
|
2841
2930
|
* @param _step
|
|
2842
2931
|
*/
|
|
2843
2932
|
async addStep(_step) {
|
|
2844
|
-
throw new
|
|
2933
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
2845
2934
|
}
|
|
2846
2935
|
/**
|
|
2847
|
-
* overwrite cancel method to throw
|
|
2936
|
+
* overwrite cancel method to throw WorkflowAuthError with the disabledMessage
|
|
2848
2937
|
*/
|
|
2849
2938
|
async cancel() {
|
|
2850
|
-
throw new
|
|
2939
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
2851
2940
|
}
|
|
2852
2941
|
/**
|
|
2853
2942
|
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
@@ -2870,18 +2959,14 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
2870
2959
|
headers: context.headers,
|
|
2871
2960
|
steps: [],
|
|
2872
2961
|
url: context.url,
|
|
2873
|
-
failureUrl: context.failureUrl,
|
|
2874
2962
|
initialPayload: context.requestPayload,
|
|
2875
2963
|
env: context.env,
|
|
2876
|
-
retries: context.retries,
|
|
2877
|
-
retryDelay: context.retryDelay,
|
|
2878
|
-
flowControl: context.flowControl,
|
|
2879
2964
|
label: context.label
|
|
2880
2965
|
});
|
|
2881
2966
|
try {
|
|
2882
2967
|
await routeFunction(disabledContext);
|
|
2883
2968
|
} catch (error) {
|
|
2884
|
-
if (isInstanceOf(error,
|
|
2969
|
+
if (isInstanceOf(error, WorkflowAuthError) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
|
|
2885
2970
|
return ok("step-found");
|
|
2886
2971
|
}
|
|
2887
2972
|
console.warn(
|
|
@@ -2914,13 +2999,6 @@ var processRawSteps = (rawSteps) => {
|
|
|
2914
2999
|
const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
|
|
2915
3000
|
const otherSteps = stepsToDecode.map((rawStep) => {
|
|
2916
3001
|
const step = JSON.parse(decodeBase64(rawStep.body));
|
|
2917
|
-
if (step.waitEventId) {
|
|
2918
|
-
const newOut = {
|
|
2919
|
-
eventData: step.out ? decodeBase64(step.out) : void 0,
|
|
2920
|
-
timeout: step.waitTimeout ?? false
|
|
2921
|
-
};
|
|
2922
|
-
step.out = newOut;
|
|
2923
|
-
}
|
|
2924
3002
|
return step;
|
|
2925
3003
|
});
|
|
2926
3004
|
const steps = [initialStep, ...otherSteps];
|
|
@@ -2948,7 +3026,7 @@ var deduplicateSteps = (steps) => {
|
|
|
2948
3026
|
}
|
|
2949
3027
|
return deduplicatedSteps;
|
|
2950
3028
|
};
|
|
2951
|
-
var checkIfLastOneIsDuplicate = async (steps,
|
|
3029
|
+
var checkIfLastOneIsDuplicate = async (steps, dispatchDebug) => {
|
|
2952
3030
|
if (steps.length < 2) {
|
|
2953
3031
|
return false;
|
|
2954
3032
|
}
|
|
@@ -2959,14 +3037,41 @@ var checkIfLastOneIsDuplicate = async (steps, debug) => {
|
|
|
2959
3037
|
const step = steps[index];
|
|
2960
3038
|
if (step.stepId === lastStepId && step.targetStep === lastTargetStepId) {
|
|
2961
3039
|
const message = `Upstash Workflow: The step '${step.stepName}' with id '${step.stepId}' has run twice during workflow execution. Rest of the workflow will continue running as usual.`;
|
|
2962
|
-
await
|
|
2963
|
-
|
|
3040
|
+
await dispatchDebug?.("onWarning", {
|
|
3041
|
+
warning: message
|
|
3042
|
+
});
|
|
2964
3043
|
return true;
|
|
2965
3044
|
}
|
|
2966
3045
|
}
|
|
2967
3046
|
return false;
|
|
2968
3047
|
};
|
|
2969
3048
|
var validateRequest = (request) => {
|
|
3049
|
+
if (request.headers.get(WORKFLOW_UNKOWN_SDK_VERSION_HEADER)) {
|
|
3050
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
3051
|
+
if (!workflowRunId2) {
|
|
3052
|
+
throw new WorkflowError(
|
|
3053
|
+
"Couldn't get workflow id from header when handling unknown sdk request"
|
|
3054
|
+
);
|
|
3055
|
+
}
|
|
3056
|
+
return {
|
|
3057
|
+
unknownSdk: true,
|
|
3058
|
+
isFirstInvocation: true,
|
|
3059
|
+
workflowRunId: workflowRunId2
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
if (request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
3063
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
3064
|
+
if (!workflowRunId2) {
|
|
3065
|
+
throw new WorkflowError(
|
|
3066
|
+
"Couldn't get workflow id from header when handling failure callback request"
|
|
3067
|
+
);
|
|
3068
|
+
}
|
|
3069
|
+
return {
|
|
3070
|
+
unknownSdk: false,
|
|
3071
|
+
isFirstInvocation: true,
|
|
3072
|
+
workflowRunId: workflowRunId2
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
2970
3075
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
2971
3076
|
const isFirstInvocation = !versionHeader;
|
|
2972
3077
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
@@ -2980,11 +3085,20 @@ var validateRequest = (request) => {
|
|
|
2980
3085
|
}
|
|
2981
3086
|
return {
|
|
2982
3087
|
isFirstInvocation,
|
|
2983
|
-
workflowRunId
|
|
3088
|
+
workflowRunId,
|
|
3089
|
+
unknownSdk: false
|
|
2984
3090
|
};
|
|
2985
3091
|
};
|
|
2986
|
-
var parseRequest = async (
|
|
2987
|
-
|
|
3092
|
+
var parseRequest = async ({
|
|
3093
|
+
requestPayload,
|
|
3094
|
+
isFirstInvocation,
|
|
3095
|
+
unknownSdk,
|
|
3096
|
+
workflowRunId,
|
|
3097
|
+
requester,
|
|
3098
|
+
messageId,
|
|
3099
|
+
dispatchDebug
|
|
3100
|
+
}) => {
|
|
3101
|
+
if (isFirstInvocation && !unknownSdk) {
|
|
2988
3102
|
return {
|
|
2989
3103
|
rawInitialPayload: requestPayload ?? "",
|
|
2990
3104
|
steps: [],
|
|
@@ -2994,16 +3108,14 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
2994
3108
|
} else {
|
|
2995
3109
|
let rawSteps;
|
|
2996
3110
|
if (!requestPayload) {
|
|
2997
|
-
await
|
|
2998
|
-
"
|
|
2999
|
-
|
|
3000
|
-
"request payload is empty, steps will be fetched from QStash."
|
|
3001
|
-
);
|
|
3111
|
+
await dispatchDebug?.("onInfo", {
|
|
3112
|
+
info: "request payload is empty, steps will be fetched from QStash."
|
|
3113
|
+
});
|
|
3002
3114
|
const { steps: fetchedSteps, workflowRunEnded } = await getSteps(
|
|
3003
3115
|
requester,
|
|
3004
3116
|
workflowRunId,
|
|
3005
3117
|
messageId,
|
|
3006
|
-
|
|
3118
|
+
dispatchDebug
|
|
3007
3119
|
);
|
|
3008
3120
|
if (workflowRunEnded) {
|
|
3009
3121
|
return {
|
|
@@ -3018,7 +3130,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
3018
3130
|
rawSteps = JSON.parse(requestPayload);
|
|
3019
3131
|
}
|
|
3020
3132
|
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
3021
|
-
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps,
|
|
3133
|
+
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, dispatchDebug);
|
|
3022
3134
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
3023
3135
|
return {
|
|
3024
3136
|
rawInitialPayload,
|
|
@@ -3028,16 +3140,21 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
3028
3140
|
};
|
|
3029
3141
|
}
|
|
3030
3142
|
};
|
|
3031
|
-
var handleFailure = async (
|
|
3032
|
-
|
|
3143
|
+
var handleFailure = async ({
|
|
3144
|
+
request,
|
|
3145
|
+
requestPayload,
|
|
3146
|
+
qstashClient,
|
|
3147
|
+
initialPayloadParser,
|
|
3148
|
+
routeFunction,
|
|
3149
|
+
failureFunction,
|
|
3150
|
+
env,
|
|
3151
|
+
dispatchDebug
|
|
3152
|
+
}) => {
|
|
3153
|
+
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true" && !request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
3033
3154
|
return ok({ result: "not-failure-callback" });
|
|
3034
3155
|
}
|
|
3035
3156
|
if (!failureFunction) {
|
|
3036
|
-
return
|
|
3037
|
-
new WorkflowError(
|
|
3038
|
-
"Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
|
|
3039
|
-
)
|
|
3040
|
-
);
|
|
3157
|
+
return ok({ result: "failure-function-undefined" });
|
|
3041
3158
|
}
|
|
3042
3159
|
try {
|
|
3043
3160
|
const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
|
|
@@ -3065,23 +3182,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3065
3182
|
headers: userHeaders,
|
|
3066
3183
|
steps: [],
|
|
3067
3184
|
url,
|
|
3068
|
-
failureUrl: url,
|
|
3069
|
-
debug,
|
|
3070
3185
|
env,
|
|
3071
|
-
retries,
|
|
3072
|
-
retryDelay,
|
|
3073
|
-
flowControl,
|
|
3074
3186
|
telemetry: void 0,
|
|
3075
3187
|
// not going to make requests in authentication check
|
|
3076
|
-
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0
|
|
3188
|
+
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3189
|
+
middlewareManager: void 0
|
|
3077
3190
|
});
|
|
3078
3191
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3079
3192
|
routeFunction,
|
|
3080
3193
|
workflowContext
|
|
3081
3194
|
);
|
|
3082
3195
|
if (authCheck.isErr()) {
|
|
3083
|
-
await
|
|
3084
|
-
|
|
3196
|
+
await dispatchDebug?.("onError", {
|
|
3197
|
+
error: authCheck.error
|
|
3198
|
+
});
|
|
3199
|
+
return err(authCheck.error);
|
|
3085
3200
|
} else if (authCheck.value === "run-ended") {
|
|
3086
3201
|
return err(new WorkflowError("Not authorized to run the failure function."));
|
|
3087
3202
|
}
|
|
@@ -3092,74 +3207,309 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3092
3207
|
failHeaders: header,
|
|
3093
3208
|
failStack
|
|
3094
3209
|
});
|
|
3095
|
-
return ok({ result: "
|
|
3210
|
+
return ok({ result: "failure-function-executed", response: failureResponse });
|
|
3096
3211
|
} catch (error) {
|
|
3097
3212
|
return err(error);
|
|
3098
3213
|
}
|
|
3099
3214
|
};
|
|
3100
3215
|
|
|
3101
|
-
// src/serve/
|
|
3216
|
+
// src/serve/multi-region/handlers.ts
|
|
3102
3217
|
var import_qstash10 = require("@upstash/qstash");
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3218
|
+
|
|
3219
|
+
// src/serve/multi-region/utils.ts
|
|
3220
|
+
var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
|
|
3221
|
+
var getRegionFromEnvironment = (environment) => {
|
|
3222
|
+
const region = environment.QSTASH_REGION;
|
|
3223
|
+
return normalizeRegionHeader(region);
|
|
3224
|
+
};
|
|
3225
|
+
function readEnvironmentVariables(environmentVariables, environment, region) {
|
|
3226
|
+
const result = {};
|
|
3227
|
+
for (const variable of environmentVariables) {
|
|
3228
|
+
const key = region ? `${region}_${variable}` : variable;
|
|
3229
|
+
result[variable] = environment[key];
|
|
3230
|
+
}
|
|
3231
|
+
return result;
|
|
3232
|
+
}
|
|
3233
|
+
function readClientEnvironmentVariables(environment, region) {
|
|
3234
|
+
return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
|
|
3235
|
+
}
|
|
3236
|
+
function readReceiverEnvironmentVariables(environment, region) {
|
|
3237
|
+
return readEnvironmentVariables(
|
|
3238
|
+
["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
|
|
3239
|
+
environment,
|
|
3240
|
+
region
|
|
3108
3241
|
);
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3242
|
+
}
|
|
3243
|
+
function normalizeRegionHeader(region) {
|
|
3244
|
+
if (!region) {
|
|
3245
|
+
return void 0;
|
|
3246
|
+
}
|
|
3247
|
+
region = region.replaceAll("-", "_").toUpperCase();
|
|
3248
|
+
if (VALID_REGIONS.includes(region)) {
|
|
3249
|
+
return region;
|
|
3250
|
+
}
|
|
3251
|
+
console.warn(
|
|
3252
|
+
`[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
|
|
3253
|
+
", "
|
|
3254
|
+
)}.`
|
|
3255
|
+
);
|
|
3256
|
+
return void 0;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
// src/serve/multi-region/handlers.ts
|
|
3260
|
+
var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
|
|
3261
|
+
if (qstashHandlers.mode === "single-region") {
|
|
3262
|
+
return qstashHandlers.handlers;
|
|
3263
|
+
}
|
|
3264
|
+
let targetRegion;
|
|
3265
|
+
if (isFirstInvocation) {
|
|
3266
|
+
targetRegion = qstashHandlers.defaultRegion;
|
|
3267
|
+
} else {
|
|
3268
|
+
const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
|
|
3269
|
+
targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
|
|
3270
|
+
}
|
|
3271
|
+
const handler = qstashHandlers.handlers[targetRegion];
|
|
3272
|
+
if (!handler) {
|
|
3273
|
+
console.warn(
|
|
3274
|
+
`[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
|
|
3275
|
+
);
|
|
3276
|
+
return qstashHandlers.handlers[qstashHandlers.defaultRegion];
|
|
3277
|
+
}
|
|
3278
|
+
return handler;
|
|
3279
|
+
};
|
|
3280
|
+
var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
|
|
3281
|
+
const clientEnv = readClientEnvironmentVariables(environment, region);
|
|
3282
|
+
const client = new import_qstash10.Client({
|
|
3283
|
+
...clientOptions,
|
|
3284
|
+
baseUrl: clientEnv.QSTASH_URL,
|
|
3285
|
+
token: clientEnv.QSTASH_TOKEN
|
|
3286
|
+
});
|
|
3287
|
+
const receiver = getReceiver(environment, receiverConfig, region);
|
|
3288
|
+
return { client, receiver };
|
|
3289
|
+
};
|
|
3290
|
+
var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
|
|
3291
|
+
const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
|
|
3292
|
+
if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
|
|
3293
|
+
return {
|
|
3294
|
+
isMultiRegion: true,
|
|
3295
|
+
defaultRegion: getRegionFromEnvironment(environment),
|
|
3296
|
+
clientOptions: qstashClientOption
|
|
3297
|
+
};
|
|
3298
|
+
} else {
|
|
3299
|
+
return { isMultiRegion: false };
|
|
3300
|
+
}
|
|
3301
|
+
};
|
|
3302
|
+
var getQStashHandlers = ({
|
|
3303
|
+
environment,
|
|
3304
|
+
qstashClientOption,
|
|
3305
|
+
receiverConfig
|
|
3306
|
+
}) => {
|
|
3307
|
+
const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
|
|
3308
|
+
if (multiRegion.isMultiRegion) {
|
|
3309
|
+
const regions = ["US_EAST_1", "EU_CENTRAL_1"];
|
|
3310
|
+
const handlers = {};
|
|
3311
|
+
for (const region of regions) {
|
|
3312
|
+
try {
|
|
3313
|
+
handlers[region] = createRegionalHandler(
|
|
3314
|
+
environment,
|
|
3315
|
+
receiverConfig,
|
|
3316
|
+
region,
|
|
3317
|
+
multiRegion.clientOptions
|
|
3154
3318
|
);
|
|
3319
|
+
} catch (error) {
|
|
3320
|
+
console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
|
|
3155
3321
|
}
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3322
|
+
}
|
|
3323
|
+
return {
|
|
3324
|
+
mode: "multi-region",
|
|
3325
|
+
handlers,
|
|
3326
|
+
defaultRegion: multiRegion.defaultRegion
|
|
3327
|
+
};
|
|
3328
|
+
} else {
|
|
3329
|
+
return {
|
|
3330
|
+
mode: "single-region",
|
|
3331
|
+
handlers: {
|
|
3332
|
+
client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash10.Client({
|
|
3333
|
+
...qstashClientOption,
|
|
3334
|
+
baseUrl: environment.QSTASH_URL,
|
|
3335
|
+
token: environment.QSTASH_TOKEN
|
|
3336
|
+
}),
|
|
3337
|
+
receiver: getReceiver(environment, receiverConfig)
|
|
3338
|
+
}
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
var getReceiver = (environment, receiverConfig, region) => {
|
|
3343
|
+
if (typeof receiverConfig === "string") {
|
|
3344
|
+
if (receiverConfig === "set-to-undefined") {
|
|
3345
|
+
return void 0;
|
|
3346
|
+
}
|
|
3347
|
+
const receiverEnv = readReceiverEnvironmentVariables(environment, region);
|
|
3348
|
+
return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash10.Receiver({
|
|
3349
|
+
currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
|
|
3350
|
+
nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
|
|
3351
|
+
}) : void 0;
|
|
3352
|
+
} else {
|
|
3353
|
+
return receiverConfig;
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
var getQStashHandlerOptions = (...params) => {
|
|
3357
|
+
const handlers = getQStashHandlers(...params);
|
|
3358
|
+
return {
|
|
3359
|
+
qstashHandlers: handlers,
|
|
3360
|
+
defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
|
|
3361
|
+
defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
|
|
3362
|
+
};
|
|
3363
|
+
};
|
|
3364
|
+
|
|
3365
|
+
// src/middleware/middleware.ts
|
|
3366
|
+
var WorkflowMiddleware = class {
|
|
3367
|
+
name;
|
|
3368
|
+
initCallbacks;
|
|
3369
|
+
/**
|
|
3370
|
+
* Callback functions
|
|
3371
|
+
*
|
|
3372
|
+
* Initially set to undefined, will be populated after init is called
|
|
3373
|
+
*/
|
|
3374
|
+
middlewareCallbacks = void 0;
|
|
3375
|
+
constructor(parameters) {
|
|
3376
|
+
this.name = parameters.name;
|
|
3377
|
+
if ("init" in parameters) {
|
|
3378
|
+
this.initCallbacks = parameters.init;
|
|
3379
|
+
} else {
|
|
3380
|
+
this.middlewareCallbacks = parameters.callbacks;
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
async ensureInit() {
|
|
3384
|
+
if (!this.middlewareCallbacks) {
|
|
3385
|
+
if (!this.initCallbacks) {
|
|
3386
|
+
throw new WorkflowError(`Middleware "${this.name}" has no callbacks or init defined.`);
|
|
3387
|
+
}
|
|
3388
|
+
this.middlewareCallbacks = await this.initCallbacks();
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
/**
|
|
3392
|
+
* Gets a callback function by name.
|
|
3393
|
+
*
|
|
3394
|
+
* @param callback name of the callback to retrieve
|
|
3395
|
+
*/
|
|
3396
|
+
getCallback(callback) {
|
|
3397
|
+
return this.middlewareCallbacks?.[callback];
|
|
3398
|
+
}
|
|
3399
|
+
};
|
|
3400
|
+
|
|
3401
|
+
// src/middleware/logging.ts
|
|
3402
|
+
var loggingMiddleware = new WorkflowMiddleware({
|
|
3403
|
+
name: "logging",
|
|
3404
|
+
callbacks: {
|
|
3405
|
+
afterExecution(params) {
|
|
3406
|
+
const { context, ...rest } = params;
|
|
3407
|
+
console.log(" [Upstash Workflow]: Step executed:", {
|
|
3408
|
+
workflowRunId: context.workflowRunId,
|
|
3409
|
+
...rest
|
|
3410
|
+
});
|
|
3411
|
+
},
|
|
3412
|
+
beforeExecution(params) {
|
|
3413
|
+
const { context, ...rest } = params;
|
|
3414
|
+
console.log(" [Upstash Workflow]: Step execution started:", {
|
|
3415
|
+
workflowRunId: context.workflowRunId,
|
|
3416
|
+
...rest
|
|
3417
|
+
});
|
|
3418
|
+
},
|
|
3419
|
+
runStarted(params) {
|
|
3420
|
+
const { context, ...rest } = params;
|
|
3421
|
+
console.log(" [Upstash Workflow]: Workflow run started:", {
|
|
3422
|
+
workflowRunId: context.workflowRunId,
|
|
3423
|
+
...rest
|
|
3161
3424
|
});
|
|
3162
3425
|
},
|
|
3426
|
+
runCompleted(params) {
|
|
3427
|
+
const { context, ...rest } = params;
|
|
3428
|
+
console.log(" [Upstash Workflow]: Workflow run completed:", {
|
|
3429
|
+
workflowRunId: context.workflowRunId,
|
|
3430
|
+
...rest
|
|
3431
|
+
});
|
|
3432
|
+
},
|
|
3433
|
+
onError: onErrorWithConsole,
|
|
3434
|
+
onWarning: onWarningWithConsole,
|
|
3435
|
+
onInfo: onInfoWithConsole
|
|
3436
|
+
}
|
|
3437
|
+
});
|
|
3438
|
+
|
|
3439
|
+
// src/serve/options.ts
|
|
3440
|
+
var createResponseData = (workflowRunId, detailedFinishCondition) => {
|
|
3441
|
+
const baseHeaders = {
|
|
3442
|
+
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
3443
|
+
"Upstash-workflow-sdk": VERSION
|
|
3444
|
+
};
|
|
3445
|
+
if (detailedFinishCondition?.condition === "auth-fail") {
|
|
3446
|
+
return {
|
|
3447
|
+
text: JSON.stringify({
|
|
3448
|
+
message: AUTH_FAIL_MESSAGE,
|
|
3449
|
+
workflowRunId
|
|
3450
|
+
}),
|
|
3451
|
+
status: 400,
|
|
3452
|
+
headers: baseHeaders
|
|
3453
|
+
};
|
|
3454
|
+
} else if (detailedFinishCondition?.condition === "non-retryable-error") {
|
|
3455
|
+
return {
|
|
3456
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3457
|
+
status: 489,
|
|
3458
|
+
headers: {
|
|
3459
|
+
...baseHeaders,
|
|
3460
|
+
"Upstash-NonRetryable-Error": "true"
|
|
3461
|
+
}
|
|
3462
|
+
};
|
|
3463
|
+
} else if (detailedFinishCondition?.condition === "retry-after-error") {
|
|
3464
|
+
return {
|
|
3465
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3466
|
+
status: 429,
|
|
3467
|
+
headers: {
|
|
3468
|
+
...baseHeaders,
|
|
3469
|
+
"Retry-After": detailedFinishCondition.result.retryAfter.toString()
|
|
3470
|
+
}
|
|
3471
|
+
};
|
|
3472
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-executed") {
|
|
3473
|
+
return {
|
|
3474
|
+
text: JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
|
|
3475
|
+
status: 200,
|
|
3476
|
+
headers: baseHeaders
|
|
3477
|
+
};
|
|
3478
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-undefined") {
|
|
3479
|
+
return {
|
|
3480
|
+
text: JSON.stringify({
|
|
3481
|
+
workflowRunId,
|
|
3482
|
+
finishCondition: detailedFinishCondition.condition
|
|
3483
|
+
}),
|
|
3484
|
+
status: 200,
|
|
3485
|
+
headers: {
|
|
3486
|
+
...baseHeaders,
|
|
3487
|
+
"Upstash-Workflow-Failure-Callback-Notfound": "true"
|
|
3488
|
+
}
|
|
3489
|
+
};
|
|
3490
|
+
}
|
|
3491
|
+
return {
|
|
3492
|
+
text: JSON.stringify({
|
|
3493
|
+
workflowRunId,
|
|
3494
|
+
finishCondition: detailedFinishCondition.condition
|
|
3495
|
+
}),
|
|
3496
|
+
status: 200,
|
|
3497
|
+
headers: baseHeaders
|
|
3498
|
+
};
|
|
3499
|
+
};
|
|
3500
|
+
var processOptions = (options, internalOptions) => {
|
|
3501
|
+
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
3502
|
+
const {
|
|
3503
|
+
qstashHandlers,
|
|
3504
|
+
defaultClient: qstashClient,
|
|
3505
|
+
defaultReceiver: receiver
|
|
3506
|
+
} = getQStashHandlerOptions({
|
|
3507
|
+
environment,
|
|
3508
|
+
qstashClientOption: options?.qstashClient,
|
|
3509
|
+
receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
|
|
3510
|
+
});
|
|
3511
|
+
return {
|
|
3512
|
+
qstashClient,
|
|
3163
3513
|
initialPayloadParser: (initialRequest) => {
|
|
3164
3514
|
if (!initialRequest) {
|
|
3165
3515
|
return void 0;
|
|
@@ -3174,35 +3524,38 @@ var processOptions = (options) => {
|
|
|
3174
3524
|
throw error;
|
|
3175
3525
|
}
|
|
3176
3526
|
},
|
|
3177
|
-
receiver
|
|
3178
|
-
currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
|
|
3179
|
-
nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
|
|
3180
|
-
}) : void 0,
|
|
3527
|
+
receiver,
|
|
3181
3528
|
baseUrl: environment.UPSTASH_WORKFLOW_URL,
|
|
3182
3529
|
env: environment,
|
|
3183
|
-
retries: DEFAULT_RETRIES,
|
|
3184
|
-
useJSONContent: false,
|
|
3185
3530
|
disableTelemetry: false,
|
|
3186
|
-
|
|
3187
|
-
|
|
3531
|
+
...options,
|
|
3532
|
+
// merge middlewares
|
|
3533
|
+
middlewares: [options?.middlewares ?? [], options?.verbose ? [loggingMiddleware] : []].flat(),
|
|
3534
|
+
internal: {
|
|
3535
|
+
generateResponse: internalOptions?.generateResponse ?? ((responseData) => {
|
|
3536
|
+
return new Response(responseData.text, {
|
|
3537
|
+
status: responseData.status,
|
|
3538
|
+
headers: responseData.headers
|
|
3539
|
+
});
|
|
3540
|
+
}),
|
|
3541
|
+
useJSONContent: internalOptions?.useJSONContent ?? false,
|
|
3542
|
+
qstashHandlers
|
|
3543
|
+
}
|
|
3188
3544
|
};
|
|
3189
3545
|
};
|
|
3190
|
-
var determineUrls = async (request, url, baseUrl,
|
|
3546
|
+
var determineUrls = async (request, url, baseUrl, dispatchDebug) => {
|
|
3191
3547
|
const initialWorkflowUrl = url ?? request.url;
|
|
3192
3548
|
const workflowUrl = baseUrl ? initialWorkflowUrl.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, (_, matchedBaseUrl, path) => {
|
|
3193
3549
|
return baseUrl + (path || "");
|
|
3194
3550
|
}) : initialWorkflowUrl;
|
|
3195
3551
|
if (workflowUrl !== initialWorkflowUrl) {
|
|
3196
|
-
await
|
|
3197
|
-
|
|
3198
|
-
originalURL: initialWorkflowUrl,
|
|
3199
|
-
updatedURL: workflowUrl
|
|
3552
|
+
await dispatchDebug("onInfo", {
|
|
3553
|
+
info: `The workflow URL's base URL has been replaced with the provided baseUrl. Original URL: ${initialWorkflowUrl}, New URL: ${workflowUrl}`
|
|
3200
3554
|
});
|
|
3201
3555
|
}
|
|
3202
|
-
const workflowFailureUrl = failureFunction ? workflowUrl : failureUrl;
|
|
3203
3556
|
if (workflowUrl.includes("localhost")) {
|
|
3204
|
-
await
|
|
3205
|
-
|
|
3557
|
+
await dispatchDebug("onInfo", {
|
|
3558
|
+
info: `Workflow URL contains localhost. This can happen in local development, but shouldn't happen in production unless you have a route which contains localhost. Received: ${workflowUrl}`
|
|
3206
3559
|
});
|
|
3207
3560
|
}
|
|
3208
3561
|
if (!(workflowUrl.startsWith("http://") || workflowUrl.startsWith("https://"))) {
|
|
@@ -3211,205 +3564,224 @@ var determineUrls = async (request, url, baseUrl, failureFunction, failureUrl, d
|
|
|
3211
3564
|
);
|
|
3212
3565
|
}
|
|
3213
3566
|
return {
|
|
3214
|
-
workflowUrl
|
|
3215
|
-
workflowFailureUrl
|
|
3567
|
+
workflowUrl
|
|
3216
3568
|
};
|
|
3217
3569
|
};
|
|
3218
3570
|
var AUTH_FAIL_MESSAGE = `Failed to authenticate Workflow request. If this is unexpected, see the caveat https://upstash.com/docs/workflow/basics/caveats#avoid-non-deterministic-code-outside-context-run`;
|
|
3219
3571
|
|
|
3220
3572
|
// src/serve/index.ts
|
|
3221
|
-
var serveBase = (routeFunction, telemetry2, options) => {
|
|
3573
|
+
var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
|
|
3222
3574
|
const {
|
|
3223
|
-
qstashClient,
|
|
3224
|
-
onStepFinish,
|
|
3225
3575
|
initialPayloadParser,
|
|
3226
3576
|
url,
|
|
3227
|
-
verbose,
|
|
3228
|
-
receiver,
|
|
3229
|
-
failureUrl,
|
|
3230
3577
|
failureFunction,
|
|
3231
3578
|
baseUrl,
|
|
3232
3579
|
env,
|
|
3233
|
-
retries,
|
|
3234
|
-
retryDelay,
|
|
3235
|
-
useJSONContent,
|
|
3236
3580
|
disableTelemetry,
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
} = processOptions(options);
|
|
3581
|
+
middlewares,
|
|
3582
|
+
internal
|
|
3583
|
+
} = processOptions(options, internalOptions);
|
|
3240
3584
|
telemetry2 = disableTelemetry ? void 0 : telemetry2;
|
|
3241
|
-
const
|
|
3242
|
-
const handler = async (request) => {
|
|
3243
|
-
await
|
|
3244
|
-
|
|
3585
|
+
const { generateResponse: responseGenerator, useJSONContent } = internal;
|
|
3586
|
+
const handler = async (request, middlewareManager) => {
|
|
3587
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3588
|
+
info: `Received request for workflow execution.`
|
|
3589
|
+
});
|
|
3590
|
+
const { workflowUrl } = await determineUrls(
|
|
3245
3591
|
request,
|
|
3246
3592
|
url,
|
|
3247
3593
|
baseUrl,
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3594
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3595
|
+
);
|
|
3596
|
+
const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
|
|
3597
|
+
const regionHeader = request.headers.get("upstash-region");
|
|
3598
|
+
const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
|
|
3599
|
+
internal.qstashHandlers,
|
|
3600
|
+
regionHeader,
|
|
3601
|
+
isFirstInvocation
|
|
3251
3602
|
);
|
|
3252
3603
|
const requestPayload = await getPayload(request) ?? "";
|
|
3253
|
-
await verifyRequest(requestPayload, request.headers.get("upstash-signature"),
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3604
|
+
await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
|
|
3605
|
+
middlewareManager.assignWorkflowRunId(workflowRunId);
|
|
3606
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3607
|
+
info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
|
|
3608
|
+
});
|
|
3609
|
+
const { rawInitialPayload, steps, isLastDuplicate, workflowRunEnded } = await parseRequest({
|
|
3257
3610
|
requestPayload,
|
|
3258
3611
|
isFirstInvocation,
|
|
3612
|
+
unknownSdk,
|
|
3259
3613
|
workflowRunId,
|
|
3260
|
-
|
|
3261
|
-
request.headers.get("upstash-message-id"),
|
|
3262
|
-
|
|
3263
|
-
);
|
|
3614
|
+
requester: regionalClient.http,
|
|
3615
|
+
messageId: request.headers.get("upstash-message-id"),
|
|
3616
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3617
|
+
});
|
|
3264
3618
|
if (workflowRunEnded) {
|
|
3265
|
-
return
|
|
3266
|
-
|
|
3267
|
-
|
|
3619
|
+
return responseGenerator(
|
|
3620
|
+
createResponseData(workflowRunId, {
|
|
3621
|
+
condition: "workflow-already-ended"
|
|
3622
|
+
})
|
|
3623
|
+
);
|
|
3268
3624
|
}
|
|
3269
3625
|
if (isLastDuplicate) {
|
|
3270
|
-
return
|
|
3271
|
-
|
|
3272
|
-
|
|
3626
|
+
return responseGenerator(
|
|
3627
|
+
createResponseData(workflowRunId, {
|
|
3628
|
+
condition: "duplicate-step"
|
|
3629
|
+
})
|
|
3630
|
+
);
|
|
3273
3631
|
}
|
|
3274
|
-
const failureCheck = await handleFailure(
|
|
3632
|
+
const failureCheck = await handleFailure({
|
|
3275
3633
|
request,
|
|
3276
3634
|
requestPayload,
|
|
3277
|
-
qstashClient,
|
|
3635
|
+
qstashClient: regionalClient,
|
|
3278
3636
|
initialPayloadParser,
|
|
3279
3637
|
routeFunction,
|
|
3280
3638
|
failureFunction,
|
|
3281
3639
|
env,
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
flowControl,
|
|
3285
|
-
debug
|
|
3286
|
-
);
|
|
3640
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3641
|
+
});
|
|
3287
3642
|
if (failureCheck.isErr()) {
|
|
3288
3643
|
throw failureCheck.error;
|
|
3289
|
-
} else if (failureCheck.value.result === "
|
|
3290
|
-
await
|
|
3291
|
-
|
|
3292
|
-
condition: "failure-callback",
|
|
3293
|
-
result: failureCheck.value.response
|
|
3644
|
+
} else if (failureCheck.value.result === "failure-function-executed") {
|
|
3645
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3646
|
+
info: `Handled failure callback.`
|
|
3294
3647
|
});
|
|
3648
|
+
return responseGenerator(
|
|
3649
|
+
createResponseData(workflowRunId, {
|
|
3650
|
+
condition: "failure-callback-executed",
|
|
3651
|
+
result: failureCheck.value.response
|
|
3652
|
+
})
|
|
3653
|
+
);
|
|
3654
|
+
} else if (failureCheck.value.result === "failure-function-undefined") {
|
|
3655
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3656
|
+
info: `Failure callback invoked but no failure function defined.`
|
|
3657
|
+
});
|
|
3658
|
+
return responseGenerator(
|
|
3659
|
+
createResponseData(workflowRunId, {
|
|
3660
|
+
condition: "failure-callback-undefined"
|
|
3661
|
+
})
|
|
3662
|
+
);
|
|
3295
3663
|
}
|
|
3296
3664
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3297
3665
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3298
3666
|
const workflowContext = new WorkflowContext({
|
|
3299
|
-
qstashClient,
|
|
3667
|
+
qstashClient: regionalClient,
|
|
3300
3668
|
workflowRunId,
|
|
3301
3669
|
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
3302
3670
|
headers: recreateUserHeaders(request.headers),
|
|
3303
3671
|
steps,
|
|
3304
3672
|
url: workflowUrl,
|
|
3305
|
-
failureUrl: workflowFailureUrl,
|
|
3306
|
-
debug,
|
|
3307
3673
|
env,
|
|
3308
|
-
retries,
|
|
3309
|
-
retryDelay,
|
|
3310
3674
|
telemetry: telemetry2,
|
|
3311
3675
|
invokeCount,
|
|
3312
|
-
|
|
3313
|
-
|
|
3676
|
+
label,
|
|
3677
|
+
middlewareManager
|
|
3314
3678
|
});
|
|
3315
3679
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3316
3680
|
routeFunction,
|
|
3317
3681
|
workflowContext
|
|
3318
3682
|
);
|
|
3319
3683
|
if (authCheck.isErr()) {
|
|
3320
|
-
await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
|
|
3321
3684
|
throw authCheck.error;
|
|
3322
3685
|
} else if (authCheck.value === "run-ended") {
|
|
3323
|
-
await
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3686
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3687
|
+
error: new Error(AUTH_FAIL_MESSAGE)
|
|
3688
|
+
});
|
|
3689
|
+
return responseGenerator(
|
|
3690
|
+
createResponseData(isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId, {
|
|
3691
|
+
condition: "auth-fail"
|
|
3692
|
+
})
|
|
3328
3693
|
);
|
|
3329
3694
|
}
|
|
3330
3695
|
const callReturnCheck = await handleThirdPartyCallResult({
|
|
3331
3696
|
request,
|
|
3332
3697
|
requestPayload: rawInitialPayload,
|
|
3333
|
-
client:
|
|
3698
|
+
client: regionalClient,
|
|
3334
3699
|
workflowUrl,
|
|
3335
|
-
failureUrl: workflowFailureUrl,
|
|
3336
|
-
retries,
|
|
3337
|
-
retryDelay,
|
|
3338
|
-
flowControl,
|
|
3339
3700
|
telemetry: telemetry2,
|
|
3340
|
-
|
|
3701
|
+
middlewareManager
|
|
3341
3702
|
});
|
|
3342
3703
|
if (callReturnCheck.isErr()) {
|
|
3343
|
-
await debug?.log("ERROR", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
3344
|
-
error: callReturnCheck.error.message
|
|
3345
|
-
});
|
|
3346
3704
|
throw callReturnCheck.error;
|
|
3347
3705
|
} else if (callReturnCheck.value === "continue-workflow") {
|
|
3348
3706
|
const result = isFirstInvocation ? await triggerFirstInvocation({
|
|
3349
3707
|
workflowContext,
|
|
3350
3708
|
useJSONContent,
|
|
3351
3709
|
telemetry: telemetry2,
|
|
3352
|
-
|
|
3353
|
-
|
|
3710
|
+
invokeCount,
|
|
3711
|
+
middlewareManager,
|
|
3712
|
+
unknownSdk
|
|
3354
3713
|
}) : await triggerRouteFunction({
|
|
3355
|
-
onStep: async () =>
|
|
3714
|
+
onStep: async () => {
|
|
3715
|
+
if (steps.length === 1) {
|
|
3716
|
+
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
3717
|
+
}
|
|
3718
|
+
return await routeFunction(workflowContext);
|
|
3719
|
+
},
|
|
3356
3720
|
onCleanup: async (result2) => {
|
|
3357
|
-
await
|
|
3721
|
+
await middlewareManager.dispatchLifecycle("runCompleted", {
|
|
3722
|
+
result: result2
|
|
3723
|
+
});
|
|
3724
|
+
await triggerWorkflowDelete(
|
|
3725
|
+
workflowContext,
|
|
3726
|
+
result2,
|
|
3727
|
+
false,
|
|
3728
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3729
|
+
);
|
|
3358
3730
|
},
|
|
3359
3731
|
onCancel: async () => {
|
|
3360
3732
|
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
3361
3733
|
},
|
|
3362
|
-
|
|
3734
|
+
middlewareManager
|
|
3363
3735
|
});
|
|
3364
3736
|
if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
|
|
3365
|
-
return
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3737
|
+
return responseGenerator(
|
|
3738
|
+
createResponseData(workflowRunId, {
|
|
3739
|
+
condition: "non-retryable-error",
|
|
3740
|
+
result: result.value
|
|
3741
|
+
})
|
|
3742
|
+
);
|
|
3369
3743
|
}
|
|
3370
3744
|
if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
|
|
3371
|
-
return
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3745
|
+
return responseGenerator(
|
|
3746
|
+
createResponseData(workflowRunId, {
|
|
3747
|
+
condition: "retry-after-error",
|
|
3748
|
+
result: result.value
|
|
3749
|
+
})
|
|
3750
|
+
);
|
|
3375
3751
|
}
|
|
3376
3752
|
if (result.isErr()) {
|
|
3377
|
-
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
3378
3753
|
throw result.error;
|
|
3379
3754
|
}
|
|
3380
|
-
await
|
|
3381
|
-
|
|
3382
|
-
condition: "success"
|
|
3755
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3756
|
+
info: `Workflow endpoint execution completed successfully.`
|
|
3383
3757
|
});
|
|
3758
|
+
return responseGenerator(
|
|
3759
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3760
|
+
condition: "success"
|
|
3761
|
+
})
|
|
3762
|
+
);
|
|
3384
3763
|
} else if (callReturnCheck.value === "workflow-ended") {
|
|
3385
|
-
return
|
|
3386
|
-
|
|
3387
|
-
|
|
3764
|
+
return responseGenerator(
|
|
3765
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3766
|
+
condition: "workflow-already-ended"
|
|
3767
|
+
})
|
|
3768
|
+
);
|
|
3388
3769
|
}
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3770
|
+
return responseGenerator(
|
|
3771
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3772
|
+
condition: "fromCallback"
|
|
3773
|
+
})
|
|
3774
|
+
);
|
|
3393
3775
|
};
|
|
3394
3776
|
const safeHandler = async (request) => {
|
|
3777
|
+
const middlewareManager = new MiddlewareManager(middlewares);
|
|
3395
3778
|
try {
|
|
3396
|
-
return await handler(request);
|
|
3779
|
+
return await handler(request, middlewareManager);
|
|
3397
3780
|
} catch (error) {
|
|
3398
3781
|
const formattedError = formatWorkflowError(error);
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
}
|
|
3402
|
-
const formattedOnErrorError = formatWorkflowError(onErrorError);
|
|
3403
|
-
const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
|
|
3404
|
-
Original error: '${formattedError.message}'`;
|
|
3405
|
-
console.error(errorMessage);
|
|
3406
|
-
return new Response(JSON.stringify({ error: errorMessage }), {
|
|
3407
|
-
status: 500,
|
|
3408
|
-
headers: {
|
|
3409
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3410
|
-
}
|
|
3411
|
-
});
|
|
3412
|
-
}
|
|
3782
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3783
|
+
error: isInstanceOf(error, Error) ? error : new Error(formattedError.message)
|
|
3784
|
+
});
|
|
3413
3785
|
return new Response(JSON.stringify(formattedError), {
|
|
3414
3786
|
status: 500,
|
|
3415
3787
|
headers: {
|
|
@@ -3456,10 +3828,14 @@ function createExpressHandler(params) {
|
|
|
3456
3828
|
headers: new Headers(request_.headers),
|
|
3457
3829
|
body: requestBody
|
|
3458
3830
|
});
|
|
3459
|
-
const { handler: serveHandler } = serveBase(
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3831
|
+
const { handler: serveHandler } = serveBase(
|
|
3832
|
+
routeFunction,
|
|
3833
|
+
telemetry,
|
|
3834
|
+
options,
|
|
3835
|
+
{
|
|
3836
|
+
useJSONContent: true
|
|
3837
|
+
}
|
|
3838
|
+
);
|
|
3463
3839
|
const response = await serveHandler(webRequest);
|
|
3464
3840
|
for (const [key, value] of response.headers.entries()) {
|
|
3465
3841
|
res.setHeader(key, value);
|
|
@@ -3479,7 +3855,8 @@ var createWorkflow = (...params) => {
|
|
|
3479
3855
|
return {
|
|
3480
3856
|
routeFunction,
|
|
3481
3857
|
options,
|
|
3482
|
-
workflowId: void 0
|
|
3858
|
+
workflowId: void 0,
|
|
3859
|
+
useJSONContent: true
|
|
3483
3860
|
};
|
|
3484
3861
|
};
|
|
3485
3862
|
var serveMany = (workflows, options) => {
|