@upstash/workflow 0.3.0-rc → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/astro.d.mts +3 -3
- package/astro.d.ts +3 -3
- package/astro.js +877 -655
- package/astro.mjs +1 -1
- package/{chunk-AGYYZKP7.mjs → chunk-5GWDM6XJ.mjs} +871 -650
- package/cloudflare.d.mts +3 -3
- package/cloudflare.d.ts +3 -3
- package/cloudflare.js +877 -655
- package/cloudflare.mjs +1 -1
- package/express.d.mts +3 -3
- package/express.d.ts +3 -3
- package/express.js +887 -660
- package/express.mjs +11 -6
- package/h3.d.mts +3 -3
- package/h3.d.ts +3 -3
- package/h3.js +882 -656
- package/h3.mjs +6 -2
- package/hono.d.mts +3 -3
- package/hono.d.ts +3 -3
- package/hono.js +877 -655
- package/hono.mjs +1 -1
- package/index.d.mts +73 -64
- package/index.d.ts +73 -64
- package/index.js +906 -669
- package/index.mjs +30 -15
- package/nextjs.d.mts +4 -4
- package/nextjs.d.ts +4 -4
- package/nextjs.js +878 -656
- package/nextjs.mjs +2 -2
- package/package.json +1 -1
- package/{serve-many-DEwKPF6H.d.mts → serve-many-CFlNO2Iq.d.ts} +2 -2
- package/{serve-many-DVtHRxeg.d.ts → serve-many-qpxb-yr-.d.mts} +2 -2
- package/solidjs.d.mts +2 -2
- package/solidjs.d.ts +2 -2
- package/solidjs.js +882 -656
- package/solidjs.mjs +6 -2
- package/svelte.d.mts +5 -6
- package/svelte.d.ts +5 -6
- package/svelte.js +890 -661
- package/svelte.mjs +14 -7
- package/tanstack.d.mts +3 -3
- package/tanstack.d.ts +3 -3
- package/tanstack.js +877 -655
- package/tanstack.mjs +1 -1
- package/{types-DESkn7K9.d.ts → types-ByzQdZjb.d.mts} +206 -238
- package/{types-DESkn7K9.d.mts → types-ByzQdZjb.d.ts} +206 -238
package/index.js
CHANGED
|
@@ -25,9 +25,10 @@ __export(src_exports, {
|
|
|
25
25
|
WorkflowAbort: () => WorkflowAbort,
|
|
26
26
|
WorkflowContext: () => WorkflowContext,
|
|
27
27
|
WorkflowError: () => WorkflowError,
|
|
28
|
-
|
|
28
|
+
WorkflowMiddleware: () => WorkflowMiddleware,
|
|
29
29
|
WorkflowNonRetryableError: () => WorkflowNonRetryableError,
|
|
30
30
|
WorkflowRetryAfterError: () => WorkflowRetryAfterError,
|
|
31
|
+
loggingMiddleware: () => loggingMiddleware,
|
|
31
32
|
serve: () => serve
|
|
32
33
|
});
|
|
33
34
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -44,26 +45,36 @@ var WorkflowError = class extends import_qstash.QstashError {
|
|
|
44
45
|
}
|
|
45
46
|
};
|
|
46
47
|
var WorkflowAbort = class extends Error {
|
|
47
|
-
stepInfo;
|
|
48
48
|
stepName;
|
|
49
|
+
stepInfo;
|
|
49
50
|
/**
|
|
50
|
-
* whether workflow is to be canceled on abort
|
|
51
|
-
*/
|
|
52
|
-
cancelWorkflow;
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
55
51
|
* @param stepName name of the aborting step
|
|
56
52
|
* @param stepInfo step information
|
|
57
|
-
* @param cancelWorkflow
|
|
58
53
|
*/
|
|
59
|
-
constructor(stepName, stepInfo
|
|
54
|
+
constructor(stepName, stepInfo) {
|
|
60
55
|
super(
|
|
61
56
|
`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}'.`
|
|
62
57
|
);
|
|
63
58
|
this.name = "WorkflowAbort";
|
|
64
59
|
this.stepName = stepName;
|
|
65
60
|
this.stepInfo = stepInfo;
|
|
66
|
-
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var WorkflowAuthError = class extends WorkflowAbort {
|
|
64
|
+
/**
|
|
65
|
+
* @param stepName name of the step found during authorization
|
|
66
|
+
*/
|
|
67
|
+
constructor(stepName) {
|
|
68
|
+
super(stepName);
|
|
69
|
+
this.name = "WorkflowAuthError";
|
|
70
|
+
this.message = `This is an Upstash Workflow error thrown during authorization check. Found step '${stepName}' during dry-run.`;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var WorkflowCancelAbort = class extends WorkflowAbort {
|
|
74
|
+
constructor() {
|
|
75
|
+
super("cancel");
|
|
76
|
+
this.name = "WorkflowCancelAbort";
|
|
77
|
+
this.message = "Workflow has been canceled by user via context.cancel().";
|
|
67
78
|
}
|
|
68
79
|
};
|
|
69
80
|
var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
@@ -71,22 +82,22 @@ var WorkflowNonRetryableError = class extends WorkflowAbort {
|
|
|
71
82
|
* @param message error message to be displayed
|
|
72
83
|
*/
|
|
73
84
|
constructor(message) {
|
|
74
|
-
super("
|
|
85
|
+
super("non-retryable-error");
|
|
75
86
|
this.name = "WorkflowNonRetryableError";
|
|
76
|
-
|
|
87
|
+
this.message = message ?? "Workflow failed with non-retryable error.";
|
|
77
88
|
}
|
|
78
89
|
};
|
|
79
90
|
var WorkflowRetryAfterError = class extends WorkflowAbort {
|
|
80
91
|
retryAfter;
|
|
81
92
|
/**
|
|
82
|
-
* @param retryAfter time in seconds after which the workflow should be retried
|
|
83
93
|
* @param message error message to be displayed
|
|
94
|
+
* @param retryAfter time in seconds after which the workflow should be retried
|
|
84
95
|
*/
|
|
85
96
|
constructor(message, retryAfter) {
|
|
86
|
-
super("retry"
|
|
97
|
+
super("retry-after-error");
|
|
87
98
|
this.name = "WorkflowRetryAfterError";
|
|
99
|
+
this.message = message;
|
|
88
100
|
this.retryAfter = retryAfter;
|
|
89
|
-
if (message) this.message = message;
|
|
90
101
|
}
|
|
91
102
|
};
|
|
92
103
|
var formatWorkflowError = (error) => {
|
|
@@ -145,15 +156,21 @@ var makeCancelRequest = async (requester, workflowRunId) => {
|
|
|
145
156
|
});
|
|
146
157
|
return true;
|
|
147
158
|
};
|
|
148
|
-
var getSteps = async (requester, workflowRunId, messageId,
|
|
159
|
+
var getSteps = async (requester, workflowRunId, messageId, dispatchDebug) => {
|
|
149
160
|
try {
|
|
150
161
|
const steps = await requester.request({
|
|
151
162
|
path: ["v2", "workflows", "runs", workflowRunId],
|
|
152
163
|
parseResponseAsJson: true
|
|
153
164
|
});
|
|
165
|
+
if (steps.length === 1) {
|
|
166
|
+
return {
|
|
167
|
+
steps,
|
|
168
|
+
workflowRunEnded: false
|
|
169
|
+
};
|
|
170
|
+
}
|
|
154
171
|
if (!messageId) {
|
|
155
|
-
await
|
|
156
|
-
|
|
172
|
+
await dispatchDebug?.("onInfo", {
|
|
173
|
+
info: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
|
|
157
174
|
});
|
|
158
175
|
return { steps, workflowRunEnded: false };
|
|
159
176
|
} else {
|
|
@@ -162,16 +179,15 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
|
|
|
162
179
|
return { steps: [], workflowRunEnded: false };
|
|
163
180
|
}
|
|
164
181
|
const filteredSteps = steps.slice(0, index + 1);
|
|
165
|
-
await
|
|
166
|
-
|
|
182
|
+
await dispatchDebug?.("onInfo", {
|
|
183
|
+
info: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
|
|
167
184
|
});
|
|
168
185
|
return { steps: filteredSteps, workflowRunEnded: false };
|
|
169
186
|
}
|
|
170
187
|
} catch (error) {
|
|
171
188
|
if (isInstanceOf(error, import_qstash2.QstashError) && error.status === 404) {
|
|
172
|
-
await
|
|
173
|
-
|
|
174
|
-
error
|
|
189
|
+
await dispatchDebug?.("onWarning", {
|
|
190
|
+
warning: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed."
|
|
175
191
|
});
|
|
176
192
|
return { steps: void 0, workflowRunEnded: true };
|
|
177
193
|
} else {
|
|
@@ -185,15 +201,18 @@ var WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId";
|
|
|
185
201
|
var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
|
|
186
202
|
var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
|
|
187
203
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
204
|
+
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
188
205
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
189
206
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
190
207
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
208
|
+
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
209
|
+
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
191
210
|
var WORKFLOW_PROTOCOL_VERSION = "1";
|
|
192
211
|
var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
193
212
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
194
213
|
var NO_CONCURRENCY = 1;
|
|
195
214
|
var DEFAULT_RETRIES = 3;
|
|
196
|
-
var VERSION = "
|
|
215
|
+
var VERSION = "v1.0.0";
|
|
197
216
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
198
217
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
199
218
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
@@ -211,8 +230,8 @@ var NANOID_LENGTH = 21;
|
|
|
211
230
|
function getRandomInt() {
|
|
212
231
|
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
213
232
|
}
|
|
214
|
-
function nanoid() {
|
|
215
|
-
return Array.from({ length
|
|
233
|
+
function nanoid(length = NANOID_LENGTH) {
|
|
234
|
+
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
216
235
|
}
|
|
217
236
|
function getWorkflowRunId(id) {
|
|
218
237
|
return `wfr_${id ?? nanoid()}`;
|
|
@@ -229,6 +248,46 @@ function decodeBase64(base64) {
|
|
|
229
248
|
return binString;
|
|
230
249
|
}
|
|
231
250
|
}
|
|
251
|
+
function getUserIdFromToken(qstashClient) {
|
|
252
|
+
try {
|
|
253
|
+
const token = qstashClient.token;
|
|
254
|
+
const decodedToken = decodeBase64(token);
|
|
255
|
+
const tokenPayload = JSON.parse(decodedToken);
|
|
256
|
+
const userId = tokenPayload.UserID;
|
|
257
|
+
if (!userId) {
|
|
258
|
+
throw new WorkflowError("QStash token payload does not contain userId");
|
|
259
|
+
}
|
|
260
|
+
return userId;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
throw new WorkflowError(
|
|
263
|
+
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function getQStashUrl(qstashClient) {
|
|
268
|
+
try {
|
|
269
|
+
const requester = qstashClient.http;
|
|
270
|
+
const baseUrl = requester.baseUrl;
|
|
271
|
+
if (!baseUrl) {
|
|
272
|
+
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
273
|
+
}
|
|
274
|
+
return baseUrl;
|
|
275
|
+
} catch (error) {
|
|
276
|
+
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function getEventId() {
|
|
280
|
+
return `evt_${nanoid(15)}`;
|
|
281
|
+
}
|
|
282
|
+
function stringifyBody(body) {
|
|
283
|
+
if (body === void 0) {
|
|
284
|
+
return void 0;
|
|
285
|
+
}
|
|
286
|
+
if (typeof body === "string") {
|
|
287
|
+
return body;
|
|
288
|
+
}
|
|
289
|
+
return JSON.stringify(body);
|
|
290
|
+
}
|
|
232
291
|
|
|
233
292
|
// node_modules/neverthrow/dist/index.es.js
|
|
234
293
|
var defaultErrorConfig = {
|
|
@@ -654,7 +713,9 @@ var StepTypes = [
|
|
|
654
713
|
"Call",
|
|
655
714
|
"Wait",
|
|
656
715
|
"Notify",
|
|
657
|
-
"Invoke"
|
|
716
|
+
"Invoke",
|
|
717
|
+
"CreateWebhook",
|
|
718
|
+
"WaitForWebhook"
|
|
658
719
|
];
|
|
659
720
|
|
|
660
721
|
// src/workflow-requests.ts
|
|
@@ -670,23 +731,26 @@ var triggerFirstInvocation = async (params) => {
|
|
|
670
731
|
invokeCount,
|
|
671
732
|
delay,
|
|
672
733
|
notBefore,
|
|
673
|
-
|
|
734
|
+
failureUrl,
|
|
735
|
+
retries,
|
|
736
|
+
retryDelay,
|
|
737
|
+
flowControl,
|
|
738
|
+
unknownSdk
|
|
674
739
|
}) => {
|
|
675
740
|
const { headers } = getHeaders({
|
|
676
741
|
initHeaderValue: "true",
|
|
677
742
|
workflowConfig: {
|
|
678
743
|
workflowRunId: workflowContext.workflowRunId,
|
|
679
744
|
workflowUrl: workflowContext.url,
|
|
680
|
-
failureUrl
|
|
681
|
-
retries
|
|
682
|
-
retryDelay
|
|
745
|
+
failureUrl,
|
|
746
|
+
retries,
|
|
747
|
+
retryDelay,
|
|
683
748
|
telemetry,
|
|
684
|
-
flowControl
|
|
749
|
+
flowControl,
|
|
685
750
|
useJSONContent: useJSONContent ?? false
|
|
686
751
|
},
|
|
687
752
|
invokeCount: invokeCount ?? 0,
|
|
688
|
-
userHeaders: workflowContext.headers
|
|
689
|
-
keepTriggerConfig
|
|
753
|
+
userHeaders: workflowContext.headers
|
|
690
754
|
});
|
|
691
755
|
if (workflowContext.headers.get("content-type")) {
|
|
692
756
|
headers["content-type"] = workflowContext.headers.get("content-type");
|
|
@@ -694,6 +758,9 @@ var triggerFirstInvocation = async (params) => {
|
|
|
694
758
|
if (useJSONContent) {
|
|
695
759
|
headers["content-type"] = "application/json";
|
|
696
760
|
}
|
|
761
|
+
if (unknownSdk) {
|
|
762
|
+
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
763
|
+
}
|
|
697
764
|
if (workflowContext.label) {
|
|
698
765
|
headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
|
|
699
766
|
}
|
|
@@ -714,21 +781,15 @@ var triggerFirstInvocation = async (params) => {
|
|
|
714
781
|
for (let i = 0; i < results.length; i++) {
|
|
715
782
|
const result = results[i];
|
|
716
783
|
const invocationParams = firstInvocationParams[i];
|
|
784
|
+
invocationParams.middlewareManager?.assignContext(invocationParams.workflowContext);
|
|
717
785
|
if (result.deduplicated) {
|
|
718
|
-
await invocationParams.
|
|
719
|
-
|
|
720
|
-
headers: invocationBatch[i].headers,
|
|
721
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
722
|
-
url: invocationParams.workflowContext.url,
|
|
723
|
-
messageId: result.messageId
|
|
786
|
+
await invocationParams.middlewareManager?.dispatchDebug("onWarning", {
|
|
787
|
+
warning: `Workflow run ${invocationParams.workflowContext.workflowRunId} already exists. A new one isn't created.`
|
|
724
788
|
});
|
|
725
789
|
invocationStatuses.push("workflow-run-already-exists");
|
|
726
790
|
} else {
|
|
727
|
-
await invocationParams.
|
|
728
|
-
|
|
729
|
-
requestPayload: invocationParams.workflowContext.requestPayload,
|
|
730
|
-
url: invocationParams.workflowContext.url,
|
|
731
|
-
messageId: result.messageId
|
|
791
|
+
await invocationParams.middlewareManager?.dispatchDebug("onInfo", {
|
|
792
|
+
info: `Workflow run started successfully with URL ${invocationParams.workflowContext.url}.`
|
|
732
793
|
});
|
|
733
794
|
invocationStatuses.push("success");
|
|
734
795
|
}
|
|
@@ -750,7 +811,7 @@ var triggerRouteFunction = async ({
|
|
|
750
811
|
onCleanup,
|
|
751
812
|
onStep,
|
|
752
813
|
onCancel,
|
|
753
|
-
|
|
814
|
+
middlewareManager
|
|
754
815
|
}) => {
|
|
755
816
|
try {
|
|
756
817
|
const result = await onStep();
|
|
@@ -759,27 +820,25 @@ var triggerRouteFunction = async ({
|
|
|
759
820
|
} catch (error) {
|
|
760
821
|
const error_ = error;
|
|
761
822
|
if (isInstanceOf(error, import_qstash3.QstashError) && error.status === 400) {
|
|
762
|
-
await
|
|
763
|
-
|
|
764
|
-
name: error.name,
|
|
765
|
-
errorMessage: error.message
|
|
823
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
824
|
+
warning: `Tried to append to a cancelled workflow. Exiting without publishing. Error: ${error.message}`
|
|
766
825
|
});
|
|
767
826
|
return ok("workflow-was-finished");
|
|
768
827
|
} else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
|
|
769
828
|
return ok(error_);
|
|
770
|
-
} else if (
|
|
771
|
-
return err(error_);
|
|
772
|
-
} else if (error_.cancelWorkflow) {
|
|
829
|
+
} else if (isInstanceOf(error_, WorkflowCancelAbort)) {
|
|
773
830
|
await onCancel();
|
|
774
831
|
return ok("workflow-finished");
|
|
775
|
-
} else {
|
|
832
|
+
} else if (isInstanceOf(error_, WorkflowAbort)) {
|
|
776
833
|
return ok("step-finished");
|
|
834
|
+
} else {
|
|
835
|
+
return err(error_);
|
|
777
836
|
}
|
|
778
837
|
}
|
|
779
838
|
};
|
|
780
|
-
var triggerWorkflowDelete = async (workflowContext, result,
|
|
781
|
-
await
|
|
782
|
-
|
|
839
|
+
var triggerWorkflowDelete = async (workflowContext, result, cancel = false, dispatchDebug) => {
|
|
840
|
+
await dispatchDebug?.("onInfo", {
|
|
841
|
+
info: `Deleting workflow run ${workflowContext.workflowRunId} from QStash` + (cancel ? " with cancel=true." : ".")
|
|
783
842
|
});
|
|
784
843
|
await workflowContext.qstashClient.http.request({
|
|
785
844
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
@@ -787,11 +846,9 @@ var triggerWorkflowDelete = async (workflowContext, result, debug, cancel = fals
|
|
|
787
846
|
parseResponseAsJson: false,
|
|
788
847
|
body: JSON.stringify(result)
|
|
789
848
|
});
|
|
790
|
-
await
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
`workflow run ${workflowContext.workflowRunId} deleted.`
|
|
794
|
-
);
|
|
849
|
+
await dispatchDebug?.("onInfo", {
|
|
850
|
+
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
851
|
+
});
|
|
795
852
|
};
|
|
796
853
|
var recreateUserHeaders = (headers) => {
|
|
797
854
|
const filteredHeaders = new Headers();
|
|
@@ -813,12 +870,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
813
870
|
requestPayload,
|
|
814
871
|
client,
|
|
815
872
|
workflowUrl,
|
|
816
|
-
failureUrl,
|
|
817
|
-
retries,
|
|
818
|
-
retryDelay,
|
|
819
873
|
telemetry,
|
|
820
|
-
|
|
821
|
-
debug
|
|
874
|
+
middlewareManager
|
|
822
875
|
}) => {
|
|
823
876
|
try {
|
|
824
877
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
@@ -835,7 +888,7 @@ var handleThirdPartyCallResult = async ({
|
|
|
835
888
|
client.http,
|
|
836
889
|
workflowRunId2,
|
|
837
890
|
messageId,
|
|
838
|
-
|
|
891
|
+
middlewareManager?.dispatchDebug.bind(middlewareManager)
|
|
839
892
|
);
|
|
840
893
|
if (workflowRunEnded) {
|
|
841
894
|
return ok("workflow-ended");
|
|
@@ -849,9 +902,8 @@ var handleThirdPartyCallResult = async ({
|
|
|
849
902
|
}
|
|
850
903
|
const callbackMessage = JSON.parse(callbackPayload);
|
|
851
904
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
852
|
-
await
|
|
853
|
-
|
|
854
|
-
body: atob(callbackMessage.body ?? "")
|
|
905
|
+
await middlewareManager?.dispatchDebug("onWarning", {
|
|
906
|
+
warning: `Third party call returned status ${callbackMessage.status}. Retrying (${callbackMessage.retried} out of ${callbackMessage.maxRetries}).`
|
|
855
907
|
});
|
|
856
908
|
console.warn(
|
|
857
909
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
@@ -884,11 +936,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
884
936
|
workflowConfig: {
|
|
885
937
|
workflowRunId,
|
|
886
938
|
workflowUrl,
|
|
887
|
-
|
|
888
|
-
retries,
|
|
889
|
-
retryDelay,
|
|
890
|
-
telemetry,
|
|
891
|
-
flowControl
|
|
939
|
+
telemetry
|
|
892
940
|
},
|
|
893
941
|
userHeaders,
|
|
894
942
|
invokeCount: Number(invokeCount)
|
|
@@ -905,19 +953,17 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
905
953
|
out: JSON.stringify(callResponse),
|
|
906
954
|
concurrent: Number(concurrentString)
|
|
907
955
|
};
|
|
908
|
-
await
|
|
909
|
-
|
|
910
|
-
headers: requestHeaders,
|
|
911
|
-
url: workflowUrl
|
|
956
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
957
|
+
info: `Submitting third party call result, step ${stepName} (${stepIdString}).`
|
|
912
958
|
});
|
|
913
|
-
|
|
959
|
+
await client.publishJSON({
|
|
914
960
|
headers: requestHeaders,
|
|
915
961
|
method: "POST",
|
|
916
962
|
body: callResultStep,
|
|
917
963
|
url: workflowUrl
|
|
918
964
|
});
|
|
919
|
-
await
|
|
920
|
-
|
|
965
|
+
await middlewareManager?.dispatchDebug("onInfo", {
|
|
966
|
+
info: `Third party call result submitted successfully, step ${stepName} (${stepIdString}).`
|
|
921
967
|
});
|
|
922
968
|
return ok("is-call-return");
|
|
923
969
|
} else {
|
|
@@ -966,15 +1012,17 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
|
|
|
966
1012
|
// src/context/steps.ts
|
|
967
1013
|
var BaseLazyStep = class _BaseLazyStep {
|
|
968
1014
|
stepName;
|
|
969
|
-
|
|
1015
|
+
context;
|
|
1016
|
+
constructor(context, stepName) {
|
|
1017
|
+
this.context = context;
|
|
970
1018
|
if (!stepName) {
|
|
971
1019
|
throw new WorkflowError(
|
|
972
1020
|
"A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
|
|
973
1021
|
);
|
|
974
1022
|
}
|
|
975
1023
|
if (typeof stepName !== "string") {
|
|
976
|
-
|
|
977
|
-
|
|
1024
|
+
throw new WorkflowError(
|
|
1025
|
+
`A workflow step name must be a string. Received "${stepName}" (${typeof stepName}).`
|
|
978
1026
|
);
|
|
979
1027
|
}
|
|
980
1028
|
this.stepName = stepName;
|
|
@@ -984,13 +1032,14 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
984
1032
|
*
|
|
985
1033
|
* will be called when returning the steps to the context from auto executor
|
|
986
1034
|
*
|
|
987
|
-
* @param
|
|
1035
|
+
* @param step step
|
|
988
1036
|
* @returns parsed out field
|
|
989
1037
|
*/
|
|
990
|
-
parseOut(
|
|
1038
|
+
parseOut(step) {
|
|
1039
|
+
const out = step.out;
|
|
991
1040
|
if (out === void 0) {
|
|
992
1041
|
if (this.allowUndefinedOut) {
|
|
993
|
-
return
|
|
1042
|
+
return this.handleUndefinedOut(step);
|
|
994
1043
|
} else {
|
|
995
1044
|
throw new WorkflowError(
|
|
996
1045
|
`Error while parsing output of ${this.stepType} step. Expected a string, but got: undefined`
|
|
@@ -998,27 +1047,26 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
998
1047
|
}
|
|
999
1048
|
}
|
|
1000
1049
|
if (typeof out === "object") {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
return out;
|
|
1006
|
-
}
|
|
1007
|
-
return {
|
|
1008
|
-
...out,
|
|
1009
|
-
eventData: _BaseLazyStep.tryParsing(out.eventData)
|
|
1010
|
-
};
|
|
1050
|
+
console.warn(
|
|
1051
|
+
`Error while parsing ${this.stepType} step output. Expected a string, but got object. Please reach out to Upstash Support.`
|
|
1052
|
+
);
|
|
1053
|
+
return out;
|
|
1011
1054
|
}
|
|
1012
1055
|
if (typeof out !== "string") {
|
|
1013
1056
|
throw new WorkflowError(
|
|
1014
1057
|
`Error while parsing output of ${this.stepType} step. Expected a string or undefined, but got: ${typeof out}`
|
|
1015
1058
|
);
|
|
1016
1059
|
}
|
|
1017
|
-
return this.safeParseOut(out);
|
|
1060
|
+
return this.safeParseOut(out, step);
|
|
1018
1061
|
}
|
|
1019
|
-
|
|
1062
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1063
|
+
safeParseOut(out, step) {
|
|
1020
1064
|
return _BaseLazyStep.tryParsing(out);
|
|
1021
1065
|
}
|
|
1066
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1067
|
+
handleUndefinedOut(step) {
|
|
1068
|
+
return void 0;
|
|
1069
|
+
}
|
|
1022
1070
|
static tryParsing(stepOut) {
|
|
1023
1071
|
try {
|
|
1024
1072
|
return JSON.parse(stepOut);
|
|
@@ -1036,12 +1084,8 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1036
1084
|
workflowConfig: {
|
|
1037
1085
|
workflowRunId: context.workflowRunId,
|
|
1038
1086
|
workflowUrl: context.url,
|
|
1039
|
-
failureUrl: context.failureUrl,
|
|
1040
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1041
|
-
retryDelay: context.retryDelay,
|
|
1042
1087
|
useJSONContent: false,
|
|
1043
|
-
telemetry
|
|
1044
|
-
flowControl: context.flowControl
|
|
1088
|
+
telemetry
|
|
1045
1089
|
},
|
|
1046
1090
|
userHeaders: context.headers,
|
|
1047
1091
|
invokeCount,
|
|
@@ -1057,9 +1101,6 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1057
1101
|
body,
|
|
1058
1102
|
headers,
|
|
1059
1103
|
method: "POST",
|
|
1060
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1061
|
-
retryDelay: context.retryDelay,
|
|
1062
|
-
flowControl: context.flowControl,
|
|
1063
1104
|
url: context.url
|
|
1064
1105
|
}
|
|
1065
1106
|
]);
|
|
@@ -1069,8 +1110,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
|
|
|
1069
1110
|
stepFunction;
|
|
1070
1111
|
stepType = "Run";
|
|
1071
1112
|
allowUndefinedOut = true;
|
|
1072
|
-
constructor(stepName, stepFunction) {
|
|
1073
|
-
super(stepName);
|
|
1113
|
+
constructor(context, stepName, stepFunction) {
|
|
1114
|
+
super(context, stepName);
|
|
1074
1115
|
this.stepFunction = stepFunction;
|
|
1075
1116
|
}
|
|
1076
1117
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1100,8 +1141,8 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1100
1141
|
sleep;
|
|
1101
1142
|
stepType = "SleepFor";
|
|
1102
1143
|
allowUndefinedOut = true;
|
|
1103
|
-
constructor(stepName, sleep) {
|
|
1104
|
-
super(stepName);
|
|
1144
|
+
constructor(context, stepName, sleep) {
|
|
1145
|
+
super(context, stepName);
|
|
1105
1146
|
this.sleep = sleep;
|
|
1106
1147
|
}
|
|
1107
1148
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1130,9 +1171,6 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1130
1171
|
headers,
|
|
1131
1172
|
method: "POST",
|
|
1132
1173
|
url: context.url,
|
|
1133
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1134
|
-
retryDelay: context.retryDelay,
|
|
1135
|
-
flowControl: context.flowControl,
|
|
1136
1174
|
delay: isParallel ? void 0 : this.sleep
|
|
1137
1175
|
}
|
|
1138
1176
|
]);
|
|
@@ -1142,8 +1180,8 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1142
1180
|
sleepUntil;
|
|
1143
1181
|
stepType = "SleepUntil";
|
|
1144
1182
|
allowUndefinedOut = true;
|
|
1145
|
-
constructor(stepName, sleepUntil) {
|
|
1146
|
-
super(stepName);
|
|
1183
|
+
constructor(context, stepName, sleepUntil) {
|
|
1184
|
+
super(context, stepName);
|
|
1147
1185
|
this.sleepUntil = sleepUntil;
|
|
1148
1186
|
}
|
|
1149
1187
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1175,9 +1213,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1175
1213
|
headers,
|
|
1176
1214
|
method: "POST",
|
|
1177
1215
|
url: context.url,
|
|
1178
|
-
retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
|
|
1179
|
-
retryDelay: context.retryDelay,
|
|
1180
|
-
flowControl: context.flowControl,
|
|
1181
1216
|
notBefore: isParallel ? void 0 : this.sleepUntil
|
|
1182
1217
|
}
|
|
1183
1218
|
]);
|
|
@@ -1192,20 +1227,18 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1192
1227
|
retryDelay;
|
|
1193
1228
|
timeout;
|
|
1194
1229
|
flowControl;
|
|
1195
|
-
stringifyBody;
|
|
1196
1230
|
stepType = "Call";
|
|
1197
1231
|
allowUndefinedOut = false;
|
|
1198
|
-
constructor(
|
|
1199
|
-
super(stepName);
|
|
1200
|
-
this.url = url;
|
|
1201
|
-
this.method = method;
|
|
1202
|
-
this.body = body;
|
|
1203
|
-
this.headers = headers;
|
|
1204
|
-
this.retries = retries;
|
|
1205
|
-
this.retryDelay = retryDelay;
|
|
1206
|
-
this.timeout = timeout;
|
|
1207
|
-
this.flowControl = flowControl;
|
|
1208
|
-
this.stringifyBody = stringifyBody;
|
|
1232
|
+
constructor(params) {
|
|
1233
|
+
super(params.context, params.stepName);
|
|
1234
|
+
this.url = params.url;
|
|
1235
|
+
this.method = params.method ?? "GET";
|
|
1236
|
+
this.body = params.body;
|
|
1237
|
+
this.headers = params.headers ?? {};
|
|
1238
|
+
this.retries = params.retries ?? 0;
|
|
1239
|
+
this.retryDelay = params.retryDelay;
|
|
1240
|
+
this.timeout = params.timeout;
|
|
1241
|
+
this.flowControl = params.flowControl;
|
|
1209
1242
|
}
|
|
1210
1243
|
getPlanStep(concurrent, targetStep) {
|
|
1211
1244
|
return {
|
|
@@ -1302,7 +1335,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1302
1335
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
1303
1336
|
"Upstash-Callback-Workflow-Init": "false",
|
|
1304
1337
|
"Upstash-Callback-Workflow-Url": context.url,
|
|
1305
|
-
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger",
|
|
1338
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1306
1339
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
1307
1340
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
1308
1341
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
|
|
@@ -1315,22 +1348,10 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1315
1348
|
};
|
|
1316
1349
|
}
|
|
1317
1350
|
async submitStep({ context, headers }) {
|
|
1318
|
-
let callBody;
|
|
1319
|
-
if (this.stringifyBody) {
|
|
1320
|
-
callBody = JSON.stringify(this.body);
|
|
1321
|
-
} else {
|
|
1322
|
-
if (typeof this.body === "string") {
|
|
1323
|
-
callBody = this.body;
|
|
1324
|
-
} else {
|
|
1325
|
-
throw new WorkflowError(
|
|
1326
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your call step."
|
|
1327
|
-
);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
1351
|
return await context.qstashClient.batch([
|
|
1331
1352
|
{
|
|
1332
1353
|
headers,
|
|
1333
|
-
body:
|
|
1354
|
+
body: this.body,
|
|
1334
1355
|
method: this.method,
|
|
1335
1356
|
url: this.url,
|
|
1336
1357
|
retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
|
|
@@ -1340,13 +1361,12 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
|
|
|
1340
1361
|
]);
|
|
1341
1362
|
}
|
|
1342
1363
|
};
|
|
1343
|
-
var
|
|
1364
|
+
var LazyWaitEventStep = class extends BaseLazyStep {
|
|
1344
1365
|
eventId;
|
|
1345
1366
|
timeout;
|
|
1346
|
-
stepType = "Wait";
|
|
1347
1367
|
allowUndefinedOut = false;
|
|
1348
|
-
constructor(stepName, eventId, timeout) {
|
|
1349
|
-
super(stepName);
|
|
1368
|
+
constructor(context, stepName, eventId, timeout) {
|
|
1369
|
+
super(context, stepName);
|
|
1350
1370
|
this.eventId = eventId;
|
|
1351
1371
|
this.timeout = timeout;
|
|
1352
1372
|
}
|
|
@@ -1371,13 +1391,6 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1371
1391
|
concurrent
|
|
1372
1392
|
});
|
|
1373
1393
|
}
|
|
1374
|
-
safeParseOut(out) {
|
|
1375
|
-
const result = JSON.parse(out);
|
|
1376
|
-
return {
|
|
1377
|
-
...result,
|
|
1378
|
-
eventData: BaseLazyStep.tryParsing(result.eventData)
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1381
1394
|
getHeaders({ context, telemetry, invokeCount, step }) {
|
|
1382
1395
|
const headers = super.getHeaders({ context, telemetry, invokeCount, step });
|
|
1383
1396
|
headers.headers["Upstash-Workflow-CallType"] = "step";
|
|
@@ -1411,7 +1424,7 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1411
1424
|
timeoutHeaders,
|
|
1412
1425
|
step: {
|
|
1413
1426
|
stepId: step.stepId,
|
|
1414
|
-
stepType:
|
|
1427
|
+
stepType: this.stepType,
|
|
1415
1428
|
stepName: step.stepName,
|
|
1416
1429
|
concurrent: step.concurrent,
|
|
1417
1430
|
targetStep: step.targetStep
|
|
@@ -1432,8 +1445,8 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
|
|
|
1432
1445
|
};
|
|
1433
1446
|
var LazyNotifyStep = class extends LazyFunctionStep {
|
|
1434
1447
|
stepType = "Notify";
|
|
1435
|
-
constructor(stepName, eventId, eventData, requester) {
|
|
1436
|
-
super(stepName, async () => {
|
|
1448
|
+
constructor(context, stepName, eventId, eventData, requester) {
|
|
1449
|
+
super(context, stepName, async () => {
|
|
1437
1450
|
const notifyResponse = await makeNotifyRequest(requester, eventId, eventData);
|
|
1438
1451
|
return {
|
|
1439
1452
|
eventId,
|
|
@@ -1458,28 +1471,10 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1458
1471
|
* workflow id of the invoked workflow
|
|
1459
1472
|
*/
|
|
1460
1473
|
workflowId;
|
|
1461
|
-
constructor(stepName, {
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
workflowRunId,
|
|
1466
|
-
retries,
|
|
1467
|
-
retryDelay,
|
|
1468
|
-
flowControl,
|
|
1469
|
-
stringifyBody = true
|
|
1470
|
-
}) {
|
|
1471
|
-
super(stepName);
|
|
1472
|
-
this.params = {
|
|
1473
|
-
workflow,
|
|
1474
|
-
body,
|
|
1475
|
-
headers,
|
|
1476
|
-
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1477
|
-
retries,
|
|
1478
|
-
retryDelay,
|
|
1479
|
-
flowControl,
|
|
1480
|
-
stringifyBody
|
|
1481
|
-
};
|
|
1482
|
-
const { workflowId } = workflow;
|
|
1474
|
+
constructor(context, stepName, params) {
|
|
1475
|
+
super(context, stepName);
|
|
1476
|
+
this.params = params;
|
|
1477
|
+
const { workflowId } = params.workflow;
|
|
1483
1478
|
if (!workflowId) {
|
|
1484
1479
|
throw new WorkflowError("You can only invoke workflow which has a workflowId");
|
|
1485
1480
|
}
|
|
@@ -1519,31 +1514,18 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1519
1514
|
workflowConfig: {
|
|
1520
1515
|
workflowRunId: context.workflowRunId,
|
|
1521
1516
|
workflowUrl: context.url,
|
|
1522
|
-
failureUrl: context.failureUrl,
|
|
1523
|
-
retries: context.retries,
|
|
1524
|
-
retryDelay: context.retryDelay,
|
|
1525
1517
|
telemetry,
|
|
1526
|
-
flowControl: context.flowControl,
|
|
1527
1518
|
useJSONContent: false
|
|
1528
1519
|
},
|
|
1529
1520
|
userHeaders: context.headers,
|
|
1530
1521
|
invokeCount
|
|
1531
1522
|
});
|
|
1523
|
+
context.qstashClient.http.headers?.forEach((value, key) => {
|
|
1524
|
+
invokerHeaders[key] = value;
|
|
1525
|
+
});
|
|
1532
1526
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1533
|
-
let invokeBody;
|
|
1534
|
-
if (this.params.stringifyBody) {
|
|
1535
|
-
invokeBody = JSON.stringify(this.params.body);
|
|
1536
|
-
} else {
|
|
1537
|
-
if (typeof this.params.body === "string") {
|
|
1538
|
-
invokeBody = this.params.body;
|
|
1539
|
-
} else {
|
|
1540
|
-
throw new WorkflowError(
|
|
1541
|
-
"When stringifyBody is false, body must be a string. Please check the body type of your invoke step."
|
|
1542
|
-
);
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
1527
|
const request = {
|
|
1546
|
-
body:
|
|
1528
|
+
body: stringifyBody(this.params.body),
|
|
1547
1529
|
headers: Object.fromEntries(
|
|
1548
1530
|
Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
|
|
1549
1531
|
),
|
|
@@ -1554,34 +1536,19 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1554
1536
|
return JSON.stringify(request);
|
|
1555
1537
|
}
|
|
1556
1538
|
getHeaders({ context, telemetry, invokeCount }) {
|
|
1557
|
-
const {
|
|
1558
|
-
workflow,
|
|
1559
|
-
headers = {},
|
|
1560
|
-
workflowRunId = getWorkflowRunId(),
|
|
1561
|
-
retries,
|
|
1562
|
-
retryDelay,
|
|
1563
|
-
flowControl
|
|
1564
|
-
} = this.params;
|
|
1539
|
+
const { workflow, headers = {}, workflowRunId, retries, retryDelay, flowControl } = this.params;
|
|
1565
1540
|
const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
|
|
1566
|
-
const {
|
|
1567
|
-
retries: workflowRetries,
|
|
1568
|
-
retryDelay: workflowRetryDelay,
|
|
1569
|
-
failureFunction,
|
|
1570
|
-
failureUrl,
|
|
1571
|
-
useJSONContent,
|
|
1572
|
-
flowControl: workflowFlowControl
|
|
1573
|
-
} = workflow.options;
|
|
1574
1541
|
const { headers: triggerHeaders, contentType } = getHeaders({
|
|
1575
1542
|
initHeaderValue: "true",
|
|
1576
1543
|
workflowConfig: {
|
|
1577
|
-
workflowRunId,
|
|
1544
|
+
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1578
1545
|
workflowUrl: newUrl,
|
|
1579
|
-
retries
|
|
1580
|
-
retryDelay
|
|
1546
|
+
retries,
|
|
1547
|
+
retryDelay,
|
|
1581
1548
|
telemetry,
|
|
1582
|
-
failureUrl:
|
|
1583
|
-
flowControl
|
|
1584
|
-
useJSONContent: useJSONContent ?? false
|
|
1549
|
+
failureUrl: newUrl,
|
|
1550
|
+
flowControl,
|
|
1551
|
+
useJSONContent: workflow.useJSONContent ?? false
|
|
1585
1552
|
},
|
|
1586
1553
|
invokeCount: invokeCount + 1,
|
|
1587
1554
|
userHeaders: new Headers(headers)
|
|
@@ -1600,6 +1567,88 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1600
1567
|
return [result];
|
|
1601
1568
|
}
|
|
1602
1569
|
};
|
|
1570
|
+
var LazyCreateWebhookStep = class extends BaseLazyStep {
|
|
1571
|
+
stepType = "CreateWebhook";
|
|
1572
|
+
allowUndefinedOut = false;
|
|
1573
|
+
getPlanStep(concurrent, targetStep) {
|
|
1574
|
+
return {
|
|
1575
|
+
stepId: 0,
|
|
1576
|
+
stepName: this.stepName,
|
|
1577
|
+
stepType: this.stepType,
|
|
1578
|
+
concurrent,
|
|
1579
|
+
targetStep
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
async getResultStep(concurrent, stepId) {
|
|
1583
|
+
return {
|
|
1584
|
+
stepId,
|
|
1585
|
+
stepName: this.stepName,
|
|
1586
|
+
stepType: this.stepType,
|
|
1587
|
+
out: void 0,
|
|
1588
|
+
concurrent
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
getBody({ step, context }) {
|
|
1592
|
+
const userId = getUserIdFromToken(context.qstashClient);
|
|
1593
|
+
const workflowRunId = context.workflowRunId;
|
|
1594
|
+
const eventId = getEventId();
|
|
1595
|
+
const qstashUrl = getQStashUrl(this.context.qstashClient);
|
|
1596
|
+
return JSON.stringify({
|
|
1597
|
+
...step,
|
|
1598
|
+
out: JSON.stringify({
|
|
1599
|
+
webhookUrl: `${qstashUrl}/v2/workflows/hooks/${userId}/${workflowRunId}/${eventId}`,
|
|
1600
|
+
eventId
|
|
1601
|
+
})
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
var LazyWaitForWebhookStep = class extends LazyWaitEventStep {
|
|
1606
|
+
stepType = "WaitForWebhook";
|
|
1607
|
+
allowUndefinedOut = true;
|
|
1608
|
+
constructor(context, stepName, webhook, timeout) {
|
|
1609
|
+
super(context, stepName, webhook.eventId, timeout);
|
|
1610
|
+
}
|
|
1611
|
+
safeParseOut(out) {
|
|
1612
|
+
const eventData = decodeBase64(out);
|
|
1613
|
+
const parsedEventData = BaseLazyStep.tryParsing(eventData);
|
|
1614
|
+
const body = parsedEventData.body;
|
|
1615
|
+
const parsedBody = typeof body === "string" ? decodeBase64(body) : void 0;
|
|
1616
|
+
const request = new Request(
|
|
1617
|
+
`${parsedEventData.proto}://${parsedEventData.host}${parsedEventData.url}`,
|
|
1618
|
+
{
|
|
1619
|
+
method: parsedEventData.method,
|
|
1620
|
+
headers: parsedEventData.header,
|
|
1621
|
+
body: parsedBody
|
|
1622
|
+
}
|
|
1623
|
+
);
|
|
1624
|
+
return {
|
|
1625
|
+
request,
|
|
1626
|
+
timeout: false
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
handleUndefinedOut() {
|
|
1630
|
+
return {
|
|
1631
|
+
timeout: true,
|
|
1632
|
+
request: void 0
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
var LazyWaitForEventStep = class extends LazyWaitEventStep {
|
|
1637
|
+
stepType = "Wait";
|
|
1638
|
+
allowUndefinedOut = true;
|
|
1639
|
+
parseWaitForEventOut(out, waitTimeout) {
|
|
1640
|
+
return {
|
|
1641
|
+
eventData: out ? BaseLazyStep.tryParsing(decodeBase64(out)) : void 0,
|
|
1642
|
+
timeout: waitTimeout ?? false
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
safeParseOut(out, step) {
|
|
1646
|
+
return this.parseWaitForEventOut(out, step.waitTimeout);
|
|
1647
|
+
}
|
|
1648
|
+
handleUndefinedOut(step) {
|
|
1649
|
+
return this.parseWaitForEventOut(void 0, step.waitTimeout);
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1603
1652
|
|
|
1604
1653
|
// src/qstash/headers.ts
|
|
1605
1654
|
var WorkflowHeaders = class {
|
|
@@ -1609,14 +1658,15 @@ var WorkflowHeaders = class {
|
|
|
1609
1658
|
initHeaderValue;
|
|
1610
1659
|
stepInfo;
|
|
1611
1660
|
headers;
|
|
1612
|
-
|
|
1661
|
+
/**
|
|
1662
|
+
* @param params workflow header parameters
|
|
1663
|
+
*/
|
|
1613
1664
|
constructor({
|
|
1614
1665
|
userHeaders,
|
|
1615
1666
|
workflowConfig,
|
|
1616
1667
|
invokeCount,
|
|
1617
1668
|
initHeaderValue,
|
|
1618
|
-
stepInfo
|
|
1619
|
-
keepTriggerConfig
|
|
1669
|
+
stepInfo
|
|
1620
1670
|
}) {
|
|
1621
1671
|
this.userHeaders = userHeaders;
|
|
1622
1672
|
this.workflowConfig = workflowConfig;
|
|
@@ -1628,7 +1678,6 @@ var WorkflowHeaders = class {
|
|
|
1628
1678
|
workflowHeaders: {},
|
|
1629
1679
|
failureHeaders: {}
|
|
1630
1680
|
};
|
|
1631
|
-
this.keepTriggerConfig = keepTriggerConfig;
|
|
1632
1681
|
}
|
|
1633
1682
|
getHeaders() {
|
|
1634
1683
|
this.addBaseHeaders();
|
|
@@ -1647,7 +1696,7 @@ var WorkflowHeaders = class {
|
|
|
1647
1696
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1648
1697
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1649
1698
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1650
|
-
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger
|
|
1699
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
|
|
1651
1700
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1652
1701
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1653
1702
|
};
|
|
@@ -1717,12 +1766,12 @@ var WorkflowHeaders = class {
|
|
|
1717
1766
|
}
|
|
1718
1767
|
this.headers.workflowHeaders["Failure-Callback"] = this.workflowConfig.failureUrl;
|
|
1719
1768
|
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
|
|
1720
|
-
this.headers.failureHeaders[`Forward
|
|
1769
|
+
this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_CALLBACK_HEADER}`] = "true";
|
|
1721
1770
|
this.headers.failureHeaders["Workflow-Runid"] = this.workflowConfig.workflowRunId;
|
|
1722
1771
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1723
1772
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1724
1773
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1725
|
-
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger";
|
|
1774
|
+
this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
1726
1775
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1727
1776
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1728
1777
|
}
|
|
@@ -1792,14 +1841,13 @@ var submitParallelSteps = async ({
|
|
|
1792
1841
|
initialStepCount,
|
|
1793
1842
|
invokeCount,
|
|
1794
1843
|
telemetry,
|
|
1795
|
-
|
|
1844
|
+
dispatchDebug
|
|
1796
1845
|
}) => {
|
|
1797
1846
|
const planSteps = steps.map(
|
|
1798
1847
|
(step, index) => step.getPlanStep(steps.length, initialStepCount + index)
|
|
1799
1848
|
);
|
|
1800
|
-
await
|
|
1801
|
-
|
|
1802
|
-
steps: planSteps
|
|
1849
|
+
await dispatchDebug("onInfo", {
|
|
1850
|
+
info: `Submitting ${planSteps.length} parallel steps.`
|
|
1803
1851
|
});
|
|
1804
1852
|
const result = await context.qstashClient.batch(
|
|
1805
1853
|
planSteps.map((planStep) => {
|
|
@@ -1808,10 +1856,6 @@ var submitParallelSteps = async ({
|
|
|
1808
1856
|
workflowConfig: {
|
|
1809
1857
|
workflowRunId: context.workflowRunId,
|
|
1810
1858
|
workflowUrl: context.url,
|
|
1811
|
-
failureUrl: context.failureUrl,
|
|
1812
|
-
retries: context.retries,
|
|
1813
|
-
retryDelay: context.retryDelay,
|
|
1814
|
-
flowControl: context.flowControl,
|
|
1815
1859
|
telemetry
|
|
1816
1860
|
},
|
|
1817
1861
|
userHeaders: context.headers,
|
|
@@ -1827,13 +1871,11 @@ var submitParallelSteps = async ({
|
|
|
1827
1871
|
};
|
|
1828
1872
|
})
|
|
1829
1873
|
);
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
})
|
|
1836
|
-
});
|
|
1874
|
+
if (result && result.length > 0) {
|
|
1875
|
+
await dispatchDebug("onInfo", {
|
|
1876
|
+
info: `Submitted ${planSteps.length} parallel steps. messageIds: ${result.filter((r) => r).map((r) => r.messageId).join(", ")}.`
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1837
1879
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
1838
1880
|
};
|
|
1839
1881
|
var submitSingleStep = async ({
|
|
@@ -1843,14 +1885,13 @@ var submitSingleStep = async ({
|
|
|
1843
1885
|
invokeCount,
|
|
1844
1886
|
concurrency,
|
|
1845
1887
|
telemetry,
|
|
1846
|
-
|
|
1888
|
+
dispatchDebug,
|
|
1889
|
+
dispatchLifecycle
|
|
1847
1890
|
}) => {
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
fromRequest: false,
|
|
1851
|
-
step: resultStep,
|
|
1852
|
-
stepCount: stepId
|
|
1891
|
+
await dispatchLifecycle("beforeExecution", {
|
|
1892
|
+
stepName: lazyStep.stepName
|
|
1853
1893
|
});
|
|
1894
|
+
const resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
1854
1895
|
const { headers } = lazyStep.getHeaders({
|
|
1855
1896
|
context,
|
|
1856
1897
|
step: resultStep,
|
|
@@ -1864,10 +1905,6 @@ var submitSingleStep = async ({
|
|
|
1864
1905
|
invokeCount,
|
|
1865
1906
|
telemetry
|
|
1866
1907
|
});
|
|
1867
|
-
await debug?.log("SUBMIT", "SUBMIT_STEP", {
|
|
1868
|
-
length: 1,
|
|
1869
|
-
steps: [resultStep]
|
|
1870
|
-
});
|
|
1871
1908
|
const submitResult = await lazyStep.submitStep({
|
|
1872
1909
|
context,
|
|
1873
1910
|
body,
|
|
@@ -1877,13 +1914,11 @@ var submitSingleStep = async ({
|
|
|
1877
1914
|
step: resultStep,
|
|
1878
1915
|
telemetry
|
|
1879
1916
|
});
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
})
|
|
1886
|
-
});
|
|
1917
|
+
if (submitResult && submitResult[0]) {
|
|
1918
|
+
await dispatchDebug("onInfo", {
|
|
1919
|
+
info: `Submitted step "${resultStep.stepName}" with messageId: ${submitResult[0].messageId}.`
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1887
1922
|
return resultStep;
|
|
1888
1923
|
};
|
|
1889
1924
|
|
|
@@ -1892,21 +1927,31 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1892
1927
|
context;
|
|
1893
1928
|
promises = /* @__PURE__ */ new WeakMap();
|
|
1894
1929
|
activeLazyStepList;
|
|
1895
|
-
debug;
|
|
1896
1930
|
nonPlanStepCount;
|
|
1897
1931
|
steps;
|
|
1898
1932
|
indexInCurrentList = 0;
|
|
1899
1933
|
invokeCount;
|
|
1900
1934
|
telemetry;
|
|
1935
|
+
dispatchDebug;
|
|
1936
|
+
dispatchLifecycle;
|
|
1901
1937
|
stepCount = 0;
|
|
1902
1938
|
planStepCount = 0;
|
|
1903
1939
|
executingStep = false;
|
|
1904
|
-
|
|
1940
|
+
/**
|
|
1941
|
+
* @param context workflow context
|
|
1942
|
+
* @param steps list of steps
|
|
1943
|
+
* @param dispatchDebug debug event dispatcher
|
|
1944
|
+
* @param dispatchLifecycle lifecycle event dispatcher
|
|
1945
|
+
* @param telemetry optional telemetry information
|
|
1946
|
+
* @param invokeCount optional invoke count
|
|
1947
|
+
*/
|
|
1948
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount) {
|
|
1905
1949
|
this.context = context;
|
|
1906
1950
|
this.steps = steps;
|
|
1951
|
+
this.dispatchDebug = dispatchDebug;
|
|
1952
|
+
this.dispatchLifecycle = dispatchLifecycle;
|
|
1907
1953
|
this.telemetry = telemetry;
|
|
1908
1954
|
this.invokeCount = invokeCount ?? 0;
|
|
1909
|
-
this.debug = debug;
|
|
1910
1955
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
1911
1956
|
}
|
|
1912
1957
|
/**
|
|
@@ -1974,7 +2019,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1974
2019
|
/**
|
|
1975
2020
|
* Executes a step:
|
|
1976
2021
|
* - If the step result is available in the steps, returns the result
|
|
1977
|
-
* - If the result is not
|
|
2022
|
+
* - If the result is not available, runs the function
|
|
1978
2023
|
* - Sends the result to QStash
|
|
1979
2024
|
*
|
|
1980
2025
|
* @param lazyStep lazy step to execute
|
|
@@ -1984,12 +2029,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1984
2029
|
if (this.stepCount < this.nonPlanStepCount) {
|
|
1985
2030
|
const step = this.steps[this.stepCount + this.planStepCount];
|
|
1986
2031
|
validateStep(lazyStep, step);
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2032
|
+
const parsedOut = lazyStep.parseOut(step);
|
|
2033
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2034
|
+
if (isLastMemoized) {
|
|
2035
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2036
|
+
stepName: lazyStep.stepName,
|
|
2037
|
+
result: parsedOut
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
return parsedOut;
|
|
1993
2041
|
}
|
|
1994
2042
|
const resultStep = await submitSingleStep({
|
|
1995
2043
|
context: this.context,
|
|
@@ -1998,15 +2046,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1998
2046
|
invokeCount: this.invokeCount,
|
|
1999
2047
|
concurrency: 1,
|
|
2000
2048
|
telemetry: this.telemetry,
|
|
2001
|
-
|
|
2049
|
+
dispatchDebug: this.dispatchDebug,
|
|
2050
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2002
2051
|
});
|
|
2003
2052
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2004
2053
|
}
|
|
2005
2054
|
/**
|
|
2006
2055
|
* Runs steps in parallel.
|
|
2007
2056
|
*
|
|
2008
|
-
* @param
|
|
2009
|
-
* @param stepFunctions list of async functions to run in parallel
|
|
2057
|
+
* @param parallelSteps list of lazy steps to run in parallel
|
|
2010
2058
|
* @returns results of the functions run in parallel
|
|
2011
2059
|
*/
|
|
2012
2060
|
async runParallel(parallelSteps) {
|
|
@@ -2019,12 +2067,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2019
2067
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
2020
2068
|
);
|
|
2021
2069
|
}
|
|
2022
|
-
await this.
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2070
|
+
await this.dispatchDebug("onInfo", {
|
|
2071
|
+
info: `Executing parallel steps with: ` + JSON.stringify({
|
|
2072
|
+
parallelCallState,
|
|
2073
|
+
initialStepCount,
|
|
2074
|
+
plannedParallelStepCount,
|
|
2075
|
+
stepCount: this.stepCount,
|
|
2076
|
+
planStepCount: this.planStepCount
|
|
2077
|
+
})
|
|
2028
2078
|
});
|
|
2029
2079
|
switch (parallelCallState) {
|
|
2030
2080
|
case "first": {
|
|
@@ -2034,7 +2084,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2034
2084
|
initialStepCount,
|
|
2035
2085
|
invokeCount: this.invokeCount,
|
|
2036
2086
|
telemetry: this.telemetry,
|
|
2037
|
-
|
|
2087
|
+
dispatchDebug: this.dispatchDebug
|
|
2038
2088
|
});
|
|
2039
2089
|
break;
|
|
2040
2090
|
}
|
|
@@ -2056,7 +2106,8 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2056
2106
|
invokeCount: this.invokeCount,
|
|
2057
2107
|
concurrency: parallelSteps.length,
|
|
2058
2108
|
telemetry: this.telemetry,
|
|
2059
|
-
|
|
2109
|
+
dispatchDebug: this.dispatchDebug,
|
|
2110
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2060
2111
|
});
|
|
2061
2112
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2062
2113
|
} catch (error) {
|
|
@@ -2070,13 +2121,34 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2070
2121
|
break;
|
|
2071
2122
|
}
|
|
2072
2123
|
case "discard": {
|
|
2124
|
+
const resultStep = this.steps.at(-1);
|
|
2125
|
+
const lazyStep = parallelSteps.find(
|
|
2126
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2127
|
+
);
|
|
2128
|
+
if (lazyStep) {
|
|
2129
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2130
|
+
stepName: lazyStep.stepName,
|
|
2131
|
+
result: lazyStep.parseOut(resultStep)
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2073
2134
|
throw new WorkflowAbort("discarded parallel");
|
|
2074
2135
|
}
|
|
2075
2136
|
case "last": {
|
|
2076
2137
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
2077
2138
|
validateParallelSteps(parallelSteps, parallelResultSteps);
|
|
2139
|
+
const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
|
|
2140
|
+
if (isLastMemoized) {
|
|
2141
|
+
const resultStep = this.steps.at(-1);
|
|
2142
|
+
const lazyStep = parallelSteps.find(
|
|
2143
|
+
(planStep, index) => resultStep.stepId - index === initialStepCount
|
|
2144
|
+
);
|
|
2145
|
+
await this.dispatchLifecycle("afterExecution", {
|
|
2146
|
+
stepName: lazyStep.stepName,
|
|
2147
|
+
result: lazyStep.parseOut(resultStep)
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2078
2150
|
return parallelResultSteps.map(
|
|
2079
|
-
(step, index) => parallelSteps[index].parseOut(step
|
|
2151
|
+
(step, index) => parallelSteps[index].parseOut(step)
|
|
2080
2152
|
);
|
|
2081
2153
|
}
|
|
2082
2154
|
}
|
|
@@ -2132,7 +2204,6 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2132
2204
|
* @param index index of the current step
|
|
2133
2205
|
* @returns result[index] if lazyStepList > 1, otherwise result
|
|
2134
2206
|
*/
|
|
2135
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
2136
2207
|
static getResult(lazyStepList, result, index) {
|
|
2137
2208
|
if (lazyStepList.length === 1) {
|
|
2138
2209
|
return result;
|
|
@@ -2219,15 +2290,18 @@ var getProviderInfo = (api) => {
|
|
|
2219
2290
|
// src/context/api/base.ts
|
|
2220
2291
|
var BaseWorkflowApi = class {
|
|
2221
2292
|
context;
|
|
2293
|
+
/**
|
|
2294
|
+
* @param context workflow context
|
|
2295
|
+
*/
|
|
2222
2296
|
constructor({ context }) {
|
|
2223
2297
|
this.context = context;
|
|
2224
2298
|
}
|
|
2225
2299
|
/**
|
|
2226
2300
|
* context.call which uses a QStash API
|
|
2227
2301
|
*
|
|
2228
|
-
* @param stepName
|
|
2229
|
-
* @param settings
|
|
2230
|
-
* @returns
|
|
2302
|
+
* @param stepName name of the step
|
|
2303
|
+
* @param settings call settings including api configuration
|
|
2304
|
+
* @returns call response
|
|
2231
2305
|
*/
|
|
2232
2306
|
async callApi(stepName, settings) {
|
|
2233
2307
|
const { url, appendHeaders, method } = getProviderInfo(settings.api);
|
|
@@ -2235,7 +2309,7 @@ var BaseWorkflowApi = class {
|
|
|
2235
2309
|
return await this.context.call(stepName, {
|
|
2236
2310
|
url,
|
|
2237
2311
|
method: userMethod ?? method,
|
|
2238
|
-
body,
|
|
2312
|
+
body: typeof body === "string" ? body : JSON.stringify(body),
|
|
2239
2313
|
headers: {
|
|
2240
2314
|
...appendHeaders,
|
|
2241
2315
|
...headers
|
|
@@ -2320,6 +2394,129 @@ var getNewUrlFromWorkflowId = (url, workflowId) => {
|
|
|
2320
2394
|
return url.replace(/[^/]+$/, workflowId);
|
|
2321
2395
|
};
|
|
2322
2396
|
|
|
2397
|
+
// src/middleware/default-callbacks.ts
|
|
2398
|
+
var onErrorWithConsole = async ({ workflowRunId, error }) => {
|
|
2399
|
+
console.error(` [Upstash Workflow]: Error in workflow run ${workflowRunId}: ` + error);
|
|
2400
|
+
};
|
|
2401
|
+
var onWarningWithConsole = async ({ workflowRunId, warning }) => {
|
|
2402
|
+
console.warn(` [Upstash Workflow]: Warning in workflow run ${workflowRunId}: ` + warning);
|
|
2403
|
+
};
|
|
2404
|
+
var onInfoWithConsole = async ({
|
|
2405
|
+
workflowRunId,
|
|
2406
|
+
info
|
|
2407
|
+
}) => {
|
|
2408
|
+
console.info(` [Upstash Workflow]: Info in workflow run ${workflowRunId}: ` + info);
|
|
2409
|
+
};
|
|
2410
|
+
|
|
2411
|
+
// src/middleware/manager.ts
|
|
2412
|
+
var MiddlewareManager = class {
|
|
2413
|
+
middlewares;
|
|
2414
|
+
workflowRunId;
|
|
2415
|
+
context;
|
|
2416
|
+
/**
|
|
2417
|
+
* @param middlewares list of workflow middlewares
|
|
2418
|
+
*/
|
|
2419
|
+
constructor(middlewares = []) {
|
|
2420
|
+
this.middlewares = middlewares;
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Assign workflow run ID - will be passed to debug events
|
|
2424
|
+
*
|
|
2425
|
+
* @param workflowRunId workflow run id to assign
|
|
2426
|
+
*/
|
|
2427
|
+
assignWorkflowRunId(workflowRunId) {
|
|
2428
|
+
this.workflowRunId = workflowRunId;
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* Assign context - required for lifecycle events
|
|
2432
|
+
*
|
|
2433
|
+
* also assigns workflowRunId from context
|
|
2434
|
+
*
|
|
2435
|
+
* @param context workflow context to assign
|
|
2436
|
+
*/
|
|
2437
|
+
assignContext(context) {
|
|
2438
|
+
this.context = context;
|
|
2439
|
+
this.workflowRunId = context.workflowRunId;
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Internal method to execute middlewares with common error handling logic
|
|
2443
|
+
*
|
|
2444
|
+
* @param event event name to dispatch
|
|
2445
|
+
* @param params event parameters
|
|
2446
|
+
*/
|
|
2447
|
+
async executeMiddlewares(event, params) {
|
|
2448
|
+
await Promise.all(this.middlewares.map((m) => m.ensureInit()));
|
|
2449
|
+
await Promise.all(
|
|
2450
|
+
this.middlewares.map(async (middleware) => {
|
|
2451
|
+
const callback = middleware.getCallback(event);
|
|
2452
|
+
if (callback) {
|
|
2453
|
+
try {
|
|
2454
|
+
await callback(params);
|
|
2455
|
+
} catch (error) {
|
|
2456
|
+
try {
|
|
2457
|
+
const onErrorCallback = middleware.getCallback("onError") ?? onErrorWithConsole;
|
|
2458
|
+
await onErrorCallback({
|
|
2459
|
+
workflowRunId: this.workflowRunId,
|
|
2460
|
+
error
|
|
2461
|
+
});
|
|
2462
|
+
} catch (onErrorError) {
|
|
2463
|
+
console.error(
|
|
2464
|
+
`Failed while executing "onError" of middleware "${middleware.name}", falling back to logging the error to console. Error: ${onErrorError}`
|
|
2465
|
+
);
|
|
2466
|
+
onErrorWithConsole({
|
|
2467
|
+
workflowRunId: this.workflowRunId,
|
|
2468
|
+
error
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
})
|
|
2474
|
+
);
|
|
2475
|
+
if (event === "onError") {
|
|
2476
|
+
onErrorWithConsole({
|
|
2477
|
+
workflowRunId: this.workflowRunId,
|
|
2478
|
+
...params
|
|
2479
|
+
});
|
|
2480
|
+
} else if (event === "onWarning") {
|
|
2481
|
+
onWarningWithConsole({
|
|
2482
|
+
workflowRunId: this.workflowRunId,
|
|
2483
|
+
...params
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
/**
|
|
2488
|
+
* Dispatch a debug event (onError, onWarning, onInfo)
|
|
2489
|
+
*
|
|
2490
|
+
* @param event debug event name
|
|
2491
|
+
* @param params event parameters
|
|
2492
|
+
*/
|
|
2493
|
+
async dispatchDebug(event, params) {
|
|
2494
|
+
const paramsWithRunId = {
|
|
2495
|
+
...params,
|
|
2496
|
+
workflowRunId: this.workflowRunId
|
|
2497
|
+
};
|
|
2498
|
+
await this.executeMiddlewares(event, paramsWithRunId);
|
|
2499
|
+
}
|
|
2500
|
+
/**
|
|
2501
|
+
* Dispatch a lifecycle event (beforeExecution, afterExecution, runStarted, runCompleted)
|
|
2502
|
+
*
|
|
2503
|
+
* @param event lifecycle event name
|
|
2504
|
+
* @param params event parameters
|
|
2505
|
+
*/
|
|
2506
|
+
async dispatchLifecycle(event, params) {
|
|
2507
|
+
if (!this.context) {
|
|
2508
|
+
throw new WorkflowError(
|
|
2509
|
+
`Something went wrong while calling middlewares. Lifecycle event "${event}" was called before assignContext.`
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
const paramsWithContext = {
|
|
2513
|
+
...params,
|
|
2514
|
+
context: this.context
|
|
2515
|
+
};
|
|
2516
|
+
await this.executeMiddlewares(event, paramsWithContext);
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
|
|
2323
2520
|
// src/context/context.ts
|
|
2324
2521
|
var WorkflowContext = class {
|
|
2325
2522
|
executor;
|
|
@@ -2364,25 +2561,6 @@ var WorkflowContext = class {
|
|
|
2364
2561
|
* ```
|
|
2365
2562
|
*/
|
|
2366
2563
|
url;
|
|
2367
|
-
/**
|
|
2368
|
-
* URL to call in case of workflow failure with QStash failure callback
|
|
2369
|
-
*
|
|
2370
|
-
* https://upstash.com/docs/qstash/features/callbacks#what-is-a-failure-callback
|
|
2371
|
-
*
|
|
2372
|
-
* Can be overwritten by passing a `failureUrl` parameter in `serve`:
|
|
2373
|
-
*
|
|
2374
|
-
* ```ts
|
|
2375
|
-
* export const POST = serve(
|
|
2376
|
-
* async (context) => {
|
|
2377
|
-
* ...
|
|
2378
|
-
* },
|
|
2379
|
-
* {
|
|
2380
|
-
* failureUrl: "new-url-value"
|
|
2381
|
-
* }
|
|
2382
|
-
* )
|
|
2383
|
-
* ```
|
|
2384
|
-
*/
|
|
2385
|
-
failureUrl;
|
|
2386
2564
|
/**
|
|
2387
2565
|
* Payload of the request which started the workflow.
|
|
2388
2566
|
*
|
|
@@ -2438,46 +2616,6 @@ var WorkflowContext = class {
|
|
|
2438
2616
|
* Default value is set to `process.env`.
|
|
2439
2617
|
*/
|
|
2440
2618
|
env;
|
|
2441
|
-
/**
|
|
2442
|
-
* Number of retries
|
|
2443
|
-
*/
|
|
2444
|
-
retries;
|
|
2445
|
-
/**
|
|
2446
|
-
* Delay between retries.
|
|
2447
|
-
*
|
|
2448
|
-
* By default, the `retryDelay` is exponential backoff.
|
|
2449
|
-
* More details can be found in: https://upstash.com/docs/qstash/features/retry.
|
|
2450
|
-
*
|
|
2451
|
-
* The `retryDelay` option allows you to customize the delay (in milliseconds) between retry attempts when message delivery fails.
|
|
2452
|
-
*
|
|
2453
|
-
* You can use mathematical expressions and the following built-in functions to calculate the delay dynamically.
|
|
2454
|
-
* The special variable `retried` represents the current retry attempt count (starting from 0).
|
|
2455
|
-
*
|
|
2456
|
-
* Supported functions:
|
|
2457
|
-
* - `pow`
|
|
2458
|
-
* - `sqrt`
|
|
2459
|
-
* - `abs`
|
|
2460
|
-
* - `exp`
|
|
2461
|
-
* - `floor`
|
|
2462
|
-
* - `ceil`
|
|
2463
|
-
* - `round`
|
|
2464
|
-
* - `min`
|
|
2465
|
-
* - `max`
|
|
2466
|
-
*
|
|
2467
|
-
* Examples of valid `retryDelay` values:
|
|
2468
|
-
* ```ts
|
|
2469
|
-
* 1000 // 1 second
|
|
2470
|
-
* 1000 * (1 + retried) // 1 second multiplied by the current retry attempt
|
|
2471
|
-
* pow(2, retried) // 2 to the power of the current retry attempt
|
|
2472
|
-
* max(10, pow(2, retried)) // The greater of 10 or 2^retried
|
|
2473
|
-
* ```
|
|
2474
|
-
*/
|
|
2475
|
-
retryDelay;
|
|
2476
|
-
/**
|
|
2477
|
-
* Settings for controlling the number of active requests
|
|
2478
|
-
* and number of requests per second with the same key.
|
|
2479
|
-
*/
|
|
2480
|
-
flowControl;
|
|
2481
2619
|
/**
|
|
2482
2620
|
* Label to apply to the workflow run.
|
|
2483
2621
|
*
|
|
@@ -2500,30 +2638,31 @@ var WorkflowContext = class {
|
|
|
2500
2638
|
headers,
|
|
2501
2639
|
steps,
|
|
2502
2640
|
url,
|
|
2503
|
-
failureUrl,
|
|
2504
|
-
debug,
|
|
2505
2641
|
initialPayload,
|
|
2506
2642
|
env,
|
|
2507
|
-
retries,
|
|
2508
|
-
retryDelay,
|
|
2509
2643
|
telemetry,
|
|
2510
2644
|
invokeCount,
|
|
2511
|
-
|
|
2512
|
-
|
|
2645
|
+
label,
|
|
2646
|
+
middlewareManager
|
|
2513
2647
|
}) {
|
|
2514
2648
|
this.qstashClient = qstashClient;
|
|
2515
2649
|
this.workflowRunId = workflowRunId;
|
|
2516
2650
|
this.steps = steps;
|
|
2517
2651
|
this.url = url;
|
|
2518
|
-
this.failureUrl = failureUrl;
|
|
2519
2652
|
this.headers = headers;
|
|
2520
2653
|
this.requestPayload = initialPayload;
|
|
2521
2654
|
this.env = env ?? {};
|
|
2522
|
-
this.retries = retries ?? DEFAULT_RETRIES;
|
|
2523
|
-
this.retryDelay = retryDelay;
|
|
2524
|
-
this.flowControl = flowControl;
|
|
2525
2655
|
this.label = label;
|
|
2526
|
-
|
|
2656
|
+
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2657
|
+
middlewareManagerInstance.assignContext(this);
|
|
2658
|
+
this.executor = new AutoExecutor(
|
|
2659
|
+
this,
|
|
2660
|
+
this.steps,
|
|
2661
|
+
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
2662
|
+
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
2663
|
+
telemetry,
|
|
2664
|
+
invokeCount
|
|
2665
|
+
);
|
|
2527
2666
|
}
|
|
2528
2667
|
/**
|
|
2529
2668
|
* Executes a workflow step
|
|
@@ -2554,7 +2693,7 @@ var WorkflowContext = class {
|
|
|
2554
2693
|
*/
|
|
2555
2694
|
async run(stepName, stepFunction) {
|
|
2556
2695
|
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
2557
|
-
return await this.addStep(new LazyFunctionStep(stepName, wrappedStepFunction));
|
|
2696
|
+
return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
|
|
2558
2697
|
}
|
|
2559
2698
|
/**
|
|
2560
2699
|
* Stops the execution for the duration provided.
|
|
@@ -2568,7 +2707,7 @@ var WorkflowContext = class {
|
|
|
2568
2707
|
* @returns undefined
|
|
2569
2708
|
*/
|
|
2570
2709
|
async sleep(stepName, duration) {
|
|
2571
|
-
await this.addStep(new LazySleepStep(stepName, duration));
|
|
2710
|
+
await this.addStep(new LazySleepStep(this, stepName, duration));
|
|
2572
2711
|
}
|
|
2573
2712
|
/**
|
|
2574
2713
|
* Stops the execution until the date time provided.
|
|
@@ -2590,48 +2729,38 @@ var WorkflowContext = class {
|
|
|
2590
2729
|
datetime = typeof datetime === "string" ? new Date(datetime) : datetime;
|
|
2591
2730
|
time = Math.round(datetime.getTime() / 1e3);
|
|
2592
2731
|
}
|
|
2593
|
-
await this.addStep(new LazySleepUntilStep(stepName, time));
|
|
2732
|
+
await this.addStep(new LazySleepUntilStep(this, stepName, time));
|
|
2594
2733
|
}
|
|
2595
2734
|
async call(stepName, settings) {
|
|
2596
2735
|
let callStep;
|
|
2597
2736
|
if ("workflow" in settings) {
|
|
2598
2737
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
2599
|
-
|
|
2738
|
+
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2739
|
+
callStep = new LazyCallStep({
|
|
2740
|
+
context: this,
|
|
2600
2741
|
stepName,
|
|
2601
2742
|
url,
|
|
2602
|
-
"POST",
|
|
2603
|
-
|
|
2604
|
-
settings.headers || {},
|
|
2605
|
-
settings.retries || 0,
|
|
2606
|
-
settings.retryDelay,
|
|
2607
|
-
settings.timeout,
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
);
|
|
2743
|
+
method: "POST",
|
|
2744
|
+
body: stringBody,
|
|
2745
|
+
headers: settings.headers || {},
|
|
2746
|
+
retries: settings.retries || 0,
|
|
2747
|
+
retryDelay: settings.retryDelay,
|
|
2748
|
+
timeout: settings.timeout,
|
|
2749
|
+
flowControl: settings.flowControl
|
|
2750
|
+
});
|
|
2611
2751
|
} else {
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
method = "GET",
|
|
2615
|
-
body,
|
|
2616
|
-
headers = {},
|
|
2617
|
-
retries = 0,
|
|
2618
|
-
retryDelay,
|
|
2619
|
-
timeout,
|
|
2620
|
-
flowControl,
|
|
2621
|
-
stringifyBody = true
|
|
2622
|
-
} = settings;
|
|
2623
|
-
callStep = new LazyCallStep(
|
|
2752
|
+
callStep = new LazyCallStep({
|
|
2753
|
+
context: this,
|
|
2624
2754
|
stepName,
|
|
2625
|
-
url,
|
|
2626
|
-
method,
|
|
2627
|
-
body,
|
|
2628
|
-
headers,
|
|
2629
|
-
retries,
|
|
2630
|
-
retryDelay,
|
|
2631
|
-
timeout,
|
|
2632
|
-
flowControl
|
|
2633
|
-
|
|
2634
|
-
);
|
|
2755
|
+
url: settings.url,
|
|
2756
|
+
method: settings.method ?? "GET",
|
|
2757
|
+
body: settings.body,
|
|
2758
|
+
headers: settings.headers ?? {},
|
|
2759
|
+
retries: settings.retries ?? 0,
|
|
2760
|
+
retryDelay: settings.retryDelay,
|
|
2761
|
+
timeout: settings.timeout,
|
|
2762
|
+
flowControl: settings.flowControl
|
|
2763
|
+
});
|
|
2635
2764
|
}
|
|
2636
2765
|
return await this.addStep(callStep);
|
|
2637
2766
|
}
|
|
@@ -2672,7 +2801,9 @@ var WorkflowContext = class {
|
|
|
2672
2801
|
async waitForEvent(stepName, eventId, options = {}) {
|
|
2673
2802
|
const { timeout = "7d" } = options;
|
|
2674
2803
|
const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
|
|
2675
|
-
return await this.addStep(
|
|
2804
|
+
return await this.addStep(
|
|
2805
|
+
new LazyWaitForEventStep(this, stepName, eventId, timeoutStr)
|
|
2806
|
+
);
|
|
2676
2807
|
}
|
|
2677
2808
|
/**
|
|
2678
2809
|
* Notify workflow runs waiting for an event
|
|
@@ -2697,20 +2828,28 @@ var WorkflowContext = class {
|
|
|
2697
2828
|
*/
|
|
2698
2829
|
async notify(stepName, eventId, eventData) {
|
|
2699
2830
|
return await this.addStep(
|
|
2700
|
-
new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
|
|
2831
|
+
new LazyNotifyStep(this, stepName, eventId, eventData, this.qstashClient.http)
|
|
2701
2832
|
);
|
|
2702
2833
|
}
|
|
2703
2834
|
async invoke(stepName, settings) {
|
|
2704
|
-
return await this.addStep(
|
|
2835
|
+
return await this.addStep(
|
|
2836
|
+
new LazyInvokeStep(this, stepName, settings)
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
async createWebhook(stepName) {
|
|
2840
|
+
return await this.addStep(new LazyCreateWebhookStep(this, stepName));
|
|
2841
|
+
}
|
|
2842
|
+
async waitForWebhook(stepName, webhook, timeout) {
|
|
2843
|
+
return await this.addStep(new LazyWaitForWebhookStep(this, stepName, webhook, timeout));
|
|
2705
2844
|
}
|
|
2706
2845
|
/**
|
|
2707
2846
|
* Cancel the current workflow run
|
|
2708
2847
|
*
|
|
2709
|
-
* Will throw
|
|
2848
|
+
* Will throw WorkflowCancelAbort to stop workflow execution.
|
|
2710
2849
|
* Shouldn't be inside try/catch.
|
|
2711
2850
|
*/
|
|
2712
2851
|
async cancel() {
|
|
2713
|
-
throw new
|
|
2852
|
+
throw new WorkflowCancelAbort();
|
|
2714
2853
|
}
|
|
2715
2854
|
/**
|
|
2716
2855
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
@@ -2726,74 +2865,25 @@ var WorkflowContext = class {
|
|
|
2726
2865
|
}
|
|
2727
2866
|
};
|
|
2728
2867
|
|
|
2729
|
-
// src/logger.ts
|
|
2730
|
-
var LOG_LEVELS = ["DEBUG", "INFO", "SUBMIT", "WARN", "ERROR"];
|
|
2731
|
-
var WorkflowLogger = class _WorkflowLogger {
|
|
2732
|
-
logs = [];
|
|
2733
|
-
options;
|
|
2734
|
-
workflowRunId = void 0;
|
|
2735
|
-
constructor(options) {
|
|
2736
|
-
this.options = options;
|
|
2737
|
-
}
|
|
2738
|
-
async log(level, eventType, details) {
|
|
2739
|
-
if (this.shouldLog(level)) {
|
|
2740
|
-
const timestamp = Date.now();
|
|
2741
|
-
const logEntry = {
|
|
2742
|
-
timestamp,
|
|
2743
|
-
workflowRunId: this.workflowRunId ?? "",
|
|
2744
|
-
logLevel: level,
|
|
2745
|
-
eventType,
|
|
2746
|
-
details
|
|
2747
|
-
};
|
|
2748
|
-
this.logs.push(logEntry);
|
|
2749
|
-
if (this.options.logOutput === "console") {
|
|
2750
|
-
this.writeToConsole(logEntry);
|
|
2751
|
-
}
|
|
2752
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2753
|
-
}
|
|
2754
|
-
}
|
|
2755
|
-
setWorkflowRunId(workflowRunId) {
|
|
2756
|
-
this.workflowRunId = workflowRunId;
|
|
2757
|
-
}
|
|
2758
|
-
writeToConsole(logEntry) {
|
|
2759
|
-
const JSON_SPACING = 2;
|
|
2760
|
-
const logMethod = logEntry.logLevel === "ERROR" ? console.error : logEntry.logLevel === "WARN" ? console.warn : console.log;
|
|
2761
|
-
logMethod(JSON.stringify(logEntry, void 0, JSON_SPACING));
|
|
2762
|
-
}
|
|
2763
|
-
shouldLog(level) {
|
|
2764
|
-
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
2765
|
-
}
|
|
2766
|
-
static getLogger(verbose) {
|
|
2767
|
-
if (typeof verbose === "object") {
|
|
2768
|
-
return verbose;
|
|
2769
|
-
} else {
|
|
2770
|
-
return verbose ? new _WorkflowLogger({
|
|
2771
|
-
logLevel: "INFO",
|
|
2772
|
-
logOutput: "console"
|
|
2773
|
-
}) : void 0;
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
};
|
|
2777
|
-
|
|
2778
2868
|
// src/serve/authorization.ts
|
|
2779
2869
|
var import_qstash9 = require("@upstash/qstash");
|
|
2780
2870
|
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
2781
2871
|
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
2782
2872
|
disabled = true;
|
|
2783
2873
|
/**
|
|
2784
|
-
* overwrite the WorkflowContext.addStep method to always raise
|
|
2874
|
+
* overwrite the WorkflowContext.addStep method to always raise WorkflowAuthError
|
|
2785
2875
|
* error in order to stop the execution whenever we encounter a step.
|
|
2786
2876
|
*
|
|
2787
2877
|
* @param _step
|
|
2788
2878
|
*/
|
|
2789
2879
|
async addStep(_step) {
|
|
2790
|
-
throw new
|
|
2880
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
2791
2881
|
}
|
|
2792
2882
|
/**
|
|
2793
|
-
* overwrite cancel method to throw
|
|
2883
|
+
* overwrite cancel method to throw WorkflowAuthError with the disabledMessage
|
|
2794
2884
|
*/
|
|
2795
2885
|
async cancel() {
|
|
2796
|
-
throw new
|
|
2886
|
+
throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
|
|
2797
2887
|
}
|
|
2798
2888
|
/**
|
|
2799
2889
|
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
@@ -2816,18 +2906,14 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
2816
2906
|
headers: context.headers,
|
|
2817
2907
|
steps: [],
|
|
2818
2908
|
url: context.url,
|
|
2819
|
-
failureUrl: context.failureUrl,
|
|
2820
2909
|
initialPayload: context.requestPayload,
|
|
2821
2910
|
env: context.env,
|
|
2822
|
-
retries: context.retries,
|
|
2823
|
-
retryDelay: context.retryDelay,
|
|
2824
|
-
flowControl: context.flowControl,
|
|
2825
2911
|
label: context.label
|
|
2826
2912
|
});
|
|
2827
2913
|
try {
|
|
2828
2914
|
await routeFunction(disabledContext);
|
|
2829
2915
|
} catch (error) {
|
|
2830
|
-
if (isInstanceOf(error,
|
|
2916
|
+
if (isInstanceOf(error, WorkflowAuthError) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
|
|
2831
2917
|
return ok("step-found");
|
|
2832
2918
|
}
|
|
2833
2919
|
console.warn(
|
|
@@ -2860,13 +2946,6 @@ var processRawSteps = (rawSteps) => {
|
|
|
2860
2946
|
const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
|
|
2861
2947
|
const otherSteps = stepsToDecode.map((rawStep) => {
|
|
2862
2948
|
const step = JSON.parse(decodeBase64(rawStep.body));
|
|
2863
|
-
if (step.waitEventId) {
|
|
2864
|
-
const newOut = {
|
|
2865
|
-
eventData: step.out ? decodeBase64(step.out) : void 0,
|
|
2866
|
-
timeout: step.waitTimeout ?? false
|
|
2867
|
-
};
|
|
2868
|
-
step.out = newOut;
|
|
2869
|
-
}
|
|
2870
2949
|
return step;
|
|
2871
2950
|
});
|
|
2872
2951
|
const steps = [initialStep, ...otherSteps];
|
|
@@ -2894,7 +2973,7 @@ var deduplicateSteps = (steps) => {
|
|
|
2894
2973
|
}
|
|
2895
2974
|
return deduplicatedSteps;
|
|
2896
2975
|
};
|
|
2897
|
-
var checkIfLastOneIsDuplicate = async (steps,
|
|
2976
|
+
var checkIfLastOneIsDuplicate = async (steps, dispatchDebug) => {
|
|
2898
2977
|
if (steps.length < 2) {
|
|
2899
2978
|
return false;
|
|
2900
2979
|
}
|
|
@@ -2905,14 +2984,41 @@ var checkIfLastOneIsDuplicate = async (steps, debug) => {
|
|
|
2905
2984
|
const step = steps[index];
|
|
2906
2985
|
if (step.stepId === lastStepId && step.targetStep === lastTargetStepId) {
|
|
2907
2986
|
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.`;
|
|
2908
|
-
await
|
|
2909
|
-
|
|
2987
|
+
await dispatchDebug?.("onWarning", {
|
|
2988
|
+
warning: message
|
|
2989
|
+
});
|
|
2910
2990
|
return true;
|
|
2911
2991
|
}
|
|
2912
2992
|
}
|
|
2913
2993
|
return false;
|
|
2914
2994
|
};
|
|
2915
2995
|
var validateRequest = (request) => {
|
|
2996
|
+
if (request.headers.get(WORKFLOW_UNKOWN_SDK_VERSION_HEADER)) {
|
|
2997
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
2998
|
+
if (!workflowRunId2) {
|
|
2999
|
+
throw new WorkflowError(
|
|
3000
|
+
"Couldn't get workflow id from header when handling unknown sdk request"
|
|
3001
|
+
);
|
|
3002
|
+
}
|
|
3003
|
+
return {
|
|
3004
|
+
unknownSdk: true,
|
|
3005
|
+
isFirstInvocation: true,
|
|
3006
|
+
workflowRunId: workflowRunId2
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
3009
|
+
if (request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
3010
|
+
const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
|
|
3011
|
+
if (!workflowRunId2) {
|
|
3012
|
+
throw new WorkflowError(
|
|
3013
|
+
"Couldn't get workflow id from header when handling failure callback request"
|
|
3014
|
+
);
|
|
3015
|
+
}
|
|
3016
|
+
return {
|
|
3017
|
+
unknownSdk: false,
|
|
3018
|
+
isFirstInvocation: true,
|
|
3019
|
+
workflowRunId: workflowRunId2
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
2916
3022
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
2917
3023
|
const isFirstInvocation = !versionHeader;
|
|
2918
3024
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
@@ -2926,11 +3032,20 @@ var validateRequest = (request) => {
|
|
|
2926
3032
|
}
|
|
2927
3033
|
return {
|
|
2928
3034
|
isFirstInvocation,
|
|
2929
|
-
workflowRunId
|
|
3035
|
+
workflowRunId,
|
|
3036
|
+
unknownSdk: false
|
|
2930
3037
|
};
|
|
2931
3038
|
};
|
|
2932
|
-
var parseRequest = async (
|
|
2933
|
-
|
|
3039
|
+
var parseRequest = async ({
|
|
3040
|
+
requestPayload,
|
|
3041
|
+
isFirstInvocation,
|
|
3042
|
+
unknownSdk,
|
|
3043
|
+
workflowRunId,
|
|
3044
|
+
requester,
|
|
3045
|
+
messageId,
|
|
3046
|
+
dispatchDebug
|
|
3047
|
+
}) => {
|
|
3048
|
+
if (isFirstInvocation && !unknownSdk) {
|
|
2934
3049
|
return {
|
|
2935
3050
|
rawInitialPayload: requestPayload ?? "",
|
|
2936
3051
|
steps: [],
|
|
@@ -2940,16 +3055,14 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
2940
3055
|
} else {
|
|
2941
3056
|
let rawSteps;
|
|
2942
3057
|
if (!requestPayload) {
|
|
2943
|
-
await
|
|
2944
|
-
"
|
|
2945
|
-
|
|
2946
|
-
"request payload is empty, steps will be fetched from QStash."
|
|
2947
|
-
);
|
|
3058
|
+
await dispatchDebug?.("onInfo", {
|
|
3059
|
+
info: "request payload is empty, steps will be fetched from QStash."
|
|
3060
|
+
});
|
|
2948
3061
|
const { steps: fetchedSteps, workflowRunEnded } = await getSteps(
|
|
2949
3062
|
requester,
|
|
2950
3063
|
workflowRunId,
|
|
2951
3064
|
messageId,
|
|
2952
|
-
|
|
3065
|
+
dispatchDebug
|
|
2953
3066
|
);
|
|
2954
3067
|
if (workflowRunEnded) {
|
|
2955
3068
|
return {
|
|
@@ -2964,7 +3077,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
2964
3077
|
rawSteps = JSON.parse(requestPayload);
|
|
2965
3078
|
}
|
|
2966
3079
|
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
2967
|
-
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps,
|
|
3080
|
+
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, dispatchDebug);
|
|
2968
3081
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
2969
3082
|
return {
|
|
2970
3083
|
rawInitialPayload,
|
|
@@ -2974,16 +3087,21 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
|
|
|
2974
3087
|
};
|
|
2975
3088
|
}
|
|
2976
3089
|
};
|
|
2977
|
-
var handleFailure = async (
|
|
2978
|
-
|
|
3090
|
+
var handleFailure = async ({
|
|
3091
|
+
request,
|
|
3092
|
+
requestPayload,
|
|
3093
|
+
qstashClient,
|
|
3094
|
+
initialPayloadParser,
|
|
3095
|
+
routeFunction,
|
|
3096
|
+
failureFunction,
|
|
3097
|
+
env,
|
|
3098
|
+
dispatchDebug
|
|
3099
|
+
}) => {
|
|
3100
|
+
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true" && !request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
|
|
2979
3101
|
return ok({ result: "not-failure-callback" });
|
|
2980
3102
|
}
|
|
2981
3103
|
if (!failureFunction) {
|
|
2982
|
-
return
|
|
2983
|
-
new WorkflowError(
|
|
2984
|
-
"Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
|
|
2985
|
-
)
|
|
2986
|
-
);
|
|
3104
|
+
return ok({ result: "failure-function-undefined" });
|
|
2987
3105
|
}
|
|
2988
3106
|
try {
|
|
2989
3107
|
const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
|
|
@@ -3011,23 +3129,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3011
3129
|
headers: userHeaders,
|
|
3012
3130
|
steps: [],
|
|
3013
3131
|
url,
|
|
3014
|
-
failureUrl: url,
|
|
3015
|
-
debug,
|
|
3016
3132
|
env,
|
|
3017
|
-
retries,
|
|
3018
|
-
retryDelay,
|
|
3019
|
-
flowControl,
|
|
3020
3133
|
telemetry: void 0,
|
|
3021
3134
|
// not going to make requests in authentication check
|
|
3022
|
-
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0
|
|
3135
|
+
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3136
|
+
middlewareManager: void 0
|
|
3023
3137
|
});
|
|
3024
3138
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3025
3139
|
routeFunction,
|
|
3026
3140
|
workflowContext
|
|
3027
3141
|
);
|
|
3028
3142
|
if (authCheck.isErr()) {
|
|
3029
|
-
await
|
|
3030
|
-
|
|
3143
|
+
await dispatchDebug?.("onError", {
|
|
3144
|
+
error: authCheck.error
|
|
3145
|
+
});
|
|
3146
|
+
return err(authCheck.error);
|
|
3031
3147
|
} else if (authCheck.value === "run-ended") {
|
|
3032
3148
|
return err(new WorkflowError("Not authorized to run the failure function."));
|
|
3033
3149
|
}
|
|
@@ -3038,7 +3154,7 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3038
3154
|
failHeaders: header,
|
|
3039
3155
|
failStack
|
|
3040
3156
|
});
|
|
3041
|
-
return ok({ result: "
|
|
3157
|
+
return ok({ result: "failure-function-executed", response: failureResponse });
|
|
3042
3158
|
} catch (error) {
|
|
3043
3159
|
return err(error);
|
|
3044
3160
|
}
|
|
@@ -3047,7 +3163,143 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
3047
3163
|
// src/serve/options.ts
|
|
3048
3164
|
var import_qstash10 = require("@upstash/qstash");
|
|
3049
3165
|
var import_qstash11 = require("@upstash/qstash");
|
|
3050
|
-
|
|
3166
|
+
|
|
3167
|
+
// src/middleware/middleware.ts
|
|
3168
|
+
var WorkflowMiddleware = class {
|
|
3169
|
+
name;
|
|
3170
|
+
initCallbacks;
|
|
3171
|
+
/**
|
|
3172
|
+
* Callback functions
|
|
3173
|
+
*
|
|
3174
|
+
* Initially set to undefined, will be populated after init is called
|
|
3175
|
+
*/
|
|
3176
|
+
middlewareCallbacks = void 0;
|
|
3177
|
+
constructor(parameters) {
|
|
3178
|
+
this.name = parameters.name;
|
|
3179
|
+
if ("init" in parameters) {
|
|
3180
|
+
this.initCallbacks = parameters.init;
|
|
3181
|
+
} else {
|
|
3182
|
+
this.middlewareCallbacks = parameters.callbacks;
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
async ensureInit() {
|
|
3186
|
+
if (!this.middlewareCallbacks) {
|
|
3187
|
+
if (!this.initCallbacks) {
|
|
3188
|
+
throw new WorkflowError(`Middleware "${this.name}" has no callbacks or init defined.`);
|
|
3189
|
+
}
|
|
3190
|
+
this.middlewareCallbacks = await this.initCallbacks();
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
/**
|
|
3194
|
+
* Gets a callback function by name.
|
|
3195
|
+
*
|
|
3196
|
+
* @param callback name of the callback to retrieve
|
|
3197
|
+
*/
|
|
3198
|
+
getCallback(callback) {
|
|
3199
|
+
return this.middlewareCallbacks?.[callback];
|
|
3200
|
+
}
|
|
3201
|
+
};
|
|
3202
|
+
|
|
3203
|
+
// src/middleware/logging.ts
|
|
3204
|
+
var loggingMiddleware = new WorkflowMiddleware({
|
|
3205
|
+
name: "logging",
|
|
3206
|
+
callbacks: {
|
|
3207
|
+
afterExecution(params) {
|
|
3208
|
+
const { context, ...rest } = params;
|
|
3209
|
+
console.log(" [Upstash Workflow]: Step executed:", {
|
|
3210
|
+
workflowRunId: context.workflowRunId,
|
|
3211
|
+
...rest
|
|
3212
|
+
});
|
|
3213
|
+
},
|
|
3214
|
+
beforeExecution(params) {
|
|
3215
|
+
const { context, ...rest } = params;
|
|
3216
|
+
console.log(" [Upstash Workflow]: Step execution started:", {
|
|
3217
|
+
workflowRunId: context.workflowRunId,
|
|
3218
|
+
...rest
|
|
3219
|
+
});
|
|
3220
|
+
},
|
|
3221
|
+
runStarted(params) {
|
|
3222
|
+
const { context, ...rest } = params;
|
|
3223
|
+
console.log(" [Upstash Workflow]: Workflow run started:", {
|
|
3224
|
+
workflowRunId: context.workflowRunId,
|
|
3225
|
+
...rest
|
|
3226
|
+
});
|
|
3227
|
+
},
|
|
3228
|
+
runCompleted(params) {
|
|
3229
|
+
const { context, ...rest } = params;
|
|
3230
|
+
console.log(" [Upstash Workflow]: Workflow run completed:", {
|
|
3231
|
+
workflowRunId: context.workflowRunId,
|
|
3232
|
+
...rest
|
|
3233
|
+
});
|
|
3234
|
+
},
|
|
3235
|
+
onError: onErrorWithConsole,
|
|
3236
|
+
onWarning: onWarningWithConsole,
|
|
3237
|
+
onInfo: onInfoWithConsole
|
|
3238
|
+
}
|
|
3239
|
+
});
|
|
3240
|
+
|
|
3241
|
+
// src/serve/options.ts
|
|
3242
|
+
var createResponseData = (workflowRunId, detailedFinishCondition) => {
|
|
3243
|
+
const baseHeaders = {
|
|
3244
|
+
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
3245
|
+
"Upstash-workflow-sdk": VERSION
|
|
3246
|
+
};
|
|
3247
|
+
if (detailedFinishCondition?.condition === "auth-fail") {
|
|
3248
|
+
return {
|
|
3249
|
+
text: JSON.stringify({
|
|
3250
|
+
message: AUTH_FAIL_MESSAGE,
|
|
3251
|
+
workflowRunId
|
|
3252
|
+
}),
|
|
3253
|
+
status: 400,
|
|
3254
|
+
headers: baseHeaders
|
|
3255
|
+
};
|
|
3256
|
+
} else if (detailedFinishCondition?.condition === "non-retryable-error") {
|
|
3257
|
+
return {
|
|
3258
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3259
|
+
status: 489,
|
|
3260
|
+
headers: {
|
|
3261
|
+
...baseHeaders,
|
|
3262
|
+
"Upstash-NonRetryable-Error": "true"
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
3265
|
+
} else if (detailedFinishCondition?.condition === "retry-after-error") {
|
|
3266
|
+
return {
|
|
3267
|
+
text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
|
|
3268
|
+
status: 429,
|
|
3269
|
+
headers: {
|
|
3270
|
+
...baseHeaders,
|
|
3271
|
+
"Retry-After": detailedFinishCondition.result.retryAfter.toString()
|
|
3272
|
+
}
|
|
3273
|
+
};
|
|
3274
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-executed") {
|
|
3275
|
+
return {
|
|
3276
|
+
text: JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
|
|
3277
|
+
status: 200,
|
|
3278
|
+
headers: baseHeaders
|
|
3279
|
+
};
|
|
3280
|
+
} else if (detailedFinishCondition?.condition === "failure-callback-undefined") {
|
|
3281
|
+
return {
|
|
3282
|
+
text: JSON.stringify({
|
|
3283
|
+
workflowRunId,
|
|
3284
|
+
finishCondition: detailedFinishCondition.condition
|
|
3285
|
+
}),
|
|
3286
|
+
status: 200,
|
|
3287
|
+
headers: {
|
|
3288
|
+
...baseHeaders,
|
|
3289
|
+
"Upstash-Workflow-Failure-Callback-Notfound": "true"
|
|
3290
|
+
}
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
return {
|
|
3294
|
+
text: JSON.stringify({
|
|
3295
|
+
workflowRunId,
|
|
3296
|
+
finishCondition: detailedFinishCondition.condition
|
|
3297
|
+
}),
|
|
3298
|
+
status: 200,
|
|
3299
|
+
headers: baseHeaders
|
|
3300
|
+
};
|
|
3301
|
+
};
|
|
3302
|
+
var processOptions = (options, internalOptions) => {
|
|
3051
3303
|
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
3052
3304
|
const receiverEnvironmentVariablesSet = Boolean(
|
|
3053
3305
|
environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
|
|
@@ -3057,55 +3309,6 @@ var processOptions = (options) => {
|
|
|
3057
3309
|
baseUrl: environment.QSTASH_URL,
|
|
3058
3310
|
token: environment.QSTASH_TOKEN
|
|
3059
3311
|
}),
|
|
3060
|
-
onStepFinish: (workflowRunId, _finishCondition, detailedFinishCondition) => {
|
|
3061
|
-
if (detailedFinishCondition?.condition === "auth-fail") {
|
|
3062
|
-
console.error(AUTH_FAIL_MESSAGE);
|
|
3063
|
-
return new Response(
|
|
3064
|
-
JSON.stringify({
|
|
3065
|
-
message: AUTH_FAIL_MESSAGE,
|
|
3066
|
-
workflowRunId
|
|
3067
|
-
}),
|
|
3068
|
-
{
|
|
3069
|
-
status: 400,
|
|
3070
|
-
headers: {
|
|
3071
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3072
|
-
}
|
|
3073
|
-
}
|
|
3074
|
-
);
|
|
3075
|
-
} else if (detailedFinishCondition?.condition === "non-retryable-error") {
|
|
3076
|
-
return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
|
|
3077
|
-
headers: {
|
|
3078
|
-
"Upstash-NonRetryable-Error": "true",
|
|
3079
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3080
|
-
},
|
|
3081
|
-
status: 489
|
|
3082
|
-
});
|
|
3083
|
-
} else if (detailedFinishCondition?.condition === "retry-after-error") {
|
|
3084
|
-
return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
|
|
3085
|
-
headers: {
|
|
3086
|
-
"Retry-After": detailedFinishCondition.result.retryAfter.toString(),
|
|
3087
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3088
|
-
},
|
|
3089
|
-
status: 429
|
|
3090
|
-
});
|
|
3091
|
-
} else if (detailedFinishCondition?.condition === "failure-callback") {
|
|
3092
|
-
return new Response(
|
|
3093
|
-
JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
|
|
3094
|
-
{
|
|
3095
|
-
status: 200,
|
|
3096
|
-
headers: {
|
|
3097
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
);
|
|
3101
|
-
}
|
|
3102
|
-
return new Response(JSON.stringify({ workflowRunId }), {
|
|
3103
|
-
status: 200,
|
|
3104
|
-
headers: {
|
|
3105
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3106
|
-
}
|
|
3107
|
-
});
|
|
3108
|
-
},
|
|
3109
3312
|
initialPayloadParser: (initialRequest) => {
|
|
3110
3313
|
if (!initialRequest) {
|
|
3111
3314
|
return void 0;
|
|
@@ -3126,29 +3329,34 @@ var processOptions = (options) => {
|
|
|
3126
3329
|
}) : void 0,
|
|
3127
3330
|
baseUrl: environment.UPSTASH_WORKFLOW_URL,
|
|
3128
3331
|
env: environment,
|
|
3129
|
-
retries: DEFAULT_RETRIES,
|
|
3130
|
-
useJSONContent: false,
|
|
3131
3332
|
disableTelemetry: false,
|
|
3132
|
-
|
|
3133
|
-
|
|
3333
|
+
...options,
|
|
3334
|
+
// merge middlewares
|
|
3335
|
+
middlewares: [options?.middlewares ?? [], options?.verbose ? [loggingMiddleware] : []].flat(),
|
|
3336
|
+
internal: {
|
|
3337
|
+
generateResponse: internalOptions?.generateResponse ?? ((responseData) => {
|
|
3338
|
+
return new Response(responseData.text, {
|
|
3339
|
+
status: responseData.status,
|
|
3340
|
+
headers: responseData.headers
|
|
3341
|
+
});
|
|
3342
|
+
}),
|
|
3343
|
+
useJSONContent: internalOptions?.useJSONContent ?? false
|
|
3344
|
+
}
|
|
3134
3345
|
};
|
|
3135
3346
|
};
|
|
3136
|
-
var determineUrls = async (request, url, baseUrl,
|
|
3347
|
+
var determineUrls = async (request, url, baseUrl, dispatchDebug) => {
|
|
3137
3348
|
const initialWorkflowUrl = url ?? request.url;
|
|
3138
3349
|
const workflowUrl = baseUrl ? initialWorkflowUrl.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, (_, matchedBaseUrl, path) => {
|
|
3139
3350
|
return baseUrl + (path || "");
|
|
3140
3351
|
}) : initialWorkflowUrl;
|
|
3141
3352
|
if (workflowUrl !== initialWorkflowUrl) {
|
|
3142
|
-
await
|
|
3143
|
-
|
|
3144
|
-
originalURL: initialWorkflowUrl,
|
|
3145
|
-
updatedURL: workflowUrl
|
|
3353
|
+
await dispatchDebug("onInfo", {
|
|
3354
|
+
info: `The workflow URL's base URL has been replaced with the provided baseUrl. Original URL: ${initialWorkflowUrl}, New URL: ${workflowUrl}`
|
|
3146
3355
|
});
|
|
3147
3356
|
}
|
|
3148
|
-
const workflowFailureUrl = failureFunction ? workflowUrl : failureUrl;
|
|
3149
3357
|
if (workflowUrl.includes("localhost")) {
|
|
3150
|
-
await
|
|
3151
|
-
|
|
3358
|
+
await dispatchDebug("onInfo", {
|
|
3359
|
+
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}`
|
|
3152
3360
|
});
|
|
3153
3361
|
}
|
|
3154
3362
|
if (!(workflowUrl.startsWith("http://") || workflowUrl.startsWith("https://"))) {
|
|
@@ -3157,67 +3365,68 @@ var determineUrls = async (request, url, baseUrl, failureFunction, failureUrl, d
|
|
|
3157
3365
|
);
|
|
3158
3366
|
}
|
|
3159
3367
|
return {
|
|
3160
|
-
workflowUrl
|
|
3161
|
-
workflowFailureUrl
|
|
3368
|
+
workflowUrl
|
|
3162
3369
|
};
|
|
3163
3370
|
};
|
|
3164
3371
|
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`;
|
|
3165
3372
|
|
|
3166
3373
|
// src/serve/index.ts
|
|
3167
|
-
var serveBase = (routeFunction, telemetry, options) => {
|
|
3374
|
+
var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
3168
3375
|
const {
|
|
3169
3376
|
qstashClient,
|
|
3170
|
-
onStepFinish,
|
|
3171
3377
|
initialPayloadParser,
|
|
3172
3378
|
url,
|
|
3173
|
-
verbose,
|
|
3174
3379
|
receiver,
|
|
3175
|
-
failureUrl,
|
|
3176
3380
|
failureFunction,
|
|
3177
3381
|
baseUrl,
|
|
3178
3382
|
env,
|
|
3179
|
-
retries,
|
|
3180
|
-
retryDelay,
|
|
3181
|
-
useJSONContent,
|
|
3182
3383
|
disableTelemetry,
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
} = processOptions(options);
|
|
3384
|
+
middlewares,
|
|
3385
|
+
internal
|
|
3386
|
+
} = processOptions(options, internalOptions);
|
|
3186
3387
|
telemetry = disableTelemetry ? void 0 : telemetry;
|
|
3187
|
-
const
|
|
3188
|
-
const handler = async (request) => {
|
|
3189
|
-
await
|
|
3190
|
-
|
|
3388
|
+
const { generateResponse: responseGenerator, useJSONContent } = internal;
|
|
3389
|
+
const handler = async (request, middlewareManager) => {
|
|
3390
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3391
|
+
info: `Received request for workflow execution.`
|
|
3392
|
+
});
|
|
3393
|
+
const { workflowUrl } = await determineUrls(
|
|
3191
3394
|
request,
|
|
3192
3395
|
url,
|
|
3193
3396
|
baseUrl,
|
|
3194
|
-
|
|
3195
|
-
failureUrl,
|
|
3196
|
-
debug
|
|
3397
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3197
3398
|
);
|
|
3198
3399
|
const requestPayload = await getPayload(request) ?? "";
|
|
3199
3400
|
await verifyRequest(requestPayload, request.headers.get("upstash-signature"), receiver);
|
|
3200
|
-
const { isFirstInvocation, workflowRunId } = validateRequest(request);
|
|
3201
|
-
|
|
3202
|
-
|
|
3401
|
+
const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
|
|
3402
|
+
middlewareManager.assignWorkflowRunId(workflowRunId);
|
|
3403
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3404
|
+
info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
|
|
3405
|
+
});
|
|
3406
|
+
const { rawInitialPayload, steps, isLastDuplicate, workflowRunEnded } = await parseRequest({
|
|
3203
3407
|
requestPayload,
|
|
3204
3408
|
isFirstInvocation,
|
|
3409
|
+
unknownSdk,
|
|
3205
3410
|
workflowRunId,
|
|
3206
|
-
qstashClient.http,
|
|
3207
|
-
request.headers.get("upstash-message-id"),
|
|
3208
|
-
|
|
3209
|
-
);
|
|
3411
|
+
requester: qstashClient.http,
|
|
3412
|
+
messageId: request.headers.get("upstash-message-id"),
|
|
3413
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3414
|
+
});
|
|
3210
3415
|
if (workflowRunEnded) {
|
|
3211
|
-
return
|
|
3212
|
-
|
|
3213
|
-
|
|
3416
|
+
return responseGenerator(
|
|
3417
|
+
createResponseData(workflowRunId, {
|
|
3418
|
+
condition: "workflow-already-ended"
|
|
3419
|
+
})
|
|
3420
|
+
);
|
|
3214
3421
|
}
|
|
3215
3422
|
if (isLastDuplicate) {
|
|
3216
|
-
return
|
|
3217
|
-
|
|
3218
|
-
|
|
3423
|
+
return responseGenerator(
|
|
3424
|
+
createResponseData(workflowRunId, {
|
|
3425
|
+
condition: "duplicate-step"
|
|
3426
|
+
})
|
|
3427
|
+
);
|
|
3219
3428
|
}
|
|
3220
|
-
const failureCheck = await handleFailure(
|
|
3429
|
+
const failureCheck = await handleFailure({
|
|
3221
3430
|
request,
|
|
3222
3431
|
requestPayload,
|
|
3223
3432
|
qstashClient,
|
|
@@ -3225,19 +3434,29 @@ var serveBase = (routeFunction, telemetry, options) => {
|
|
|
3225
3434
|
routeFunction,
|
|
3226
3435
|
failureFunction,
|
|
3227
3436
|
env,
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
flowControl,
|
|
3231
|
-
debug
|
|
3232
|
-
);
|
|
3437
|
+
dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3438
|
+
});
|
|
3233
3439
|
if (failureCheck.isErr()) {
|
|
3234
3440
|
throw failureCheck.error;
|
|
3235
|
-
} else if (failureCheck.value.result === "
|
|
3236
|
-
await
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3441
|
+
} else if (failureCheck.value.result === "failure-function-executed") {
|
|
3442
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3443
|
+
info: `Handled failure callback.`
|
|
3444
|
+
});
|
|
3445
|
+
return responseGenerator(
|
|
3446
|
+
createResponseData(workflowRunId, {
|
|
3447
|
+
condition: "failure-callback-executed",
|
|
3448
|
+
result: failureCheck.value.response
|
|
3449
|
+
})
|
|
3450
|
+
);
|
|
3451
|
+
} else if (failureCheck.value.result === "failure-function-undefined") {
|
|
3452
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3453
|
+
info: `Failure callback invoked but no failure function defined.`
|
|
3240
3454
|
});
|
|
3455
|
+
return responseGenerator(
|
|
3456
|
+
createResponseData(workflowRunId, {
|
|
3457
|
+
condition: "failure-callback-undefined"
|
|
3458
|
+
})
|
|
3459
|
+
);
|
|
3241
3460
|
}
|
|
3242
3461
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3243
3462
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
@@ -3248,29 +3467,26 @@ var serveBase = (routeFunction, telemetry, options) => {
|
|
|
3248
3467
|
headers: recreateUserHeaders(request.headers),
|
|
3249
3468
|
steps,
|
|
3250
3469
|
url: workflowUrl,
|
|
3251
|
-
failureUrl: workflowFailureUrl,
|
|
3252
|
-
debug,
|
|
3253
3470
|
env,
|
|
3254
|
-
retries,
|
|
3255
|
-
retryDelay,
|
|
3256
3471
|
telemetry,
|
|
3257
3472
|
invokeCount,
|
|
3258
|
-
|
|
3259
|
-
|
|
3473
|
+
label,
|
|
3474
|
+
middlewareManager
|
|
3260
3475
|
});
|
|
3261
3476
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3262
3477
|
routeFunction,
|
|
3263
3478
|
workflowContext
|
|
3264
3479
|
);
|
|
3265
3480
|
if (authCheck.isErr()) {
|
|
3266
|
-
await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
|
|
3267
3481
|
throw authCheck.error;
|
|
3268
3482
|
} else if (authCheck.value === "run-ended") {
|
|
3269
|
-
await
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3483
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3484
|
+
error: new Error(AUTH_FAIL_MESSAGE)
|
|
3485
|
+
});
|
|
3486
|
+
return responseGenerator(
|
|
3487
|
+
createResponseData(isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId, {
|
|
3488
|
+
condition: "auth-fail"
|
|
3489
|
+
})
|
|
3274
3490
|
);
|
|
3275
3491
|
}
|
|
3276
3492
|
const callReturnCheck = await handleThirdPartyCallResult({
|
|
@@ -3278,84 +3494,91 @@ var serveBase = (routeFunction, telemetry, options) => {
|
|
|
3278
3494
|
requestPayload: rawInitialPayload,
|
|
3279
3495
|
client: qstashClient,
|
|
3280
3496
|
workflowUrl,
|
|
3281
|
-
failureUrl: workflowFailureUrl,
|
|
3282
|
-
retries,
|
|
3283
|
-
retryDelay,
|
|
3284
|
-
flowControl,
|
|
3285
3497
|
telemetry,
|
|
3286
|
-
|
|
3498
|
+
middlewareManager
|
|
3287
3499
|
});
|
|
3288
3500
|
if (callReturnCheck.isErr()) {
|
|
3289
|
-
await debug?.log("ERROR", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
3290
|
-
error: callReturnCheck.error.message
|
|
3291
|
-
});
|
|
3292
3501
|
throw callReturnCheck.error;
|
|
3293
3502
|
} else if (callReturnCheck.value === "continue-workflow") {
|
|
3294
3503
|
const result = isFirstInvocation ? await triggerFirstInvocation({
|
|
3295
3504
|
workflowContext,
|
|
3296
3505
|
useJSONContent,
|
|
3297
3506
|
telemetry,
|
|
3298
|
-
|
|
3299
|
-
|
|
3507
|
+
invokeCount,
|
|
3508
|
+
middlewareManager,
|
|
3509
|
+
unknownSdk
|
|
3300
3510
|
}) : await triggerRouteFunction({
|
|
3301
|
-
onStep: async () =>
|
|
3511
|
+
onStep: async () => {
|
|
3512
|
+
if (steps.length === 1) {
|
|
3513
|
+
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
3514
|
+
}
|
|
3515
|
+
return await routeFunction(workflowContext);
|
|
3516
|
+
},
|
|
3302
3517
|
onCleanup: async (result2) => {
|
|
3303
|
-
await
|
|
3518
|
+
await middlewareManager.dispatchLifecycle("runCompleted", {
|
|
3519
|
+
result: result2
|
|
3520
|
+
});
|
|
3521
|
+
await triggerWorkflowDelete(
|
|
3522
|
+
workflowContext,
|
|
3523
|
+
result2,
|
|
3524
|
+
false,
|
|
3525
|
+
middlewareManager.dispatchDebug.bind(middlewareManager)
|
|
3526
|
+
);
|
|
3304
3527
|
},
|
|
3305
3528
|
onCancel: async () => {
|
|
3306
3529
|
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
3307
3530
|
},
|
|
3308
|
-
|
|
3531
|
+
middlewareManager
|
|
3309
3532
|
});
|
|
3310
3533
|
if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
|
|
3311
|
-
return
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3534
|
+
return responseGenerator(
|
|
3535
|
+
createResponseData(workflowRunId, {
|
|
3536
|
+
condition: "non-retryable-error",
|
|
3537
|
+
result: result.value
|
|
3538
|
+
})
|
|
3539
|
+
);
|
|
3315
3540
|
}
|
|
3316
3541
|
if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
|
|
3317
|
-
return
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3542
|
+
return responseGenerator(
|
|
3543
|
+
createResponseData(workflowRunId, {
|
|
3544
|
+
condition: "retry-after-error",
|
|
3545
|
+
result: result.value
|
|
3546
|
+
})
|
|
3547
|
+
);
|
|
3321
3548
|
}
|
|
3322
3549
|
if (result.isErr()) {
|
|
3323
|
-
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
3324
3550
|
throw result.error;
|
|
3325
3551
|
}
|
|
3326
|
-
await
|
|
3327
|
-
|
|
3328
|
-
condition: "success"
|
|
3552
|
+
await middlewareManager.dispatchDebug("onInfo", {
|
|
3553
|
+
info: `Workflow endpoint execution completed successfully.`
|
|
3329
3554
|
});
|
|
3555
|
+
return responseGenerator(
|
|
3556
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3557
|
+
condition: "success"
|
|
3558
|
+
})
|
|
3559
|
+
);
|
|
3330
3560
|
} else if (callReturnCheck.value === "workflow-ended") {
|
|
3331
|
-
return
|
|
3332
|
-
|
|
3333
|
-
|
|
3561
|
+
return responseGenerator(
|
|
3562
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3563
|
+
condition: "workflow-already-ended"
|
|
3564
|
+
})
|
|
3565
|
+
);
|
|
3334
3566
|
}
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3567
|
+
return responseGenerator(
|
|
3568
|
+
createResponseData(workflowContext.workflowRunId, {
|
|
3569
|
+
condition: "fromCallback"
|
|
3570
|
+
})
|
|
3571
|
+
);
|
|
3339
3572
|
};
|
|
3340
3573
|
const safeHandler = async (request) => {
|
|
3574
|
+
const middlewareManager = new MiddlewareManager(middlewares);
|
|
3341
3575
|
try {
|
|
3342
|
-
return await handler(request);
|
|
3576
|
+
return await handler(request, middlewareManager);
|
|
3343
3577
|
} catch (error) {
|
|
3344
3578
|
const formattedError = formatWorkflowError(error);
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
}
|
|
3348
|
-
const formattedOnErrorError = formatWorkflowError(onErrorError);
|
|
3349
|
-
const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
|
|
3350
|
-
Original error: '${formattedError.message}'`;
|
|
3351
|
-
console.error(errorMessage);
|
|
3352
|
-
return new Response(JSON.stringify({ error: errorMessage }), {
|
|
3353
|
-
status: 500,
|
|
3354
|
-
headers: {
|
|
3355
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3356
|
-
}
|
|
3357
|
-
});
|
|
3358
|
-
}
|
|
3579
|
+
await middlewareManager.dispatchDebug("onError", {
|
|
3580
|
+
error: isInstanceOf(error, Error) ? error : new Error(formattedError.message)
|
|
3581
|
+
});
|
|
3359
3582
|
return new Response(JSON.stringify(formattedError), {
|
|
3360
3583
|
status: 500,
|
|
3361
3584
|
headers: {
|
|
@@ -3388,15 +3611,19 @@ var DLQ = class _DLQ {
|
|
|
3388
3611
|
/**
|
|
3389
3612
|
* list the items in the DLQ
|
|
3390
3613
|
*
|
|
3391
|
-
* @param
|
|
3392
|
-
* @param
|
|
3393
|
-
* @param
|
|
3614
|
+
* @param parameters - Optional parameters object
|
|
3615
|
+
* @param parameters.cursor - Optional cursor for pagination
|
|
3616
|
+
* @param parameters.count - Optional number of items to return
|
|
3617
|
+
* @param parameters.filter - Optional filter options to apply to the DLQ items.
|
|
3394
3618
|
* The available filter options are:
|
|
3395
3619
|
* - `fromDate`: Filter items which entered the DLQ after this date.
|
|
3396
3620
|
* - `toDate`: Filter items which entered the DLQ before this date.
|
|
3397
3621
|
* - `url`: Filter items by the URL they were sent to.
|
|
3398
3622
|
* - `responseStatus`: Filter items by the response status code.
|
|
3399
|
-
*
|
|
3623
|
+
* - `workflowRunId`: Filter items by workflow run ID.
|
|
3624
|
+
* - `workflowCreatedAt`: Filter items by workflow creation time.
|
|
3625
|
+
* - `failureFunctionState`: Filter items by failure callback state.
|
|
3626
|
+
* - `label`: Filter items by label.
|
|
3400
3627
|
*/
|
|
3401
3628
|
async list(parameters) {
|
|
3402
3629
|
const { cursor, count, filter } = parameters || {};
|
|
@@ -3439,8 +3666,8 @@ var DLQ = class _DLQ {
|
|
|
3439
3666
|
* Retry the failure callback of a workflow run whose failureUrl/failureFunction
|
|
3440
3667
|
* request has failed.
|
|
3441
3668
|
*
|
|
3442
|
-
* @param dlqId - The ID of the DLQ message to retry
|
|
3443
|
-
* @returns
|
|
3669
|
+
* @param dlqId - The ID of the DLQ message to retry
|
|
3670
|
+
* @returns response with workflow run information
|
|
3444
3671
|
*/
|
|
3445
3672
|
async retryFailureFunction({ dlqId }) {
|
|
3446
3673
|
const response = await this.client.http.request({
|
|
@@ -3449,6 +3676,11 @@ var DLQ = class _DLQ {
|
|
|
3449
3676
|
});
|
|
3450
3677
|
return response;
|
|
3451
3678
|
}
|
|
3679
|
+
/**
|
|
3680
|
+
* Handles DLQ options and prepares headers and query parameters.
|
|
3681
|
+
*
|
|
3682
|
+
* @param options - DLQ resume/restart options
|
|
3683
|
+
*/
|
|
3452
3684
|
static handleDLQOptions(options) {
|
|
3453
3685
|
const { dlqId, flowControl, retries } = options;
|
|
3454
3686
|
const headers = {};
|
|
@@ -3465,6 +3697,11 @@ var DLQ = class _DLQ {
|
|
|
3465
3697
|
headers
|
|
3466
3698
|
};
|
|
3467
3699
|
}
|
|
3700
|
+
/**
|
|
3701
|
+
* Converts DLQ ID(s) to query parameter string.
|
|
3702
|
+
*
|
|
3703
|
+
* @param dlqId - Single DLQ ID or array of DLQ IDs
|
|
3704
|
+
*/
|
|
3468
3705
|
static getDlqIdQueryParameter(dlqId) {
|
|
3469
3706
|
const dlqIds = Array.isArray(dlqId) ? dlqId : [dlqId];
|
|
3470
3707
|
const paramsArray = dlqIds.map((id) => ["dlqIds", id]);
|
|
@@ -3603,7 +3840,7 @@ var Client4 = class {
|
|
|
3603
3840
|
const isBatchInput = Array.isArray(params);
|
|
3604
3841
|
const options = isBatchInput ? params : [params];
|
|
3605
3842
|
const invocations = options.map((option) => {
|
|
3606
|
-
const failureUrl = option.
|
|
3843
|
+
const failureUrl = option.failureUrl ?? option.url;
|
|
3607
3844
|
const finalWorkflowRunId = getWorkflowRunId(option.workflowRunId);
|
|
3608
3845
|
const context = new WorkflowContext({
|
|
3609
3846
|
qstashClient: this.client,
|
|
@@ -3615,11 +3852,7 @@ var Client4 = class {
|
|
|
3615
3852
|
steps: [],
|
|
3616
3853
|
url: option.url,
|
|
3617
3854
|
workflowRunId: finalWorkflowRunId,
|
|
3618
|
-
retries: option.retries,
|
|
3619
|
-
retryDelay: option.retryDelay,
|
|
3620
3855
|
telemetry: option.disableTelemetry ? void 0 : { sdk: SDK_TELEMETRY },
|
|
3621
|
-
flowControl: option.flowControl,
|
|
3622
|
-
failureUrl,
|
|
3623
3856
|
label: option.label
|
|
3624
3857
|
});
|
|
3625
3858
|
return {
|
|
@@ -3627,7 +3860,10 @@ var Client4 = class {
|
|
|
3627
3860
|
telemetry: option.disableTelemetry ? void 0 : { sdk: SDK_TELEMETRY },
|
|
3628
3861
|
delay: option.delay,
|
|
3629
3862
|
notBefore: option.notBefore,
|
|
3630
|
-
|
|
3863
|
+
failureUrl,
|
|
3864
|
+
retries: option.retries,
|
|
3865
|
+
retryDelay: option.retryDelay,
|
|
3866
|
+
flowControl: option.flowControl
|
|
3631
3867
|
};
|
|
3632
3868
|
});
|
|
3633
3869
|
const result = await triggerFirstInvocation(invocations);
|
|
@@ -3707,8 +3943,9 @@ var Client4 = class {
|
|
|
3707
3943
|
WorkflowAbort,
|
|
3708
3944
|
WorkflowContext,
|
|
3709
3945
|
WorkflowError,
|
|
3710
|
-
|
|
3946
|
+
WorkflowMiddleware,
|
|
3711
3947
|
WorkflowNonRetryableError,
|
|
3712
3948
|
WorkflowRetryAfterError,
|
|
3949
|
+
loggingMiddleware,
|
|
3713
3950
|
serve
|
|
3714
3951
|
});
|