@upstash/workflow 0.1.4 → 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 +348 -191
- package/astro.mjs +1 -1
- package/{chunk-HO2SB246.mjs → chunk-5R2BFC3N.mjs} +419 -194
- package/cloudflare.d.mts +1 -1
- package/cloudflare.d.ts +1 -1
- package/cloudflare.js +348 -191
- package/cloudflare.mjs +1 -1
- package/express.d.mts +1 -1
- package/express.d.ts +1 -1
- package/express.js +369 -189
- package/express.mjs +17 -4
- package/h3.d.mts +1 -1
- package/h3.d.ts +1 -1
- package/h3.js +348 -191
- package/h3.mjs +1 -1
- package/hono.d.mts +1 -1
- package/hono.d.ts +1 -1
- package/hono.js +348 -191
- package/hono.mjs +1 -1
- package/index.d.mts +74 -21
- package/index.d.ts +74 -21
- package/index.js +426 -211
- package/index.mjs +5 -5
- package/nextjs.d.mts +1 -1
- package/nextjs.d.ts +1 -1
- package/nextjs.js +348 -191
- 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 +348 -191
- package/solidjs.mjs +1 -1
- package/svelte.d.mts +1 -1
- package/svelte.d.ts +1 -1
- package/svelte.js +348 -191
- 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/express.js
CHANGED
|
@@ -15316,9 +15316,19 @@ var require_object_inspect = __commonJS({
|
|
|
15316
15316
|
var utilInspect = require_util_inspect();
|
|
15317
15317
|
var inspectCustom = utilInspect.custom;
|
|
15318
15318
|
var inspectSymbol = isSymbol(inspectCustom) ? inspectCustom : null;
|
|
15319
|
+
var quotes = {
|
|
15320
|
+
__proto__: null,
|
|
15321
|
+
"double": '"',
|
|
15322
|
+
single: "'"
|
|
15323
|
+
};
|
|
15324
|
+
var quoteREs = {
|
|
15325
|
+
__proto__: null,
|
|
15326
|
+
"double": /(["\\])/g,
|
|
15327
|
+
single: /(['\\])/g
|
|
15328
|
+
};
|
|
15319
15329
|
module2.exports = function inspect_(obj, options, depth, seen) {
|
|
15320
15330
|
var opts = options || {};
|
|
15321
|
-
if (has(opts, "quoteStyle") && (
|
|
15331
|
+
if (has(opts, "quoteStyle") && !has(quotes, opts.quoteStyle)) {
|
|
15322
15332
|
throw new TypeError('option "quoteStyle" must be "single" or "double"');
|
|
15323
15333
|
}
|
|
15324
15334
|
if (has(opts, "maxStringLength") && (typeof opts.maxStringLength === "number" ? opts.maxStringLength < 0 && opts.maxStringLength !== Infinity : opts.maxStringLength !== null)) {
|
|
@@ -15499,7 +15509,8 @@ var require_object_inspect = __commonJS({
|
|
|
15499
15509
|
return String(obj);
|
|
15500
15510
|
};
|
|
15501
15511
|
function wrapQuotes(s, defaultStyle, opts) {
|
|
15502
|
-
var
|
|
15512
|
+
var style = opts.quoteStyle || defaultStyle;
|
|
15513
|
+
var quoteChar = quotes[style];
|
|
15503
15514
|
return quoteChar + s + quoteChar;
|
|
15504
15515
|
}
|
|
15505
15516
|
function quote(s) {
|
|
@@ -15674,7 +15685,9 @@ var require_object_inspect = __commonJS({
|
|
|
15674
15685
|
var trailer = "... " + remaining + " more character" + (remaining > 1 ? "s" : "");
|
|
15675
15686
|
return inspectString($slice.call(str, 0, opts.maxStringLength), opts) + trailer;
|
|
15676
15687
|
}
|
|
15677
|
-
var
|
|
15688
|
+
var quoteRE = quoteREs[opts.quoteStyle || "single"];
|
|
15689
|
+
quoteRE.lastIndex = 0;
|
|
15690
|
+
var s = $replace.call($replace.call(str, quoteRE, "\\$1"), /[\x00-\x1f]/g, lowbyte);
|
|
15678
15691
|
return wrapQuotes(s, "single", opts);
|
|
15679
15692
|
}
|
|
15680
15693
|
function lowbyte(c) {
|
|
@@ -19657,14 +19670,14 @@ var require_etag = __commonJS({
|
|
|
19657
19670
|
"node_modules/etag/index.js"(exports2, module2) {
|
|
19658
19671
|
"use strict";
|
|
19659
19672
|
module2.exports = etag;
|
|
19660
|
-
var
|
|
19673
|
+
var crypto = require("crypto");
|
|
19661
19674
|
var Stats = require("fs").Stats;
|
|
19662
19675
|
var toString = Object.prototype.toString;
|
|
19663
19676
|
function entitytag(entity) {
|
|
19664
19677
|
if (entity.length === 0) {
|
|
19665
19678
|
return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
|
|
19666
19679
|
}
|
|
19667
|
-
var hash =
|
|
19680
|
+
var hash = crypto.createHash("sha1").update(entity, "utf8").digest("base64").substring(0, 27);
|
|
19668
19681
|
var len = typeof entity === "string" ? Buffer.byteLength(entity, "utf8") : entity.length;
|
|
19669
19682
|
return '"' + len.toString(16) + "-" + hash + '"';
|
|
19670
19683
|
}
|
|
@@ -22560,11 +22573,11 @@ var require_request = __commonJS({
|
|
|
22560
22573
|
var require_cookie_signature = __commonJS({
|
|
22561
22574
|
"node_modules/cookie-signature/index.js"(exports2) {
|
|
22562
22575
|
"use strict";
|
|
22563
|
-
var
|
|
22576
|
+
var crypto = require("crypto");
|
|
22564
22577
|
exports2.sign = function(val, secret) {
|
|
22565
22578
|
if ("string" != typeof val) throw new TypeError("Cookie value must be provided as a string.");
|
|
22566
22579
|
if ("string" != typeof secret) throw new TypeError("Secret string must be provided.");
|
|
22567
|
-
return val + "." +
|
|
22580
|
+
return val + "." + crypto.createHmac("sha256", secret).update(val).digest("base64").replace(/\=+$/, "");
|
|
22568
22581
|
};
|
|
22569
22582
|
exports2.unsign = function(val, secret) {
|
|
22570
22583
|
if ("string" != typeof val) throw new TypeError("Signed cookie string must be provided.");
|
|
@@ -22573,7 +22586,7 @@ var require_cookie_signature = __commonJS({
|
|
|
22573
22586
|
return sha1(mac) == sha1(val) ? str : false;
|
|
22574
22587
|
};
|
|
22575
22588
|
function sha1(str) {
|
|
22576
|
-
return
|
|
22589
|
+
return crypto.createHash("sha1").update(str).digest("hex");
|
|
22577
22590
|
}
|
|
22578
22591
|
}
|
|
22579
22592
|
});
|
|
@@ -23549,22 +23562,33 @@ module.exports = __toCommonJS(express_exports);
|
|
|
23549
23562
|
|
|
23550
23563
|
// src/error.ts
|
|
23551
23564
|
var import_qstash = require("@upstash/qstash");
|
|
23552
|
-
var
|
|
23565
|
+
var WorkflowError = class extends import_qstash.QstashError {
|
|
23553
23566
|
constructor(message) {
|
|
23554
23567
|
super(message);
|
|
23555
|
-
this.name = "
|
|
23568
|
+
this.name = "WorkflowError";
|
|
23556
23569
|
}
|
|
23557
23570
|
};
|
|
23558
|
-
var
|
|
23571
|
+
var WorkflowAbort = class extends Error {
|
|
23559
23572
|
stepInfo;
|
|
23560
23573
|
stepName;
|
|
23561
|
-
|
|
23574
|
+
/**
|
|
23575
|
+
* whether workflow is to be canceled on abort
|
|
23576
|
+
*/
|
|
23577
|
+
cancelWorkflow;
|
|
23578
|
+
/**
|
|
23579
|
+
*
|
|
23580
|
+
* @param stepName name of the aborting step
|
|
23581
|
+
* @param stepInfo step information
|
|
23582
|
+
* @param cancelWorkflow
|
|
23583
|
+
*/
|
|
23584
|
+
constructor(stepName, stepInfo, cancelWorkflow = false) {
|
|
23562
23585
|
super(
|
|
23563
23586
|
`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}'.`
|
|
23564
23587
|
);
|
|
23565
|
-
this.name = "
|
|
23588
|
+
this.name = "WorkflowAbort";
|
|
23566
23589
|
this.stepName = stepName;
|
|
23567
23590
|
this.stepInfo = stepInfo;
|
|
23591
|
+
this.cancelWorkflow = cancelWorkflow;
|
|
23568
23592
|
}
|
|
23569
23593
|
};
|
|
23570
23594
|
var formatWorkflowError = (error) => {
|
|
@@ -23586,6 +23610,44 @@ var makeNotifyRequest = async (requester, eventId, eventData) => {
|
|
|
23586
23610
|
});
|
|
23587
23611
|
return result;
|
|
23588
23612
|
};
|
|
23613
|
+
var makeCancelRequest = async (requester, workflowRunId) => {
|
|
23614
|
+
await requester.request({
|
|
23615
|
+
path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
|
|
23616
|
+
method: "DELETE",
|
|
23617
|
+
parseResponseAsJson: false
|
|
23618
|
+
});
|
|
23619
|
+
return true;
|
|
23620
|
+
};
|
|
23621
|
+
var getSteps = async (requester, workflowRunId, messageId, debug) => {
|
|
23622
|
+
try {
|
|
23623
|
+
const steps = await requester.request({
|
|
23624
|
+
path: ["v2", "workflows", "runs", workflowRunId],
|
|
23625
|
+
parseResponseAsJson: true
|
|
23626
|
+
});
|
|
23627
|
+
if (!messageId) {
|
|
23628
|
+
await debug?.log("INFO", "ENDPOINT_START", {
|
|
23629
|
+
message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
|
|
23630
|
+
});
|
|
23631
|
+
return steps;
|
|
23632
|
+
} else {
|
|
23633
|
+
const index = steps.findIndex((item) => item.messageId === messageId);
|
|
23634
|
+
if (index === -1) {
|
|
23635
|
+
return [];
|
|
23636
|
+
}
|
|
23637
|
+
const filteredSteps = steps.slice(0, index + 1);
|
|
23638
|
+
await debug?.log("INFO", "ENDPOINT_START", {
|
|
23639
|
+
message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
|
|
23640
|
+
});
|
|
23641
|
+
return filteredSteps;
|
|
23642
|
+
}
|
|
23643
|
+
} catch (error) {
|
|
23644
|
+
await debug?.log("ERROR", "ERROR", {
|
|
23645
|
+
message: "failed while fetching steps.",
|
|
23646
|
+
error
|
|
23647
|
+
});
|
|
23648
|
+
throw new WorkflowError(`Failed while pulling steps. ${error}`);
|
|
23649
|
+
}
|
|
23650
|
+
};
|
|
23589
23651
|
|
|
23590
23652
|
// src/context/steps.ts
|
|
23591
23653
|
var BaseLazyStep = class {
|
|
@@ -24200,6 +24262,7 @@ var StepTypes = [
|
|
|
24200
24262
|
];
|
|
24201
24263
|
|
|
24202
24264
|
// src/workflow-requests.ts
|
|
24265
|
+
var import_qstash2 = require("@upstash/qstash");
|
|
24203
24266
|
var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
24204
24267
|
const { headers } = getHeaders(
|
|
24205
24268
|
"true",
|
|
@@ -24210,20 +24273,32 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
24210
24273
|
workflowContext.failureUrl,
|
|
24211
24274
|
retries
|
|
24212
24275
|
);
|
|
24213
|
-
await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
|
|
24214
|
-
headers,
|
|
24215
|
-
requestPayload: workflowContext.requestPayload,
|
|
24216
|
-
url: workflowContext.url
|
|
24217
|
-
});
|
|
24218
24276
|
try {
|
|
24219
24277
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
24220
|
-
await workflowContext.qstashClient.publish({
|
|
24278
|
+
const result = await workflowContext.qstashClient.publish({
|
|
24221
24279
|
headers,
|
|
24222
24280
|
method: "POST",
|
|
24223
24281
|
body,
|
|
24224
24282
|
url: workflowContext.url
|
|
24225
24283
|
});
|
|
24226
|
-
|
|
24284
|
+
if (result.deduplicated) {
|
|
24285
|
+
await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
|
|
24286
|
+
message: `Workflow run ${workflowContext.workflowRunId} already exists. A new one isn't created.`,
|
|
24287
|
+
headers,
|
|
24288
|
+
requestPayload: workflowContext.requestPayload,
|
|
24289
|
+
url: workflowContext.url,
|
|
24290
|
+
messageId: result.messageId
|
|
24291
|
+
});
|
|
24292
|
+
return ok("workflow-run-already-exists");
|
|
24293
|
+
} else {
|
|
24294
|
+
await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
|
|
24295
|
+
headers,
|
|
24296
|
+
requestPayload: workflowContext.requestPayload,
|
|
24297
|
+
url: workflowContext.url,
|
|
24298
|
+
messageId: result.messageId
|
|
24299
|
+
});
|
|
24300
|
+
return ok("success");
|
|
24301
|
+
}
|
|
24227
24302
|
} catch (error) {
|
|
24228
24303
|
const error_ = error;
|
|
24229
24304
|
return err(error_);
|
|
@@ -24231,7 +24306,9 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
|
|
|
24231
24306
|
};
|
|
24232
24307
|
var triggerRouteFunction = async ({
|
|
24233
24308
|
onCleanup,
|
|
24234
|
-
onStep
|
|
24309
|
+
onStep,
|
|
24310
|
+
onCancel,
|
|
24311
|
+
debug
|
|
24235
24312
|
}) => {
|
|
24236
24313
|
try {
|
|
24237
24314
|
await onStep();
|
|
@@ -24239,19 +24316,50 @@ var triggerRouteFunction = async ({
|
|
|
24239
24316
|
return ok("workflow-finished");
|
|
24240
24317
|
} catch (error) {
|
|
24241
24318
|
const error_ = error;
|
|
24242
|
-
|
|
24319
|
+
if (error instanceof import_qstash2.QstashError && error.status === 400) {
|
|
24320
|
+
await debug?.log("WARN", "RESPONSE_WORKFLOW", {
|
|
24321
|
+
message: `tried to append to a cancelled workflow. exiting without publishing.`,
|
|
24322
|
+
name: error.name,
|
|
24323
|
+
errorMessage: error.message
|
|
24324
|
+
});
|
|
24325
|
+
return ok("workflow-was-finished");
|
|
24326
|
+
} else if (!(error_ instanceof WorkflowAbort)) {
|
|
24327
|
+
return err(error_);
|
|
24328
|
+
} else if (error_.cancelWorkflow) {
|
|
24329
|
+
await onCancel();
|
|
24330
|
+
return ok("workflow-finished");
|
|
24331
|
+
} else {
|
|
24332
|
+
return ok("step-finished");
|
|
24333
|
+
}
|
|
24243
24334
|
}
|
|
24244
24335
|
};
|
|
24245
24336
|
var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
|
|
24246
24337
|
await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
|
|
24247
24338
|
deletedWorkflowRunId: workflowContext.workflowRunId
|
|
24248
24339
|
});
|
|
24249
|
-
|
|
24250
|
-
|
|
24251
|
-
|
|
24252
|
-
|
|
24253
|
-
|
|
24254
|
-
|
|
24340
|
+
try {
|
|
24341
|
+
await workflowContext.qstashClient.http.request({
|
|
24342
|
+
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
24343
|
+
method: "DELETE",
|
|
24344
|
+
parseResponseAsJson: false
|
|
24345
|
+
});
|
|
24346
|
+
await debug?.log(
|
|
24347
|
+
"SUBMIT",
|
|
24348
|
+
"SUBMIT_CLEANUP",
|
|
24349
|
+
`workflow run ${workflowContext.workflowRunId} deleted.`
|
|
24350
|
+
);
|
|
24351
|
+
return { deleted: true };
|
|
24352
|
+
} catch (error) {
|
|
24353
|
+
if (error instanceof import_qstash2.QstashError && error.status === 404) {
|
|
24354
|
+
await debug?.log("WARN", "SUBMIT_CLEANUP", {
|
|
24355
|
+
message: `Failed to remove workflow run ${workflowContext.workflowRunId} as it doesn't exist.`,
|
|
24356
|
+
name: error.name,
|
|
24357
|
+
errorMessage: error.message
|
|
24358
|
+
});
|
|
24359
|
+
return { deleted: false };
|
|
24360
|
+
}
|
|
24361
|
+
throw error;
|
|
24362
|
+
}
|
|
24255
24363
|
};
|
|
24256
24364
|
var recreateUserHeaders = (headers) => {
|
|
24257
24365
|
const filteredHeaders = new Headers();
|
|
@@ -24267,15 +24375,32 @@ var recreateUserHeaders = (headers) => {
|
|
|
24267
24375
|
var handleThirdPartyCallResult = async (request, requestPayload, client, workflowUrl, failureUrl, retries, debug) => {
|
|
24268
24376
|
try {
|
|
24269
24377
|
if (request.headers.get("Upstash-Workflow-Callback")) {
|
|
24270
|
-
|
|
24378
|
+
let callbackPayload;
|
|
24379
|
+
if (requestPayload) {
|
|
24380
|
+
callbackPayload = requestPayload;
|
|
24381
|
+
} else {
|
|
24382
|
+
const workflowRunId2 = request.headers.get("upstash-workflow-runid");
|
|
24383
|
+
const messageId = request.headers.get("upstash-message-id");
|
|
24384
|
+
if (!workflowRunId2)
|
|
24385
|
+
throw new WorkflowError("workflow run id missing in context.call lazy fetch.");
|
|
24386
|
+
if (!messageId) throw new WorkflowError("message id missing in context.call lazy fetch.");
|
|
24387
|
+
const steps = await getSteps(client.http, workflowRunId2, messageId, debug);
|
|
24388
|
+
const failingStep = steps.find((step) => step.messageId === messageId);
|
|
24389
|
+
if (!failingStep)
|
|
24390
|
+
throw new WorkflowError(
|
|
24391
|
+
"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.`)
|
|
24392
|
+
);
|
|
24393
|
+
callbackPayload = atob(failingStep.body);
|
|
24394
|
+
}
|
|
24395
|
+
const callbackMessage = JSON.parse(callbackPayload);
|
|
24271
24396
|
if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
|
|
24272
24397
|
await debug?.log("WARN", "SUBMIT_THIRD_PARTY_RESULT", {
|
|
24273
24398
|
status: callbackMessage.status,
|
|
24274
|
-
body: atob(callbackMessage.body)
|
|
24399
|
+
body: atob(callbackMessage.body ?? "")
|
|
24275
24400
|
});
|
|
24276
24401
|
console.warn(
|
|
24277
24402
|
`Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
|
|
24278
|
-
${atob(callbackMessage.body)}`
|
|
24403
|
+
${atob(callbackMessage.body ?? "")}`
|
|
24279
24404
|
);
|
|
24280
24405
|
return ok("call-will-retry");
|
|
24281
24406
|
}
|
|
@@ -24309,7 +24434,7 @@ ${atob(callbackMessage.body)}`
|
|
|
24309
24434
|
);
|
|
24310
24435
|
const callResponse = {
|
|
24311
24436
|
status: callbackMessage.status,
|
|
24312
|
-
body: atob(callbackMessage.body),
|
|
24437
|
+
body: atob(callbackMessage.body ?? ""),
|
|
24313
24438
|
header: callbackMessage.header
|
|
24314
24439
|
};
|
|
24315
24440
|
const callResultStep = {
|
|
@@ -24340,9 +24465,7 @@ ${atob(callbackMessage.body)}`
|
|
|
24340
24465
|
} catch (error) {
|
|
24341
24466
|
const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
|
|
24342
24467
|
return err(
|
|
24343
|
-
new
|
|
24344
|
-
`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`
|
|
24345
|
-
)
|
|
24468
|
+
new WorkflowError(`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`)
|
|
24346
24469
|
);
|
|
24347
24470
|
}
|
|
24348
24471
|
};
|
|
@@ -24350,7 +24473,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
24350
24473
|
const baseHeaders = {
|
|
24351
24474
|
[WORKFLOW_INIT_HEADER]: initHeaderValue,
|
|
24352
24475
|
[WORKFLOW_ID_HEADER]: workflowRunId,
|
|
24353
|
-
[WORKFLOW_URL_HEADER]: workflowUrl
|
|
24476
|
+
[WORKFLOW_URL_HEADER]: workflowUrl,
|
|
24477
|
+
[WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody"
|
|
24354
24478
|
};
|
|
24355
24479
|
if (!step?.callUrl) {
|
|
24356
24480
|
baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
@@ -24363,8 +24487,8 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
24363
24487
|
}
|
|
24364
24488
|
if (step?.callUrl) {
|
|
24365
24489
|
baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
|
|
24366
|
-
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete";
|
|
24367
|
-
if (retries) {
|
|
24490
|
+
baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
|
|
24491
|
+
if (retries !== void 0) {
|
|
24368
24492
|
baseHeaders["Upstash-Callback-Retries"] = retries.toString();
|
|
24369
24493
|
baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
|
|
24370
24494
|
}
|
|
@@ -24399,6 +24523,7 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
|
|
|
24399
24523
|
"Upstash-Callback-Workflow-CallType": "fromCallback",
|
|
24400
24524
|
"Upstash-Callback-Workflow-Init": "false",
|
|
24401
24525
|
"Upstash-Callback-Workflow-Url": workflowUrl,
|
|
24526
|
+
"Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
|
|
24402
24527
|
"Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
|
|
24403
24528
|
"Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
|
|
24404
24529
|
"Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
|
|
@@ -24447,7 +24572,7 @@ var verifyRequest = async (body, signature, verifier) => {
|
|
|
24447
24572
|
throw new Error("Signature in `Upstash-Signature` header is not valid");
|
|
24448
24573
|
}
|
|
24449
24574
|
} catch (error) {
|
|
24450
|
-
throw new
|
|
24575
|
+
throw new WorkflowError(
|
|
24451
24576
|
`Failed to verify that the Workflow request comes from QStash: ${error}
|
|
24452
24577
|
|
|
24453
24578
|
If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
|
|
@@ -24487,14 +24612,14 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24487
24612
|
*
|
|
24488
24613
|
* If a function is already executing (this.executingStep), this
|
|
24489
24614
|
* means that there is a nested step which is not allowed. In this
|
|
24490
|
-
* case, addStep throws
|
|
24615
|
+
* case, addStep throws WorkflowError.
|
|
24491
24616
|
*
|
|
24492
24617
|
* @param stepInfo step plan to add
|
|
24493
24618
|
* @returns result of the step function
|
|
24494
24619
|
*/
|
|
24495
24620
|
async addStep(stepInfo) {
|
|
24496
24621
|
if (this.executingStep) {
|
|
24497
|
-
throw new
|
|
24622
|
+
throw new WorkflowError(
|
|
24498
24623
|
`A step can not be run inside another step. Tried to run '${stepInfo.stepName}' inside '${this.executingStep}'`
|
|
24499
24624
|
);
|
|
24500
24625
|
}
|
|
@@ -24579,7 +24704,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24579
24704
|
const sortedSteps = sortSteps(this.steps);
|
|
24580
24705
|
const plannedParallelStepCount = sortedSteps[initialStepCount + this.planStepCount]?.concurrent;
|
|
24581
24706
|
if (parallelCallState !== "first" && plannedParallelStepCount !== parallelSteps.length) {
|
|
24582
|
-
throw new
|
|
24707
|
+
throw new WorkflowError(
|
|
24583
24708
|
`Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
|
|
24584
24709
|
);
|
|
24585
24710
|
}
|
|
@@ -24601,7 +24726,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24601
24726
|
case "partial": {
|
|
24602
24727
|
const planStep = this.steps.at(-1);
|
|
24603
24728
|
if (!planStep || planStep.targetStep === void 0) {
|
|
24604
|
-
throw new
|
|
24729
|
+
throw new WorkflowError(
|
|
24605
24730
|
`There must be a last step and it should have targetStep larger than 0.Received: ${JSON.stringify(planStep)}`
|
|
24606
24731
|
);
|
|
24607
24732
|
}
|
|
@@ -24615,17 +24740,17 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24615
24740
|
);
|
|
24616
24741
|
await this.submitStepsToQStash([resultStep], [parallelStep]);
|
|
24617
24742
|
} catch (error) {
|
|
24618
|
-
if (error instanceof
|
|
24743
|
+
if (error instanceof WorkflowAbort) {
|
|
24619
24744
|
throw error;
|
|
24620
24745
|
}
|
|
24621
|
-
throw new
|
|
24746
|
+
throw new WorkflowError(
|
|
24622
24747
|
`Error submitting steps to QStash in partial parallel step execution: ${error}`
|
|
24623
24748
|
);
|
|
24624
24749
|
}
|
|
24625
24750
|
break;
|
|
24626
24751
|
}
|
|
24627
24752
|
case "discard": {
|
|
24628
|
-
throw new
|
|
24753
|
+
throw new WorkflowAbort("discarded parallel");
|
|
24629
24754
|
}
|
|
24630
24755
|
case "last": {
|
|
24631
24756
|
const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
|
|
@@ -24676,7 +24801,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24676
24801
|
*/
|
|
24677
24802
|
async submitStepsToQStash(steps, lazySteps) {
|
|
24678
24803
|
if (steps.length === 0) {
|
|
24679
|
-
throw new
|
|
24804
|
+
throw new WorkflowError(
|
|
24680
24805
|
`Unable to submit steps to QStash. Provided list is empty. Current step: ${this.stepCount}`
|
|
24681
24806
|
);
|
|
24682
24807
|
}
|
|
@@ -24716,7 +24841,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24716
24841
|
method: "POST",
|
|
24717
24842
|
parseResponseAsJson: false
|
|
24718
24843
|
});
|
|
24719
|
-
throw new
|
|
24844
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
24720
24845
|
}
|
|
24721
24846
|
const result = await this.context.qstashClient.batchJSON(
|
|
24722
24847
|
steps.map((singleStep, index) => {
|
|
@@ -24768,7 +24893,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24768
24893
|
};
|
|
24769
24894
|
})
|
|
24770
24895
|
});
|
|
24771
|
-
throw new
|
|
24896
|
+
throw new WorkflowAbort(steps[0].stepName, steps[0]);
|
|
24772
24897
|
}
|
|
24773
24898
|
/**
|
|
24774
24899
|
* Get the promise by executing the lazt steps list. If there is a single
|
|
@@ -24793,7 +24918,7 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24793
24918
|
} else if (Array.isArray(result) && lazyStepList.length === result.length && index < lazyStepList.length) {
|
|
24794
24919
|
return result[index];
|
|
24795
24920
|
} else {
|
|
24796
|
-
throw new
|
|
24921
|
+
throw new WorkflowError(
|
|
24797
24922
|
`Unexpected parallel call result while executing step ${index}: '${result}'. Expected ${lazyStepList.length} many items`
|
|
24798
24923
|
);
|
|
24799
24924
|
}
|
|
@@ -24805,12 +24930,12 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
24805
24930
|
};
|
|
24806
24931
|
var validateStep = (lazyStep, stepFromRequest) => {
|
|
24807
24932
|
if (lazyStep.stepName !== stepFromRequest.stepName) {
|
|
24808
|
-
throw new
|
|
24933
|
+
throw new WorkflowError(
|
|
24809
24934
|
`Incompatible step name. Expected '${lazyStep.stepName}', got '${stepFromRequest.stepName}' from the request`
|
|
24810
24935
|
);
|
|
24811
24936
|
}
|
|
24812
24937
|
if (lazyStep.stepType !== stepFromRequest.stepType) {
|
|
24813
|
-
throw new
|
|
24938
|
+
throw new WorkflowError(
|
|
24814
24939
|
`Incompatible step type. Expected '${lazyStep.stepType}', got '${stepFromRequest.stepType}' from the request`
|
|
24815
24940
|
);
|
|
24816
24941
|
}
|
|
@@ -24821,12 +24946,12 @@ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
|
|
|
24821
24946
|
validateStep(lazySteps[index], stepFromRequest);
|
|
24822
24947
|
}
|
|
24823
24948
|
} catch (error) {
|
|
24824
|
-
if (error instanceof
|
|
24949
|
+
if (error instanceof WorkflowError) {
|
|
24825
24950
|
const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
|
|
24826
24951
|
const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
|
|
24827
24952
|
const requestStepNames = stepsFromRequest.map((step) => step.stepName);
|
|
24828
24953
|
const requestStepTypes = stepsFromRequest.map((step) => step.stepType);
|
|
24829
|
-
throw new
|
|
24954
|
+
throw new WorkflowError(
|
|
24830
24955
|
`Incompatible steps detected in parallel execution: ${error.message}
|
|
24831
24956
|
> Step Names from the request: ${JSON.stringify(requestStepNames)}
|
|
24832
24957
|
Step Types from the request: ${JSON.stringify(requestStepTypes)}
|
|
@@ -24939,10 +25064,6 @@ var WorkflowContext = class {
|
|
|
24939
25064
|
* headers of the initial request
|
|
24940
25065
|
*/
|
|
24941
25066
|
headers;
|
|
24942
|
-
/**
|
|
24943
|
-
* initial payload as a raw string
|
|
24944
|
-
*/
|
|
24945
|
-
rawInitialPayload;
|
|
24946
25067
|
/**
|
|
24947
25068
|
* Map of environment variables and their values.
|
|
24948
25069
|
*
|
|
@@ -24977,7 +25098,6 @@ var WorkflowContext = class {
|
|
|
24977
25098
|
failureUrl,
|
|
24978
25099
|
debug,
|
|
24979
25100
|
initialPayload,
|
|
24980
|
-
rawInitialPayload,
|
|
24981
25101
|
env,
|
|
24982
25102
|
retries
|
|
24983
25103
|
}) {
|
|
@@ -24988,7 +25108,6 @@ var WorkflowContext = class {
|
|
|
24988
25108
|
this.failureUrl = failureUrl;
|
|
24989
25109
|
this.headers = headers;
|
|
24990
25110
|
this.requestPayload = initialPayload;
|
|
24991
|
-
this.rawInitialPayload = rawInitialPayload ?? JSON.stringify(this.requestPayload);
|
|
24992
25111
|
this.env = env ?? {};
|
|
24993
25112
|
this.retries = retries ?? DEFAULT_RETRIES;
|
|
24994
25113
|
this.executor = new AutoExecutor(this, this.steps, debug);
|
|
@@ -25009,7 +25128,7 @@ var WorkflowContext = class {
|
|
|
25009
25128
|
* const [result1, result2] = await Promise.all([
|
|
25010
25129
|
* context.run("step 1", () => {
|
|
25011
25130
|
* return "result1"
|
|
25012
|
-
* })
|
|
25131
|
+
* }),
|
|
25013
25132
|
* context.run("step 2", async () => {
|
|
25014
25133
|
* return await fetchResults()
|
|
25015
25134
|
* })
|
|
@@ -25027,6 +25146,10 @@ var WorkflowContext = class {
|
|
|
25027
25146
|
/**
|
|
25028
25147
|
* Stops the execution for the duration provided.
|
|
25029
25148
|
*
|
|
25149
|
+
* ```typescript
|
|
25150
|
+
* await context.sleep('sleep1', 3) // wait for three seconds
|
|
25151
|
+
* ```
|
|
25152
|
+
*
|
|
25030
25153
|
* @param stepName
|
|
25031
25154
|
* @param duration sleep duration in seconds
|
|
25032
25155
|
* @returns undefined
|
|
@@ -25037,6 +25160,10 @@ var WorkflowContext = class {
|
|
|
25037
25160
|
/**
|
|
25038
25161
|
* Stops the execution until the date time provided.
|
|
25039
25162
|
*
|
|
25163
|
+
* ```typescript
|
|
25164
|
+
* await context.sleepUntil('sleep1', Date.now() / 1000 + 3) // wait for three seconds
|
|
25165
|
+
* ```
|
|
25166
|
+
*
|
|
25040
25167
|
* @param stepName
|
|
25041
25168
|
* @param datetime time to sleep until. Can be provided as a number (in unix seconds),
|
|
25042
25169
|
* as a Date object or a string (passed to `new Date(datetimeString)`)
|
|
@@ -25060,7 +25187,7 @@ var WorkflowContext = class {
|
|
|
25060
25187
|
* const { status, body } = await context.call<string>(
|
|
25061
25188
|
* "post call step",
|
|
25062
25189
|
* {
|
|
25063
|
-
* url:
|
|
25190
|
+
* url: "https://www.some-endpoint.com/api",
|
|
25064
25191
|
* method: "POST",
|
|
25065
25192
|
* body: "my-payload"
|
|
25066
25193
|
* }
|
|
@@ -25114,45 +25241,43 @@ var WorkflowContext = class {
|
|
|
25114
25241
|
}
|
|
25115
25242
|
}
|
|
25116
25243
|
/**
|
|
25117
|
-
*
|
|
25118
|
-
* timeout ends
|
|
25244
|
+
* Pauses workflow execution until a specific event occurs or a timeout is reached.
|
|
25119
25245
|
*
|
|
25120
|
-
|
|
25121
|
-
* const
|
|
25122
|
-
*
|
|
25123
|
-
*
|
|
25124
|
-
|
|
25125
|
-
* );
|
|
25126
|
-
* ```
|
|
25246
|
+
*```ts
|
|
25247
|
+
* const result = await workflow.waitForEvent("payment-confirmed", {
|
|
25248
|
+
* timeout: "5m"
|
|
25249
|
+
* });
|
|
25250
|
+
*```
|
|
25127
25251
|
*
|
|
25128
|
-
* To notify a waiting workflow
|
|
25252
|
+
* To notify a waiting workflow:
|
|
25129
25253
|
*
|
|
25130
25254
|
* ```ts
|
|
25131
25255
|
* import { Client } from "@upstash/workflow";
|
|
25132
25256
|
*
|
|
25133
|
-
* const client = new Client({ token: });
|
|
25257
|
+
* const client = new Client({ token: "<QSTASH_TOKEN>" });
|
|
25134
25258
|
*
|
|
25135
25259
|
* await client.notify({
|
|
25136
|
-
* eventId: "
|
|
25137
|
-
*
|
|
25260
|
+
* eventId: "payment.confirmed",
|
|
25261
|
+
* data: {
|
|
25262
|
+
* amount: 99.99,
|
|
25263
|
+
* currency: "USD"
|
|
25264
|
+
* }
|
|
25138
25265
|
* })
|
|
25139
25266
|
* ```
|
|
25140
25267
|
*
|
|
25268
|
+
* Alternatively, you can use the `context.notify` method.
|
|
25269
|
+
*
|
|
25141
25270
|
* @param stepName
|
|
25142
|
-
* @param eventId
|
|
25143
|
-
* @param
|
|
25144
|
-
* @returns
|
|
25145
|
-
* timeout
|
|
25146
|
-
* is the
|
|
25271
|
+
* @param eventId - Unique identifier for the event to wait for
|
|
25272
|
+
* @param options - Configuration options.
|
|
25273
|
+
* @returns `{ timeout: boolean, eventData: unknown }`.
|
|
25274
|
+
* The `timeout` property specifies if the workflow has timed out. The `eventData`
|
|
25275
|
+
* is the data passed when notifying this workflow of an event.
|
|
25147
25276
|
*/
|
|
25148
|
-
async waitForEvent(stepName, eventId,
|
|
25149
|
-
const
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
eventId,
|
|
25153
|
-
typeof timeout === "string" ? timeout : `${timeout}s`
|
|
25154
|
-
)
|
|
25155
|
-
);
|
|
25277
|
+
async waitForEvent(stepName, eventId, options = {}) {
|
|
25278
|
+
const { timeout = "7d" } = options;
|
|
25279
|
+
const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
|
|
25280
|
+
const result = await this.addStep(new LazyWaitForEventStep(stepName, eventId, timeoutStr));
|
|
25156
25281
|
try {
|
|
25157
25282
|
return {
|
|
25158
25283
|
...result,
|
|
@@ -25162,6 +25287,27 @@ var WorkflowContext = class {
|
|
|
25162
25287
|
return result;
|
|
25163
25288
|
}
|
|
25164
25289
|
}
|
|
25290
|
+
/**
|
|
25291
|
+
* Notify workflow runs waiting for an event
|
|
25292
|
+
*
|
|
25293
|
+
* ```ts
|
|
25294
|
+
* const { eventId, eventData, notifyResponse } = await context.notify(
|
|
25295
|
+
* "notify step", "event-id", "event-data"
|
|
25296
|
+
* );
|
|
25297
|
+
* ```
|
|
25298
|
+
*
|
|
25299
|
+
* Upon `context.notify`, the workflow runs waiting for the given eventId (context.waitForEvent)
|
|
25300
|
+
* will receive the given event data and resume execution.
|
|
25301
|
+
*
|
|
25302
|
+
* The response includes the same eventId and eventData. Additionally, there is
|
|
25303
|
+
* a notifyResponse field which contains a list of `Waiter` objects, each corresponding
|
|
25304
|
+
* to a notified workflow run.
|
|
25305
|
+
*
|
|
25306
|
+
* @param stepName
|
|
25307
|
+
* @param eventId event id to notify
|
|
25308
|
+
* @param eventData event data to notify with
|
|
25309
|
+
* @returns notify response which has event id, event data and list of waiters which were notified
|
|
25310
|
+
*/
|
|
25165
25311
|
async notify(stepName, eventId, eventData) {
|
|
25166
25312
|
const result = await this.addStep(
|
|
25167
25313
|
new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
|
|
@@ -25175,6 +25321,15 @@ var WorkflowContext = class {
|
|
|
25175
25321
|
return result;
|
|
25176
25322
|
}
|
|
25177
25323
|
}
|
|
25324
|
+
/**
|
|
25325
|
+
* Cancel the current workflow run
|
|
25326
|
+
*
|
|
25327
|
+
* Will throw WorkflowAbort to stop workflow execution.
|
|
25328
|
+
* Shouldn't be inside try/catch.
|
|
25329
|
+
*/
|
|
25330
|
+
async cancel() {
|
|
25331
|
+
throw new WorkflowAbort("cancel", void 0, true);
|
|
25332
|
+
}
|
|
25178
25333
|
/**
|
|
25179
25334
|
* Adds steps to the executor. Needed so that it can be overwritten in
|
|
25180
25335
|
* DisabledWorkflowContext.
|
|
@@ -25215,7 +25370,8 @@ var WorkflowLogger = class _WorkflowLogger {
|
|
|
25215
25370
|
}
|
|
25216
25371
|
writeToConsole(logEntry) {
|
|
25217
25372
|
const JSON_SPACING = 2;
|
|
25218
|
-
console.
|
|
25373
|
+
const logMethod = logEntry.logLevel === "ERROR" ? console.error : logEntry.logLevel === "WARN" ? console.warn : console.log;
|
|
25374
|
+
logMethod(JSON.stringify(logEntry, void 0, JSON_SPACING));
|
|
25219
25375
|
}
|
|
25220
25376
|
shouldLog(level) {
|
|
25221
25377
|
return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
|
|
@@ -25233,11 +25389,13 @@ var WorkflowLogger = class _WorkflowLogger {
|
|
|
25233
25389
|
};
|
|
25234
25390
|
|
|
25235
25391
|
// src/utils.ts
|
|
25236
|
-
var import_node_crypto = __toESM(require("crypto"));
|
|
25237
25392
|
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
25238
25393
|
var NANOID_LENGTH = 21;
|
|
25394
|
+
function getRandomInt() {
|
|
25395
|
+
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
25396
|
+
}
|
|
25239
25397
|
function nanoid() {
|
|
25240
|
-
return
|
|
25398
|
+
return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
25241
25399
|
}
|
|
25242
25400
|
function getWorkflowRunId(id) {
|
|
25243
25401
|
return `wfr_${id ?? nanoid()}`;
|
|
@@ -25255,6 +25413,63 @@ function decodeBase64(base64) {
|
|
|
25255
25413
|
}
|
|
25256
25414
|
}
|
|
25257
25415
|
|
|
25416
|
+
// src/serve/authorization.ts
|
|
25417
|
+
var import_qstash3 = require("@upstash/qstash");
|
|
25418
|
+
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
25419
|
+
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
25420
|
+
/**
|
|
25421
|
+
* overwrite the WorkflowContext.addStep method to always raise WorkflowAbort
|
|
25422
|
+
* error in order to stop the execution whenever we encounter a step.
|
|
25423
|
+
*
|
|
25424
|
+
* @param _step
|
|
25425
|
+
*/
|
|
25426
|
+
async addStep(_step) {
|
|
25427
|
+
throw new WorkflowAbort(_DisabledWorkflowContext.disabledMessage);
|
|
25428
|
+
}
|
|
25429
|
+
/**
|
|
25430
|
+
* overwrite cancel method to do nothing
|
|
25431
|
+
*/
|
|
25432
|
+
async cancel() {
|
|
25433
|
+
return;
|
|
25434
|
+
}
|
|
25435
|
+
/**
|
|
25436
|
+
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
25437
|
+
* route function with the new context.
|
|
25438
|
+
*
|
|
25439
|
+
* - returns "run-ended" if there are no steps found or
|
|
25440
|
+
* if the auth failed and user called `return`
|
|
25441
|
+
* - returns "step-found" if DisabledWorkflowContext.addStep is called.
|
|
25442
|
+
* - if there is another error, returns the error.
|
|
25443
|
+
*
|
|
25444
|
+
* @param routeFunction
|
|
25445
|
+
*/
|
|
25446
|
+
static async tryAuthentication(routeFunction, context) {
|
|
25447
|
+
const disabledContext = new _DisabledWorkflowContext({
|
|
25448
|
+
qstashClient: new import_qstash3.Client({
|
|
25449
|
+
baseUrl: "disabled-client",
|
|
25450
|
+
token: "disabled-client"
|
|
25451
|
+
}),
|
|
25452
|
+
workflowRunId: context.workflowRunId,
|
|
25453
|
+
headers: context.headers,
|
|
25454
|
+
steps: [],
|
|
25455
|
+
url: context.url,
|
|
25456
|
+
failureUrl: context.failureUrl,
|
|
25457
|
+
initialPayload: context.requestPayload,
|
|
25458
|
+
env: context.env,
|
|
25459
|
+
retries: context.retries
|
|
25460
|
+
});
|
|
25461
|
+
try {
|
|
25462
|
+
await routeFunction(disabledContext);
|
|
25463
|
+
} catch (error) {
|
|
25464
|
+
if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage) {
|
|
25465
|
+
return ok("step-found");
|
|
25466
|
+
}
|
|
25467
|
+
return err(error);
|
|
25468
|
+
}
|
|
25469
|
+
return ok("run-ended");
|
|
25470
|
+
}
|
|
25471
|
+
};
|
|
25472
|
+
|
|
25258
25473
|
// src/workflow-parser.ts
|
|
25259
25474
|
var getPayload = async (request) => {
|
|
25260
25475
|
try {
|
|
@@ -25263,8 +25478,8 @@ var getPayload = async (request) => {
|
|
|
25263
25478
|
return;
|
|
25264
25479
|
}
|
|
25265
25480
|
};
|
|
25266
|
-
var
|
|
25267
|
-
const [encodedInitialPayload, ...encodedSteps] =
|
|
25481
|
+
var processRawSteps = (rawSteps) => {
|
|
25482
|
+
const [encodedInitialPayload, ...encodedSteps] = rawSteps;
|
|
25268
25483
|
const rawInitialPayload = decodeBase64(encodedInitialPayload.body);
|
|
25269
25484
|
const initialStep = {
|
|
25270
25485
|
stepId: 0,
|
|
@@ -25274,27 +25489,21 @@ var parsePayload = async (rawPayload, debug) => {
|
|
|
25274
25489
|
concurrent: NO_CONCURRENCY
|
|
25275
25490
|
};
|
|
25276
25491
|
const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
|
|
25277
|
-
const otherSteps =
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25285
|
-
|
|
25286
|
-
|
|
25287
|
-
}
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
};
|
|
25293
|
-
step.out = newOut;
|
|
25294
|
-
}
|
|
25295
|
-
return step;
|
|
25296
|
-
})
|
|
25297
|
-
);
|
|
25492
|
+
const otherSteps = stepsToDecode.map((rawStep) => {
|
|
25493
|
+
const step = JSON.parse(decodeBase64(rawStep.body));
|
|
25494
|
+
try {
|
|
25495
|
+
step.out = JSON.parse(step.out);
|
|
25496
|
+
} catch {
|
|
25497
|
+
}
|
|
25498
|
+
if (step.waitEventId) {
|
|
25499
|
+
const newOut = {
|
|
25500
|
+
eventData: step.out ? decodeBase64(step.out) : void 0,
|
|
25501
|
+
timeout: step.waitTimeout ?? false
|
|
25502
|
+
};
|
|
25503
|
+
step.out = newOut;
|
|
25504
|
+
}
|
|
25505
|
+
return step;
|
|
25506
|
+
});
|
|
25298
25507
|
const steps = [initialStep, ...otherSteps];
|
|
25299
25508
|
return {
|
|
25300
25509
|
rawInitialPayload,
|
|
@@ -25342,20 +25551,20 @@ var validateRequest = (request) => {
|
|
|
25342
25551
|
const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
|
|
25343
25552
|
const isFirstInvocation = !versionHeader;
|
|
25344
25553
|
if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
|
|
25345
|
-
throw new
|
|
25554
|
+
throw new WorkflowError(
|
|
25346
25555
|
`Incompatible workflow sdk protocol version. Expected ${WORKFLOW_PROTOCOL_VERSION}, got ${versionHeader} from the request.`
|
|
25347
25556
|
);
|
|
25348
25557
|
}
|
|
25349
25558
|
const workflowRunId = isFirstInvocation ? getWorkflowRunId() : request.headers.get(WORKFLOW_ID_HEADER) ?? "";
|
|
25350
25559
|
if (workflowRunId.length === 0) {
|
|
25351
|
-
throw new
|
|
25560
|
+
throw new WorkflowError("Couldn't get workflow id from header");
|
|
25352
25561
|
}
|
|
25353
25562
|
return {
|
|
25354
25563
|
isFirstInvocation,
|
|
25355
25564
|
workflowRunId
|
|
25356
25565
|
};
|
|
25357
25566
|
};
|
|
25358
|
-
var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
25567
|
+
var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requester, messageId, debug) => {
|
|
25359
25568
|
if (isFirstInvocation) {
|
|
25360
25569
|
return {
|
|
25361
25570
|
rawInitialPayload: requestPayload ?? "",
|
|
@@ -25363,10 +25572,18 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
25363
25572
|
isLastDuplicate: false
|
|
25364
25573
|
};
|
|
25365
25574
|
} else {
|
|
25575
|
+
let rawSteps;
|
|
25366
25576
|
if (!requestPayload) {
|
|
25367
|
-
|
|
25577
|
+
await debug?.log(
|
|
25578
|
+
"INFO",
|
|
25579
|
+
"ENDPOINT_START",
|
|
25580
|
+
"request payload is empty, steps will be fetched from QStash."
|
|
25581
|
+
);
|
|
25582
|
+
rawSteps = await getSteps(requester, workflowRunId, messageId, debug);
|
|
25583
|
+
} else {
|
|
25584
|
+
rawSteps = JSON.parse(requestPayload);
|
|
25368
25585
|
}
|
|
25369
|
-
const { rawInitialPayload, steps } =
|
|
25586
|
+
const { rawInitialPayload, steps } = processRawSteps(rawSteps);
|
|
25370
25587
|
const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, debug);
|
|
25371
25588
|
const deduplicatedSteps = deduplicateSteps(steps);
|
|
25372
25589
|
return {
|
|
@@ -25376,13 +25593,13 @@ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
|
|
|
25376
25593
|
};
|
|
25377
25594
|
}
|
|
25378
25595
|
};
|
|
25379
|
-
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, failureFunction, debug) => {
|
|
25596
|
+
var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, debug) => {
|
|
25380
25597
|
if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
|
|
25381
25598
|
return ok("not-failure-callback");
|
|
25382
25599
|
}
|
|
25383
25600
|
if (!failureFunction) {
|
|
25384
25601
|
return err(
|
|
25385
|
-
new
|
|
25602
|
+
new WorkflowError(
|
|
25386
25603
|
"Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
|
|
25387
25604
|
)
|
|
25388
25605
|
);
|
|
@@ -25393,92 +25610,48 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
|
|
|
25393
25610
|
);
|
|
25394
25611
|
const decodedBody = body ? decodeBase64(body) : "{}";
|
|
25395
25612
|
const errorPayload = JSON.parse(decodedBody);
|
|
25396
|
-
const {
|
|
25397
|
-
rawInitialPayload,
|
|
25398
|
-
steps,
|
|
25399
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25400
|
-
isLastDuplicate: _isLastDuplicate
|
|
25401
|
-
} = await parseRequest(decodeBase64(sourceBody), false, debug);
|
|
25402
25613
|
const workflowContext = new WorkflowContext({
|
|
25403
25614
|
qstashClient,
|
|
25404
25615
|
workflowRunId,
|
|
25405
|
-
initialPayload: initialPayloadParser(
|
|
25406
|
-
rawInitialPayload,
|
|
25616
|
+
initialPayload: initialPayloadParser(decodeBase64(sourceBody)),
|
|
25407
25617
|
headers: recreateUserHeaders(new Headers(sourceHeader)),
|
|
25408
|
-
steps,
|
|
25618
|
+
steps: [],
|
|
25409
25619
|
url,
|
|
25410
25620
|
failureUrl: url,
|
|
25411
25621
|
debug
|
|
25412
25622
|
});
|
|
25413
|
-
|
|
25623
|
+
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
25624
|
+
routeFunction,
|
|
25625
|
+
workflowContext
|
|
25626
|
+
);
|
|
25627
|
+
if (authCheck.isErr()) {
|
|
25628
|
+
await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
|
|
25629
|
+
throw authCheck.error;
|
|
25630
|
+
} else if (authCheck.value === "run-ended") {
|
|
25631
|
+
return err(new WorkflowError("Not authorized to run the failure function."));
|
|
25632
|
+
}
|
|
25633
|
+
await failureFunction({
|
|
25634
|
+
context: workflowContext,
|
|
25635
|
+
failStatus: status,
|
|
25636
|
+
failResponse: errorPayload.message,
|
|
25637
|
+
failHeaders: header
|
|
25638
|
+
});
|
|
25414
25639
|
} catch (error) {
|
|
25415
25640
|
return err(error);
|
|
25416
25641
|
}
|
|
25417
25642
|
return ok("is-failure-callback");
|
|
25418
25643
|
};
|
|
25419
25644
|
|
|
25420
|
-
// src/serve/authorization.ts
|
|
25421
|
-
var import_qstash2 = require("@upstash/qstash");
|
|
25422
|
-
var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
|
|
25423
|
-
static disabledMessage = "disabled-qstash-worklfow-run";
|
|
25424
|
-
/**
|
|
25425
|
-
* overwrite the WorkflowContext.addStep method to always raise QStashWorkflowAbort
|
|
25426
|
-
* error in order to stop the execution whenever we encounter a step.
|
|
25427
|
-
*
|
|
25428
|
-
* @param _step
|
|
25429
|
-
*/
|
|
25430
|
-
async addStep(_step) {
|
|
25431
|
-
throw new QStashWorkflowAbort(_DisabledWorkflowContext.disabledMessage);
|
|
25432
|
-
}
|
|
25433
|
-
/**
|
|
25434
|
-
* copies the passed context to create a DisabledWorkflowContext. Then, runs the
|
|
25435
|
-
* route function with the new context.
|
|
25436
|
-
*
|
|
25437
|
-
* - returns "run-ended" if there are no steps found or
|
|
25438
|
-
* if the auth failed and user called `return`
|
|
25439
|
-
* - returns "step-found" if DisabledWorkflowContext.addStep is called.
|
|
25440
|
-
* - if there is another error, returns the error.
|
|
25441
|
-
*
|
|
25442
|
-
* @param routeFunction
|
|
25443
|
-
*/
|
|
25444
|
-
static async tryAuthentication(routeFunction, context) {
|
|
25445
|
-
const disabledContext = new _DisabledWorkflowContext({
|
|
25446
|
-
qstashClient: new import_qstash2.Client({
|
|
25447
|
-
baseUrl: "disabled-client",
|
|
25448
|
-
token: "disabled-client"
|
|
25449
|
-
}),
|
|
25450
|
-
workflowRunId: context.workflowRunId,
|
|
25451
|
-
headers: context.headers,
|
|
25452
|
-
steps: [],
|
|
25453
|
-
url: context.url,
|
|
25454
|
-
failureUrl: context.failureUrl,
|
|
25455
|
-
initialPayload: context.requestPayload,
|
|
25456
|
-
rawInitialPayload: context.rawInitialPayload,
|
|
25457
|
-
env: context.env,
|
|
25458
|
-
retries: context.retries
|
|
25459
|
-
});
|
|
25460
|
-
try {
|
|
25461
|
-
await routeFunction(disabledContext);
|
|
25462
|
-
} catch (error) {
|
|
25463
|
-
if (error instanceof QStashWorkflowAbort && error.stepName === this.disabledMessage) {
|
|
25464
|
-
return ok("step-found");
|
|
25465
|
-
}
|
|
25466
|
-
return err(error);
|
|
25467
|
-
}
|
|
25468
|
-
return ok("run-ended");
|
|
25469
|
-
}
|
|
25470
|
-
};
|
|
25471
|
-
|
|
25472
25645
|
// src/serve/options.ts
|
|
25473
|
-
var import_qstash3 = require("@upstash/qstash");
|
|
25474
25646
|
var import_qstash4 = require("@upstash/qstash");
|
|
25647
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
25475
25648
|
var processOptions = (options) => {
|
|
25476
25649
|
const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
|
|
25477
25650
|
const receiverEnvironmentVariablesSet = Boolean(
|
|
25478
25651
|
environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
|
|
25479
25652
|
);
|
|
25480
25653
|
return {
|
|
25481
|
-
qstashClient: new
|
|
25654
|
+
qstashClient: new import_qstash5.Client({
|
|
25482
25655
|
baseUrl: environment.QSTASH_URL,
|
|
25483
25656
|
token: environment.QSTASH_TOKEN
|
|
25484
25657
|
}),
|
|
@@ -25499,7 +25672,7 @@ var processOptions = (options) => {
|
|
|
25499
25672
|
throw error;
|
|
25500
25673
|
}
|
|
25501
25674
|
},
|
|
25502
|
-
receiver: receiverEnvironmentVariablesSet ? new
|
|
25675
|
+
receiver: receiverEnvironmentVariablesSet ? new import_qstash4.Receiver({
|
|
25503
25676
|
currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
|
|
25504
25677
|
nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
|
|
25505
25678
|
}) : void 0,
|
|
@@ -25561,6 +25734,9 @@ var serve = (routeFunction, options) => {
|
|
|
25561
25734
|
const { rawInitialPayload, steps, isLastDuplicate } = await parseRequest(
|
|
25562
25735
|
requestPayload,
|
|
25563
25736
|
isFirstInvocation,
|
|
25737
|
+
workflowRunId,
|
|
25738
|
+
qstashClient.http,
|
|
25739
|
+
request.headers.get("upstash-message-id"),
|
|
25564
25740
|
debug
|
|
25565
25741
|
);
|
|
25566
25742
|
if (isLastDuplicate) {
|
|
@@ -25571,6 +25747,7 @@ var serve = (routeFunction, options) => {
|
|
|
25571
25747
|
requestPayload,
|
|
25572
25748
|
qstashClient,
|
|
25573
25749
|
initialPayloadParser,
|
|
25750
|
+
routeFunction,
|
|
25574
25751
|
failureFunction
|
|
25575
25752
|
);
|
|
25576
25753
|
if (failureCheck.isErr()) {
|
|
@@ -25583,7 +25760,6 @@ var serve = (routeFunction, options) => {
|
|
|
25583
25760
|
qstashClient,
|
|
25584
25761
|
workflowRunId,
|
|
25585
25762
|
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
25586
|
-
rawInitialPayload,
|
|
25587
25763
|
headers: recreateUserHeaders(request.headers),
|
|
25588
25764
|
steps,
|
|
25589
25765
|
url: workflowUrl,
|
|
@@ -25621,7 +25797,11 @@ var serve = (routeFunction, options) => {
|
|
|
25621
25797
|
onStep: async () => routeFunction(workflowContext),
|
|
25622
25798
|
onCleanup: async () => {
|
|
25623
25799
|
await triggerWorkflowDelete(workflowContext, debug);
|
|
25624
|
-
}
|
|
25800
|
+
},
|
|
25801
|
+
onCancel: async () => {
|
|
25802
|
+
await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
|
|
25803
|
+
},
|
|
25804
|
+
debug
|
|
25625
25805
|
});
|
|
25626
25806
|
if (result.isErr()) {
|
|
25627
25807
|
await debug?.log("ERROR", "ERROR", { error: result.error.message });
|
|
@@ -25647,7 +25827,7 @@ var serve = (routeFunction, options) => {
|
|
|
25647
25827
|
};
|
|
25648
25828
|
|
|
25649
25829
|
// src/client/index.ts
|
|
25650
|
-
var
|
|
25830
|
+
var import_qstash6 = require("@upstash/qstash");
|
|
25651
25831
|
|
|
25652
25832
|
// platforms/express.ts
|
|
25653
25833
|
var import_express = __toESM(require_express2());
|