@upstash/workflow 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/astro.d.mts +1 -1
- package/astro.d.ts +1 -1
- package/astro.js +344 -179
- package/astro.mjs +1 -1
- package/{chunk-OJZEI7LO.mjs → chunk-5R2BFC3N.mjs} +415 -192
- package/cloudflare.d.mts +1 -1
- package/cloudflare.d.ts +1 -1
- package/cloudflare.js +344 -179
- package/cloudflare.mjs +1 -1
- package/express.d.mts +1 -1
- package/express.d.ts +1 -1
- package/express.js +360 -182
- package/express.mjs +17 -4
- package/h3.d.mts +1 -1
- package/h3.d.ts +1 -1
- package/h3.js +344 -179
- package/h3.mjs +1 -1
- package/hono.d.mts +1 -1
- package/hono.d.ts +1 -1
- package/hono.js +344 -179
- package/hono.mjs +1 -1
- package/index.d.mts +74 -21
- package/index.d.ts +74 -21
- package/index.js +422 -199
- package/index.mjs +5 -5
- package/nextjs.d.mts +1 -1
- package/nextjs.d.ts +1 -1
- package/nextjs.js +344 -179
- package/nextjs.mjs +1 -1
- package/package.json +1 -1
- package/solidjs.d.mts +1 -1
- package/solidjs.d.ts +1 -1
- package/solidjs.js +344 -179
- package/solidjs.mjs +1 -1
- package/svelte.d.mts +1 -1
- package/svelte.d.ts +1 -1
- package/svelte.js +344 -179
- package/svelte.mjs +1 -1
- package/{types-CQuc-j8n.d.mts → types-Cki_MHrh.d.mts} +85 -31
- package/{types-CQuc-j8n.d.ts → types-Cki_MHrh.d.ts} +85 -31
package/h3.js
CHANGED
|
@@ -338,22 +338,33 @@ var H3Response = globalThis.Response;
|
|
|
338
338
|
|
|
339
339
|
// src/error.ts
|
|
340
340
|
var import_qstash = require("@upstash/qstash");
|
|
341
|
-
var
|
|
341
|
+
var WorkflowError = class extends import_qstash.QstashError {
|
|
342
342
|
constructor(message) {
|
|
343
343
|
super(message);
|
|
344
|
-
this.name = "
|
|
344
|
+
this.name = "WorkflowError";
|
|
345
345
|
}
|
|
346
346
|
};
|
|
347
|
-
var
|
|
347
|
+
var WorkflowAbort = class extends Error {
|
|
348
348
|
stepInfo;
|
|
349
349
|
stepName;
|
|
350
|
-
|
|
350
|
+
/**
|
|
351
|
+
* whether workflow is to be canceled on abort
|
|
352
|
+
*/
|
|
353
|
+
cancelWorkflow;
|
|
354
|
+
/**
|
|
355
|
+
*
|
|
356
|
+
* @param stepName name of the aborting step
|
|
357
|
+
* @param stepInfo step information
|
|
358
|
+
* @param cancelWorkflow
|
|
359
|
+
*/
|
|
360
|
+
constructor(stepName, stepInfo, cancelWorkflow = false) {
|
|
351
361
|
super(
|
|
352
362
|
`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}'.`
|
|
353
363
|
);
|
|
354
|
-
this.name = "
|
|
364
|
+
this.name = "WorkflowAbort";
|
|
355
365
|
this.stepName = stepName;
|
|
356
366
|
this.stepInfo = stepInfo;
|
|
367
|
+
this.cancelWorkflow = cancelWorkflow;
|
|
357
368
|
}
|
|
358
369
|
};
|
|
359
370
|
var formatWorkflowError = (error) => {
|
|
@@ -375,6 +386,44 @@ var makeNotifyRequest = async (requester, eventId, eventData) => {
|
|
|
375
386
|
});
|
|
376
387
|
return result;
|
|
377
388
|
};
|
|
389
|
+
var makeCancelRequest = async (requester, workflowRunId) => {
|
|
390
|
+
await requester.request({
|
|
391
|
+
path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
|
|
392
|
+
method: "DELETE",
|
|
393
|
+
parseResponseAsJson: false
|
|
394
|
+
});
|
|
395
|
+
return true;
|
|
396
|
+
};
|
|
397
|
+
var getSteps = async (requester, workflowRunId, messageId, debug) => {
|
|
398
|
+
try {
|
|
399
|
+
const steps = await requester.request({
|
|
400
|
+
path: ["v2", "workflows", "runs", workflowRunId],
|
|
401
|
+
parseResponseAsJson: true
|
|
402
|
+
});
|
|
403
|
+
if (!messageId) {
|
|
404
|
+
await debug?.log("INFO", "ENDPOINT_START", {
|
|
405
|
+
message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
|
|
406
|
+
});
|
|
407
|
+
return steps;
|
|
408
|
+
} else {
|
|
409
|
+
const index = steps.findIndex((item) => item.messageId === messageId);
|
|
410
|
+
if (index === -1) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
const filteredSteps = steps.slice(0, index + 1);
|
|
414
|
+
await debug?.log("INFO", "ENDPOINT_START", {
|
|
415
|
+
message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
|
|
416
|
+
});
|
|
417
|
+
return filteredSteps;
|
|
418
|
+
}
|
|
419
|
+
} catch (error) {
|
|
420
|
+
await debug?.log("ERROR", "ERROR", {
|
|
421
|
+
message: "failed while fetching steps.",
|
|
422
|
+
error
|
|
423
|
+
});
|
|
424
|
+
throw new WorkflowError(`Failed while pulling steps. ${error}`);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
378
427
|
|
|
379
428
|
// src/context/steps.ts
|
|
380
429
|
var BaseLazyStep = class {
|
|
@@ -989,6 +1038,7 @@ var StepTypes = [
|
|
|
989
1038
|
];
|
|
990
1039
|
|
|
991
1040
|
// src/workflow-requests.ts
|
|
1041
|
+
var import_qstash2 = require("@upstash/qstash");
|
|
992
1042
|
var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
993
1043
|
const { headers } = getHeaders(
|
|
994
1044
|
"true",
|
|
@@ -999,20 +1049,32 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
999
1049
|
workflowContext.failureUrl,
|
|
1000
1050
|
retries
|
|
1001
1051
|
);
|
|
1002
|
-
await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
|
|
1003
|
-
headers,
|
|
1004
|
-
requestPayload: workflowContext.requestPayload,
|
|
1005
|
-
url: workflowContext.url
|
|
1006
|
-
});
|
|
1007
1052
|
try {
|
|
1008
1053
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
1009
|
-
await workflowContext.qstashClient.publish({
|
|
1054
|
+
const result = await workflowContext.qstashClient.publish({
|
|
1010
1055
|
headers,
|
|
1011
1056
|
method: "POST",
|
|
1012
1057
|
body,
|
|
1013
1058
|
url: workflowContext.url
|
|
1014
1059
|
});
|
|
1015
|
-
|
|
1060
|
+
if (result.deduplicated) {
|
|
1061
|
+
await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
|
|
1062
|
+
message: `Workflow run ${workflowContext.workflowRunId} already exists. A new one isn't created.`,
|
|
1063
|
+
headers,
|
|
1064
|
+
requestPayload: workflowContext.requestPayload,
|
|
1065
|
+
url: workflowContext.url,
|
|
1066
|
+
messageId: result.messageId
|
|
1067
|
+
});
|
|
1068
|
+
return ok("workflow-run-already-exists");
|
|
1069
|
+
} else {
|
|
1070
|
+
await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
|
|
1071
|
+
headers,
|
|
1072
|
+
requestPayload: workflowContext.requestPayload,
|
|
1073
|
+
url: workflowContext.url,
|
|
1074
|
+
messageId: result.messageId
|
|
1075
|
+
});
|
|
1076
|
+
return ok("success");
|
|
1077
|
+
}
|
|
1016
1078
|
} catch (error) {
|
|
1017
1079
|
const error_ = error;
|
|
1018
1080
|
return err(error_);
|
|
@@ -1020,7 +1082,9 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
1020
1082
|
};
|
|
1021
1083
|
var triggerRouteFunction = async ({
|
|
1022
1084
|
onCleanup,
|
|
1023
|
-
onStep
|
|
1085
|
+
onStep,
|
|
1086
|
+
onCancel,
|
|
1087
|
+
debug
|
|
1024
1088
|
}) => {
|
|
1025
1089
|
try {
|
|
1026
1090
|
await onStep();
|
|
@@ -1028,19 +1092,50 @@ var triggerRouteFunction = async ({
|
|
|
1028
1092
|
return ok("workflow-finished");
|
|
1029
1093
|
} catch (error) {
|
|
1030
1094
|
const error_ = error;
|
|
1031
|
-
|
|
1095
|
+
if (error instanceof import_qstash2.QstashError && error.status === 400) {
|
|
1096
|
+
await debug?.log("WARN", "RESPONSE_WORKFLOW", {
|
|
1097
|
+
message: `tried to append to a cancelled workflow. exiting without publishing.`,
|
|
1098
|
+
name: error.name,
|
|
1099
|
+
errorMessage: error.message
|
|
1100
|
+
});
|
|
1101
|
+
return ok("workflow-was-finished");
|
|
1102
|
+
} else if (!(error_ instanceof WorkflowAbort)) {
|
|
1103
|
+
return err(error_);
|
|
1104
|
+
} else if (error_.cancelWorkflow) {
|
|
1105
|
+
await onCancel();
|
|
1106
|
+
return ok("workflow-finished");
|
|
1107
|
+
} else {
|
|
1108
|
+
return ok("step-finished");
|
|
1109
|
+
}
|
|
1032
1110
|
}
|
|
1033
1111
|
};
|
|
1034
1112
|
var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
|
|
1035
1113
|
await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
|
|
1036
1114
|
deletedWorkflowRunId: workflowContext.workflowRunId
|
|
1037
1115
|
});
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1116
|
+
try {
|
|
1117
|
+
await workflowContext.qstashClient.http.request({
|
|
1118
|
+
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
1119
|
+
method: "DELETE",
|
|
1120
|
+
parseResponseAsJson: false
|
|
1121
|
+
});
|
|
1122
|
+
await debug?.log(
|
|
1123
|
+
"SUBMIT",
|
|
1124
|
+
"SUBMIT_CLEANUP",
|
|
1125
|
+
`workflow run ${workflowContext.workflowRunId} deleted.`
|
|
1126
|
+
);
|
|
1127
|
+
return { deleted: true };
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
if (error instanceof import_qstash2.QstashError && error.status === 404) {
|
|
1130
|
+
await debug?.log("WARN", "SUBMIT_CLEANUP", {
|
|
1131
|
+
message: `Failed to remove workflow run ${workflowContext.workflowRunId} as it doesn't exist.`,
|
|
1132
|
+
name: error.name,
|
|
1133
|
+
errorMessage: error.message
|
|
1134
|
+
});
|
|
1135
|
+
return { deleted: false };
|
|
1136
|
+
}
|
|
1137
|
+
throw error;
|
|
1138
|
+
}
|
|
1044
1139
|
};
|
|
1045
1140
|
var recreateUserHeaders = (headers) => {
|
|
1046
1141
|
const filteredHeaders = new Headers();
|
|
@@ -1056,15 +1151,32 @@ var recreateUserHeaders = (headers) => {
|
|
|
1056
1151
|
var handleThirdPartyCallResult = async (request, requestPayload, client, workflowUrl, failureUrl, retries, debug) => {
|
|
1057
1152
|
try {
|
|
1058
1153
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
1059
|
-
|
|
1154
|
+
let callbackPayload;
|
|
1155
|
+
if (requestPayload) {
|
|
1156
|
+
callbackPayload = requestPayload;
|
|
1157
|
+
} else {
|
|
1158
|
+
const workflowRunId2 = request.headers.get("upstash-workflow-runid");
|
|
1159
|
+
const messageId = request.headers.get("upstash-message-id");
|
|
1160
|
+
if (!workflowRunId2)
|
|
1161
|
+
throw new WorkflowError("workflow run id missing in context.call lazy fetch.");
|
|
1162
|
+
if (!messageId) throw new WorkflowError("message id missing in context.call lazy fetch.");
|
|
1163
|
+
const steps = await getSteps(client.http, workflowRunId2, messageId, debug);
|
|
1164
|
+
const failingStep = steps.find((step) => step.messageId === messageId);
|
|
1165
|
+
if (!failingStep)
|
|
1166
|
+
throw new WorkflowError(
|
|
1167
|
+
"Failed to submit the context.call." + (steps.length === 0 ? "No steps found." : `No step was found with matching messageId ${messageId} out of ${steps.length} steps.`)
|
|
1168
|
+
);
|
|
1169
|
+
callbackPayload = atob(failingStep.body);
|
|
1170
|
+
}
|
|
1171
|
+
const callbackMessage = JSON.parse(callbackPayload);
|
|
1060
1172
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
1061
1173
|
await debug?.log("WARN", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
1062
1174
|
status: callbackMessage.status,
|
|
1063
|
-
body: atob(callbackMessage.body)
|
|
1175
|
+
body: atob(callbackMessage.body ?? "")
|
|
1064
1176
|
});
|
|
1065
1177
|
console.warn(
|
|
1066
1178
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
1067
|
-
${atob(callbackMessage.body)}`
|
|
1179
|
+
${atob(callbackMessage.body ?? "")}`
|
|
1068
1180
|
);
|
|
1069
1181
|
return ok("call-will-retry");
|
|
1070
1182
|
}
|
|
@@ -1098,7 +1210,7 @@ ${atob(callbackMessage.body)}`
|
|
|
1098
1210
|
);
|
|
1099
1211
|
const callResponse = {
|
|
1100
1212
|
status: callbackMessage.status,
|
|
1101
|
-
body: atob(callbackMessage.body),
|
|
1213
|
+
body: atob(callbackMessage.body ?? ""),
|
|
1102
1214
|
header: callbackMessage.header
|
|
1103
1215
|
};
|
|
1104
1216
|
const callResultStep = {
|
|
@@ -1129,9 +1241,7 @@ ${atob(callbackMessage.body)}`
|
|
|
1129
1241
|
} catch (error) {
|
|
1130
1242
|
const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
|
|
1131
1243
|
return err(
|
|
1132
|
-
new
|
|
1133
|
-
`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`
|
|
1134
|
-
)
|
|
1244
|
+
new WorkflowError(`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`)
|
|
1135
1245
|
);
|
|
1136
1246
|
}
|
|
1137
1247
|
};
|
|
@@ -1139,7 +1249,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
1139
1249
|
const baseHeaders = {
|
|
1140
1250
|
[WORKFLOW_INIT_HEADER]: initHeaderValue,
|
|
1141
1251
|
[WORKFLOW_ID_HEADER]: workflowRunId,
|
|
1142
|
-
[WORKFLOW_URL_HEADER]: workflowUrl
|
|
1252
|
+
[WORKFLOW_URL_HEADER]: workflowUrl,
|
|
1253
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody"
|
|
1143
1254
|
};
|
|
1144
1255
|
if (!step?.callUrl) {
|
|
1145
1256
|
baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
@@ -1152,8 +1263,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
1152
1263
|
}
|
|
1153
1264
|
if (step?.callUrl) {
|
|
1154
1265
|
baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
|
|
1155
|
-
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete";
|
|
1156
|
-
if (retries) {
|
|
1266
|
+
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
|
|
1267
|
+
if (retries !== void 0) {
|
|
1157
1268
|
baseHeaders["Upstash-Callback-Retries"] = retries.toString();
|
|
1158
1269
|
baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
|
|
1159
1270
|
}
|
|
@@ -1188,6 +1299,7 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
1188
1299
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
1189
1300
|
"Upstash-Callback-Workflow-Init": "false",
|
|
1190
1301
|
"Upstash-Callback-Workflow-Url": workflowUrl,
|
|
1302
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
|
|
1191
1303
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
1192
1304
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
1193
1305
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
|
|
@@ -1236,7 +1348,7 @@ var verifyRequest = async (body, signature, verifier) => {
|
|
|
1236
1348
|
throw new Error("Signature in `Upstash-Signature` header is not valid");
|
|
1237
1349
|
}
|
|
1238
1350
|
} catch (error) {
|
|
1239
|
-
throw new
|
|
1351
|
+
throw new WorkflowError(
|
|
1240
1352
|
`Failed to verify that the Workflow request comes from QStash: ${error}
|
|
1241
1353
|
|
|
1242
1354
|
If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
|
|
@@ -1276,14 +1388,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1276
1388
|
*
|
|
1277
1389
|
* If a function is already executing (this.executingStep), this
|
|
1278
1390
|
* means that there is a nested step which is not allowed. In this
|
|
1279
|
-
* case, addStep throws
|
|
1391
|
+
* case, addStep throws WorkflowError.
|
|
1280
1392
|
*
|
|
1281
1393
|
* @param stepInfo step plan to add
|
|
1282
1394
|
* @returns result of the step function
|
|
1283
1395
|
*/
|
|
1284
1396
|
async addStep(stepInfo) {
|
|
1285
1397
|
if (this.executingStep) {
|
|
1286
|
-
throw new
|
|
1398
|
+
throw new WorkflowError(
|
|
1287
1399
|
`A step can not be run inside another step. Tried to run '${stepInfo.stepName}' inside '${this.executingStep}'`
|
|
1288
1400
|
);
|
|
1289
1401
|
}
|
|
@@ -1368,7 +1480,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1368
1480
|
const sortedSteps = sortSteps(this.steps);
|
|
1369
1481
|
const plannedParallelStepCount = sortedSteps[initialStepCount + this.planStepCount]?.concurrent;
|
|
1370
1482
|
if (parallelCallState !== "first" && plannedParallelStepCount !== parallelSteps.length) {
|
|
1371
|
-
throw new
|
|
1483
|
+
throw new WorkflowError(
|
|
1372
1484
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
1373
1485
|
);
|
|
1374
1486
|
}
|
|
@@ -1390,7 +1502,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1390
1502
|
case "partial": {
|
|
1391
1503
|
const planStep = this.steps.at(-1);
|
|
1392
1504
|
if (!planStep || planStep.targetStep === void 0) {
|
|
1393
|
-
throw new
|
|
1505
|
+
throw new WorkflowError(
|
|
1394
1506
|
`There must be a last step and it should have targetStep larger than 0.Received: ${JSON.stringify(planStep)}`
|
|
1395
1507
|
);
|
|
1396
1508
|
}
|
|
@@ -1404,17 +1516,17 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1404
1516
|
);
|
|
1405
1517
|
await this.submitStepsToQStash([resultStep], [parallelStep]);
|
|
1406
1518
|
} catch (error) {
|
|
1407
|
-
if (error instanceof
|
|
1519
|
+
if (error instanceof WorkflowAbort) {
|
|
1408
1520
|
throw error;
|
|
1409
1521
|
}
|
|
1410
|
-
throw new
|
|
1522
|
+
throw new WorkflowError(
|
|
1411
1523
|
`Error submitting steps to QStash in partial parallel step execution: ${error}`
|
|
1412
1524
|
);
|
|
1413
1525
|
}
|
|
1414
1526
|
break;
|
|
1415
1527
|
}
|
|
1416
1528
|
case "discard": {
|
|
1417
|
-
throw new
|
|
1529
|
+
throw new WorkflowAbort("discarded parallel");
|
|
1418
1530
|
}
|
|
1419
1531
|
case "last": {
|
|
1420
1532
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
@@ -1465,7 +1577,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1465
1577
|
*/
|
|
1466
1578
|
async submitStepsToQStash(steps, lazySteps) {
|
|
1467
1579
|
if (steps.length === 0) {
|
|
1468
|
-
throw new
|
|
1580
|
+
throw new WorkflowError(
|
|
1469
1581
|
`Unable to submit steps to QStash. Provided list is empty. Current step: ${this.stepCount}`
|
|
1470
1582
|
);
|
|
1471
1583
|
}
|
|
@@ -1505,7 +1617,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1505
1617
|
method: "POST",
|
|
1506
1618
|
parseResponseAsJson: false
|
|
1507
1619
|
});
|
|
1508
|
-
throw new
|
|
1620
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
1509
1621
|
}
|
|
1510
1622
|
const result = await this.context.qstashClient.batchJSON(
|
|
1511
1623
|
steps.map((singleStep, index) => {
|
|
@@ -1557,7 +1669,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1557
1669
|
};
|
|
1558
1670
|
})
|
|
1559
1671
|
});
|
|
1560
|
-
throw new
|
|
1672
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
1561
1673
|
}
|
|
1562
1674
|
/**
|
|
1563
1675
|
* Get the promise by executing the lazt steps list. If there is a single
|
|
@@ -1582,7 +1694,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1582
1694
|
} else if (Array.isArray(result) && lazyStepList.length === result.length && index < lazyStepList.length) {
|
|
1583
1695
|
return result[index];
|
|
1584
1696
|
} else {
|
|
1585
|
-
throw new
|
|
1697
|
+
throw new WorkflowError(
|
|
1586
1698
|
`Unexpected parallel call result while executing step ${index}: '${result}'. Expected ${lazyStepList.length} many items`
|
|
1587
1699
|
);
|
|
1588
1700
|
}
|
|
@@ -1594,12 +1706,12 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
1594
1706
|
};
|
|
1595
1707
|
var validateStep = (lazyStep, stepFromRequest) => {
|
|
1596
1708
|
if (lazyStep.stepName !== stepFromRequest.stepName) {
|
|
1597
|
-
throw new
|
|
1709
|
+
throw new WorkflowError(
|
|
1598
1710
|
`Incompatible step name. Expected '${lazyStep.stepName}', got '${stepFromRequest.stepName}' from the request`
|
|
1599
1711
|
);
|
|
1600
1712
|
}
|
|
1601
1713
|
if (lazyStep.stepType !== stepFromRequest.stepType) {
|
|
1602
|
-
throw new
|
|
1714
|
+
throw new WorkflowError(
|
|
1603
1715
|
`Incompatible step type. Expected '${lazyStep.stepType}', got '${stepFromRequest.stepType}' from the request`
|
|
1604
1716
|
);
|
|
1605
1717
|
}
|
|
@@ -1610,12 +1722,12 @@ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
|
|
|
1610
1722
|
validateStep(lazySteps[index], stepFromRequest);
|
|
1611
1723
|
}
|
|
1612
1724
|
} catch (error) {
|
|
1613
|
-
if (error instanceof
|
|
1725
|
+
if (error instanceof WorkflowError) {
|
|
1614
1726
|
const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
|
|
1615
1727
|
const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
|
|
1616
1728
|
const requestStepNames = stepsFromRequest.map((step) => step.stepName);
|
|
1617
1729
|
const requestStepTypes = stepsFromRequest.map((step) => step.stepType);
|
|
1618
|
-
throw new
|
|
1730
|
+
throw new WorkflowError(
|
|
1619
1731
|
`Incompatible steps detected in parallel execution: ${error.message}
|
|
1620
1732
|
> Step Names from the request: ${JSON.stringify(requestStepNames)}
|
|
1621
1733
|
Step Types from the request: ${JSON.stringify(requestStepTypes)}
|
|
@@ -1728,10 +1840,6 @@ var WorkflowContext = class {
|
|
|
1728
1840
|
* headers of the initial request
|
|
1729
1841
|
*/
|
|
1730
1842
|
headers;
|
|
1731
|
-
/**
|
|
1732
|
-
* initial payload as a raw string
|
|
1733
|
-
*/
|
|
1734
|
-
rawInitialPayload;
|
|
1735
1843
|
/**
|
|
1736
1844
|
* Map of environment variables and their values.
|
|
1737
1845
|
*
|
|
@@ -1766,7 +1874,6 @@ var WorkflowContext = class {
|
|
|
1766
1874
|
failureUrl,
|
|
1767
1875
|
debug,
|
|
1768
1876
|
initialPayload,
|
|
1769
|
-
rawInitialPayload,
|
|
1770
1877
|
env,
|
|
1771
1878
|
retries
|
|
1772
1879
|
}) {
|
|
@@ -1777,7 +1884,6 @@ var WorkflowContext = class {
|
|
|
1777
1884
|
this.failureUrl = failureUrl;
|
|
1778
1885
|
this.headers = headers;
|
|
1779
1886
|
this.requestPayload = initialPayload;
|
|
1780
|
-
this.rawInitialPayload = rawInitialPayload ?? JSON.stringify(this.requestPayload);
|
|
1781
1887
|
this.env = env ?? {};
|
|
1782
1888
|
this.retries = retries ?? DEFAULT_RETRIES;
|
|
1783
1889
|
this.executor = new AutoExecutor(this, this.steps, debug);
|
|
@@ -1798,7 +1904,7 @@ var WorkflowContext = class {
|
|
|
1798
1904
|
* const [result1, result2] = await Promise.all([
|
|
1799
1905
|
* context.run("step 1", () => {
|
|
1800
1906
|
* return "result1"
|
|
1801
|
-
* })
|
|
1907
|
+
* }),
|
|
1802
1908
|
* context.run("step 2", async () => {
|
|
1803
1909
|
* return await fetchResults()
|
|
1804
1910
|
* })
|
|
@@ -1816,6 +1922,10 @@ var WorkflowContext = class {
|
|
|
1816
1922
|
/**
|
|
1817
1923
|
* Stops the execution for the duration provided.
|
|
1818
1924
|
*
|
|
1925
|
+
* ```typescript
|
|
1926
|
+
* await context.sleep('sleep1', 3) // wait for three seconds
|
|
1927
|
+
* ```
|
|
1928
|
+
*
|
|
1819
1929
|
* @param stepName
|
|
1820
1930
|
* @param duration sleep duration in seconds
|
|
1821
1931
|
* @returns undefined
|
|
@@ -1826,6 +1936,10 @@ var WorkflowContext = class {
|
|
|
1826
1936
|
/**
|
|
1827
1937
|
* Stops the execution until the date time provided.
|
|
1828
1938
|
*
|
|
1939
|
+
* ```typescript
|
|
1940
|
+
* await context.sleepUntil('sleep1', Date.now() / 1000 + 3) // wait for three seconds
|
|
1941
|
+
* ```
|
|
1942
|
+
*
|
|
1829
1943
|
* @param stepName
|
|
1830
1944
|
* @param datetime time to sleep until. Can be provided as a number (in unix seconds),
|
|
1831
1945
|
* as a Date object or a string (passed to `new Date(datetimeString)`)
|
|
@@ -1849,7 +1963,7 @@ var WorkflowContext = class {
|
|
|
1849
1963
|
* const { status, body } = await context.call<string>(
|
|
1850
1964
|
* "post call step",
|
|
1851
1965
|
* {
|
|
1852
|
-
* url:
|
|
1966
|
+
* url: "https://www.some-endpoint.com/api",
|
|
1853
1967
|
* method: "POST",
|
|
1854
1968
|
* body: "my-payload"
|
|
1855
1969
|
* }
|
|
@@ -1903,45 +2017,43 @@ var WorkflowContext = class {
|
|
|
1903
2017
|
}
|
|
1904
2018
|
}
|
|
1905
2019
|
/**
|
|
1906
|
-
*
|
|
1907
|
-
* timeout ends
|
|
2020
|
+
* Pauses workflow execution until a specific event occurs or a timeout is reached.
|
|
1908
2021
|
*
|
|
1909
|
-
|
|
1910
|
-
* const
|
|
1911
|
-
*
|
|
1912
|
-
*
|
|
1913
|
-
|
|
1914
|
-
* );
|
|
1915
|
-
* ```
|
|
2022
|
+
*```ts
|
|
2023
|
+
* const result = await workflow.waitForEvent("payment-confirmed", {
|
|
2024
|
+
* timeout: "5m"
|
|
2025
|
+
* });
|
|
2026
|
+
*```
|
|
1916
2027
|
*
|
|
1917
|
-
* To notify a waiting workflow
|
|
2028
|
+
* To notify a waiting workflow:
|
|
1918
2029
|
*
|
|
1919
2030
|
* ```ts
|
|
1920
2031
|
* import { Client } from "@upstash/workflow";
|
|
1921
2032
|
*
|
|
1922
|
-
* const client = new Client({ token: });
|
|
2033
|
+
* const client = new Client({ token: "<QSTASH_TOKEN>" });
|
|
1923
2034
|
*
|
|
1924
2035
|
* await client.notify({
|
|
1925
|
-
* eventId: "
|
|
1926
|
-
*
|
|
2036
|
+
* eventId: "payment.confirmed",
|
|
2037
|
+
* data: {
|
|
2038
|
+
* amount: 99.99,
|
|
2039
|
+
* currency: "USD"
|
|
2040
|
+
* }
|
|
1927
2041
|
* })
|
|
1928
2042
|
* ```
|
|
1929
2043
|
*
|
|
2044
|
+
* Alternatively, you can use the `context.notify` method.
|
|
2045
|
+
*
|
|
1930
2046
|
* @param stepName
|
|
1931
|
-
* @param eventId
|
|
1932
|
-
* @param
|
|
1933
|
-
* @returns
|
|
1934
|
-
* timeout
|
|
1935
|
-
* is the
|
|
2047
|
+
* @param eventId - Unique identifier for the event to wait for
|
|
2048
|
+
* @param options - Configuration options.
|
|
2049
|
+
* @returns `{ timeout: boolean, eventData: unknown }`.
|
|
2050
|
+
* The `timeout` property specifies if the workflow has timed out. The `eventData`
|
|
2051
|
+
* is the data passed when notifying this workflow of an event.
|
|
1936
2052
|
*/
|
|
1937
|
-
async waitForEvent(stepName, eventId,
|
|
1938
|
-
const
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
eventId,
|
|
1942
|
-
typeof timeout === "string" ? timeout : `${timeout}s`
|
|
1943
|
-
)
|
|
1944
|
-
);
|
|
2053
|
+
async waitForEvent(stepName, eventId, options = {}) {
|
|
2054
|
+
const { timeout = "7d" } = options;
|
|
2055
|
+
const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
|
|
2056
|
+
const result = await this.addStep(new LazyWaitForEventStep(stepName, eventId, timeoutStr));
|
|
1945
2057
|
try {
|
|
1946
2058
|
return {
|
|
1947
2059
|
...result,
|
|
@@ -1951,6 +2063,27 @@ var WorkflowContext = class {
|
|
|
1951
2063
|
return result;
|
|
1952
2064
|
}
|
|
1953
2065
|
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Notify workflow runs waiting for an event
|
|
2068
|
+
*
|
|
2069
|
+
* ```ts
|
|
2070
|
+
* const { eventId, eventData, notifyResponse } = await context.notify(
|
|
2071
|
+
* "notify step", "event-id", "event-data"
|
|
2072
|
+
* );
|
|
2073
|
+
* ```
|
|
2074
|
+
*
|
|
2075
|
+
* Upon `context.notify`, the workflow runs waiting for the given eventId (context.waitForEvent)
|
|
2076
|
+
* will receive the given event data and resume execution.
|
|
2077
|
+
*
|
|
2078
|
+
* The response includes the same eventId and eventData. Additionally, there is
|
|
2079
|
+
* a notifyResponse field which contains a list of `Waiter` objects, each corresponding
|
|
2080
|
+
* to a notified workflow run.
|
|
2081
|
+
*
|
|
2082
|
+
* @param stepName
|
|
2083
|
+
* @param eventId event id to notify
|
|
2084
|
+
* @param eventData event data to notify with
|
|
2085
|
+
* @returns notify response which has event id, event data and list of waiters which were notified
|
|
2086
|
+
*/
|
|
1954
2087
|
async notify(stepName, eventId, eventData) {
|
|
1955
2088
|
const result = await this.addStep(
|
|
1956
2089
|
new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
|
|
@@ -1964,6 +2097,15 @@ var WorkflowContext = class {
|
|
|
1964
2097
|
return result;
|
|
1965
2098
|
}
|
|
1966
2099
|
}
|
|
2100
|
+
/**
|
|
2101
|
+
* Cancel the current workflow run
|
|
2102
|
+
*
|
|
2103
|
+
* Will throw WorkflowAbort to stop workflow execution.
|
|
2104
|
+
* Shouldn't be inside try/catch.
|
|
2105
|
+
*/
|
|
2106
|
+
async cancel() {
|
|
2107
|
+
throw new WorkflowAbort("cancel", void 0, true);
|
|
2108
|
+
}
|
|
1967
2109
|
/**
|
|
1968
2110
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
1969
2111
|
* DisabledWorkflowContext.
|
|
@@ -2004,7 +2146,8 @@ var WorkflowLogger = class _WorkflowLogger {
|
|
|
2004
2146
|
}
|
|
2005
2147
|
writeToConsole(logEntry) {
|
|
2006
2148
|
const JSON_SPACING = 2;
|
|
2007
|
-
console.
|
|
2149
|
+
const logMethod = logEntry.logLevel === "ERROR" ? console.error : logEntry.logLevel === "WARN" ? console.warn : console.log;
|
|
2150
|
+
logMethod(JSON.stringify(logEntry, void 0, JSON_SPACING));
|
|
2008
2151
|
}
|
|
2009
2152
|
shouldLog(level) {
|
|
2010
2153
|
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
@@ -2046,6 +2189,63 @@ function decodeBase64(base64) {
|
|
|
2046
2189
|
}
|
|
2047
2190
|
}
|
|
2048
2191
|
|
|
2192
|
+
// src/serve/authorization.ts
|
|
2193
|
+
var import_qstash3 = require("@upstash/qstash");
|
|
2194
|
+
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
2195
|
+
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
2196
|
+
/**
|
|
2197
|
+
* overwrite the WorkflowContext.addStep method to always raise WorkflowAbort
|
|
2198
|
+
* error in order to stop the execution whenever we encounter a step.
|
|
2199
|
+
*
|
|
2200
|
+
* @param _step
|
|
2201
|
+
*/
|
|
2202
|
+
async addStep(_step) {
|
|
2203
|
+
throw new WorkflowAbort(_DisabledWorkflowContext.disabledMessage);
|
|
2204
|
+
}
|
|
2205
|
+
/**
|
|
2206
|
+
* overwrite cancel method to do nothing
|
|
2207
|
+
*/
|
|
2208
|
+
async cancel() {
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
2213
|
+
* route function with the new context.
|
|
2214
|
+
*
|
|
2215
|
+
* - returns "run-ended" if there are no steps found or
|
|
2216
|
+
* if the auth failed and user called `return`
|
|
2217
|
+
* - returns "step-found" if DisabledWorkflowContext.addStep is called.
|
|
2218
|
+
* - if there is another error, returns the error.
|
|
2219
|
+
*
|
|
2220
|
+
* @param routeFunction
|
|
2221
|
+
*/
|
|
2222
|
+
static async tryAuthentication(routeFunction, context) {
|
|
2223
|
+
const disabledContext = new _DisabledWorkflowContext({
|
|
2224
|
+
qstashClient: new import_qstash3.Client({
|
|
2225
|
+
baseUrl: "disabled-client",
|
|
2226
|
+
token: "disabled-client"
|
|
2227
|
+
}),
|
|
2228
|
+
workflowRunId: context.workflowRunId,
|
|
2229
|
+
headers: context.headers,
|
|
2230
|
+
steps: [],
|
|
2231
|
+
url: context.url,
|
|
2232
|
+
failureUrl: context.failureUrl,
|
|
2233
|
+
initialPayload: context.requestPayload,
|
|
2234
|
+
env: context.env,
|
|
2235
|
+
retries: context.retries
|
|
2236
|
+
});
|
|
2237
|
+
try {
|
|
2238
|
+
await routeFunction(disabledContext);
|
|
2239
|
+
} catch (error) {
|
|
2240
|
+
if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage) {
|
|
2241
|
+
return ok("step-found");
|
|
2242
|
+
}
|
|
2243
|
+
return err(error);
|
|
2244
|
+
}
|
|
2245
|
+
return ok("run-ended");
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2049
2249
|
// src/workflow-parser.ts
|
|
2050
2250
|
var getPayload = async (request) => {
|
|
2051
2251
|
try {
|
|
@@ -2054,8 +2254,8 @@ var getPayload = async (request) => {
|
|
|
2054
2254
|
return;
|
|
2055
2255
|
}
|
|
2056
2256
|
};
|
|
2057
|
-
var
|
|
2058
|
-
const [encodedInitialPayload, ...encodedSteps] =
|
|
2257
|
+
var processRawSteps = (rawSteps) => {
|
|
2258
|
+
const [encodedInitialPayload, ...encodedSteps] = rawSteps;
|
|
2059
2259
|
const rawInitialPayload = decodeBase64(encodedInitialPayload.body);
|
|
2060
2260
|
const initialStep = {
|
|
2061
2261
|
stepId: 0,
|
|
@@ -2065,27 +2265,21 @@ var parsePayload = async (rawPayload, debug) => {
|
|
|
2065
2265
|
concurrent: NO_CONCURRENCY
|
|
2066
2266
|
};
|
|
2067
2267
|
const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
|
|
2068
|
-
const otherSteps =
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
};
|
|
2084
|
-
step.out = newOut;
|
|
2085
|
-
}
|
|
2086
|
-
return step;
|
|
2087
|
-
})
|
|
2088
|
-
);
|
|
2268
|
+
const otherSteps = stepsToDecode.map((rawStep) => {
|
|
2269
|
+
const step = JSON.parse(decodeBase64(rawStep.body));
|
|
2270
|
+
try {
|
|
2271
|
+
step.out = JSON.parse(step.out);
|
|
2272
|
+
} catch {
|
|
2273
|
+
}
|
|
2274
|
+
if (step.waitEventId) {
|
|
2275
|
+
const newOut = {
|
|
2276
|
+
eventData: step.out ? decodeBase64(step.out) : void 0,
|
|
2277
|
+
timeout: step.waitTimeout ?? false
|
|
2278
|
+
};
|
|
2279
|
+
step.out = newOut;
|
|
2280
|
+
}
|
|
2281
|
+
return step;
|
|
2282
|
+
});
|
|
2089
2283
|
const steps = [initialStep, ...otherSteps];
|
|
2090
2284
|
return {
|
|
2091
2285
|
rawInitialPayload,
|
|
@@ -2133,20 +2327,20 @@ var validateRequest = (request) => {
|
|
|
2133
2327
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
2134
2328
|
const isFirstInvocation = !versionHeader;
|
|
2135
2329
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
2136
|
-
throw new
|
|
2330
|
+
throw new WorkflowError(
|
|
2137
2331
|
`Incompatible workflow sdk protocol version. Expected ${WORKFLOW_PROTOCOL_VERSION}, got ${versionHeader} from the request.`
|
|
2138
2332
|
);
|
|
2139
2333
|
}
|
|
2140
2334
|
const workflowRunId = isFirstInvocation ? getWorkflowRunId() : request.headers.get(WORKFLOW_ID_HEADER) ?? "";
|
|
2141
2335
|
if (workflowRunId.length === 0) {
|
|
2142
|
-
throw new
|
|
2336
|
+
throw new WorkflowError("Couldn't get workflow id from header");
|
|
2143
2337
|
}
|
|
2144
2338
|
return {
|
|
2145
2339
|
isFirstInvocation,
|
|
2146
2340
|
workflowRunId
|
|
2147
2341
|
};
|
|
2148
2342
|
};
|
|
2149
|
-
var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
2343
|
+
var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requester, messageId, debug) => {
|
|
2150
2344
|
if (isFirstInvocation) {
|
|
2151
2345
|
return {
|
|
2152
2346
|
rawInitialPayload: requestPayload ?? "",
|
|
@@ -2154,10 +2348,18 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
2154
2348
|
isLastDuplicate: false
|
|
2155
2349
|
};
|
|
2156
2350
|
} else {
|
|
2351
|
+
let rawSteps;
|
|
2157
2352
|
if (!requestPayload) {
|
|
2158
|
-
|
|
2353
|
+
await debug?.log(
|
|
2354
|
+
"INFO",
|
|
2355
|
+
"ENDPOINT_START",
|
|
2356
|
+
"request payload is empty, steps will be fetched from QStash."
|
|
2357
|
+
);
|
|
2358
|
+
rawSteps = await getSteps(requester, workflowRunId, messageId, debug);
|
|
2359
|
+
} else {
|
|
2360
|
+
rawSteps = JSON.parse(requestPayload);
|
|
2159
2361
|
}
|
|
2160
|
-
const { rawInitialPayload, steps } =
|
|
2362
|
+
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
2161
2363
|
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, debug);
|
|
2162
2364
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
2163
2365
|
return {
|
|
@@ -2167,13 +2369,13 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
2167
2369
|
};
|
|
2168
2370
|
}
|
|
2169
2371
|
};
|
|
2170
|
-
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, failureFunction, debug) => {
|
|
2372
|
+
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, debug) => {
|
|
2171
2373
|
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
|
|
2172
2374
|
return ok("not-failure-callback");
|
|
2173
2375
|
}
|
|
2174
2376
|
if (!failureFunction) {
|
|
2175
2377
|
return err(
|
|
2176
|
-
new
|
|
2378
|
+
new WorkflowError(
|
|
2177
2379
|
"Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
|
|
2178
2380
|
)
|
|
2179
2381
|
);
|
|
@@ -2184,92 +2386,48 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
2184
2386
|
);
|
|
2185
2387
|
const decodedBody = body ? decodeBase64(body) : "{}";
|
|
2186
2388
|
const errorPayload = JSON.parse(decodedBody);
|
|
2187
|
-
const {
|
|
2188
|
-
rawInitialPayload,
|
|
2189
|
-
steps,
|
|
2190
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2191
|
-
isLastDuplicate: _isLastDuplicate
|
|
2192
|
-
} = await parseRequest(decodeBase64(sourceBody), false, debug);
|
|
2193
2389
|
const workflowContext = new WorkflowContext({
|
|
2194
2390
|
qstashClient,
|
|
2195
2391
|
workflowRunId,
|
|
2196
|
-
initialPayload: initialPayloadParser(
|
|
2197
|
-
rawInitialPayload,
|
|
2392
|
+
initialPayload: initialPayloadParser(decodeBase64(sourceBody)),
|
|
2198
2393
|
headers: recreateUserHeaders(new Headers(sourceHeader)),
|
|
2199
|
-
steps,
|
|
2394
|
+
steps: [],
|
|
2200
2395
|
url,
|
|
2201
2396
|
failureUrl: url,
|
|
2202
2397
|
debug
|
|
2203
2398
|
});
|
|
2204
|
-
|
|
2399
|
+
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
2400
|
+
routeFunction,
|
|
2401
|
+
workflowContext
|
|
2402
|
+
);
|
|
2403
|
+
if (authCheck.isErr()) {
|
|
2404
|
+
await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
|
|
2405
|
+
throw authCheck.error;
|
|
2406
|
+
} else if (authCheck.value === "run-ended") {
|
|
2407
|
+
return err(new WorkflowError("Not authorized to run the failure function."));
|
|
2408
|
+
}
|
|
2409
|
+
await failureFunction({
|
|
2410
|
+
context: workflowContext,
|
|
2411
|
+
failStatus: status,
|
|
2412
|
+
failResponse: errorPayload.message,
|
|
2413
|
+
failHeaders: header
|
|
2414
|
+
});
|
|
2205
2415
|
} catch (error) {
|
|
2206
2416
|
return err(error);
|
|
2207
2417
|
}
|
|
2208
2418
|
return ok("is-failure-callback");
|
|
2209
2419
|
};
|
|
2210
2420
|
|
|
2211
|
-
// src/serve/authorization.ts
|
|
2212
|
-
var import_qstash2 = require("@upstash/qstash");
|
|
2213
|
-
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
2214
|
-
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
2215
|
-
/**
|
|
2216
|
-
* overwrite the WorkflowContext.addStep method to always raise QStashWorkflowAbort
|
|
2217
|
-
* error in order to stop the execution whenever we encounter a step.
|
|
2218
|
-
*
|
|
2219
|
-
* @param _step
|
|
2220
|
-
*/
|
|
2221
|
-
async addStep(_step) {
|
|
2222
|
-
throw new QStashWorkflowAbort(_DisabledWorkflowContext.disabledMessage);
|
|
2223
|
-
}
|
|
2224
|
-
/**
|
|
2225
|
-
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
2226
|
-
* route function with the new context.
|
|
2227
|
-
*
|
|
2228
|
-
* - returns "run-ended" if there are no steps found or
|
|
2229
|
-
* if the auth failed and user called `return`
|
|
2230
|
-
* - returns "step-found" if DisabledWorkflowContext.addStep is called.
|
|
2231
|
-
* - if there is another error, returns the error.
|
|
2232
|
-
*
|
|
2233
|
-
* @param routeFunction
|
|
2234
|
-
*/
|
|
2235
|
-
static async tryAuthentication(routeFunction, context) {
|
|
2236
|
-
const disabledContext = new _DisabledWorkflowContext({
|
|
2237
|
-
qstashClient: new import_qstash2.Client({
|
|
2238
|
-
baseUrl: "disabled-client",
|
|
2239
|
-
token: "disabled-client"
|
|
2240
|
-
}),
|
|
2241
|
-
workflowRunId: context.workflowRunId,
|
|
2242
|
-
headers: context.headers,
|
|
2243
|
-
steps: [],
|
|
2244
|
-
url: context.url,
|
|
2245
|
-
failureUrl: context.failureUrl,
|
|
2246
|
-
initialPayload: context.requestPayload,
|
|
2247
|
-
rawInitialPayload: context.rawInitialPayload,
|
|
2248
|
-
env: context.env,
|
|
2249
|
-
retries: context.retries
|
|
2250
|
-
});
|
|
2251
|
-
try {
|
|
2252
|
-
await routeFunction(disabledContext);
|
|
2253
|
-
} catch (error) {
|
|
2254
|
-
if (error instanceof QStashWorkflowAbort && error.stepName === this.disabledMessage) {
|
|
2255
|
-
return ok("step-found");
|
|
2256
|
-
}
|
|
2257
|
-
return err(error);
|
|
2258
|
-
}
|
|
2259
|
-
return ok("run-ended");
|
|
2260
|
-
}
|
|
2261
|
-
};
|
|
2262
|
-
|
|
2263
2421
|
// src/serve/options.ts
|
|
2264
|
-
var import_qstash3 = require("@upstash/qstash");
|
|
2265
2422
|
var import_qstash4 = require("@upstash/qstash");
|
|
2423
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
2266
2424
|
var processOptions = (options) => {
|
|
2267
2425
|
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
2268
2426
|
const receiverEnvironmentVariablesSet = Boolean(
|
|
2269
2427
|
environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
|
|
2270
2428
|
);
|
|
2271
2429
|
return {
|
|
2272
|
-
qstashClient: new
|
|
2430
|
+
qstashClient: new import_qstash5.Client({
|
|
2273
2431
|
baseUrl: environment.QSTASH_URL,
|
|
2274
2432
|
token: environment.QSTASH_TOKEN
|
|
2275
2433
|
}),
|
|
@@ -2290,7 +2448,7 @@ var processOptions = (options) => {
|
|
|
2290
2448
|
throw error;
|
|
2291
2449
|
}
|
|
2292
2450
|
},
|
|
2293
|
-
receiver: receiverEnvironmentVariablesSet ? new
|
|
2451
|
+
receiver: receiverEnvironmentVariablesSet ? new import_qstash4.Receiver({
|
|
2294
2452
|
currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
|
|
2295
2453
|
nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
|
|
2296
2454
|
}) : void 0,
|
|
@@ -2352,6 +2510,9 @@ var serve = (routeFunction, options) => {
|
|
|
2352
2510
|
const { rawInitialPayload, steps, isLastDuplicate } = await parseRequest(
|
|
2353
2511
|
requestPayload,
|
|
2354
2512
|
isFirstInvocation,
|
|
2513
|
+
workflowRunId,
|
|
2514
|
+
qstashClient.http,
|
|
2515
|
+
request.headers.get("upstash-message-id"),
|
|
2355
2516
|
debug
|
|
2356
2517
|
);
|
|
2357
2518
|
if (isLastDuplicate) {
|
|
@@ -2362,6 +2523,7 @@ var serve = (routeFunction, options) => {
|
|
|
2362
2523
|
requestPayload,
|
|
2363
2524
|
qstashClient,
|
|
2364
2525
|
initialPayloadParser,
|
|
2526
|
+
routeFunction,
|
|
2365
2527
|
failureFunction
|
|
2366
2528
|
);
|
|
2367
2529
|
if (failureCheck.isErr()) {
|
|
@@ -2374,7 +2536,6 @@ var serve = (routeFunction, options) => {
|
|
|
2374
2536
|
qstashClient,
|
|
2375
2537
|
workflowRunId,
|
|
2376
2538
|
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
2377
|
-
rawInitialPayload,
|
|
2378
2539
|
headers: recreateUserHeaders(request.headers),
|
|
2379
2540
|
steps,
|
|
2380
2541
|
url: workflowUrl,
|
|
@@ -2412,7 +2573,11 @@ var serve = (routeFunction, options) => {
|
|
|
2412
2573
|
onStep: async () => routeFunction(workflowContext),
|
|
2413
2574
|
onCleanup: async () => {
|
|
2414
2575
|
await triggerWorkflowDelete(workflowContext, debug);
|
|
2415
|
-
}
|
|
2576
|
+
},
|
|
2577
|
+
onCancel: async () => {
|
|
2578
|
+
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
2579
|
+
},
|
|
2580
|
+
debug
|
|
2416
2581
|
});
|
|
2417
2582
|
if (result.isErr()) {
|
|
2418
2583
|
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
@@ -2438,7 +2603,7 @@ var serve = (routeFunction, options) => {
|
|
|
2438
2603
|
};
|
|
2439
2604
|
|
|
2440
2605
|
// src/client/index.ts
|
|
2441
|
-
var
|
|
2606
|
+
var import_qstash6 = require("@upstash/qstash");
|
|
2442
2607
|
|
|
2443
2608
|
// platforms/h3.ts
|
|
2444
2609
|
function transformHeaders(headers) {
|