@upstash/workflow 0.2.22-rc → 0.2.23-rc

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/express.js CHANGED
@@ -39,7 +39,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
39
39
  var DEFAULT_CONTENT_TYPE = "application/json";
40
40
  var NO_CONCURRENCY = 1;
41
41
  var DEFAULT_RETRIES = 3;
42
- var VERSION = "v0.2.21";
42
+ var VERSION = "v0.2.22";
43
43
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
44
44
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
45
45
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -47,61 +47,11 @@ var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
47
47
  var TELEMETRY_HEADER_AGENT = "Upstash-Telemetry-Agent";
48
48
 
49
49
  // src/client/utils.ts
50
- var import_qstash = require("@upstash/qstash");
51
- var makeNotifyRequest = async (requester, eventId, eventData) => {
52
- const result = await requester.request({
53
- path: ["v2", "notify", eventId],
54
- method: "POST",
55
- body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
56
- });
57
- return result;
58
- };
59
- var makeCancelRequest = async (requester, workflowRunId) => {
60
- await requester.request({
61
- path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
62
- method: "DELETE",
63
- parseResponseAsJson: false
64
- });
65
- return true;
66
- };
67
- var getSteps = async (requester, workflowRunId, messageId, debug) => {
68
- try {
69
- const steps = await requester.request({
70
- path: ["v2", "workflows", "runs", workflowRunId],
71
- parseResponseAsJson: true
72
- });
73
- if (!messageId) {
74
- await debug?.log("INFO", "ENDPOINT_START", {
75
- message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
76
- });
77
- return { steps, workflowRunEnded: false };
78
- } else {
79
- const index = steps.findIndex((item) => item.messageId === messageId);
80
- if (index === -1) {
81
- return { steps: [], workflowRunEnded: false };
82
- }
83
- const filteredSteps = steps.slice(0, index + 1);
84
- await debug?.log("INFO", "ENDPOINT_START", {
85
- message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
86
- });
87
- return { steps: filteredSteps, workflowRunEnded: false };
88
- }
89
- } catch (error) {
90
- if (error instanceof import_qstash.QstashError && error.status === 404) {
91
- await debug?.log("WARN", "ENDPOINT_START", {
92
- message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
93
- error
94
- });
95
- return { steps: void 0, workflowRunEnded: true };
96
- } else {
97
- throw error;
98
- }
99
- }
100
- };
50
+ var import_qstash2 = require("@upstash/qstash");
101
51
 
102
52
  // src/error.ts
