@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
|
@@ -7,26 +7,36 @@ var WorkflowError = class extends QstashError {
|
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
9
|
var WorkflowAbort = class extends Error {
|
|
10
|
-
stepInfo;
|
|
11
10
|
stepName;
|
|
11
|
+
stepInfo;
|
|
12
12
|
/**
|
|
13
|
-
* whether workflow is to be canceled on abort
|
|
14
|
-
*/
|
|
15
|
-
cancelWorkflow;
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
13
|
* @param stepName name of the aborting step
|
|
19
14
|
* @param stepInfo step information
|
|
20
|
-
* @param cancelWorkflow
|
|
21
15
|
*/
|
|
22
|
-
constructor(stepName, stepInfo
|
|
16
|
+
constructor(stepName, stepInfo) {
|
|
23
17
|
super(
|
|
24
18
|
`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}'.`
|
|
25
19
|
);
|
|
26
20
|
this.name = "WorkflowAbort";
|
|
27
21
|
this.stepName = stepName;
|
|
28
22
|
this.stepInfo = stepInfo;
|
|
29
|
-
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var WorkflowAuthError = class extends WorkflowAbort {
|
|
26
|
+
/**
|
|
27
|
+
* @param stepName name of the step found during authorization
|
|
28
|
+
*/
|
|
29
|
+
constructor(stepName) {
|
|
30
|
+
super(stepName);
|
|
31
|
+
this.name = "WorkflowAuthError";
|
|
32
|
+
this.message = `This is an Upstash Workflow error thrown during authorization check. Found step '${stepName}' during dry-run.`;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var WorkflowCancelAbort = class extends WorkflowAbort {
|
|
36
|
+
constructor() {
|
|
37
|
+
super("cancel");
|
|
38
|
+
this.name = "WorkflowCancelAbort";
|
|
39
|
+
this.message = "Workflow has been canceled by user via context.cancel().";
|
|
30
40
|
}
|
|
31
41
|
};
|
|
32
42
|
var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
@@ -34,22 +44,22 @@ var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
|
34
44
|
* @param message error message to be displayed
|
|
35
45
|
*/
|
|
36
46
|
constructor(message) {
|
|
37
|
-
super("
|
|
47
|
+
super("non-retryable-error");
|
|
38
48
|
this.name = "WorkflowNonRetryableError";
|
|
39
|
-
|
|
49
|
+
this.message = message ?? "Workflow failed with non-retryable error.";
|
|
40
50
|
}
|
|
41
51
|
};
|
|
42
52
|
var WorkflowRetryAfterError = class extends WorkflowAbort {
|
|
43
53
|
retryAfter;
|
|
44
54
|
/**
|
|
45
|
-
* @param retryAfter time in seconds after which the workflow should be retried
|
|
46
55
|
* @param message error message to be displayed
|
|
56
|
+
* @param retryAfter time in seconds after which the workflow should be retried
|
|
47
57
|
*/
|
|
48
58
|
constructor(message, retryAfter) {
|
|
49
|
-
super("retry"
|
|
59
|
+
super("retry-after-error");
|
|
50
60
|
this.name = "WorkflowRetryAfterError";
|
|
61
|
+
this.message = message;
|
|
51
62
|
this.retryAfter = retryAfter;
|
|
52
|
-
if (message) this.message = message;
|
|
53
63
|
}
|
|
54
64
|
};
|
|
55
65
|
var formatWorkflowError = (error) => {
|
|
@@ -89,15 +99,18 @@ var WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId";
|
|
|
89
99
|
var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
|
|
90
100
|
var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
|
|
91
101
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
102
|
+
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
92
103
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
93
104
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
94
105
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
106
|
+
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
107
|
+
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
95
108
|
var WORKFLOW_PROTOCOL_VERSION = "1";
|
|
96
109
|
var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
97
110
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
98
111
|
var NO_CONCURRENCY = 1;
|
|
99
112
|
var DEFAULT_RETRIES = 3;
|
|
100
|
-
var VERSION = "
|
|
113
|
+
var VERSION = "v1.0.0";
|
|
101
114
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
102
115
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
103
116
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
@@ -112,7 +125,9 @@ var StepTypes = [
|
|
|
112
125
|
"Call",
|
|
113
126
|
"Wait",
|
|
114
127
|
"Notify",
|
|
115
|
-
"Invoke"
|
|
128
|
+
"Invoke",
|
|
129
|
+
"CreateWebhook",
|
|
130
|
+
"WaitForWebhook"
|
|
116
131
|
];
|
|
117
132
|
|
|
118
133
|
// src/serve/serve-many.ts
|
|
@@ -221,15 +236,21 @@ var makeCancelRequest = async (requester, workflowRunId) => {
|
|
|
221
236
|
});
|
|
222
237
|
return true;
|
|
223
238
|
};
|
|
224
|
-
var getSteps = async (requester, workflowRunId, messageId,
|
|
239
|
+
var getSteps = async (requester, workflowRunId, messageId, dispatchDebug) => {
|
|
225
240
|
try {
|
|
226
241
|
const steps = await requester.request({
|
|
227
242
|
path: ["v2", "workflows", "runs", workflowRunId],
|
|
228
243
|
parseResponseAsJson: true
|
|
229
244
|
});
|
|
245
|
+
if (steps.length === 1) {
|
|
246
|
+
return {
|
|
247
|
+
steps,
|
|
248
|
+
workflowRunEnded: false
|
|
249
|
+
};
|
|
250
|
+
}
|
|
230
251
|
if (!messageId) {
|
|
231
|
-
await
|
|
232
|
-
|
|
252
|
+
await dispatchDebug?.("onInfo", {
|
|
253
|
+
info: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
|
|
233
254
|
});
|
|
234
255
|
return { steps, workflowRunEnded: false };
|
|
235
256
|
} else {
|
|
@@ -238,16 +259,15 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
|
|
|
238
259
|
return { steps: [], workflowRunEnded: false };
|
|
239
260
|
}
|
|
240
261
|
const filteredSteps = steps.slice(0, index + 1);
|
|
241
|
-
await
|
|
242
|
-
|
|
262
|
+
await dispatchDebug?.("onInfo", {
|
|
263
|
+
info: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
|
|
243
264
|
});
|
|
244
265
|
return { steps: filteredSteps, workflowRunEnded: false };
|
|
245
266
|
}
|
|
246
267
|
} catch (error) {
|
|
247
268
|
if (isInstanceOf(error, QstashError2) && error.status === 404) {
|
|
248
|
-
await
|
|
249
|
-
|
|
250
|
-
error
|
|
269
|
+
await dispatchDebug?.("onWarning", {
|
|
270
|
+
warning: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed."
|
|
251
271
|
});
|
|
252
272
|
return { steps: void 0, workflowRunEnded: true };
|
|
253
273
|
} else {
|
|
@@ -262,8 +282,8 @@ var NANOID_LENGTH = 21;
|
|
|
262
282
|
function getRandomInt() {
|
|
263
283
|
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
264
284
|
}
|
|
265
|
-
function nanoid() {
|
|
266
|
-
return Array.from({ length
|
|
285
|
+
function nanoid(length = NANOID_LENGTH) {
|
|
286
|
+
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
267
287
|
}
|
|
268
288
|
function getWorkflowRunId(id) {
|
|
269
289
|
return `wfr_${id ?? nanoid()}`;
|
|
@@ -280,6 +300,46 @@ function decodeBase64(base64) {
|
|
|
280
300
|
return binString;
|
|
281
301
|
}
|
|
282
302
|
}
|
|
303
|
+
function getUserIdFromToken(qstashClient) {
|
|
304
|
+
try {
|
|
305
|
+
const token = qstashClient.token;
|
|
306
|
+
const decodedToken = decodeBase64(token);
|
|
307
|
+
const tokenPayload = JSON.parse(decodedToken);
|
|
308
|
+
const userId = tokenPayload.UserID;
|
|
309
|
+
if (!userId) {
|
|
310
|
+
throw new WorkflowError("QStash token payload does not contain userId");
|
|
311
|
+
}
|
|
312
|
+
return userId;
|
|
313
|
+
} catch (error) {
|
|
314
|
+
throw new WorkflowError(
|
|
315
|
+
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function getQStashUrl(qstashClient) {
|
|
320
|
+
try {
|
|
321
|
+
const requester = qstashClient.http;
|
|
322
|
+
const baseUrl = requester.baseUrl;
|
|
323
|
+
if (!baseUrl) {
|
|
324
|
+
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
325
|
+
}
|
|
326
|
+
return baseUrl;
|
|
327
|
+
} catch (error) {
|
|
328
|
+
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function getEventId() {
|
|
332
|
+
return `evt_${nanoid(15)}`;
|
|
333
|
+
}
|
|
334
|
+
function stringifyBody(body) {
|
|
335
|
+
if (body === void 0) {
|
|
336
|
+
return void 0;
|
|
337
|
+
}
|
|
338
|
+
if (typeof body === "string") {
|
|
339
|
+
return body;
|
|
340
|
+
}
|
|
341
|
+
return JSON.stringify(body);
|
|
342
|
+
}
|
|
283
343
|
|
|
284
344
|
// node_modules/neverthrow/dist/index.es.js
|
|
285
345
|
var defaultErrorConfig = {
|
|
@@ -709,23 +769,26 @@ var triggerFirstInvocation = async (params) => {
|
|
|
709
769
|
invokeCount,
|
|
710
770
|
delay,
|
|
711
771
|
notBefore,
|
|
712
|
-
|
|
772
|
+
failureUrl,
|
|
773
|
+
retries,
|
|
774
|
+
retryDelay,
|
|
775
|
+
flowControl,
|
|
776
|
+
unknownSdk
|
|
713
777
|
}) => {
|
|
714
778
|
const { headers } = getHeaders({
|
|
715
779
|
initHeaderValue: "true",
|
|
716
780
|
workflowConfig: {
|
|
717
781
|
workflowRunId: workflowContext.workflowRunId,
|
|
718
782
|
workflowUrl: workflowContext.url,
|
|
719
|
-
failureUrl
|
|
720
|
-
retries
|
|
721
|
-
retryDelay
|
|
783
|
+
failureUrl,
|
|
784
|
+
retries,
|
|
785
|
+
retryDelay,
|
|
722
786
|
telemetry,
|
|
723
|
-
flowControl
|
|
787
|
+
flowControl,
|
|
724
788
|
useJSONContent: useJSONContent ?? false
|
|
725
789
|
},
|
|
726
790
|
invokeCount: invokeCount ?? 0,
|
|
727
|
-
userHeaders: workflowContext.headers
|
|
728
|
-
keepTriggerConfig
|
|
791
|
+
userHeaders: workflowContext.headers
|
|
729
792
|
});
|
|
730
793
|
if (workflowContext.headers.get("content-type")) {
|
|
731
794
|
headers["content-type"] = workflowContext.headers.get("content-type");
|
|
@@ -733,6 +796,9 @@ var triggerFirstInvocation = async (params) => {
|
|
|
733
796
|
if (useJSONContent) {
|
|
734
797
|
headers["content-type"] = "application/json";
|
|
735
798
|
}
|
|
799
|
+
if (unknownSdk) {
|
|
800
|
+
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
801
|
+
}
|
|
736
802
|
if (workflowContext.label) {
|
|
737
803
|
headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
|
|
738
804
|
}
|
|
@@ -753,21 +819,15 @@ var triggerFirstInvocation = async (params) => {
|
|
|
753
819
|
for (let i = 0; i < results.length; i++) {
|
|
754
820
|
const result = results[i];
|
|
755
821
|
const invocationParams = firstInvocationParams[i];
|
|
822
|
+
invocationParams.middlewareManager?.assignContext(invocationParams.workflowContext);
|
|
756
823
|
if (result.deduplicated) {
|
|
757
|
-
await invocationParams.
|
|
758
|
-
|
|
759
|
-
headers: invocationBatch[i].headers,
|
|
760
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
761
|
-
url: invocationParams.workflowContext.url,
|
|
762
|
-
messageId: result.messageId
|
|
824
|
+
await invocationParams.middlewareManager?.dispatchDebug("onWarning", {
|
|
825
|
+
warning: `Workflow run ${invocationParams.workflowContext.workflowRunId} already exists. A new one isn't created.`
|
|
763
826
|
});
|
|
764
827
|
invocationStatuses.push("workflow-run-already-exists");
|
|
765
828
|
} else {
|
|
766
|
-
await invocationParams.
|
|
767
|
-
|
|
768
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
769
|
-
url: invocationParams.workflowContext.url,
|
|
770
|
-
messageId: result.messageId
|
|
829
|
+
await invocationParams.middlewareManager?.dispatchDebug("onInfo", {
|
|
830
|
+
info: `Workflow run started successfully with URL ${invocationParams.workflowContext.url}.`
|
|
771
831
|
});
|
|
772
832
|
invocationStatuses.push("success");
|
|
773
833
|
}
|
|
@@ -789,7 +849,7 @@ var triggerRouteFunction = async ({
|
|
|
789
849
|
onCleanup,
|
|
790
850
|
onStep,
|
|
791
851
|
onCancel,
|
|
792
|
-
|
|
852
|
+
middlewareManager
|
|
793
853
|
}) => {
|
|
794
854
|
try {
|
|
795
855
|
const result = await onStep();
|
|
@@ -798,27 +858,25 @@ var triggerRouteFunction = async ({
|
|
|
798
858
|
} catch (error) {
|
|
799
859
|
const error_ = error;
|
|
800
860
|
if (isInstanceOf(error, QstashError3) && error.status === 400) {
|
|
801
|
-
await
|
|
802
|
-
|
|
803
|
-
name: error.name,
|
|
804
|
-
errorMessage: error.message
|
|
861
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
862
|
+
warning: `Tried to append to a cancelled workflow. Exiting without publishing. Error: ${error.message}`
|
|
805
863
|
});
|
|
806
864
|
return ok("workflow-was-finished");
|
|
807
865
|
} else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
|
|
808
866
|
return ok(error_);
|
|
809
|
-
} else if (
|
|
810
|
-
return err(error_);
|
|
811
|
-
} else if (error_.cancelWorkflow) {
|
|
867
|
+
} else if (isInstanceOf(error_, WorkflowCancelAbort)) {
|
|
812
868
|
await onCancel();
|
|
813
869
|
return ok("workflow-finished");
|
|
814
|
-
} else {
|
|
870
|
+
} else if (isInstanceOf(error_, WorkflowAbort)) {
|
|
815
871
|
return ok("step-finished");
|
|
872
|
+
} else {
|
|
873
|
+
return err(error_);
|
|
816
874
|
}
|
|
817
875
|
}
|
|
818
876
|
};
|
|
819
|
-
var triggerWorkflowDelete = async (workflowContext, result,
|
|
820
|
-
await
|
|
821
|
-
|
|
877
|
+
var triggerWorkflowDelete = async (workflowContext, result, cancel = false, dispatchDebug) => {
|
|
878
|
+
await dispatchDebug?.("onInfo", {
|
|
879
|
+
info: `Deleting workflow run ${workflowContext.workflowRunId} from QStash` + (cancel ? " with cancel=true." : ".")
|
|
822
880
|
});
|
|
823
881
|
await workflowContext.qstashClient.http.request({
|
|
824
882
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
@@ -826,18 +884,16 @@ var triggerWorkflowDelete = async (workflowContext, result, debug, cancel = fals
|
|
|
826
884
|
parseResponseAsJson: false,
|
|
827
885
|
body: JSON.stringify(result)
|
|
828
886
|
});
|
|
829
|
-
await
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
`workflow run ${workflowContext.workflowRunId} deleted.`
|
|
833
|
-
);
|
|
887
|
+
await dispatchDebug?.("onInfo", {
|
|
888
|
+
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
889
|
+
});
|
|
834
890
|
};
|
|
835
891
|
var recreateUserHeaders = (headers) => {
|
|
836
892
|
const filteredHeaders = new Headers();
|
|
837
893
|
const pairs = headers.entries();
|
|
838
894
|
for (const [header, value] of pairs) {
|
|
839
895
|
const headerLowerCase = header.toLowerCase();
|
|
840
|
-
const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
|
|
896
|
+
const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
|
|
841
897
|
!headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
|
|
842
898
|
headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
|
|
843
899
|
headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
|
|
@@ -852,12 +908,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
852
908
|
requestPayload,
|
|
853
909
|
client,
|
|
854
910
|
workflowUrl,
|
|
855
|
-
failureUrl,
|
|
856
|
-
retries,
|
|
857
|
-
retryDelay,
|
|
858
911
|
telemetry,
|
|
859
|
-
|
|
860
|
-
debug
|
|
912
|
+
middlewareManager
|
|
861
913
|
}) => {
|
|
862
914
|
try {
|
|
863
915
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
@@ -874,7 +926,7 @@ var handleThirdPartyCallResult = async ({
|
|
|
874
926
|
client.http,
|
|
875
927
|
workflowRunId2,
|
|
876
928
|
messageId,
|
|
877
|
-
|
|
929
|
+
middlewareManager?.dispatchDebug.bind(middlewareManager)
|
|
878
930
|
);
|
|
879
931
|
if (workflowRunEnded) {
|
|
880
932
|
return ok("workflow-ended");
|
|
@@ -888,9 +940,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
888
940
|
}
|
|
889
941
|
const callbackMessage = JSON.parse(callbackPayload);
|
|
890
942
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
891
|
-
await
|
|
892
|
-
|
|
893
|
-
body: atob(callbackMessage.body ?? "")
|
|
943
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
944
|
+
warning: `Third party call returned status ${callbackMessage.status}. Retrying (${callbackMessage.retried} out of ${callbackMessage.maxRetries}).`
|
|
894
945
|
});
|
|
895
946
|
console.warn(
|
|
896
947
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
@@ -923,11 +974,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
923
974
|
workflowConfig: {
|
|
924
975
|
workflowRunId,
|
|
925
976
|
workflowUrl,
|
|
926
|
-
|
|
927
|
-
retries,
|
|
928
|
-
retryDelay,
|
|
929
|
-
telemetry,
|
|
930
|
-
flowControl
|
|
977
|
+
telemetry
|
|
931
978
|
},
|
|
932
979
|
userHeaders,
|
|
933
980
|
invokeCount: Number(invokeCount)
|
|
@@ -944,19 +991,17 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
944
991
|
out: JSON.stringify(callResponse),
|
|
945
992
|
concurrent: Number(concurrentString)
|
|
946
993
|
};
|
|
947
|
-
await
|
|
948
|
-
|
|
949
|
-
headers: requestHeaders,
|
|
950
|
-
url: workflowUrl
|
|
994
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
995
|
+
info: `Submitting third party call result, step ${stepName} (${stepIdString}).`
|
|
951
996
|
});
|
|
952
|
-
|
|
997
|
+
await client.publishJSON({
|
|
953
998
|
headers: requestHeaders,
|
|
954
999
|
method: "POST",
|
|
955
1000
|
body: callResultStep,
|
|
956
1001
|
url: workflowUrl
|
|
957
1002
|
});
|
|
958
|
-
await
|
|
959
|
-
|
|
1003
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
1004
|
+
info: `Third party call result submitted successfully, step ${stepName} (${stepIdString}).`
|
|
960
1005
|
});
|
|
961
1006
|
return ok("is-call-return");
|
|
962
1007
|
} else {
|
|
@@ -1005,15 +1050,17 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
|
|
|
1005
1050
|
// src/context/steps.ts
|
|
1006
1051
|
var BaseLazyStep = class _BaseLazyStep {
|
|
1007
1052
|
stepName;
|
|
1008
|
-
|
|
1053
|
+
context;
|
|
1054
|
+
constructor(context, stepName) {
|
|
1055
|
+
this.context = context;
|
|
1009
1056
|
if (!stepName) {
|
|
1010
1057
|
throw new WorkflowError(
|
|
1011
1058
|
"A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
|
|
1012
1059
|
);
|
|
1013
1060
|
}
|
|
1014
1061
|
if (typeof stepName !== "string") {
|
|
1015
|
-
|
|
1016
|
-
|
|
1062
|
+
throw new WorkflowError(
|
|
1063
|
+
`A workflow step name must be a string. Received "${stepName}" (${typeof stepName}).`
|
|
1017
1064
|
);
|
|
1018
1065
|
}
|
|
1019
1066
|
this.stepName = stepName;
|
|
@@ -1023,13 +1070,14 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1023
1070
|
*
|
|
1024
1071
|
* will be called when returning the steps to the context from auto executor
|
|
1025
1072
|
*
|
|
1026
|
-
* @param
|
|
1073
|
+
* @param step step
|
|
1027
1074
|
* @returns parsed out field
|
|
1028
1075
|
*/
|
|
1029
|
-
parseOut(
|
|
1076
|
+
parseOut(step) {
|
|
1077
|
+
const out = step.out;
|
|
1030
1078
|
if (out === void 0) {
|
|
1031
1079
|
if (this.allowUndefinedOut) {
|
|
1032
|
-
return
|
|
1080
|
+
return this.handleUndefinedOut(step);
|
|
1033
1081
|
} else {
|
|
1034
1082
|
throw new WorkflowError(
|
|
1035
1083
|
`Error while parsing output of ${this.stepType} step. Expected a string, but got: undefined`
|
|
@@ -1037,27 +1085,26 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1037
1085
|
}
|
|
1038
1086
|
}
|
|
1039
1087
|
if (typeof out === "object") {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
return out;
|
|
1045
|
-
}
|
|
1046
|
-
return {
|
|
1047
|
-
...out,
|
|
1048
|
-
eventData: _BaseLazyStep.tryParsing(out.eventData)
|
|
1049
|
-
};
|
|
1088
|
+
console.warn(
|
|
1089
|
+
`Error while parsing ${this.stepType} step output. Expected a string, but got object. Please reach out to Upstash Support.`
|
|
1090
|
+
);
|
|
1091
|
+
return out;
|
|
1050
1092
|
}
|
|
1051
1093
|
if (typeof out !== "string") {
|
|
1052
1094
|
throw new WorkflowError(
|
|
1053
1095
|
`Error while parsing output of ${this.stepType} step. Expected a string or undefined, but got: ${typeof out}`
|
|
1054
1096
|
);
|
|
1055
1097
|
}
|
|
1056
|
-
return this.safeParseOut(out);
|
|
1098
|
+
return this.safeParseOut(out, step);
|
|
1057
1099
|
}
|
|
1058
|
-
|
|
1100
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1101
|
+
safeParseOut(out, step) {
|
|
1059
1102
|
return _BaseLazyStep.tryParsing(out);
|
|
1060
1103
|
}
|
|
1104
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1105
|
+
handleUndefinedOut(step) {
|
|
1106
|
+
return void 0;
|
|
1107
|
+
}
|
|
1061
1108
|
static tryParsing(stepOut) {
|
|
1062
1109
|
try {
|
|
1063
1110
|
return JSON.parse(stepOut);
|
|
@@ -1075,12 +1122,8 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1075
1122
|
workflowConfig: {
|
|
1076
1123
|
workflowRunId: context.workflowRunId,
|
|
1077
1124
|
workflowUrl: context.url,
|
|
1078
|
-
failureUrl: context.failureUrl,
|
|
1079
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1080
|
-
retryDelay: context.retryDelay,
|
|
1081
1125
|
useJSONContent: false,
|
|
1082
|
-
telemetry
|
|
1083
|
-
flowControl: context.flowControl
|
|
1126
|
+
telemetry
|
|
1084
1127
|
},
|
|
1085
1128
|
userHeaders: context.headers,
|
|
1086
1129
|
invokeCount,
|
|
@@ -1096,9 +1139,6 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1096
1139
|
body,
|
|
1097
1140
|
headers,
|
|
1098
1141
|
method: "POST",
|
|
1099
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1100
|
-
retryDelay: context.retryDelay,
|
|
1101
|
-
flowControl: context.flowControl,
|
|
1102
1142
|
url: context.url
|
|
1103
1143
|
}
|
|
1104
1144
|
]);
|
|
@@ -1108,8 +1148,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
|
|
|
1108
1148
|
stepFunction;
|
|
1109
1149
|
stepType = "Run";
|
|
1110
1150
|
allowUndefinedOut = true;
|
|
1111
|
-
constructor(stepName, stepFunction) {
|
|
1112
|
-
super(stepName);
|
|
1151
|
+
constructor(context, stepName, stepFunction) {
|
|
1152
|
+
super(context, stepName);
|
|
1113
1153
|
this.stepFunction = stepFunction;
|
|
1114
1154
|
}
|
|
1115
1155
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1139,8 +1179,8 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1139
1179
|
sleep;
|
|
1140
1180
|
stepType = "SleepFor";
|
|
1141
1181
|
allowUndefinedOut = true;
|
|
1142
|
-
constructor(stepName, sleep) {
|
|
1143
|
-
super(stepName);
|
|
1182
|
+
constructor(context, stepName, sleep) {
|
|
1183
|
+
super(context, stepName);
|
|
1144
1184
|
this.sleep = sleep;
|
|
1145
1185
|
}
|
|
1146
1186
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1169,9 +1209,6 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1169
1209
|
headers,
|
|
1170
1210
|
method: "POST",
|
|
1171
1211
|
url: context.url,
|
|
1172
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1173
|
-
retryDelay: context.retryDelay,
|
|
1174
|
-
flowControl: context.flowControl,
|
|
1175
1212
|
delay: isParallel ? void 0 : this.sleep
|
|
1176
1213
|
}
|
|
1177
1214
|
]);
|
|
@@ -1181,8 +1218,8 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1181
1218
|
sleepUntil;
|
|
1182
1219
|
stepType = "SleepUntil";
|
|
1183
1220
|
allowUndefinedOut = true;
|
|
1184
|
-
constructor(stepName, sleepUntil) {
|
|
1185
|
-
super(stepName);
|
|
1221
|
+
constructor(context, stepName, sleepUntil) {
|
|
1222
|
+
super(context, stepName);
|
|
1186
1223
|
this.sleepUntil = sleepUntil;
|
|
1187
1224
|
}
|
|
1188
1225
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1214,9 +1251,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1214
1251
|
headers,
|
|
1215
1252
|
method: "POST",
|
|
1216
1253
|
url: context.url,
|
|
1217
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1218
|
-
retryDelay: context.retryDelay,
|
|
1219
|
-
flowControl: context.flowControl,
|
|
1220
1254
|
notBefore: isParallel ? void 0 : this.sleepUntil
|
|
1221
1255
|
}
|
|
1222
1256
|
]);
|
|
@@ -1231,20 +1265,18 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1231
1265
|
retryDelay;
|
|
1232
1266
|
timeout;
|
|
1233
1267
|
flowControl;
|
|
1234
|
-
stringifyBody;
|
|
1235
1268
|
stepType = "Call";
|
|
1236
1269
|
allowUndefinedOut = false;
|
|
1237
|
-
constructor(
|
|
1238
|
-
super(stepName);
|
|
1239
|
-
this.url = url;
|
|
1240
|
-
this.method = method;
|
|
1241
|
-
this.body = body;
|
|
1242
|
-
this.headers = headers;
|
|
1243
|
-
this.retries = retries;
|
|
1244
|
-
this.retryDelay = retryDelay;
|
|
1245
|
-
this.timeout = timeout;
|
|
1246
|
-
this.flowControl = flowControl;
|
|
1247
|
-
this.stringifyBody = stringifyBody;
|
|
1270
|
+
constructor(params) {
|
|
1271
|
+
super(params.context, params.stepName);
|
|
1272
|
+
this.url = params.url;
|
|
1273
|
+
this.method = params.method ?? "GET";
|
|
1274
|
+
this.body = params.body;
|
|
1275
|
+
this.headers = params.headers ?? {};
|
|
1276
|
+
this.retries = params.retries ?? 0;
|
|
1277
|
+
this.retryDelay = params.retryDelay;
|
|
1278
|
+
this.timeout = params.timeout;
|
|
1279
|
+
this.flowControl = params.flowControl;
|
|
1248
1280
|
}
|
|
1249
1281
|
getPlanStep(concurrent, targetStep) {
|
|
1250
1282
|
return {
|
|
@@ -1341,7 +1373,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1341
1373
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
1342
1374
|
"Upstash-Callback-Workflow-Init": "false",
|
|
1343
1375
|
"Upstash-Callback-Workflow-Url": context.url,
|
|
1344
|
-
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger",
|
|
1376
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1345
1377
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
1346
1378
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
1347
1379
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
|
|
@@ -1354,22 +1386,10 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1354
1386
|
};
|
|
1355
1387
|
}
|
|
1356
1388
|
async submitStep({ context, headers }) {
|
|
1357
|
-
let callBody;
|
|
1358
|
-
if (this.stringifyBody) {
|
|
1359
|
-
callBody = JSON.stringify(this.body);
|
|
1360
|
-
} else {
|
|
1361
|
-
if (typeof this.body === "string") {
|
|
1362
|
-
callBody = this.body;
|
|
1363
|
-
} else {
|
|
1364
|
-
throw new WorkflowError(
|
|
1365
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your call step."
|
|
1366
|
-
);
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
1389
|
return await context.qstashClient.batch([
|
|
1370
1390
|
{
|
|
1371
1391
|
headers,
|
|
1372
|
-
body:
|
|
1392
|
+
body: this.body,
|
|
1373
1393
|
method: this.method,
|
|
1374
1394
|
url: this.url,
|
|
1375
1395
|
retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
|
|
@@ -1379,13 +1399,12 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1379
1399
|
]);
|
|
1380
1400
|
}
|
|
1381
1401
|
};
|
|
1382
|
-
var
|
|
1402
|
+
var LazyWaitEventStep = class extends BaseLazyStep {
|
|
1383
1403
|
eventId;
|
|
1384
1404
|
timeout;
|
|
1385
|
-
stepType = "Wait";
|
|
1386
1405
|
allowUndefinedOut = false;
|
|
1387
|
-
constructor(stepName, eventId, timeout) {
|
|
1388
|
-
super(stepName);
|
|
1406
|
+
constructor(context, stepName, eventId, timeout) {
|
|
1407
|
+
super(context, stepName);
|
|
1389
1408
|
this.eventId = eventId;
|
|
1390
1409
|
this.timeout = timeout;
|
|
1391
1410
|
}
|
|
@@ -1410,13 +1429,6 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1410
1429
|
concurrent
|
|
1411
1430
|
});
|
|
1412
1431
|
}
|
|
1413
|
-
safeParseOut(out) {
|
|
1414
|
-
const result = JSON.parse(out);
|
|
1415
|
-
return {
|
|
1416
|
-
...result,
|
|
1417
|
-
eventData: BaseLazyStep.tryParsing(result.eventData)
|
|
1418
|
-
};
|
|
1419
|
-
}
|
|
1420
1432
|
getHeaders({ context, telemetry, invokeCount, step }) {
|
|
1421
1433
|
const headers = super.getHeaders({ context, telemetry, invokeCount, step });
|
|
1422
1434
|
headers.headers["Upstash-Workflow-CallType"] = "step";
|
|
@@ -1450,7 +1462,7 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1450
1462
|
timeoutHeaders,
|
|
1451
1463
|
step: {
|
|
1452
1464
|
stepId: step.stepId,
|
|
1453
|
-
stepType:
|
|
1465
|
+
stepType: this.stepType,
|
|
1454
1466
|
stepName: step.stepName,
|
|
1455
1467
|
concurrent: step.concurrent,
|
|
1456
1468
|
targetStep: step.targetStep
|
|
@@ -1471,8 +1483,8 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1471
1483
|
};
|
|
1472
1484
|
var LazyNotifyStep = class extends LazyFunctionStep {
|
|
1473
1485
|
stepType = "Notify";
|
|
1474
|
-
constructor(stepName, eventId, eventData, requester) {
|
|
1475
|
-
super(stepName, async () => {
|
|
1486
|
+
constructor(context, stepName, eventId, eventData, requester) {
|
|
1487
|
+
super(context, stepName, async () => {
|
|
1476
1488
|
const notifyResponse = await makeNotifyRequest(requester, eventId, eventData);
|
|
1477
1489
|
return {
|
|
1478
1490
|
eventId,
|
|
@@ -1497,28 +1509,10 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1497
1509
|
* workflow id of the invoked workflow
|
|
1498
1510
|
*/
|
|
1499
1511
|
workflowId;
|
|
1500
|
-
constructor(stepName, {
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
workflowRunId,
|
|
1505
|
-
retries,
|
|
1506
|
-
retryDelay,
|
|
1507
|
-
flowControl,
|
|
1508
|
-
stringifyBody = true
|
|
1509
|
-
}) {
|
|
1510
|
-
super(stepName);
|
|
1511
|
-
this.params = {
|
|
1512
|
-
workflow,
|
|
1513
|
-
body,
|
|
1514
|
-
headers,
|
|
1515
|
-
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1516
|
-
retries,
|
|
1517
|
-
retryDelay,
|
|
1518
|
-
flowControl,
|
|
1519
|
-
stringifyBody
|
|
1520
|
-
};
|
|
1521
|
-
const { workflowId } = workflow;
|
|
1512
|
+
constructor(context, stepName, params) {
|
|
1513
|
+
super(context, stepName);
|
|
1514
|
+
this.params = params;
|
|
1515
|
+
const { workflowId } = params.workflow;
|
|
1522
1516
|
if (!workflowId) {
|
|
1523
1517
|
throw new WorkflowError("You can only invoke workflow which has a workflowId");
|
|
1524
1518
|
}
|
|
@@ -1558,31 +1552,18 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1558
1552
|
workflowConfig: {
|
|
1559
1553
|
workflowRunId: context.workflowRunId,
|
|
1560
1554
|
workflowUrl: context.url,
|
|
1561
|
-
failureUrl: context.failureUrl,
|
|
1562
|
-
retries: context.retries,
|
|
1563
|
-
retryDelay: context.retryDelay,
|
|
1564
1555
|
telemetry,
|
|
1565
|
-
flowControl: context.flowControl,
|
|
1566
1556
|
useJSONContent: false
|
|
1567
1557
|
},
|
|
1568
1558
|
userHeaders: context.headers,
|
|
1569
1559
|
invokeCount
|
|
1570
1560
|
});
|
|
1561
|
+
context.qstashClient.http.headers?.forEach((value, key) => {
|
|
1562
|
+
invokerHeaders[key] = value;
|
|
1563
|
+
});
|
|
1571
1564
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1572
|
-
let invokeBody;
|
|
1573
|
-
if (this.params.stringifyBody) {
|
|
1574
|
-
invokeBody = JSON.stringify(this.params.body);
|
|
1575
|
-
} else {
|
|
1576
|
-
if (typeof this.params.body === "string") {
|
|
1577
|
-
invokeBody = this.params.body;
|
|
1578
|
-
} else {
|
|
1579
|
-
throw new WorkflowError(
|
|
1580
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your invoke step."
|
|
1581
|
-
);
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
1565
|
const request = {
|
|
1585
|
-
body:
|
|
1566
|
+
body: stringifyBody(this.params.body),
|
|
1586
1567
|
headers: Object.fromEntries(
|
|
1587
1568
|
Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
|
|
1588
1569
|
),
|
|
@@ -1593,34 +1574,19 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1593
1574
|
return JSON.stringify(request);
|
|
1594
1575
|
}
|
|
1595
1576
|
getHeaders({ context, telemetry, invokeCount }) {
|
|
1596
|
-
const {
|
|
1597
|
-
workflow,
|
|
1598
|
-
headers = {},
|
|
1599
|
-
workflowRunId = getWorkflowRunId(),
|
|
1600
|
-
retries,
|
|
1601
|
-
retryDelay,
|
|
1602
|
-
flowControl
|
|
1603
|
-
} = this.params;
|
|
1577
|
+
const { workflow, headers = {}, workflowRunId, retries, retryDelay, flowControl } = this.params;
|
|
1604
1578
|
const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
|
|
1605
|
-
const {
|
|
1606
|
-
retries: workflowRetries,
|
|
1607
|
-
retryDelay: workflowRetryDelay,
|
|
1608
|
-
failureFunction,
|
|
1609
|
-
failureUrl,
|
|
1610
|
-
useJSONContent,
|
|
1611
|
-
flowControl: workflowFlowControl
|
|
1612
|
-
} = workflow.options;
|
|
1613
1579
|
const { headers: triggerHeaders, contentType } = getHeaders({
|
|
1614
1580
|
initHeaderValue: "true",
|
|
1615
1581
|
workflowConfig: {
|
|
1616
|
-
workflowRunId,
|
|
1582
|
+
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1617
1583
|
workflowUrl: newUrl,
|
|
1618
|
-
retries
|
|
1619
|
-
retryDelay
|
|
1584
|
+
retries,
|
|
1585
|
+
retryDelay,
|
|
1620
1586
|
telemetry,
|
|
1621
|
-
failureUrl:
|
|
1622
|
-
flowControl
|
|
1623
|
-
useJSONContent: useJSONContent ?? false
|
|
1587
|
+
failureUrl: newUrl,
|
|
1588
|
+
flowControl,
|
|
1589
|
+
useJSONContent: workflow.useJSONContent ?? false
|
|
1624
1590
|
},
|
|
1625
1591
|
invokeCount: invokeCount + 1,
|
|
1626
1592
|
userHeaders: new Headers(headers)
|
|
@@ -1639,6 +1605,88 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1639
1605
|
return [result];
|
|
1640
1606
|
}
|
|
1641
1607
|
};
|
|
1608
|
+
var LazyCreateWebhookStep = class extends BaseLazyStep {
|
|
1609
|
+
stepType = "CreateWebhook";
|
|
1610
|
+
allowUndefinedOut = false;
|
|
1611
|
+
getPlanStep(concurrent, targetStep) {
|
|
1612
|
+
return {
|
|
1613
|
+
stepId: 0,
|
|
1614
|
+
stepName: this.stepName,
|
|
1615
|
+
stepType: this.stepType,
|
|
1616
|
+
concurrent,
|
|
1617
|
+
targetStep
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
async getResultStep(concurrent, stepId) {
|
|
1621
|
+
return {
|
|
1622
|
+
stepId,
|
|
1623
|
+
stepName: this.stepName,
|
|
1624
|
+
stepType: this.stepType,
|
|
1625
|
+
out: void 0,
|
|
1626
|
+
concurrent
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
getBody({ step, context }) {
|
|
1630
|
+
const userId = getUserIdFromToken(context.qstashClient);
|
|
1631
|
+
const workflowRunId = context.workflowRunId;
|
|
1632
|
+
const eventId = getEventId();
|
|
1633
|
+
const qstashUrl = getQStashUrl(this.context.qstashClient);
|
|
1634
|
+
return JSON.stringify({
|
|
1635
|
+
...step,
|
|
1636
|
+
out: JSON.stringify({
|
|
1637
|
+
webhookUrl: `${qstashUrl}/v2/workflows/hooks/${userId}/${workflowRunId}/${eventId}`,
|
|
1638
|
+
eventId
|
|
1639
|
+
})
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
var LazyWaitForWebhookStep = class extends LazyWaitEventStep {
|
|
1644
|
+
stepType = "WaitForWebhook";
|
|
1645
|
+
allowUndefinedOut = true;
|
|
1646
|
+
constructor(context, stepName, webhook, timeout) {
|
|
1647
|
+
super(context, stepName, webhook.eventId, timeout);
|
|
1648
|
+
}
|
|
1649
|
+
safeParseOut(out) {
|
|
1650
|
+
const eventData = decodeBase64(out);
|
|
1651
|
+
const parsedEventData = BaseLazyStep.tryParsing(eventData);
|
|
1652
|
+
const body = parsedEventData.body;
|
|
1653
|
+
const parsedBody = typeof body === "string" ? decodeBase64(body) : void 0;
|
|
1654
|
+
const request = new Request(
|
|
1655
|
+
`${parsedEventData.proto}://${parsedEventData.host}${parsedEventData.url}`,
|
|
1656
|
+
{
|
|
1657
|
+
method: parsedEventData.method,
|
|
1658
|
+
headers: parsedEventData.header,
|
|
1659
|
+
body: parsedBody
|
|
1660
|
+
}
|
|
1661
|
+
);
|
|
1662
|
+
return {
|
|
1663
|
+
request,
|
|
1664
|
+
timeout: false
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
handleUndefinedOut() {
|
|
1668
|
+
return {
|
|
1669
|
+
timeout: true,
|
|
1670
|
+
request: void 0
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
var LazyWaitForEventStep = class extends LazyWaitEventStep {
|
|
1675
|
+
stepType = "Wait";
|
|
1676
|
+
allowUndefinedOut = true;
|
|
1677
|
+
parseWaitForEventOut(out, waitTimeout) {
|
|
1678
|
+
return {
|
|
1679
|
+
eventData: out ? BaseLazyStep.tryParsing(decodeBase64(out)) : void 0,
|
|
1680
|
+
timeout: waitTimeout ?? false
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
safeParseOut(out, step) {
|
|
1684
|
+
return this.parseWaitForEventOut(out, step.waitTimeout);
|
|
1685
|
+
}
|
|
1686
|
+
handleUndefinedOut(step) {
|
|
1687
|
+
return this.parseWaitForEventOut(void 0, step.waitTimeout);
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1642
1690
|
|
|
1643
1691
|
// src/qstash/headers.ts
|
|
1644
1692
|
var WorkflowHeaders = class {
|
|
@@ -1648,14 +1696,15 @@ var WorkflowHeaders = class {
|
|
|
1648
1696
|
initHeaderValue;
|
|
1649
1697
|
stepInfo;
|
|
1650
1698
|
headers;
|
|
1651
|
-
|
|
1699
|
+
/**
|
|
1700
|
+
* @param params workflow header parameters
|
|
1701
|
+
*/
|
|
1652
1702
|
constructor({
|
|
1653
1703
|
userHeaders,
|
|
1654
1704
|
workflowConfig,
|
|
1655
1705
|
invokeCount,
|
|
1656
1706
|
initHeaderValue,
|
|
1657
|
-
stepInfo
|
|
1658
|
-
keepTriggerConfig
|
|
1707
|
+
stepInfo
|
|
1659
1708
|
}) {
|
|
1660
1709
|
this.userHeaders = userHeaders;
|
|
1661
1710
|
this.workflowConfig = workflowConfig;
|
|
@@ -1667,7 +1716,6 @@ var WorkflowHeaders = class {
|
|
|
1667
1716
|
workflowHeaders: {},
|
|
1668
1717
|
failureHeaders: {}
|
|
1669
1718
|
};
|
|
1670
|
-
this.keepTriggerConfig = keepTriggerConfig;
|
|
1671
1719
|
}
|
|
1672
1720
|
getHeaders() {
|
|
1673
1721
|
this.addBaseHeaders();
|
|
@@ -1686,7 +1734,7 @@ var WorkflowHeaders = class {
|
|
|
1686
1734
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1687
1735
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1688
1736
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1689
|
-
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger
|
|
1737
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1690
1738
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1691
1739
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1692
1740
|
};
|
|
@@ -1756,12 +1804,12 @@ var WorkflowHeaders = class {
|
|
|
1756
1804
|
}
|
|
1757
1805
|
this.headers.workflowHeaders["Failure-Callback"] = this.workflowConfig.failureUrl;
|
|
1758
1806
|
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
|
|
1759
|
-
this.headers.failureHeaders[`Forward
|
|
1807
|
+
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_CALLBACK_HEADER}`] = "true";
|
|
1760
1808
|
this.headers.failureHeaders["Workflow-Runid"] = this.workflowConfig.workflowRunId;
|
|
1761
1809
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1762
1810
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1763
1811
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1764
|
-
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger";
|
|
1812
|
+
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
1765
1813
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1766
1814
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1767
1815
|
}
|
|
@@ -1831,14 +1879,13 @@ var submitParallelSteps = async ({
|
|
|
1831
1879
|
initialStepCount,
|
|
1832
1880
|
invokeCount,
|
|
1833
1881
|
telemetry,
|
|
1834
|
-
|
|
1882
|
+
dispatchDebug
|
|
1835
1883
|
}) => {
|
|
1836
1884
|
const planSteps = steps.map(
|
|
1837
1885
|
(step, index) => step.getPlanStep(steps.length, initialStepCount + index)
|
|
1838
1886
|
);
|
|
1839
|
-
await
|
|
1840
|
-
|
|
1841
|
-
steps: planSteps
|
|
1887
|
+
await dispatchDebug("onInfo", {
|
|
1888
|
+
info: `Submitting ${planSteps.length} parallel steps.`
|
|
1842
1889
|
});
|
|
1843
1890
|
const result = await context.qstashClient.batch(
|
|
1844
1891
|
planSteps.map((planStep) => {
|
|
@@ -1847,10 +1894,6 @@ var submitParallelSteps = async ({
|
|
|
1847
1894
|
workflowConfig: {
|
|
1848
1895
|
workflowRunId: context.workflowRunId,
|
|
1849
1896
|
workflowUrl: context.url,
|
|
1850
|
-
failureUrl: context.failureUrl,
|
|
1851
|
-
retries: context.retries,
|
|
1852
|
-
retryDelay: context.retryDelay,
|
|
1853
|
-
flowControl: context.flowControl,
|
|
1854
1897
|
telemetry
|
|
1855
1898
|
},
|
|
1856
1899
|
userHeaders: context.headers,
|
|
@@ -1866,13 +1909,11 @@ var submitParallelSteps = async ({
|
|
|
1866
1909
|
};
|
|
1867
1910
|
})
|
|
1868
1911
|
);
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
})
|
|
1875
|
-
});
|
|
1912
|
+
if (result && result.length > 0) {
|
|
1913
|
+
await dispatchDebug("onInfo", {
|
|
1914
|
+
info: `Submitted ${planSteps.length} parallel steps. messageIds: ${result.filter((r) => r).map((r) => r.messageId).join(", ")}.`
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1876
1917
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
1877
1918
|
};
|
|
1878
1919
|
var submitSingleStep = async ({
|
|
@@ -1882,14 +1923,13 @@ var submitSingleStep = async ({
|
|
|
1882
1923
|
invokeCount,
|
|
1883
1924
|
concurrency,
|
|
1884
1925
|
telemetry,
|
|
1885
|
-
|
|
1926
|
+
dispatchDebug,
|
|
1927
|
+
dispatchLifecycle
|
|
1886
1928
|
}) => {
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
fromRequest: false,
|
|
1890
|
-
step: resultStep,
|
|
1891
|
-
stepCount: stepId
|
|
1929
|
+
await dispatchLifecycle("beforeExecution", {
|
|
1930
|
+
stepName: lazyStep.stepName
|
|
1892
1931
|
});
|
|
1932
|
+
const resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
1893
1933
|
const { headers } = lazyStep.getHeaders({
|
|
1894
1934
|
context,
|
|
1895
1935
|
step: resultStep,
|
|
@@ -1903,10 +1943,6 @@ var submitSingleStep = async ({
|
|
|
1903
1943
|
invokeCount,
|
|
1904
1944
|
telemetry
|
|
1905
1945
|
});
|
|
1906
|
-
await debug?.log("SUBMIT", "SUBMIT_STEP", {
|
|
1907
|
-
length: 1,
|
|
1908
|
-
steps: [resultStep]
|
|
1909
|
-
});
|
|
1910
1946
|
const submitResult = await lazyStep.submitStep({
|
|
1911
1947
|
context,
|
|
1912
1948
|
body,
|
|
@@ -1916,13 +1952,11 @@ var submitSingleStep = async ({
|
|
|
1916
1952
|
step: resultStep,
|
|
1917
1953
|
telemetry
|
|
1918
1954
|
});
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
})
|
|
1925
|
-
});
|
|
1955
|
+
if (submitResult && submitResult[0]) {
|
|
1956
|
+
await dispatchDebug("onInfo", {
|
|
1957
|
+
info: `Submitted step "${resultStep.stepName}" with messageId: ${submitResult[0].messageId}.`
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1926
1960
|
return resultStep;
|
|
1927
1961
|
};
|
|
1928
1962
|
|
|
@@ -1931,21 +1965,31 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1931
1965
|
context;
|
|
1932
1966
|
promises = /* @__PURE__ */ new WeakMap();
|
|
1933
1967
|
activeLazyStepList;
|
|
1934
|
-
debug;
|
|
1935
1968
|
nonPlanStepCount;
|
|
1936
1969
|
steps;
|
|
1937
1970
|
indexInCurrentList = 0;
|
|
1938
1971
|
invokeCount;
|
|
1939
1972
|
telemetry;
|
|
1973
|
+
dispatchDebug;
|
|
1974
|
+
dispatchLifecycle;
|
|
1940
1975
|
stepCount = 0;
|
|
1941
1976
|
planStepCount = 0;
|
|
1942
1977
|
executingStep = false;
|
|
1943
|
-
|
|
1978
|
+
/**
|
|
1979
|
+
* @param context workflow context
|
|
1980
|
+
* @param steps list of steps
|
|
1981
|
+
* @param dispatchDebug debug event dispatcher
|
|
1982
|
+
* @param dispatchLifecycle lifecycle event dispatcher
|
|
1983
|
+
* @param telemetry optional telemetry information
|
|
1984
|
+
* @param invokeCount optional invoke count
|
|
1985
|
+
*/
|
|
1986
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount) {
|
|
1944
1987
|
this.context = context;
|
|
1945
1988
|
this.steps = steps;
|
|
1989
|
+
this.dispatchDebug = dispatchDebug;
|
|
1990
|
+
this.dispatchLifecycle = dispatchLifecycle;
|
|
1946
1991
|
this.telemetry = telemetry;
|
|
1947
1992
|
this.invokeCount = invokeCount ?? 0;
|
|
1948
|
-
this.debug = debug;
|
|
1949
1993
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
1950
1994
|
}
|
|
1951
1995
|
/**
|
|
@@ -2013,7 +2057,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2013
2057
|
/**
|
|
2014
2058
|
* Executes a step:
|
|
2015
2059
|
* - If the step result is available in the steps, returns the result
|
|
2016
|
-
* - If the result is not
|
|
2060
|
+
* - If the result is not available, runs the function
|
|
2017
2061
|
* - Sends the result to QStash
|
|
2018
2062
|
*
|
|
2019
2063
|
* @param lazyStep lazy step to execute
|
|
@@ -2023,12 +2067,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2023
2067
|
if (this.stepCount < this.nonPlanStepCount) {
|
|
2024
2068
|
const step = this.steps[this.stepCount + this.planStepCount];
|
|
2025
2069
|
validateStep(lazyStep, step);
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2070
|
+
const parsedOut = lazyStep.parseOut(step);
|
|
2071
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2072
|
+
if (isLastMemoized) {
|
|
2073
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2074
|
+
stepName: lazyStep.stepName,
|
|
2075
|
+
result: parsedOut
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
return parsedOut;
|
|
2032
2079
|
}
|
|
2033
2080
|
const resultStep = await submitSingleStep({
|
|
2034
2081
|
context: this.context,
|
|
@@ -2037,15 +2084,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2037
2084
|
invokeCount: this.invokeCount,
|
|
2038
2085
|
concurrency: 1,
|
|
2039
2086
|
telemetry: this.telemetry,
|
|
2040
|
-
|
|
2087
|
+
dispatchDebug: this.dispatchDebug,
|
|
2088
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2041
2089
|
});
|
|
2042
2090
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2043
2091
|
}
|
|
2044
2092
|
/**
|
|
2045
2093
|
* Runs steps in parallel.
|
|
2046
2094
|
*
|
|
2047
|
-
* @param
|
|
2048
|
-
* @param stepFunctions list of async functions to run in parallel
|
|
2095
|
+
* @param parallelSteps list of lazy steps to run in parallel
|
|
2049
2096
|
* @returns results of the functions run in parallel
|
|
2050
2097
|
*/
|
|
2051
2098
|
async runParallel(parallelSteps) {
|
|
@@ -2058,12 +2105,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2058
2105
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
2059
2106
|
);
|
|
2060
2107
|
}
|
|
2061
|
-
await this.
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2108
|
+
await this.dispatchDebug("onInfo", {
|
|
2109
|
+
info: `Executing parallel steps with: ` + JSON.stringify({
|
|
2110
|
+
parallelCallState,
|
|
2111
|
+
initialStepCount,
|
|
2112
|
+
plannedParallelStepCount,
|
|
2113
|
+
stepCount: this.stepCount,
|
|
2114
|
+
planStepCount: this.planStepCount
|
|
2115
|
+
})
|
|
2067
2116
|
});
|
|
2068
2117
|
switch (parallelCallState) {
|
|
2069
2118
|
case "first": {
|
|
@@ -2073,7 +2122,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2073
2122
|
initialStepCount,
|
|
2074
2123
|
invokeCount: this.invokeCount,
|
|
2075
2124
|
telemetry: this.telemetry,
|
|
2076
|
-
|
|
2125
|
+
dispatchDebug: this.dispatchDebug
|
|
2077
2126
|
});
|
|
2078
2127
|
break;
|
|
2079
2128
|
}
|
|
@@ -2095,7 +2144,8 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2095
2144
|
invokeCount: this.invokeCount,
|
|
2096
2145
|
concurrency: parallelSteps.length,
|
|
2097
2146
|
telemetry: this.telemetry,
|
|
2098
|
-
|
|
2147
|
+
dispatchDebug: this.dispatchDebug,
|
|
2148
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2099
2149
|
});
|
|
2100
2150
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2101
2151
|
} catch (error) {
|
|
@@ -2109,13 +2159,34 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2109
2159
|
break;
|
|
2110
2160
|
}
|
|
2111
2161
|
case "discard": {
|
|
2162
|
+
const resultStep = this.steps.at(-1);
|
|
2163
|
+
const lazyStep = parallelSteps.find(
|
|
2164
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2165
|
+
);
|
|
2166
|
+
if (lazyStep) {
|
|
2167
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2168
|
+
stepName: lazyStep.stepName,
|
|
2169
|
+
result: lazyStep.parseOut(resultStep)
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2112
2172
|
throw new WorkflowAbort("discarded parallel");
|
|
2113
2173
|
}
|
|
2114
2174
|
case "last": {
|
|
2115
2175
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
2116
2176
|
validateParallelSteps(parallelSteps, parallelResultSteps);
|
|
2177
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2178
|
+
if (isLastMemoized) {
|
|
2179
|
+
const resultStep = this.steps.at(-1);
|
|
2180
|
+
const lazyStep = parallelSteps.find(
|
|
2181
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2182
|
+
);
|
|
2183
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2184
|
+
stepName: lazyStep.stepName,
|
|
2185
|
+
result: lazyStep.parseOut(resultStep)
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2117
2188
|
return parallelResultSteps.map(
|
|
2118
|
-
(step, index) => parallelSteps[index].parseOut(step
|
|
2189
|
+
(step, index) => parallelSteps[index].parseOut(step)
|
|
2119
2190
|
);
|
|
2120
2191
|
}
|
|
2121
2192
|
}
|
|
@@ -2171,7 +2242,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2171
2242
|
* @param index index of the current step
|
|
2172
2243
|
* @returns result[index] if lazyStepList > 1, otherwise result
|
|
2173
2244
|
*/
|
|
2174
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
2175
2245
|
static getResult(lazyStepList, result, index) {
|
|
2176
2246
|
if (lazyStepList.length === 1) {
|
|
2177
2247
|
return result;
|
|
@@ -2258,15 +2328,18 @@ var getProviderInfo = (api) => {
|
|
|
2258
2328
|
// src/context/api/base.ts
|
|
2259
2329
|
var BaseWorkflowApi = class {
|
|
2260
2330
|
context;
|
|
2331
|
+
/**
|
|
2332
|
+
* @param context workflow context
|
|
2333
|
+
*/
|
|
2261
2334
|
constructor({ context }) {
|
|
2262
2335
|
this.context = context;
|
|
2263
2336
|
}
|
|
2264
2337
|
/**
|
|
2265
2338
|
* context.call which uses a QStash API
|
|
2266
2339
|
*
|
|
2267
|
-
* @param stepName
|
|
2268
|
-
* @param settings
|
|
2269
|
-
* @returns
|
|
2340
|
+
* @param stepName name of the step
|
|
2341
|
+
* @param settings call settings including api configuration
|
|
2342
|
+
* @returns call response
|
|
2270
2343
|
*/
|
|
2271
2344
|
async callApi(stepName, settings) {
|
|
2272
2345
|
const { url, appendHeaders, method } = getProviderInfo(settings.api);
|
|
@@ -2274,7 +2347,7 @@ var BaseWorkflowApi = class {
|
|
|
2274
2347
|
return await this.context.call(stepName, {
|
|
2275
2348
|
url,
|
|
2276
2349
|
method: userMethod ?? method,
|
|
2277
|
-
body,
|
|
2350
|
+
body: typeof body === "string" ? body : JSON.stringify(body),
|
|
2278
2351
|
headers: {
|
|
2279
2352
|
...appendHeaders,
|
|
2280
2353
|
...headers
|
|
@@ -2351,6 +2424,129 @@ var WorkflowApi = class extends BaseWorkflowApi {
|
|
|
2351
2424
|
}
|
|
2352
2425
|
};
|
|
2353
2426
|
|
|
2427
|
+
// src/middleware/default-callbacks.ts
|
|
2428
|
+
var onErrorWithConsole = async ({ workflowRunId, error }) => {
|
|
2429
|
+
console.error(` [Upstash Workflow]: Error in workflow run ${workflowRunId}: ` + error);
|
|
2430
|
+
};
|
|
2431
|
+
var onWarningWithConsole = async ({ workflowRunId, warning }) => {
|
|
2432
|
+
console.warn(` [Upstash Workflow]: Warning in workflow run ${workflowRunId}: ` + warning);
|
|
2433
|
+
};
|
|
2434
|
+
var onInfoWithConsole = async ({
|
|
2435
|
+
workflowRunId,
|
|
2436
|
+
info
|
|
2437
|
+
}) => {
|
|
2438
|
+
console.info(` [Upstash Workflow]: Info in workflow run ${workflowRunId}: ` + info);
|
|
2439
|
+
};
|
|
2440
|
+
|
|
2441
|
+
// src/middleware/manager.ts
|
|
2442
|
+
var MiddlewareManager = class {
|
|
2443
|
+
middlewares;
|
|
2444
|
+
workflowRunId;
|
|
2445
|
+
context;
|
|
2446
|
+
/**
|
|
2447
|
+
* @param middlewares list of workflow middlewares
|
|
2448
|
+
*/
|
|
2449
|
+
constructor(middlewares = []) {
|
|
2450
|
+
this.middlewares = middlewares;
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* Assign workflow run ID - will be passed to debug events
|
|
2454
|
+
*
|
|
2455
|
+
* @param workflowRunId workflow run id to assign
|
|
2456
|
+
*/
|
|
2457
|
+
assignWorkflowRunId(workflowRunId) {
|
|
2458
|
+
this.workflowRunId = workflowRunId;
|
|
2459
|
+
}
|
|
2460
|
+
/**
|
|
2461
|
+
* Assign context - required for lifecycle events
|
|
2462
|
+
*
|
|
2463
|
+
* also assigns workflowRunId from context
|
|
2464
|
+
*
|
|
2465
|
+
* @param context workflow context to assign
|
|
2466
|
+
*/
|
|
2467
|
+
assignContext(context) {
|
|
2468
|
+
this.context = context;
|
|
2469
|
+
this.workflowRunId = context.workflowRunId;
|
|
2470
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* Internal method to execute middlewares with common error handling logic
|
|
2473
|
+
*
|
|
2474
|
+
* @param event event name to dispatch
|
|
2475
|
+
* @param params event parameters
|
|
2476
|
+
*/
|
|
2477
|
+
async executeMiddlewares(event, params) {
|
|
2478
|
+
await Promise.all(this.middlewares.map((m) => m.ensureInit()));
|
|
2479
|
+
await Promise.all(
|
|
2480
|
+
this.middlewares.map(async (middleware) => {
|
|
2481
|
+
const callback = middleware.getCallback(event);
|
|
2482
|
+
if (callback) {
|
|
2483
|
+
try {
|
|
2484
|
+
await callback(params);
|
|
2485
|
+
} catch (error) {
|
|
2486
|
+
try {
|
|
2487
|
+
const onErrorCallback = middleware.getCallback("onError") ?? onErrorWithConsole;
|
|
2488
|
+
await onErrorCallback({
|
|
2489
|
+
workflowRunId: this.workflowRunId,
|
|
2490
|
+
error
|
|
2491
|
+
});
|
|
2492
|
+
} catch (onErrorError) {
|
|
2493
|
+
console.error(
|
|
2494
|
+
`Failed while executing "onError" of middleware "${middleware.name}", falling back to logging the error to console. Error: ${onErrorError}`
|
|
2495
|
+
);
|
|
2496
|
+
onErrorWithConsole({
|
|
2497
|
+
workflowRunId: this.workflowRunId,
|
|
2498
|
+
error
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
})
|
|
2504
|
+
);
|
|
2505
|
+
if (event === "onError") {
|
|
2506
|
+
onErrorWithConsole({
|
|
2507
|
+
workflowRunId: this.workflowRunId,
|
|
2508
|
+
...params
|
|
2509
|
+
});
|
|
2510
|
+
} else if (event === "onWarning") {
|
|
2511
|
+
onWarningWithConsole({
|
|
2512
|
+
workflowRunId: this.workflowRunId,
|
|
2513
|
+
...params
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Dispatch a debug event (onError, onWarning, onInfo)
|
|
2519
|
+
*
|
|
2520
|
+
* @param event debug event name
|
|
2521
|
+
* @param params event parameters
|
|
2522
|
+
*/
|
|
2523
|
+
async dispatchDebug(event, params) {
|
|
2524
|
+
const paramsWithRunId = {
|
|
2525
|
+
...params,
|
|
2526
|
+
workflowRunId: this.workflowRunId
|
|
2527
|
+
};
|
|
2528
|
+
await this.executeMiddlewares(event, paramsWithRunId);
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Dispatch a lifecycle event (beforeExecution, afterExecution, runStarted, runCompleted)
|
|
2532
|
+
*
|
|
2533
|
+
* @param event lifecycle event name
|
|
2534
|
+
* @param params event parameters
|
|
2535
|
+
*/
|
|
2536
|
+
async dispatchLifecycle(event, params) {
|
|
2537
|
+
if (!this.context) {
|
|
2538
|
+
throw new WorkflowError(
|
|
2539
|
+
`Something went wrong while calling middlewares. Lifecycle event "${event}" was called before assignContext.`
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
2542
|
+
const paramsWithContext = {
|
|
2543
|
+
...params,
|
|
2544
|
+
context: this.context
|
|
2545
|
+
};
|
|
2546
|
+
await this.executeMiddlewares(event, paramsWithContext);
|
|
2547
|
+
}
|
|
2548
|
+
};
|
|
2549
|
+
|
|
2354
2550
|
// src/context/context.ts
|
|
2355
2551
|
var WorkflowContext = class {
|
|
2356
2552
|
executor;
|
|
@@ -2396,32 +2592,13 @@ var WorkflowContext = class {
|
|
|
2396
2592
|
*/
|
|
2397
2593
|
url;
|
|
2398
2594
|
/**
|
|
2399
|
-
*
|
|
2400
|
-
*
|
|
2401
|
-
* https://upstash.com/docs/qstash/features/callbacks#what-is-a-failure-callback
|
|
2595
|
+
* Payload of the request which started the workflow.
|
|
2402
2596
|
*
|
|
2403
|
-
*
|
|
2597
|
+
* To specify its type, you can define `serve` as follows:
|
|
2404
2598
|
*
|
|
2405
2599
|
* ```ts
|
|
2406
|
-
*
|
|
2407
|
-
*
|
|
2408
|
-
* ...
|
|
2409
|
-
* },
|
|
2410
|
-
* {
|
|
2411
|
-
* failureUrl: "new-url-value"
|
|
2412
|
-
* }
|
|
2413
|
-
* )
|
|
2414
|
-
* ```
|
|
2415
|
-
*/
|
|
2416
|
-
failureUrl;
|
|
2417
|
-
/**
|
|
2418
|
-
* Payload of the request which started the workflow.
|
|
2419
|
-
*
|
|
2420
|
-
* To specify its type, you can define `serve` as follows:
|
|
2421
|
-
*
|
|
2422
|
-
* ```ts
|
|
2423
|
-
* // set requestPayload type to MyPayload:
|
|
2424
|
-
* export const POST = serve<MyPayload>(
|
|
2600
|
+
* // set requestPayload type to MyPayload:
|
|
2601
|
+
* export const POST = serve<MyPayload>(
|
|
2425
2602
|
* async (context) => {
|
|
2426
2603
|
* ...
|
|
2427
2604
|
* }
|
|
@@ -2469,46 +2646,6 @@ var WorkflowContext = class {
|
|
|
2469
2646
|
* Default value is set to `process.env`.
|
|
2470
2647
|
*/
|
|
2471
2648
|
env;
|
|
2472
|
-
/**
|
|
2473
|
-
* Number of retries
|
|
2474
|
-
*/
|
|
2475
|
-
retries;
|
|
2476
|
-
/**
|
|
2477
|
-
* Delay between retries.
|
|
2478
|
-
*
|
|
2479
|
-
* By default, the `retryDelay` is exponential backoff.
|
|
2480
|
-
* More details can be found in: https://upstash.com/docs/qstash/features/retry.
|
|
2481
|
-
*
|
|
2482
|
-
* The `retryDelay` option allows you to customize the delay (in milliseconds) between retry attempts when message delivery fails.
|
|
2483
|
-
*
|
|
2484
|
-
* You can use mathematical expressions and the following built-in functions to calculate the delay dynamically.
|
|
2485
|
-
* The special variable `retried` represents the current retry attempt count (starting from 0).
|
|
2486
|
-
*
|
|
2487
|
-
* Supported functions:
|
|
2488
|
-
* - `pow`
|
|
2489
|
-
* - `sqrt`
|
|
2490
|
-
* - `abs`
|
|
2491
|
-
* - `exp`
|
|
2492
|
-
* - `floor`
|
|
2493
|
-
* - `ceil`
|
|
2494
|
-
* - `round`
|
|
2495
|
-
* - `min`
|
|
2496
|
-
* - `max`
|
|
2497
|
-
*
|
|
2498
|
-
* Examples of valid `retryDelay` values:
|
|
2499
|
-
* ```ts
|
|
2500
|
-
* 1000 // 1 second
|
|
2501
|
-
* 1000 * (1 + retried) // 1 second multiplied by the current retry attempt
|
|
2502
|
-
* pow(2, retried) // 2 to the power of the current retry attempt
|
|
2503
|
-
* max(10, pow(2, retried)) // The greater of 10 or 2^retried
|
|
2504
|
-
* ```
|
|
2505
|
-
*/
|
|
2506
|
-
retryDelay;
|
|
2507
|
-
/**
|
|
2508
|
-
* Settings for controlling the number of active requests
|
|
2509
|
-
* and number of requests per second with the same key.
|
|
2510
|
-
*/
|
|
2511
|
-
flowControl;
|
|
2512
2649
|
/**
|
|
2513
2650
|
* Label to apply to the workflow run.
|
|
2514
2651
|
*
|
|
@@ -2531,30 +2668,31 @@ var WorkflowContext = class {
|
|
|
2531
2668
|
headers,
|
|
2532
2669
|
steps,
|
|
2533
2670
|
url,
|
|
2534
|
-
failureUrl,
|
|
2535
|
-
debug,
|
|
2536
2671
|
initialPayload,
|
|
2537
2672
|
env,
|
|
2538
|
-
retries,
|
|
2539
|
-
retryDelay,
|
|
2540
2673
|
telemetry,
|
|
2541
2674
|
invokeCount,
|
|
2542
|
-
|
|
2543
|
-
|
|
2675
|
+
label,
|
|
2676
|
+
middlewareManager
|
|
2544
2677
|
}) {
|
|
2545
2678
|
this.qstashClient = qstashClient;
|
|
2546
2679
|
this.workflowRunId = workflowRunId;
|
|
2547
2680
|
this.steps = steps;
|
|
2548
2681
|
this.url = url;
|
|
2549
|
-
this.failureUrl = failureUrl;
|
|
2550
2682
|
this.headers = headers;
|
|
2551
2683
|
this.requestPayload = initialPayload;
|
|
2552
2684
|
this.env = env ?? {};
|
|
2553
|
-
this.retries = retries ?? DEFAULT_RETRIES;
|
|
2554
|
-
this.retryDelay = retryDelay;
|
|
2555
|
-
this.flowControl = flowControl;
|
|
2556
2685
|
this.label = label;
|
|
2557
|
-
|
|
2686
|
+
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2687
|
+
middlewareManagerInstance.assignContext(this);
|
|
2688
|
+
this.executor = new AutoExecutor(
|
|
2689
|
+
this,
|
|
2690
|
+
this.steps,
|
|
2691
|
+
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
2692
|
+
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
2693
|
+
telemetry,
|
|
2694
|
+
invokeCount
|
|
2695
|
+
);
|
|
2558
2696
|
}
|
|
2559
2697
|
/**
|
|
2560
2698
|
* Executes a workflow step
|
|
@@ -2585,7 +2723,7 @@ var WorkflowContext = class {
|
|
|
2585
2723
|
*/
|
|
2586
2724
|
async run(stepName, stepFunction) {
|
|
2587
2725
|
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
2588
|
-
return await this.addStep(new LazyFunctionStep(stepName, wrappedStepFunction));
|
|
2726
|
+
return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
|
|
2589
2727
|
}
|
|
2590
2728
|
/**
|
|
2591
2729
|
* Stops the execution for the duration provided.
|
|
@@ -2599,7 +2737,7 @@ var WorkflowContext = class {
|
|
|
2599
2737
|
* @returns undefined
|
|
2600
2738
|
*/
|
|
2601
2739
|
async sleep(stepName, duration) {
|
|
2602
|
-
await this.addStep(new LazySleepStep(stepName, duration));
|
|
2740
|
+
await this.addStep(new LazySleepStep(this, stepName, duration));
|
|
2603
2741
|
}
|
|
2604
2742
|
/**
|
|
2605
2743
|
* Stops the execution until the date time provided.
|
|
@@ -2621,48 +2759,38 @@ var WorkflowContext = class {
|
|
|
2621
2759
|
datetime = typeof datetime === "string" ? new Date(datetime) : datetime;
|
|
2622
2760
|
time = Math.round(datetime.getTime() / 1e3);
|
|
2623
2761
|
}
|
|
2624
|
-
await this.addStep(new LazySleepUntilStep(stepName, time));
|
|
2762
|
+
await this.addStep(new LazySleepUntilStep(this, stepName, time));
|
|
2625
2763
|
}
|
|
2626
2764
|
async call(stepName, settings) {
|
|
2627
2765
|
let callStep;
|
|
2628
2766
|
if ("workflow" in settings) {
|
|
2629
2767
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
2630
|
-
|
|
2768
|
+
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2769
|
+
callStep = new LazyCallStep({
|
|
2770
|
+
context: this,
|
|
2631
2771
|
stepName,
|
|
2632
2772
|
url,
|
|
2633
|
-
"POST",
|
|
2634
|
-
|
|
2635
|
-
settings.headers || {},
|
|
2636
|
-
settings.retries || 0,
|
|
2637
|
-
settings.retryDelay,
|
|
2638
|
-
settings.timeout,
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
);
|
|
2773
|
+
method: "POST",
|
|
2774
|
+
body: stringBody,
|
|
2775
|
+
headers: settings.headers || {},
|
|
2776
|
+
retries: settings.retries || 0,
|
|
2777
|
+
retryDelay: settings.retryDelay,
|
|
2778
|
+
timeout: settings.timeout,
|
|
2779
|
+
flowControl: settings.flowControl
|
|
2780
|
+
});
|
|
2642
2781
|
} else {
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
method = "GET",
|
|
2646
|
-
body,
|
|
2647
|
-
headers = {},
|
|
2648
|
-
retries = 0,
|
|
2649
|
-
retryDelay,
|
|
2650
|
-
timeout,
|
|
2651
|
-
flowControl,
|
|
2652
|
-
stringifyBody = true
|
|
2653
|
-
} = settings;
|
|
2654
|
-
callStep = new LazyCallStep(
|
|
2782
|
+
callStep = new LazyCallStep({
|
|
2783
|
+
context: this,
|
|
2655
2784
|
stepName,
|
|
2656
|
-
url,
|
|
2657
|
-
method,
|
|
2658
|
-
body,
|
|
2659
|
-
headers,
|
|
2660
|
-
retries,
|
|
2661
|
-
retryDelay,
|
|
2662
|
-
timeout,
|
|
2663
|
-
flowControl
|
|
2664
|
-
|
|
2665
|
-
);
|
|
2785
|
+
url: settings.url,
|
|
2786
|
+
method: settings.method ?? "GET",
|
|
2787
|
+
body: settings.body,
|
|
2788
|
+
headers: settings.headers ?? {},
|
|
2789
|
+
retries: settings.retries ?? 0,
|
|
2790
|
+
retryDelay: settings.retryDelay,
|
|
2791
|
+
timeout: settings.timeout,
|
|
2792
|
+
flowControl: settings.flowControl
|
|
2793
|
+
});
|
|
2666
2794
|
}
|
|
2667
2795
|
return await this.addStep(callStep);
|
|
2668
2796
|
}
|
|
@@ -2703,7 +2831,9 @@ var WorkflowContext = class {
|
|
|
2703
2831
|
async waitForEvent(stepName, eventId, options = {}) {
|
|
2704
2832
|
const { timeout = "7d" } = options;
|
|
2705
2833
|
const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
|
|
2706
|
-
return await this.addStep(
|
|
2834
|
+
return await this.addStep(
|
|
2835
|
+
new LazyWaitForEventStep(this, stepName, eventId, timeoutStr)
|
|
2836
|
+
);
|
|
2707
2837
|
}
|
|
2708
2838
|
/**
|
|
2709
2839
|
* Notify workflow runs waiting for an event
|
|
@@ -2728,20 +2858,28 @@ var WorkflowContext = class {
|
|
|
2728
2858
|
*/
|
|
2729
2859
|
async notify(stepName, eventId, eventData) {
|
|
2730
2860
|
return await this.addStep(
|
|
2731
|
-
new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
|
|
2861
|
+
new LazyNotifyStep(this, stepName, eventId, eventData, this.qstashClient.http)
|
|
2732
2862
|
);
|
|
2733
2863
|
}
|
|
2734
2864
|
async invoke(stepName, settings) {
|
|
2735
|
-
return await this.addStep(
|
|
2865
|
+
return await this.addStep(
|
|
2866
|
+
new LazyInvokeStep(this, stepName, settings)
|
|
2867
|
+
);
|
|
2868
|
+
}
|
|
2869
|
+
async createWebhook(stepName) {
|
|
2870
|
+
return await this.addStep(new LazyCreateWebhookStep(this, stepName));
|
|
2871
|
+
}
|
|
2872
|
+
async waitForWebhook(stepName, webhook, timeout) {
|
|
2873
|
+
return await this.addStep(new LazyWaitForWebhookStep(this, stepName, webhook, timeout));
|
|
2736
2874
|
}
|
|
2737
2875
|
/**
|
|
2738
2876
|
* Cancel the current workflow run
|
|
2739
2877
|
*
|
|
2740
|
-
* Will throw
|
|
2878
|
+
* Will throw WorkflowCancelAbort to stop workflow execution.
|
|
2741
2879
|
* Shouldn't be inside try/catch.
|
|
2742
2880
|
*/
|
|
2743
2881
|
async cancel() {
|
|
2744
|
-
throw new
|
|
2882
|
+
throw new WorkflowCancelAbort();
|
|
2745
2883
|
}
|
|
2746
2884
|
/**
|
|
2747
2885
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
@@ -2757,74 +2895,99 @@ var WorkflowContext = class {
|
|
|
2757
2895
|
}
|
|
2758
2896
|
};
|
|
2759
2897
|
|
|
2760
|
-
// src/
|
|
2761
|
-
var
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
eventType,
|
|
2777
|
-
details
|
|
2778
|
-
};
|
|
2779
|
-
this.logs.push(logEntry);
|
|
2780
|
-
if (this.options.logOutput === "console") {
|
|
2781
|
-
this.writeToConsole(logEntry);
|
|
2782
|
-
}
|
|
2783
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2898
|
+
// src/middleware/middleware.ts
|
|
2899
|
+
var WorkflowMiddleware = class {
|
|
2900
|
+
name;
|
|
2901
|
+
initCallbacks;
|
|
2902
|
+
/**
|
|
2903
|
+
* Callback functions
|
|
2904
|
+
*
|
|
2905
|
+
* Initially set to undefined, will be populated after init is called
|
|
2906
|
+
*/
|
|
2907
|
+
middlewareCallbacks = void 0;
|
|
2908
|
+
constructor(parameters) {
|
|
2909
|
+
this.name = parameters.name;
|
|
2910
|
+
if ("init" in parameters) {
|
|
2911
|
+
this.initCallbacks = parameters.init;
|
|
2912
|
+
} else {
|
|
2913
|
+
this.middlewareCallbacks = parameters.callbacks;
|
|
2784
2914
|
}
|
|
2785
2915
|
}
|
|
2786
|
-
|
|
2787
|
-
this.
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
logMethod(JSON.stringify(logEntry, void 0, JSON_SPACING));
|
|
2793
|
-
}
|
|
2794
|
-
shouldLog(level) {
|
|
2795
|
-
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
2796
|
-
}
|
|
2797
|
-
static getLogger(verbose) {
|
|
2798
|
-
if (typeof verbose === "object") {
|
|
2799
|
-
return verbose;
|
|
2800
|
-
} else {
|
|
2801
|
-
return verbose ? new _WorkflowLogger({
|
|
2802
|
-
logLevel: "INFO",
|
|
2803
|
-
logOutput: "console"
|
|
2804
|
-
}) : void 0;
|
|
2916
|
+
async ensureInit() {
|
|
2917
|
+
if (!this.middlewareCallbacks) {
|
|
2918
|
+
if (!this.initCallbacks) {
|
|
2919
|
+
throw new WorkflowError(`Middleware "${this.name}" has no callbacks or init defined.`);
|
|
2920
|
+
}
|
|
2921
|
+
this.middlewareCallbacks = await this.initCallbacks();
|
|
2805
2922
|
}
|
|
2806
2923
|
}
|
|
2924
|
+
/**
|
|
2925
|
+
* Gets a callback function by name.
|
|
2926
|
+
*
|
|
2927
|
+
* @param callback name of the callback to retrieve
|
|
2928
|
+
*/
|
|
2929
|
+
getCallback(callback) {
|
|
2930
|
+
return this.middlewareCallbacks?.[callback];
|
|
2931
|
+
}
|
|
2807
2932
|
};
|
|
2808
2933
|
|
|
2934
|
+
// src/middleware/logging.ts
|
|
2935
|
+
var loggingMiddleware = new WorkflowMiddleware({
|
|
2936
|
+
name: "logging",
|
|
2937
|
+
callbacks: {
|
|
2938
|
+
afterExecution(params) {
|
|
2939
|
+
const { context, ...rest } = params;
|
|
2940
|
+
console.log(" [Upstash Workflow]: Step executed:", {
|
|
2941
|
+
workflowRunId: context.workflowRunId,
|
|
2942
|
+
...rest
|
|
2943
|
+
});
|
|
2944
|
+
},
|
|
2945
|
+
beforeExecution(params) {
|
|
2946
|
+
const { context, ...rest } = params;
|
|
2947
|
+
console.log(" [Upstash Workflow]: Step execution started:", {
|
|
2948
|
+
workflowRunId: context.workflowRunId,
|
|
2949
|
+
...rest
|
|
2950
|
+
});
|
|
2951
|
+
},
|
|
2952
|
+
runStarted(params) {
|
|
2953
|
+
const { context, ...rest } = params;
|
|
2954
|
+
console.log(" [Upstash Workflow]: Workflow run started:", {
|
|
2955
|
+
workflowRunId: context.workflowRunId,
|
|
2956
|
+
...rest
|
|
2957
|
+
});
|
|
2958
|
+
},
|
|
2959
|
+
runCompleted(params) {
|
|
2960
|
+
const { context, ...rest } = params;
|
|
2961
|
+
console.log(" [Upstash Workflow]: Workflow run completed:", {
|
|
2962
|
+
workflowRunId: context.workflowRunId,
|
|
2963
|
+
...rest
|
|
2964
|
+
});
|
|
2965
|
+
},
|
|
2966
|
+
onError: onErrorWithConsole,
|
|
2967
|
+
onWarning: onWarningWithConsole,
|
|
2968
|
+
onInfo: onInfoWithConsole
|
|
2969
|
+
}
|
|
2970
|
+
});
|
|
2971
|
+
|
|
2809
2972
|
// src/serve/authorization.ts
|
|
2810
2973
|
import { Client as Client2 } from "@upstash/qstash";
|
|
2811
2974
|
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
2812
2975
|
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
2813
2976
|
disabled = true;
|
|
2814
2977
|
/**
|
|
2815
|
-
* overwrite the WorkflowContext.addStep method to always raise
|
|
2978
|
+
* overwrite the WorkflowContext.addStep method to always raise WorkflowAuthError
|
|
2816
2979
|
* error in order to stop the execution whenever we encounter a step.
|
|
2817
2980
|
*
|
|
2818
2981
|
* @param _step
|
|
2819
2982
|
*/
|
|
2820
2983
|
async addStep(_step) {
|
|
2821
|
-
throw new
|
|
2984
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
2822
2985
|
}
|
|
2823
2986
|
/**
|
|
2824
|
-
* overwrite cancel method to throw
|
|
2987
|
+
* overwrite cancel method to throw WorkflowAuthError with the disabledMessage
|
|
2825
2988
|
*/
|
|
2826
2989
|
async cancel() {
|
|
2827
|
-
throw new
|
|
2990
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
2828
2991
|
}
|
|
2829
2992
|
/**
|
|
2830
2993
|
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
@@ -2847,18 +3010,14 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
2847
3010
|
headers: context.headers,
|
|
2848
3011
|
steps: [],
|
|
2849
3012
|
url: context.url,
|
|
2850
|
-
failureUrl: context.failureUrl,
|
|
2851
3013
|
initialPayload: context.requestPayload,
|
|
2852
3014
|
env: context.env,
|
|
2853
|
-
retries: context.retries,
|
|
2854
|
-
retryDelay: context.retryDelay,
|
|
2855
|
-
flowControl: context.flowControl,
|
|
2856
3015
|
label: context.label
|
|
2857
3016
|
});
|
|
2858
3017
|
try {
|
|
2859
3018
|
await routeFunction(disabledContext);
|
|
2860
3019
|
} catch (error) {
|
|
2861
|
-
if (isInstanceOf(error,
|
|
3020
|
+
if (isInstanceOf(error, WorkflowAuthError) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
|
|
2862
3021
|
return ok("step-found");
|
|
2863
3022
|
}
|
|
2864
3023
|
console.warn(
|
|
@@ -2891,13 +3050,6 @@ var processRawSteps = (rawSteps) => {
|
|
|
2891
3050
|
const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
|
|
2892
3051
|
const otherSteps = stepsToDecode.map((rawStep) => {
|
|
2893
3052
|
const step = JSON.parse(decodeBase64(rawStep.body));
|
|
2894
|
-
if (step.waitEventId) {
|
|
2895
|
-
const newOut = {
|
|
2896
|
-
eventData: step.out ? decodeBase64(step.out) : void 0,
|
|
2897
|
-
timeout: step.waitTimeout ?? false
|
|
2898
|
-
};
|
|
2899
|
-
step.out = newOut;
|
|
2900
|
-
}
|
|
2901
3053
|
return step;
|
|
2902
3054
|
});
|
|
2903
3055
|
const steps = [initialStep, ...otherSteps];
|
|
@@ -2925,7 +3077,7 @@ var deduplicateSteps = (steps) => {
|
|
|
2925
3077
|
}
|
|
2926
3078
|
return deduplicatedSteps;
|
|
2927
3079
|
};
|
|
2928
|
-
var checkIfLastOneIsDuplicate = async (steps,
|
|
3080
|
+
var checkIfLastOneIsDuplicate = async (steps, dispatchDebug) => {
|
|
2929
3081
|
if (steps.length < 2) {
|
|
2930
3082
|
return false;
|
|
2931
3083
|
}
|
|
@@ -2936,14 +3088,41 @@ var checkIfLastOneIsDuplicate = async (steps, debug) => {
|
|
|
2936
3088
|
const step = steps[index];
|
|
2937
3089
|
if (step.stepId === lastStepId && step.targetStep === lastTargetStepId) {
|
|
2938
3090
|
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.`;
|
|
2939
|
-
await
|
|
2940
|
-
|
|
3091
|
+
await dispatchDebug?.("onWarning", {
|
|
3092
|
+
warning: message
|
|
3093
|
+
});
|
|
2941
3094
|
return true;
|
|
2942
3095
|
}
|
|
2943
3096
|
}
|
|
2944
3097
|
return false;
|
|
2945
3098
|
};
|
|
2946
3099
|
var validateRequest = (request) => {
|
|
3100
|
+
if (request.headers.get(WORKFLOW_UNKOWN_SDK_VERSION_HEADER)) {
|
|
3101
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
3102
|
+
if (!workflowRunId2) {
|
|
3103
|
+
throw new WorkflowError(
|
|
3104
|
+
"Couldn't get workflow id from header when handling unknown sdk request"
|
|
3105
|
+
);
|
|
3106
|
+
}
|
|
3107
|
+
return {
|
|
3108
|
+
unknownSdk: true,
|
|
3109
|
+
isFirstInvocation: true,
|
|
3110
|
+
workflowRunId: workflowRunId2
|
|
3111
|
+
};
|
|
3112
|
+
}
|
|
3113
|
+
if (request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
3114
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
3115
|
+
if (!workflowRunId2) {
|
|
3116
|
+
throw new WorkflowError(
|
|
3117
|
+
"Couldn't get workflow id from header when handling failure callback request"
|
|
3118
|
+
);
|
|
3119
|
+
}
|
|
3120
|
+
return {
|
|
3121
|
+
unknownSdk: false,
|
|
3122
|
+
isFirstInvocation: true,
|
|
3123
|
+
workflowRunId: workflowRunId2
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
2947
3126
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
2948
3127
|
const isFirstInvocation = !versionHeader;
|
|
2949
3128
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
@@ -2957,11 +3136,20 @@ var validateRequest = (request) => {
|
|
|
2957
3136
|
}
|
|
2958
3137
|
return {
|
|
2959
3138
|
isFirstInvocation,
|
|
2960
|
-
workflowRunId
|
|
3139
|
+
workflowRunId,
|
|
3140
|
+
unknownSdk: false
|
|
2961
3141
|
};
|
|
2962
3142
|
};
|
|
2963
|
-
var parseRequest = async (
|
|
2964
|
-
|
|
3143
|
+
var parseRequest = async ({
|
|
3144
|
+
requestPayload,
|
|
3145
|
+
isFirstInvocation,
|
|
3146
|
+
unknownSdk,
|
|
3147
|
+
workflowRunId,
|
|
3148
|
+
requester,
|
|
3149
|
+
messageId,
|
|
3150
|
+
dispatchDebug
|
|
3151
|
+
}) => {
|
|
3152
|
+
if (isFirstInvocation && !unknownSdk) {
|
|
2965
3153
|
return {
|
|
2966
3154
|
rawInitialPayload: requestPayload ?? "",
|
|
2967
3155
|
steps: [],
|
|
@@ -2971,16 +3159,14 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
2971
3159
|
} else {
|
|
2972
3160
|
let rawSteps;
|
|
2973
3161
|
if (!requestPayload) {
|
|
2974
|
-
await
|
|
2975
|
-
"
|
|
2976
|
-
|
|
2977
|
-
"request payload is empty, steps will be fetched from QStash."
|
|
2978
|
-
);
|
|
3162
|
+
await dispatchDebug?.("onInfo", {
|
|
3163
|
+
info: "request payload is empty, steps will be fetched from QStash."
|
|
3164
|
+
});
|
|
2979
3165
|
const { steps: fetchedSteps, workflowRunEnded } = await getSteps(
|
|
2980
3166
|
requester,
|
|
2981
3167
|
workflowRunId,
|
|
2982
3168
|
messageId,
|
|
2983
|
-
|
|
3169
|
+
dispatchDebug
|
|
2984
3170
|
);
|
|
2985
3171
|
if (workflowRunEnded) {
|
|
2986
3172
|
return {
|
|
@@ -2995,7 +3181,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
2995
3181
|
rawSteps = JSON.parse(requestPayload);
|
|
2996
3182
|
}
|
|
2997
3183
|
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
2998
|
-
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps,
|
|
3184
|
+
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, dispatchDebug);
|
|
2999
3185
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
3000
3186
|
return {
|
|
3001
3187
|
rawInitialPayload,
|
|
@@ -3005,16 +3191,21 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
3005
3191
|
};
|
|
3006
3192
|
}
|
|
3007
3193
|
};
|
|
3008
|
-
var handleFailure = async (
|
|
3009
|
-
|
|
3194
|
+
var handleFailure = async ({
|
|
3195
|
+
request,
|
|
3196
|
+
requestPayload,
|
|
3197
|
+
qstashClient,
|
|
3198
|
+
initialPayloadParser,
|
|
3199
|
+
routeFunction,
|
|
3200
|
+
failureFunction,
|
|
3201
|
+
env,
|
|
3202
|
+
dispatchDebug
|
|
3203
|
+
}) => {
|
|
3204
|
+
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true" && !request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
3010
3205
|
return ok({ result: "not-failure-callback" });
|
|
3011
3206
|
}
|
|
3012
3207
|
if (!failureFunction) {
|
|
3013
|
-
return
|
|
3014
|
-
new WorkflowError(
|
|
3015
|
-
"Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
|
|
3016
|
-
)
|
|
3017
|
-
);
|
|
3208
|
+
return ok({ result: "failure-function-undefined" });
|
|
3018
3209
|
}
|
|
3019
3210
|
try {
|
|
3020
3211
|
const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
|
|
@@ -3042,23 +3233,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3042
3233
|
headers: userHeaders,
|
|
3043
3234
|
steps: [],
|
|
3044
3235
|
url,
|
|
3045
|
-
failureUrl: url,
|
|
3046
|
-
debug,
|
|
3047
3236
|
env,
|
|
3048
|
-
retries,
|
|
3049
|
-
retryDelay,
|
|
3050
|
-
flowControl,
|
|
3051
3237
|
telemetry: void 0,
|
|
3052
3238
|
// not going to make requests in authentication check
|
|
3053
|
-
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0
|
|
3239
|
+
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3240
|
+
middlewareManager: void 0
|
|
3054
3241
|
});
|
|
3055
3242
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3056
3243
|
routeFunction,
|
|
3057
3244
|
workflowContext
|
|
3058
3245
|
);
|
|
3059
3246
|
if (authCheck.isErr()) {
|
|
3060
|
-
await
|
|
3061
|
-
|
|
3247
|
+
await dispatchDebug?.("onError", {
|
|
3248
|
+
error: authCheck.error
|
|
3249
|
+
});
|
|
3250
|
+
return err(authCheck.error);
|
|
3062
3251
|
} else if (authCheck.value === "run-ended") {
|
|
3063
3252
|
return err(new WorkflowError("Not authorized to run the failure function."));
|
|
3064
3253
|
}
|
|
@@ -3069,74 +3258,235 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3069
3258
|
failHeaders: header,
|
|
3070
3259
|
failStack
|
|
3071
3260
|
});
|
|
3072
|
-
return ok({ result: "
|
|
3261
|
+
return ok({ result: "failure-function-executed", response: failureResponse });
|
|
3073
3262
|
} catch (error) {
|
|
3074
3263
|
return err(error);
|
|
3075
3264
|
}
|
|
3076
3265
|
};
|
|
3077
3266
|
|
|
3078
|
-
// src/serve/
|
|
3079
|
-
import { Receiver } from "@upstash/qstash";
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3267
|
+
// src/serve/multi-region/handlers.ts
|
|
3268
|
+
import { Client as Client3, Receiver } from "@upstash/qstash";
|
|
3269
|
+
|
|
3270
|
+
// src/serve/multi-region/utils.ts
|
|
3271
|
+
var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
|
|
3272
|
+
var getRegionFromEnvironment = (environment) => {
|
|
3273
|
+
const region = environment.QSTASH_REGION;
|
|
3274
|
+
return normalizeRegionHeader(region);
|
|
3275
|
+
};
|
|
3276
|
+
function readEnvironmentVariables(environmentVariables, environment, region) {
|
|
3277
|
+
const result = {};
|
|
3278
|
+
for (const variable of environmentVariables) {
|
|
3279
|
+
const key = region ? `${region}_${variable}` : variable;
|
|
3280
|
+
result[variable] = environment[key];
|
|
3281
|
+
}
|
|
3282
|
+
return result;
|
|
3283
|
+
}
|
|
3284
|
+
function readClientEnvironmentVariables(environment, region) {
|
|
3285
|
+
return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
|
|
3286
|
+
}
|
|
3287
|
+
function readReceiverEnvironmentVariables(environment, region) {
|
|
3288
|
+
return readEnvironmentVariables(
|
|
3289
|
+
["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
|
|
3290
|
+
environment,
|
|
3291
|
+
region
|
|
3085
3292
|
);
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3293
|
+
}
|
|
3294
|
+
function normalizeRegionHeader(region) {
|
|
3295
|
+
if (!region) {
|
|
3296
|
+
return void 0;
|
|
3297
|
+
}
|
|
3298
|
+
region = region.replaceAll("-", "_").toUpperCase();
|
|
3299
|
+
if (VALID_REGIONS.includes(region)) {
|
|
3300
|
+
return region;
|
|
3301
|
+
}
|
|
3302
|
+
console.warn(
|
|
3303
|
+
`[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
|
|
3304
|
+
", "
|
|
3305
|
+
)}.`
|
|
3306
|
+
);
|
|
3307
|
+
return void 0;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
// src/serve/multi-region/handlers.ts
|
|
3311
|
+
var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
|
|
3312
|
+
if (qstashHandlers.mode === "single-region") {
|
|
3313
|
+
return qstashHandlers.handlers;
|
|
3314
|
+
}
|
|
3315
|
+
let targetRegion;
|
|
3316
|
+
if (isFirstInvocation) {
|
|
3317
|
+
targetRegion = qstashHandlers.defaultRegion;
|
|
3318
|
+
} else {
|
|
3319
|
+
const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
|
|
3320
|
+
targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
|
|
3321
|
+
}
|
|
3322
|
+
const handler = qstashHandlers.handlers[targetRegion];
|
|
3323
|
+
if (!handler) {
|
|
3324
|
+
console.warn(
|
|
3325
|
+
`[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
|
|
3326
|
+
);
|
|
3327
|
+
return qstashHandlers.handlers[qstashHandlers.defaultRegion];
|
|
3328
|
+
}
|
|
3329
|
+
return handler;
|
|
3330
|
+
};
|
|
3331
|
+
var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
|
|
3332
|
+
const clientEnv = readClientEnvironmentVariables(environment, region);
|
|
3333
|
+
const client = new Client3({
|
|
3334
|
+
...clientOptions,
|
|
3335
|
+
baseUrl: clientEnv.QSTASH_URL,
|
|
3336
|
+
token: clientEnv.QSTASH_TOKEN
|
|
3337
|
+
});
|
|
3338
|
+
const receiver = getReceiver(environment, receiverConfig, region);
|
|
3339
|
+
return { client, receiver };
|
|
3340
|
+
};
|
|
3341
|
+
var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
|
|
3342
|
+
const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
|
|
3343
|
+
if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
|
|
3344
|
+
return {
|
|
3345
|
+
isMultiRegion: true,
|
|
3346
|
+
defaultRegion: getRegionFromEnvironment(environment),
|
|
3347
|
+
clientOptions: qstashClientOption
|
|
3348
|
+
};
|
|
3349
|
+
} else {
|
|
3350
|
+
return { isMultiRegion: false };
|
|
3351
|
+
}
|
|
3352
|
+
};
|
|
3353
|
+
var getQStashHandlers = ({
|
|
3354
|
+
environment,
|
|
3355
|
+
qstashClientOption,
|
|
3356
|
+
receiverConfig
|
|
3357
|
+
}) => {
|
|
3358
|
+
const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
|
|
3359
|
+
if (multiRegion.isMultiRegion) {
|
|
3360
|
+
const regions = ["US_EAST_1", "EU_CENTRAL_1"];
|
|
3361
|
+
const handlers = {};
|
|
3362
|
+
for (const region of regions) {
|
|
3363
|
+
try {
|
|
3364
|
+
handlers[region] = createRegionalHandler(
|
|
3365
|
+
environment,
|
|
3366
|
+
receiverConfig,
|
|
3367
|
+
region,
|
|
3368
|
+
multiRegion.clientOptions
|
|
3131
3369
|
);
|
|
3370
|
+
} catch (error) {
|
|
3371
|
+
console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
|
|
3132
3372
|
}
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3373
|
+
}
|
|
3374
|
+
return {
|
|
3375
|
+
mode: "multi-region",
|
|
3376
|
+
handlers,
|
|
3377
|
+
defaultRegion: multiRegion.defaultRegion
|
|
3378
|
+
};
|
|
3379
|
+
} else {
|
|
3380
|
+
return {
|
|
3381
|
+
mode: "single-region",
|
|
3382
|
+
handlers: {
|
|
3383
|
+
client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new Client3({
|
|
3384
|
+
...qstashClientOption,
|
|
3385
|
+
baseUrl: environment.QSTASH_URL,
|
|
3386
|
+
token: environment.QSTASH_TOKEN
|
|
3387
|
+
}),
|
|
3388
|
+
receiver: getReceiver(environment, receiverConfig)
|
|
3389
|
+
}
|
|
3390
|
+
};
|
|
3391
|
+
}
|
|
3392
|
+
};
|
|
3393
|
+
var getReceiver = (environment, receiverConfig, region) => {
|
|
3394
|
+
if (typeof receiverConfig === "string") {
|
|
3395
|
+
if (receiverConfig === "set-to-undefined") {
|
|
3396
|
+
return void 0;
|
|
3397
|
+
}
|
|
3398
|
+
const receiverEnv = readReceiverEnvironmentVariables(environment, region);
|
|
3399
|
+
return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new Receiver({
|
|
3400
|
+
currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
|
|
3401
|
+
nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
|
|
3402
|
+
}) : void 0;
|
|
3403
|
+
} else {
|
|
3404
|
+
return receiverConfig;
|
|
3405
|
+
}
|
|
3406
|
+
};
|
|
3407
|
+
var getQStashHandlerOptions = (...params) => {
|
|
3408
|
+
const handlers = getQStashHandlers(...params);
|
|
3409
|
+
return {
|
|
3410
|
+
qstashHandlers: handlers,
|
|
3411
|
+
defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
|
|
3412
|
+
defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
|
|
3413
|
+
};
|
|
3414
|
+
};
|
|
3415
|
+
|
|
3416
|
+
// src/serve/options.ts
|
|
3417
|
+
var createResponseData = (workflowRunId, detailedFinishCondition) => {
|
|
3418
|
+
const baseHeaders = {
|
|
3419
|
+
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
3420
|
+
"Upstash-workflow-sdk": VERSION
|
|
3421
|
+
};
|
|
3422
|
+
if (detailedFinishCondition?.condition === "auth-fail") {
|
|
3423
|
+
return {
|
|
3424
|
+
text: JSON.stringify({
|
|
3425
|
+
message: AUTH_FAIL_MESSAGE,
|
|
3426
|
+
workflowRunId
|
|
3427
|
+
}),
|
|
3428
|
+
status: 400,
|
|
3429
|
+
headers: baseHeaders
|
|
3430
|
+
};
|
|
3431
|
+
} else if (detailedFinishCondition?.condition === "non-retryable-error") {
|
|
3432
|
+
return {
|
|
3433
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3434
|
+
status: 489,
|
|
3435
|
+
headers: {
|
|
3436
|
+
...baseHeaders,
|
|
3437
|
+
"Upstash-NonRetryable-Error": "true"
|
|
3438
|
+
}
|
|
3439
|
+
};
|
|
3440
|
+
} else if (detailedFinishCondition?.condition === "retry-after-error") {
|
|
3441
|
+
return {
|
|
3442
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3443
|
+
status: 429,
|
|
3444
|
+
headers: {
|
|
3445
|
+
...baseHeaders,
|
|
3446
|
+
"Retry-After": detailedFinishCondition.result.retryAfter.toString()
|
|
3447
|
+
}
|
|
3448
|
+
};
|
|
3449
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-executed") {
|
|
3450
|
+
return {
|
|
3451
|
+
text: JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
|
|
3452
|
+
status: 200,
|
|
3453
|
+
headers: baseHeaders
|
|
3454
|
+
};
|
|
3455
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-undefined") {
|
|
3456
|
+
return {
|
|
3457
|
+
text: JSON.stringify({
|
|
3458
|
+
workflowRunId,
|
|
3459
|
+
finishCondition: detailedFinishCondition.condition
|
|
3460
|
+
}),
|
|
3461
|
+
status: 200,
|
|
3462
|
+
headers: {
|
|
3463
|
+
...baseHeaders,
|
|
3464
|
+
"Upstash-Workflow-Failure-Callback-Notfound": "true"
|
|
3465
|
+
}
|
|
3466
|
+
};
|
|
3467
|
+
}
|
|
3468
|
+
return {
|
|
3469
|
+
text: JSON.stringify({
|
|
3470
|
+
workflowRunId,
|
|
3471
|
+
finishCondition: detailedFinishCondition.condition
|
|
3472
|
+
}),
|
|
3473
|
+
status: 200,
|
|
3474
|
+
headers: baseHeaders
|
|
3475
|
+
};
|
|
3476
|
+
};
|
|
3477
|
+
var processOptions = (options, internalOptions) => {
|
|
3478
|
+
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
3479
|
+
const {
|
|
3480
|
+
qstashHandlers,
|
|
3481
|
+
defaultClient: qstashClient,
|
|
3482
|
+
defaultReceiver: receiver
|
|
3483
|
+
} = getQStashHandlerOptions({
|
|
3484
|
+
environment,
|
|
3485
|
+
qstashClientOption: options?.qstashClient,
|
|
3486
|
+
receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
|
|
3487
|
+
});
|
|
3488
|
+
return {
|
|
3489
|
+
qstashClient,
|
|
3140
3490
|
initialPayloadParser: (initialRequest) => {
|
|
3141
3491
|
if (!initialRequest) {
|
|
3142
3492
|
return void 0;
|
|
@@ -3151,35 +3501,38 @@ var processOptions = (options) => {
|
|
|
3151
3501
|
throw error;
|
|
3152
3502
|
}
|
|
3153
3503
|
},
|
|
3154
|
-
receiver
|
|
3155
|
-
currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
|
|
3156
|
-
nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
|
|
3157
|
-
}) : void 0,
|
|
3504
|
+
receiver,
|
|
3158
3505
|
baseUrl: environment.UPSTASH_WORKFLOW_URL,
|
|
3159
3506
|
env: environment,
|
|
3160
|
-
retries: DEFAULT_RETRIES,
|
|
3161
|
-
useJSONContent: false,
|
|
3162
3507
|
disableTelemetry: false,
|
|
3163
|
-
|
|
3164
|
-
|
|
3508
|
+
...options,
|
|
3509
|
+
// merge middlewares
|
|
3510
|
+
middlewares: [options?.middlewares ?? [], options?.verbose ? [loggingMiddleware] : []].flat(),
|
|
3511
|
+
internal: {
|
|
3512
|
+
generateResponse: internalOptions?.generateResponse ?? ((responseData) => {
|
|
3513
|
+
return new Response(responseData.text, {
|
|
3514
|
+
status: responseData.status,
|
|
3515
|
+
headers: responseData.headers
|
|
3516
|
+
});
|
|
3517
|
+
}),
|
|
3518
|
+
useJSONContent: internalOptions?.useJSONContent ?? false,
|
|
3519
|
+
qstashHandlers
|
|
3520
|
+
}
|
|
3165
3521
|
};
|
|
3166
3522
|
};
|
|
3167
|
-
var determineUrls = async (request, url, baseUrl,
|
|
3523
|
+
var determineUrls = async (request, url, baseUrl, dispatchDebug) => {
|
|
3168
3524
|
const initialWorkflowUrl = url ?? request.url;
|
|
3169
3525
|
const workflowUrl = baseUrl ? initialWorkflowUrl.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, (_, matchedBaseUrl, path) => {
|
|
3170
3526
|
return baseUrl + (path || "");
|
|
3171
3527
|
}) : initialWorkflowUrl;
|
|
3172
3528
|
if (workflowUrl !== initialWorkflowUrl) {
|
|
3173
|
-
await
|
|
3174
|
-
|
|
3175
|
-
originalURL: initialWorkflowUrl,
|
|
3176
|
-
updatedURL: workflowUrl
|
|
3529
|
+
await dispatchDebug("onInfo", {
|
|
3530
|
+
info: `The workflow URL's base URL has been replaced with the provided baseUrl. Original URL: ${initialWorkflowUrl}, New URL: ${workflowUrl}`
|
|
3177
3531
|
});
|
|
3178
3532
|
}
|
|
3179
|
-
const workflowFailureUrl = failureFunction ? workflowUrl : failureUrl;
|
|
3180
3533
|
if (workflowUrl.includes("localhost")) {
|
|
3181
|
-
await
|
|
3182
|
-
|
|
3534
|
+
await dispatchDebug("onInfo", {
|
|
3535
|
+
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}`
|
|
3183
3536
|
});
|
|
3184
3537
|
}
|
|
3185
3538
|
if (!(workflowUrl.startsWith("http://") || workflowUrl.startsWith("https://"))) {
|
|
@@ -3188,205 +3541,224 @@ var determineUrls = async (request, url, baseUrl, failureFunction, failureUrl, d
|
|
|
3188
3541
|
);
|
|
3189
3542
|
}
|
|
3190
3543
|
return {
|
|
3191
|
-
workflowUrl
|
|
3192
|
-
workflowFailureUrl
|
|
3544
|
+
workflowUrl
|
|
3193
3545
|
};
|
|
3194
3546
|
};
|
|
3195
3547
|
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`;
|
|
3196
3548
|
|
|
3197
3549
|
// src/serve/index.ts
|
|
3198
|
-
var serveBase = (routeFunction, telemetry, options) => {
|
|
3550
|
+
var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
3199
3551
|
const {
|
|
3200
|
-
qstashClient,
|
|
3201
|
-
onStepFinish,
|
|
3202
3552
|
initialPayloadParser,
|
|
3203
3553
|
url,
|
|
3204
|
-
verbose,
|
|
3205
|
-
receiver,
|
|
3206
|
-
failureUrl,
|
|
3207
3554
|
failureFunction,
|
|
3208
3555
|
baseUrl,
|
|
3209
3556
|
env,
|
|
3210
|
-
retries,
|
|
3211
|
-
retryDelay,
|
|
3212
|
-
useJSONContent,
|
|
3213
3557
|
disableTelemetry,
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
} = processOptions(options);
|
|
3558
|
+
middlewares,
|
|
3559
|
+
internal
|
|
3560
|
+
} = processOptions(options, internalOptions);
|
|
3217
3561
|
telemetry = disableTelemetry ? void 0 : telemetry;
|
|
3218
|
-
const
|
|
3219
|
-
const handler = async (request) => {
|
|
3220
|
-
await
|
|
3221
|
-
|
|
3562
|
+
const { generateResponse: responseGenerator, useJSONContent } = internal;
|
|
3563
|
+
const handler = async (request, middlewareManager) => {
|
|
3564
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3565
|
+
info: `Received request for workflow execution.`
|
|
3566
|
+
});
|
|
3567
|
+
const { workflowUrl } = await determineUrls(
|
|
3222
3568
|
request,
|
|
3223
3569
|
url,
|
|
3224
3570
|
baseUrl,
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3571
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3572
|
+
);
|
|
3573
|
+
const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
|
|
3574
|
+
const regionHeader = request.headers.get("upstash-region");
|
|
3575
|
+
const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
|
|
3576
|
+
internal.qstashHandlers,
|
|
3577
|
+
regionHeader,
|
|
3578
|
+
isFirstInvocation
|
|
3228
3579
|
);
|
|
3229
3580
|
const requestPayload = await getPayload(request) ?? "";
|
|
3230
|
-
await verifyRequest(requestPayload, request.headers.get("upstash-signature"),
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3581
|
+
await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
|
|
3582
|
+
middlewareManager.assignWorkflowRunId(workflowRunId);
|
|
3583
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3584
|
+
info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
|
|
3585
|
+
});
|
|
3586
|
+
const { rawInitialPayload, steps, isLastDuplicate, workflowRunEnded } = await parseRequest({
|
|
3234
3587
|
requestPayload,
|
|
3235
3588
|
isFirstInvocation,
|
|
3589
|
+
unknownSdk,
|
|
3236
3590
|
workflowRunId,
|
|
3237
|
-
|
|
3238
|
-
request.headers.get("upstash-message-id"),
|
|
3239
|
-
|
|
3240
|
-
);
|
|
3591
|
+
requester: regionalClient.http,
|
|
3592
|
+
messageId: request.headers.get("upstash-message-id"),
|
|
3593
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3594
|
+
});
|
|
3241
3595
|
if (workflowRunEnded) {
|
|
3242
|
-
return
|
|
3243
|
-
|
|
3244
|
-
|
|
3596
|
+
return responseGenerator(
|
|
3597
|
+
createResponseData(workflowRunId, {
|
|
3598
|
+
condition: "workflow-already-ended"
|
|
3599
|
+
})
|
|
3600
|
+
);
|
|
3245
3601
|
}
|
|
3246
3602
|
if (isLastDuplicate) {
|
|
3247
|
-
return
|
|
3248
|
-
|
|
3249
|
-
|
|
3603
|
+
return responseGenerator(
|
|
3604
|
+
createResponseData(workflowRunId, {
|
|
3605
|
+
condition: "duplicate-step"
|
|
3606
|
+
})
|
|
3607
|
+
);
|
|
3250
3608
|
}
|
|
3251
|
-
const failureCheck = await handleFailure(
|
|
3609
|
+
const failureCheck = await handleFailure({
|
|
3252
3610
|
request,
|
|
3253
3611
|
requestPayload,
|
|
3254
|
-
qstashClient,
|
|
3612
|
+
qstashClient: regionalClient,
|
|
3255
3613
|
initialPayloadParser,
|
|
3256
3614
|
routeFunction,
|
|
3257
3615
|
failureFunction,
|
|
3258
3616
|
env,
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
flowControl,
|
|
3262
|
-
debug
|
|
3263
|
-
);
|
|
3617
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3618
|
+
});
|
|
3264
3619
|
if (failureCheck.isErr()) {
|
|
3265
3620
|
throw failureCheck.error;
|
|
3266
|
-
} else if (failureCheck.value.result === "
|
|
3267
|
-
await
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3621
|
+
} else if (failureCheck.value.result === "failure-function-executed") {
|
|
3622
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3623
|
+
info: `Handled failure callback.`
|
|
3624
|
+
});
|
|
3625
|
+
return responseGenerator(
|
|
3626
|
+
createResponseData(workflowRunId, {
|
|
3627
|
+
condition: "failure-callback-executed",
|
|
3628
|
+
result: failureCheck.value.response
|
|
3629
|
+
})
|
|
3630
|
+
);
|
|
3631
|
+
} else if (failureCheck.value.result === "failure-function-undefined") {
|
|
3632
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3633
|
+
info: `Failure callback invoked but no failure function defined.`
|
|
3271
3634
|
});
|
|
3635
|
+
return responseGenerator(
|
|
3636
|
+
createResponseData(workflowRunId, {
|
|
3637
|
+
condition: "failure-callback-undefined"
|
|
3638
|
+
})
|
|
3639
|
+
);
|
|
3272
3640
|
}
|
|
3273
3641
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3274
3642
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3275
3643
|
const workflowContext = new WorkflowContext({
|
|
3276
|
-
qstashClient,
|
|
3644
|
+
qstashClient: regionalClient,
|
|
3277
3645
|
workflowRunId,
|
|
3278
3646
|
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
3279
3647
|
headers: recreateUserHeaders(request.headers),
|
|
3280
3648
|
steps,
|
|
3281
3649
|
url: workflowUrl,
|
|
3282
|
-
failureUrl: workflowFailureUrl,
|
|
3283
|
-
debug,
|
|
3284
3650
|
env,
|
|
3285
|
-
retries,
|
|
3286
|
-
retryDelay,
|
|
3287
3651
|
telemetry,
|
|
3288
3652
|
invokeCount,
|
|
3289
|
-
|
|
3290
|
-
|
|
3653
|
+
label,
|
|
3654
|
+
middlewareManager
|
|
3291
3655
|
});
|
|
3292
3656
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3293
3657
|
routeFunction,
|
|
3294
3658
|
workflowContext
|
|
3295
3659
|
);
|
|
3296
3660
|
if (authCheck.isErr()) {
|
|
3297
|
-
await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
|
|
3298
3661
|
throw authCheck.error;
|
|
3299
3662
|
} else if (authCheck.value === "run-ended") {
|
|
3300
|
-
await
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3663
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3664
|
+
error: new Error(AUTH_FAIL_MESSAGE)
|
|
3665
|
+
});
|
|
3666
|
+
return responseGenerator(
|
|
3667
|
+
createResponseData(isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId, {
|
|
3668
|
+
condition: "auth-fail"
|
|
3669
|
+
})
|
|
3305
3670
|
);
|
|
3306
3671
|
}
|
|
3307
3672
|
const callReturnCheck = await handleThirdPartyCallResult({
|
|
3308
3673
|
request,
|
|
3309
3674
|
requestPayload: rawInitialPayload,
|
|
3310
|
-
client:
|
|
3675
|
+
client: regionalClient,
|
|
3311
3676
|
workflowUrl,
|
|
3312
|
-
failureUrl: workflowFailureUrl,
|
|
3313
|
-
retries,
|
|
3314
|
-
retryDelay,
|
|
3315
|
-
flowControl,
|
|
3316
3677
|
telemetry,
|
|
3317
|
-
|
|
3678
|
+
middlewareManager
|
|
3318
3679
|
});
|
|
3319
3680
|
if (callReturnCheck.isErr()) {
|
|
3320
|
-
await debug?.log("ERROR", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
3321
|
-
error: callReturnCheck.error.message
|
|
3322
|
-
});
|
|
3323
3681
|
throw callReturnCheck.error;
|
|
3324
3682
|
} else if (callReturnCheck.value === "continue-workflow") {
|
|
3325
3683
|
const result = isFirstInvocation ? await triggerFirstInvocation({
|
|
3326
3684
|
workflowContext,
|
|
3327
3685
|
useJSONContent,
|
|
3328
3686
|
telemetry,
|
|
3329
|
-
|
|
3330
|
-
|
|
3687
|
+
invokeCount,
|
|
3688
|
+
middlewareManager,
|
|
3689
|
+
unknownSdk
|
|
3331
3690
|
}) : await triggerRouteFunction({
|
|
3332
|
-
onStep: async () =>
|
|
3691
|
+
onStep: async () => {
|
|
3692
|
+
if (steps.length === 1) {
|
|
3693
|
+
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
3694
|
+
}
|
|
3695
|
+
return await routeFunction(workflowContext);
|
|
3696
|
+
},
|
|
3333
3697
|
onCleanup: async (result2) => {
|
|
3334
|
-
await
|
|
3698
|
+
await middlewareManager.dispatchLifecycle("runCompleted", {
|
|
3699
|
+
result: result2
|
|
3700
|
+
});
|
|
3701
|
+
await triggerWorkflowDelete(
|
|
3702
|
+
workflowContext,
|
|
3703
|
+
result2,
|
|
3704
|
+
false,
|
|
3705
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3706
|
+
);
|
|
3335
3707
|
},
|
|
3336
3708
|
onCancel: async () => {
|
|
3337
3709
|
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
3338
3710
|
},
|
|
3339
|
-
|
|
3711
|
+
middlewareManager
|
|
3340
3712
|
});
|
|
3341
3713
|
if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
|
|
3342
|
-
return
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3714
|
+
return responseGenerator(
|
|
3715
|
+
createResponseData(workflowRunId, {
|
|
3716
|
+
condition: "non-retryable-error",
|
|
3717
|
+
result: result.value
|
|
3718
|
+
})
|
|
3719
|
+
);
|
|
3346
3720
|
}
|
|
3347
3721
|
if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
|
|
3348
|
-
return
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3722
|
+
return responseGenerator(
|
|
3723
|
+
createResponseData(workflowRunId, {
|
|
3724
|
+
condition: "retry-after-error",
|
|
3725
|
+
result: result.value
|
|
3726
|
+
})
|
|
3727
|
+
);
|
|
3352
3728
|
}
|
|
3353
3729
|
if (result.isErr()) {
|
|
3354
|
-
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
3355
3730
|
throw result.error;
|
|
3356
3731
|
}
|
|
3357
|
-
await
|
|
3358
|
-
|
|
3359
|
-
condition: "success"
|
|
3732
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3733
|
+
info: `Workflow endpoint execution completed successfully.`
|
|
3360
3734
|
});
|
|
3735
|
+
return responseGenerator(
|
|
3736
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3737
|
+
condition: "success"
|
|
3738
|
+
})
|
|
3739
|
+
);
|
|
3361
3740
|
} else if (callReturnCheck.value === "workflow-ended") {
|
|
3362
|
-
return
|
|
3363
|
-
|
|
3364
|
-
|
|
3741
|
+
return responseGenerator(
|
|
3742
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3743
|
+
condition: "workflow-already-ended"
|
|
3744
|
+
})
|
|
3745
|
+
);
|
|
3365
3746
|
}
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3747
|
+
return responseGenerator(
|
|
3748
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3749
|
+
condition: "fromCallback"
|
|
3750
|
+
})
|
|
3751
|
+
);
|
|
3370
3752
|
};
|
|
3371
3753
|
const safeHandler = async (request) => {
|
|
3754
|
+
const middlewareManager = new MiddlewareManager(middlewares);
|
|
3372
3755
|
try {
|
|
3373
|
-
return await handler(request);
|
|
3756
|
+
return await handler(request, middlewareManager);
|
|
3374
3757
|
} catch (error) {
|
|
3375
3758
|
const formattedError = formatWorkflowError(error);
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
}
|
|
3379
|
-
const formattedOnErrorError = formatWorkflowError(onErrorError);
|
|
3380
|
-
const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
|
|
3381
|
-
Original error: '${formattedError.message}'`;
|
|
3382
|
-
console.error(errorMessage);
|
|
3383
|
-
return new Response(JSON.stringify({ error: errorMessage }), {
|
|
3384
|
-
status: 500,
|
|
3385
|
-
headers: {
|
|
3386
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3387
|
-
}
|
|
3388
|
-
});
|
|
3389
|
-
}
|
|
3759
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3760
|
+
error: isInstanceOf(error, Error) ? error : new Error(formattedError.message)
|
|
3761
|
+
});
|
|
3390
3762
|
return new Response(JSON.stringify(formattedError), {
|
|
3391
3763
|
status: 500,
|
|
3392
3764
|
headers: {
|
|
@@ -3423,7 +3795,8 @@ export {
|
|
|
3423
3795
|
prepareFlowControl,
|
|
3424
3796
|
serveManyBase,
|
|
3425
3797
|
WorkflowContext,
|
|
3426
|
-
|
|
3798
|
+
WorkflowMiddleware,
|
|
3799
|
+
loggingMiddleware,
|
|
3427
3800
|
serveBase,
|
|
3428
3801
|
serve
|
|
3429
3802
|
};
|