103
- var import_qstash2 = require("@upstash/qstash");
104
- var WorkflowError = class extends import_qstash2.QstashError {
53
+ var import_qstash = require("@upstash/qstash");
54
+ var WorkflowError = class extends import_qstash.QstashError {
105
55
  constructor(message) {
106
56
  super(message);
107
57
  this.name = "WorkflowError";
@@ -140,6 +90,19 @@ var WorkflowNonRetryableError = class extends WorkflowAbort {
140
90
  if (message) this.message = message;
141
91
  }
142
92
  };
93
+ var WorkflowRetryAfterError = class extends WorkflowAbort {
94
+ retryAfter;
95
+ /**
96
+ * @param retryAfter time in seconds after which the workflow should be retried
97
+ * @param message error message to be displayed
98
+ */
99
+ constructor(message, retryAfter) {
100
+ super("retry", void 0, false);
101
+ this.name = "WorkflowRetryAfterError";
102
+ this.retryAfter = retryAfter;
103
+ if (message) this.message = message;
104
+ }
105
+ };
143
106
  var formatWorkflowError = (error) => {
144
107
  return error instanceof Error ? {
145
108
  error: error.name,
@@ -150,6 +113,79 @@ var formatWorkflowError = (error) => {
150
113
  message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
151
114
  };
152
115
  };
116
+ function getConstructorName(obj) {
117
+ if (obj === null || obj === void 0) {
118
+ return null;
119
+ }
120
+ const ctor = obj.constructor;
121
+ if (!ctor || ctor.name === "Object") {
122
+ return null;
123
+ }
124
+ return ctor.name;
125
+ }
126
+ function getConstructorNames(obj) {
127
+ const proto = Object.getPrototypeOf(obj);
128
+ const name = getConstructorName(proto);
129
+ if (name === null) {
130
+ return [];
131
+ }
132
+ return [name, ...getConstructorNames(proto)];
133
+ }
134
+ function isInstanceOf(v, ctor) {
135
+ return getConstructorNames(v).includes(ctor.name);
136
+ }
137
+
138
+ // src/client/utils.ts
139
+ var makeNotifyRequest = async (requester, eventId, eventData) => {
140
+ const result = await requester.request({
141
+ path: ["v2", "notify", eventId],
142
+ method: "POST",
143
+ body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
144
+ });
145
+ return result;
146
+ };
147
+ var makeCancelRequest = async (requester, workflowRunId) => {
148
+ await requester.request({
149
+ path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
150
+ method: "DELETE",
151
+ parseResponseAsJson: false
152
+ });
153
+ return true;
154
+ };
155
+ var getSteps = async (requester, workflowRunId, messageId, debug) => {
156
+ try {
157
+ const steps = await requester.request({
158
+ path: ["v2", "workflows", "runs", workflowRunId],
159
+ parseResponseAsJson: true
160
+ });
161
+ if (!messageId) {
162
+ await debug?.log("INFO", "ENDPOINT_START", {
163
+ message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
164
+ });
165
+ return { steps, workflowRunEnded: false };
166
+ } else {
167
+ const index = steps.findIndex((item) => item.messageId === messageId);
168
+ if (index === -1) {
169
+ return { steps: [], workflowRunEnded: false };
170
+ }
171
+ const filteredSteps = steps.slice(0, index + 1);
172
+ await debug?.log("INFO", "ENDPOINT_START", {
173
+ message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
174
+ });
175
+ return { steps: filteredSteps, workflowRunEnded: false };
176
+ }
177
+ } catch (error) {
178
+ if (isInstanceOf(error, import_qstash2.QstashError) && error.status === 404) {
179
+ await debug?.log("WARN", "ENDPOINT_START", {
180
+ message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
181
+ error
182
+ });
183
+ return { steps: void 0, workflowRunEnded: true };
184
+ } else {
185
+ throw error;
186
+ }
187
+ }
188
+ };
153
189
 
154
190
  // src/context/auto-executor.ts
155
191
  var import_qstash5 = require("@upstash/qstash");
@@ -163,8 +199,8 @@ var NANOID_LENGTH = 21;
163
199
  function getRandomInt() {
164
200
  return Math.floor(Math.random() * NANOID_CHARS.length);
165
201
  }
166
- function nanoid() {
167
- return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
202
+ function nanoid(length = NANOID_LENGTH) {
203
+ return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
168
204
  }
169
205
  function getWorkflowRunId(id) {
170
206
  return `wfr_${id ?? nanoid()}`;
@@ -181,6 +217,37 @@ function decodeBase64(base64) {
181
217
  return binString;
182
218
  }
183
219
  }
220
+ function getUserIdFromToken(qstashClient) {
221
+ try {
222
+ const token = qstashClient.token;
223
+ const decodedToken = decodeBase64(token);
224
+ const tokenPayload = JSON.parse(decodedToken);
225
+ const userId = tokenPayload.UserID;
226
+ if (!userId) {
227
+ throw new WorkflowError("QStash token payload does not contain userId");
228
+ }
229
+ return userId;
230
+ } catch (error) {
231
+ throw new WorkflowError(
232
+ `Failed to decode QStash token while running create webhook step: ${error.message}`
233
+ );
234
+ }
235
+ }
236
+ function getQStashUrl(qstashClient) {
237
+ try {
238
+ const requester = qstashClient.http;
239
+ const baseUrl = requester.baseUrl;
240
+ if (!baseUrl) {
241
+ throw new WorkflowError("QStash client does not have a baseUrl");
242
+ }
243
+ return baseUrl;
244
+ } catch (error) {
245
+ throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
246
+ }
247
+ }
248
+ function getEventId() {
249
+ return `evt_${nanoid(15)}`;
250
+ }
184
251
 
185
252
  // node_modules/neverthrow/dist/index.es.js
186
253
  var defaultErrorConfig = {
@@ -606,7 +673,9 @@ var StepTypes = [
606
673
  "Call",
607
674
  "Wait",
608
675
  "Notify",
609
- "Invoke"
676
+ "Invoke",
677
+ "CreateWebhook",
678
+ "WaitForWebhook"
610
679
  ];
611
680
 
612
681
  // src/workflow-requests.ts
@@ -710,17 +779,17 @@ var triggerRouteFunction = async ({
710
779
  return ok("workflow-finished");
711
780
  } catch (error) {
712
781
  const error_ = error;
713
- if (error instanceof import_qstash3.QstashError && error.status === 400) {
782
+ if (isInstanceOf(error, import_qstash3.QstashError) && error.status === 400) {
714
783
  await debug?.log("WARN", "RESPONSE_WORKFLOW", {
715
784
  message: `tried to append to a cancelled workflow. exiting without publishing.`,
716
785
  name: error.name,
717
786
  errorMessage: error.message
718
787
  });
719
788
  return ok("workflow-was-finished");
720
- } else if (!(error_ instanceof WorkflowAbort)) {
721
- return err(error_);
722
- } else if (error_ instanceof WorkflowNonRetryableError) {
789
+ } else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
723
790
  return ok(error_);
791
+ } else if (!isInstanceOf(error_, WorkflowAbort)) {
792
+ return err(error_);
724
793
  } else if (error_.cancelWorkflow) {
725
794
  await onCancel();
726
795
  return ok("workflow-finished");
@@ -918,7 +987,9 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
918
987
  // src/context/steps.ts
919
988
  var BaseLazyStep = class _BaseLazyStep {
920
989
  stepName;
921
- constructor(stepName) {
990
+ context;
991
+ constructor(context, stepName) {
992
+ this.context = context;
922
993
  if (!stepName) {
923
994
  throw new WorkflowError(
924
995
  "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
@@ -936,13 +1007,14 @@ var BaseLazyStep = class _BaseLazyStep {
936
1007
  *
937
1008
  * will be called when returning the steps to the context from auto executor
938
1009
  *
939
- * @param out field of the step
1010
+ * @param step step
940
1011
  * @returns parsed out field
941
1012
  */
942
- parseOut(out) {
1013
+ parseOut(step) {
1014
+ const out = step.out;
943
1015
  if (out === void 0) {
944
1016
  if (this.allowUndefinedOut) {
945
- return void 0;
1017
+ return this.handleUndefinedOut(step);
946
1018
  } else {
947
1019
  throw new WorkflowError(
948
1020
  `Error while parsing output of ${this.stepType} step. Expected a string, but got: undefined`
@@ -950,27 +1022,26 @@ var BaseLazyStep = class _BaseLazyStep {
950
1022
  }
951
1023
  }
952
1024
  if (typeof out === "object") {
953
- if (this.stepType !== "Wait") {
954
- console.warn(
955
- `Error while parsing ${this.stepType} step output. Expected a string, but got object. Please reach out to Upstash Support.`
956
- );
957
- return out;
958
- }
959
- return {
960
- ...out,
961
- eventData: _BaseLazyStep.tryParsing(out.eventData)
962
- };
1025
+ console.warn(
1026
+ `Error while parsing ${this.stepType} step output. Expected a string, but got object. Please reach out to Upstash Support.`
1027
+ );
1028
+ return out;
963
1029
  }
964
1030
  if (typeof out !== "string") {
965
1031
  throw new WorkflowError(
966
1032
  `Error while parsing output of ${this.stepType} step. Expected a string or undefined, but got: ${typeof out}`
967
1033
  );
968
1034
  }
969
- return this.safeParseOut(out);
1035
+ return this.safeParseOut(out, step);
970
1036
  }
971
- safeParseOut(out) {
1037
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1038
+ safeParseOut(out, step) {
972
1039
  return _BaseLazyStep.tryParsing(out);
973
1040
  }
1041
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1042
+ handleUndefinedOut(step) {
1043
+ return void 0;
1044
+ }
974
1045
  static tryParsing(stepOut) {
975
1046
  try {
976
1047
  return JSON.parse(stepOut);
@@ -1021,8 +1092,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
1021
1092
  stepFunction;
1022
1093
  stepType = "Run";
1023
1094
  allowUndefinedOut = true;
1024
- constructor(stepName, stepFunction) {
1025
- super(stepName);
1095
+ constructor(context, stepName, stepFunction) {
1096
+ super(context, stepName);
1026
1097
  this.stepFunction = stepFunction;
1027
1098
  }
1028
1099
  getPlanStep(concurrent, targetStep) {
@@ -1052,8 +1123,8 @@ var LazySleepStep = class extends BaseLazyStep {
1052
1123
  sleep;
1053
1124
  stepType = "SleepFor";
1054
1125
  allowUndefinedOut = true;
1055
- constructor(stepName, sleep) {
1056
- super(stepName);
1126
+ constructor(context, stepName, sleep) {
1127
+ super(context, stepName);
1057
1128
  this.sleep = sleep;
1058
1129
  }
1059
1130
  getPlanStep(concurrent, targetStep) {
@@ -1094,8 +1165,8 @@ var LazySleepUntilStep = class extends BaseLazyStep {
1094
1165
  sleepUntil;
1095
1166
  stepType = "SleepUntil";
1096
1167
  allowUndefinedOut = true;
1097
- constructor(stepName, sleepUntil) {
1098
- super(stepName);
1168
+ constructor(context, stepName, sleepUntil) {
1169
+ super(context, stepName);
1099
1170
  this.sleepUntil = sleepUntil;
1100
1171
  }
1101
1172
  getPlanStep(concurrent, targetStep) {
@@ -1147,8 +1218,8 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1147
1218
  stringifyBody;
1148
1219
  stepType = "Call";
1149
1220
  allowUndefinedOut = false;
1150
- constructor(stepName, url, method, body, headers, retries, retryDelay, timeout, flowControl, stringifyBody) {
1151
- super(stepName);
1221
+ constructor(context, stepName, url, method, body, headers, retries, retryDelay, timeout, flowControl, stringifyBody) {
1222
+ super(context, stepName);
1152
1223
  this.url = url;
1153
1224
  this.method = method;
1154
1225
  this.body = body;
@@ -1292,13 +1363,12 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1292
1363
  ]);
1293
1364
  }
1294
1365
  };
1295
- var LazyWaitForEventStep = class extends BaseLazyStep {
1366
+ var LazyWaitEventStep = class extends BaseLazyStep {
1296
1367
  eventId;
1297
1368
  timeout;
1298
- stepType = "Wait";
1299
1369
  allowUndefinedOut = false;
1300
- constructor(stepName, eventId, timeout) {
1301
- super(stepName);
1370
+ constructor(context, stepName, eventId, timeout) {
1371
+ super(context, stepName);
1302
1372
  this.eventId = eventId;
1303
1373
  this.timeout = timeout;
1304
1374
  }
@@ -1323,13 +1393,6 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
1323
1393
  concurrent
1324
1394
  });
1325
1395
  }
1326
- safeParseOut(out) {
1327
- const result = JSON.parse(out);
1328
- return {
1329
- ...result,
1330
- eventData: BaseLazyStep.tryParsing(result.eventData)
1331
- };
1332
- }
1333
1396
  getHeaders({ context, telemetry: telemetry2, invokeCount, step }) {
1334
1397
  const headers = super.getHeaders({ context, telemetry: telemetry2, invokeCount, step });
1335
1398
  headers.headers["Upstash-Workflow-CallType"] = "step";
@@ -1363,7 +1426,7 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
1363
1426
  timeoutHeaders,
1364
1427
  step: {
1365
1428
  stepId: step.stepId,
1366
- stepType: "Wait",
1429
+ stepType: this.stepType,
1367
1430
  stepName: step.stepName,
1368
1431
  concurrent: step.concurrent,
1369
1432
  targetStep: step.targetStep
@@ -1384,8 +1447,8 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
1384
1447
  };
1385
1448
  var LazyNotifyStep = class extends LazyFunctionStep {
1386
1449
  stepType = "Notify";
1387
- constructor(stepName, eventId, eventData, requester) {
1388
- super(stepName, async () => {
1450
+ constructor(context, stepName, eventId, eventData, requester) {
1451
+ super(context, stepName, async () => {
1389
1452
  const notifyResponse = await makeNotifyRequest(requester, eventId, eventData);
1390
1453
  return {
1391
1454
  eventId,
@@ -1410,7 +1473,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1410
1473
  * workflow id of the invoked workflow
1411
1474
  */
1412
1475
  workflowId;
1413
- constructor(stepName, {
1476
+ constructor(context, stepName, {
1414
1477
  workflow,
1415
1478
  body,
1416
1479
  headers = {},
@@ -1420,7 +1483,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1420
1483
  flowControl,
1421
1484
  stringifyBody = true
1422
1485
  }) {
1423
- super(stepName);
1486
+ super(context, stepName);
1424
1487
  this.params = {
1425
1488
  workflow,
1426
1489
  body,
@@ -1481,6 +1544,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1481
1544
  userHeaders: context.headers,
1482
1545
  invokeCount
1483
1546
  });
1547
+ context.qstashClient.http.headers?.forEach((value, key) => {
1548
+ invokerHeaders[key] = value;
1549
+ });
1484
1550
  invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1485
1551
  let invokeBody;
1486
1552
  if (this.params.stringifyBody) {
@@ -1552,6 +1618,91 @@ var LazyInvokeStep = class extends BaseLazyStep {
1552
1618
  return [result];
1553
1619
  }
1554
1620
  };
1621
+ var LazyCreateWebhookStep = class extends BaseLazyStep {
1622
+ stepType = "CreateWebhook";
1623
+ allowUndefinedOut = false;
1624
+ getPlanStep(concurrent, targetStep) {
1625
+ return {
1626
+ stepId: 0,
1627
+ stepName: this.stepName,
1628
+ stepType: this.stepType,
1629
+ concurrent,
1630
+ targetStep
1631
+ };
1632
+ }
1633
+ async getResultStep(concurrent, stepId) {
1634
+ return {
1635
+ stepId,
1636
+ stepName: this.stepName,
1637
+ stepType: this.stepType,
1638
+ out: void 0,
1639
+ concurrent
1640
+ };
1641
+ }
1642
+ getBody({ step, context }) {
1643
+ const userId = getUserIdFromToken(context.qstashClient);
1644
+ const out = [userId, context.workflowRunId, getEventId()].join("/");
1645
+ return JSON.stringify({
1646
+ ...step,
1647
+ out
1648
+ });
1649
+ }
1650
+ safeParseOut(out) {
1651
+ const [userId, workflowRunId, eventId] = out.split("/");
1652
+ const qstashUrl = getQStashUrl(this.context.qstashClient);
1653
+ return {
1654
+ webhookUrl: `${qstashUrl}/v2/workflows/hooks/${userId}/${workflowRunId}/${eventId}`,
1655
+ eventId
1656
+ };
1657
+ }
1658
+ };
1659
+ var LazyWaitForWebhookStep = class extends LazyWaitEventStep {
1660
+ stepType = "WaitForWebhook";
1661
+ allowUndefinedOut = true;
1662
+ constructor(context, stepName, webhook, timeout) {
1663
+ super(context, stepName, webhook.eventId, timeout);
1664
+ }
1665
+ safeParseOut(out) {
1666
+ const eventData = decodeBase64(out);
1667
+ const parsedEventData = BaseLazyStep.tryParsing(eventData);
1668
+ const body = parsedEventData.body;
1669
+ const parsedBody = typeof body === "string" ? decodeBase64(body) : void 0;
1670
+ const request = new Request(
1671
+ `${parsedEventData.proto}://${parsedEventData.host}${parsedEventData.url}`,
1672
+ {
1673
+ method: parsedEventData.method,
1674
+ headers: parsedEventData.header,
1675
+ body: parsedBody
1676
+ }
1677
+ );
1678
+ return {
1679
+ request,
1680
+ timeout: false
1681
+ };
1682
+ }
1683
+ handleUndefinedOut() {
1684
+ return {
1685
+ timeout: true,
1686
+ request: void 0
1687
+ };
1688
+ }
1689
+ };
1690
+ var LazyWaitForEventStep = class extends LazyWaitEventStep {
1691
+ stepType = "Wait";
1692
+ allowUndefinedOut = true;
1693
+ parseWaitForEventOut(out, waitTimeout) {
1694
+ return {
1695
+ eventData: out ? BaseLazyStep.tryParsing(decodeBase64(out)) : void 0,
1696
+ timeout: waitTimeout ?? false
1697
+ };
1698
+ }
1699
+ safeParseOut(out, step) {
1700
+ return this.parseWaitForEventOut(out, step.waitTimeout);
1701
+ }
1702
+ handleUndefinedOut(step) {
1703
+ return this.parseWaitForEventOut(void 0, step.waitTimeout);
1704
+ }
1705
+ };
1555
1706
 
1556
1707
  // src/agents/constants.ts
1557
1708
  var AGENT_NAME_HEADER = "upstash-agent-name";
@@ -1956,7 +2107,7 @@ var AutoExecutor = class _AutoExecutor {
1956
2107
  step,
1957
2108
  stepCount: this.stepCount
1958
2109
  });
1959
- return lazyStep.parseOut(step.out);
2110
+ return lazyStep.parseOut(step);
1960
2111
  }
1961
2112
  const resultStep = await submitSingleStep({
1962
2113
  context: this.context,
@@ -2027,7 +2178,7 @@ var AutoExecutor = class _AutoExecutor {
2027
2178
  });
2028
2179
  throw new WorkflowAbort(parallelStep.stepName, resultStep);
2029
2180
  } catch (error) {
2030
- if (error instanceof WorkflowAbort || error instanceof import_qstash5.QstashError && error.status === 400) {
2181
+ if (isInstanceOf(error, WorkflowAbort) || isInstanceOf(error, import_qstash5.QstashError) && error.status === 400) {
2031
2182
  throw error;
2032
2183
  }
2033
2184
  throw new WorkflowError(
@@ -2043,7 +2194,7 @@ var AutoExecutor = class _AutoExecutor {
2043
2194
  const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
2044
2195
  validateParallelSteps(parallelSteps, parallelResultSteps);
2045
2196
  return parallelResultSteps.map(
2046
- (step, index) => parallelSteps[index].parseOut(step.out)
2197
+ (step, index) => parallelSteps[index].parseOut(step)
2047
2198
  );
2048
2199
  }
2049
2200
  }
@@ -2099,7 +2250,6 @@ var AutoExecutor = class _AutoExecutor {
2099
2250
  * @param index index of the current step
2100
2251
  * @returns result[index] if lazyStepList > 1, otherwise result
2101
2252
  */
2102
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
2103
2253
  static getResult(lazyStepList, result, index) {
2104
2254
  if (lazyStepList.length === 1) {
2105
2255
  return result;
@@ -2134,7 +2284,7 @@ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
2134
2284
  validateStep(lazySteps[index], stepFromRequest);
2135
2285
  }
2136
2286
  } catch (error) {
2137
- if (error instanceof WorkflowError) {
2287
+ if (isInstanceOf(error, WorkflowError)) {
2138
2288
  const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
2139
2289
  const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
2140
2290
  const requestStepNames = stepsFromRequest.map((step) => step.stepName);
@@ -2315,7 +2465,7 @@ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
2315
2465
  headers: responseHeaders
2316
2466
  });
2317
2467
  } catch (error) {
2318
- if (error instanceof Error && error.name === "WorkflowAbort") {
2468
+ if (error instanceof Error && isInstanceOf(error, WorkflowAbort)) {
2319
2469
  throw error;
2320
2470
  } else {
2321
2471
  console.error("Error in fetch implementation:", error);
@@ -2417,10 +2567,10 @@ var Agent = class {
2417
2567
  });
2418
2568
  return { text: result.text };
2419
2569
  } catch (error) {
2420
- if (error instanceof import_ai2.ToolExecutionError) {
2421
- if (error.cause instanceof Error && error.cause.name === "WorkflowAbort") {
2570
+ if (isInstanceOf(error, import_ai2.ToolExecutionError)) {
2571
+ if (error.cause instanceof Error && isInstanceOf(error.cause, WorkflowAbort)) {
2422
2572
  throw error.cause;
2423
- } else if (error.cause instanceof import_ai2.ToolExecutionError && error.cause.cause instanceof Error && error.cause.cause.name === "WorkflowAbort") {
2573
+ } else if (isInstanceOf(error.cause, import_ai2.ToolExecutionError) && isInstanceOf(error.cause.cause, WorkflowAbort)) {
2424
2574
  throw error.cause.cause;
2425
2575
  } else {
2426
2576
  throw error;
@@ -2891,7 +3041,7 @@ var WorkflowContext = class {
2891
3041
  */
2892
3042
  async run(stepName, stepFunction) {
2893
3043
  const wrappedStepFunction = () => this.executor.wrapStep(stepName, stepFunction);
2894
- return await this.addStep(new LazyFunctionStep(stepName, wrappedStepFunction));
3044
+ return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
2895
3045
  }
2896
3046
  /**
2897
3047
  * Stops the execution for the duration provided.
@@ -2905,7 +3055,7 @@ var WorkflowContext = class {
2905
3055
  * @returns undefined
2906
3056
  */
2907
3057
  async sleep(stepName, duration) {
2908
- await this.addStep(new LazySleepStep(stepName, duration));
3058
+ await this.addStep(new LazySleepStep(this, stepName, duration));
2909
3059
  }
2910
3060
  /**
2911
3061
  * Stops the execution until the date time provided.
@@ -2927,13 +3077,14 @@ var WorkflowContext = class {
2927
3077
  datetime = typeof datetime === "string" ? new Date(datetime) : datetime;
2928
3078
  time = Math.round(datetime.getTime() / 1e3);
2929
3079
  }
2930
- await this.addStep(new LazySleepUntilStep(stepName, time));
3080
+ await this.addStep(new LazySleepUntilStep(this, stepName, time));
2931
3081
  }
2932
3082
  async call(stepName, settings) {
2933
3083
  let callStep;
2934
3084
  if ("workflow" in settings) {
2935
3085
  const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
2936
3086
  callStep = new LazyCallStep(
3087
+ this,
2937
3088
  stepName,
2938
3089
  url,
2939
3090
  "POST",
@@ -2958,6 +3109,7 @@ var WorkflowContext = class {
2958
3109
  stringifyBody = true
2959
3110
  } = settings;
2960
3111
  callStep = new LazyCallStep(
3112
+ this,
2961
3113
  stepName,
2962
3114
  url,
2963
3115
  method,
@@ -3009,7 +3161,9 @@ var WorkflowContext = class {
3009
3161
  async waitForEvent(stepName, eventId, options = {}) {
3010
3162
  const { timeout = "7d" } = options;
3011
3163
  const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
3012
- return await this.addStep(new LazyWaitForEventStep(stepName, eventId, timeoutStr));
3164
+ return await this.addStep(
3165
+ new LazyWaitForEventStep(this, stepName, eventId, timeoutStr)
3166
+ );
3013
3167
  }
3014
3168
  /**
3015
3169
  * Notify workflow runs waiting for an event
@@ -3034,11 +3188,19 @@ var WorkflowContext = class {
3034
3188
  */
3035
3189
  async notify(stepName, eventId, eventData) {
3036
3190
  return await this.addStep(
3037
- new LazyNotifyStep(stepName, eventId, eventData, this.qstashClient.http)
3191
+ new LazyNotifyStep(this, stepName, eventId, eventData, this.qstashClient.http)
3038
3192
  );
3039
3193
  }
3040
3194
  async invoke(stepName, settings) {
3041
- return await this.addStep(new LazyInvokeStep(stepName, settings));
3195
+ return await this.addStep(
3196
+ new LazyInvokeStep(this, stepName, settings)
3197
+ );
3198
+ }
3199
+ async createWebhook(stepName) {
3200
+ return await this.addStep(new LazyCreateWebhookStep(this, stepName));
3201
+ }
3202
+ async waitForWebhook(stepName, webhook, timeout) {
3203
+ return await this.addStep(new LazyWaitForWebhookStep(this, stepName, webhook, timeout));
3042
3204
  }
3043
3205
  /**
3044
3206
  * Cancel the current workflow run
@@ -3169,7 +3331,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3169
3331
  try {
3170
3332
  await routeFunction(disabledContext);
3171
3333
  } catch (error) {
3172
- if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage || error instanceof WorkflowNonRetryableError) {
3334
+ if (isInstanceOf(error, WorkflowAbort) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
3173
3335
  return ok("step-found");
3174
3336
  }
3175
3337
  console.warn(
@@ -3202,13 +3364,6 @@ var processRawSteps = (rawSteps) => {
3202
3364
  const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
3203
3365
  const otherSteps = stepsToDecode.map((rawStep) => {
3204
3366
  const step = JSON.parse(decodeBase64(rawStep.body));
3205
- if (step.waitEventId) {
3206
- const newOut = {
3207
- eventData: step.out ? decodeBase64(step.out) : void 0,
3208
- timeout: step.waitTimeout ?? false
3209
- };
3210
- step.out = newOut;
3211
- }
3212
3367
  return step;
3213
3368
  });
3214
3369
  const steps = [initialStep, ...otherSteps];
@@ -3422,13 +3577,24 @@ var processOptions = (options) => {
3422
3577
  },
3423
3578
  status: 489
3424
3579
  });
3425
- } else if (detailedFinishCondition?.condition === "failure-callback") {
3426
- return new Response(detailedFinishCondition.result ?? void 0, {
3427
- status: 200,
3580
+ } else if (detailedFinishCondition?.condition === "retry-after-error") {
3581
+ return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
3428
3582
  headers: {
3583
+ "Retry-After": detailedFinishCondition.result.retryAfter.toString(),
3429
3584
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3430
- }
3585
+ },
3586
+ status: 429
3431
3587
  });
3588
+ } else if (detailedFinishCondition?.condition === "failure-callback") {
3589
+ return new Response(
3590
+ JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
3591
+ {
3592
+ status: 200,
3593
+ headers: {
3594
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3595
+ }
3596
+ }
3597
+ );
3432
3598
  }
3433
3599
  return new Response(JSON.stringify({ workflowRunId }), {
3434
3600
  status: 200,
@@ -3638,12 +3804,18 @@ var serveBase = (routeFunction, telemetry2, options) => {
3638
3804
  },
3639
3805
  debug
3640
3806
  });
3641
- if (result.isOk() && result.value instanceof WorkflowNonRetryableError) {
3807
+ if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
3642
3808
  return onStepFinish(workflowRunId, result.value, {
3643
3809
  condition: "non-retryable-error",
3644
3810
  result: result.value
3645
3811
  });
3646
3812
  }
3813
+ if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
3814
+ return onStepFinish(workflowRunId, result.value, {
3815
+ condition: "retry-after-error",
3816
+ result: result.value
3817
+ });
3818
+ }
3647
3819
  if (result.isErr()) {
3648
3820
  await debug?.log("ERROR", "ERROR", { error: result.error.message });
3649
3821
  throw result.error;
@@ -3674,7 +3846,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
3674
3846
  const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
3675
3847
  Original error: '${formattedError.message}'`;
3676
3848
  console.error(errorMessage);
3677
- return new Response(errorMessage, {
3849
+ return new Response(JSON.stringify({ error: errorMessage }), {
3678
3850
  status: 500,
3679
3851
  headers: {
3680
3852
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
@@ -3735,7 +3907,8 @@ function createExpressHandler(params) {
3735
3907
  for (const [key, value] of response.headers.entries()) {
3736
3908
  res.setHeader(key, value);
3737
3909
  }
3738
- res.status(response.status).json(await response.json());
3910
+ const responseData = await response.json();
3911
+ res.status(response.status).json(responseData);
3739
3912
  };
3740
3913
  }
3741
3914
  function serve(routeFunction, options) {