@upstash/workflow 0.2.16 → 0.2.18

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.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-TGEGSOSN.mjs";
5
+ } from "./chunk-EHL7SSJF.mjs";
6
6
 
7
7
  // platforms/astro.ts
8
8
  var telemetry = {
@@ -37,12 +37,13 @@ var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
37
37
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
38
38
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
39
39
  var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
40
+ var WORKFLOW_LABEL_HEADER = "Upstash-Label";
40
41
  var WORKFLOW_PROTOCOL_VERSION = "1";
41
42
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
42
43
  var DEFAULT_CONTENT_TYPE = "application/json";
43
44
  var NO_CONCURRENCY = 1;
44
45
  var DEFAULT_RETRIES = 3;
45
- var VERSION = "v0.2.15";
46
+ var VERSION = "v0.2.18";
46
47
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
47
48
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
48
49
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -144,6 +145,7 @@ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
144
145
  body,
145
146
  timeout: agentCallParams?.timeout,
146
147
  retries: agentCallParams?.retries,
148
+ retryDelay: agentCallParams?.retryDelay,
147
149
  flowControl: agentCallParams?.flowControl
148
150
  });
149
151
  const responseHeaders = new Headers(
@@ -287,7 +289,10 @@ var serveManyBase = ({
287
289
  return new Response(
288
290
  `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
289
291
  {
290
- status: 404
292
+ status: 404,
293
+ headers: {
294
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
295
+ }
291
296
  }
292
297
  );
293
298
  }
@@ -296,7 +301,10 @@ var serveManyBase = ({
296
301
  return new Response(
297
302
  `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
298
303
  {
299
- status: 404
304
+ status: 404,
305
+ headers: {
306
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
307
+ }
300
308
  }
301
309
  );
302
310
  }
@@ -831,6 +839,7 @@ var triggerFirstInvocation = async (params) => {
831
839
  workflowUrl: workflowContext.url,
832
840
  failureUrl: workflowContext.failureUrl,
833
841
  retries: workflowContext.retries,
842
+ retryDelay: workflowContext.retryDelay,
834
843
  telemetry,
835
844
  flowControl: workflowContext.flowControl,
836
845
  useJSONContent: useJSONContent ?? false
@@ -844,6 +853,9 @@ var triggerFirstInvocation = async (params) => {
844
853
  if (useJSONContent) {
845
854
  headers["content-type"] = "application/json";
846
855
  }
856
+ if (workflowContext.label) {
857
+ headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
858
+ }
847
859
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
848
860
  return {
849
861
  headers,
@@ -944,10 +956,11 @@ var recreateUserHeaders = (headers) => {
944
956
  const pairs = headers.entries();
945
957
  for (const [header, value] of pairs) {
946
958
  const headerLowerCase = header.toLowerCase();
947
- if (!headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
959
+ const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
948
960
  !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
949
961
  headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
950
- headerLowerCase !== "render-proxy-ttl") {
962
+ headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
963
+ if (isUserHeader) {
951
964
  filteredHeaders.append(header, value);
952
965
  }
953
966
  }
@@ -960,6 +973,7 @@ var handleThirdPartyCallResult = async ({
960
973
  workflowUrl,
961
974
  failureUrl,
962
975
  retries,
976
+ retryDelay,
963
977
  telemetry,
964
978
  flowControl,
965
979
  debug
@@ -1030,6 +1044,7 @@ ${atob(callbackMessage.body ?? "")}`
1030
1044
  workflowUrl,
1031
1045
  failureUrl,
1032
1046
  retries,
1047
+ retryDelay,
1033
1048
  telemetry,
1034
1049
  flowControl
1035
1050
  },
@@ -1180,7 +1195,8 @@ var BaseLazyStep = class _BaseLazyStep {
1180
1195
  workflowRunId: context.workflowRunId,
1181
1196
  workflowUrl: context.url,
1182
1197
  failureUrl: context.failureUrl,
1183
- retries: context.retries,
1198
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1199
+ retryDelay: context.retryDelay,
1184
1200
  useJSONContent: false,
1185
1201
  telemetry,
1186
1202
  flowControl: context.flowControl
@@ -1199,6 +1215,9 @@ var BaseLazyStep = class _BaseLazyStep {
1199
1215
  body,
1200
1216
  headers,
1201
1217
  method: "POST",
1218
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1219
+ retryDelay: context.retryDelay,
1220
+ flowControl: context.flowControl,
1202
1221
  url: context.url
1203
1222
  }
1204
1223
  ]);
@@ -1269,6 +1288,9 @@ var LazySleepStep = class extends BaseLazyStep {
1269
1288
  headers,
1270
1289
  method: "POST",
1271
1290
  url: context.url,
1291
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1292
+ retryDelay: context.retryDelay,
1293
+ flowControl: context.flowControl,
1272
1294
  delay: isParallel ? void 0 : this.sleep
1273
1295
  }
1274
1296
  ]);
@@ -1311,6 +1333,9 @@ var LazySleepUntilStep = class extends BaseLazyStep {
1311
1333
  headers,
1312
1334
  method: "POST",
1313
1335
  url: context.url,
1336
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1337
+ retryDelay: context.retryDelay,
1338
+ flowControl: context.flowControl,
1314
1339
  notBefore: isParallel ? void 0 : this.sleepUntil
1315
1340
  }
1316
1341
  ]);
@@ -1322,17 +1347,19 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1322
1347
  body;
1323
1348
  headers;
1324
1349
  retries;
1350
+ retryDelay;
1325
1351
  timeout;
1326
1352
  flowControl;
1327
1353
  stepType = "Call";
1328
1354
  allowUndefinedOut = false;
1329
- constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
1355
+ constructor(stepName, url, method, body, headers, retries, retryDelay, timeout, flowControl) {
1330
1356
  super(stepName);
1331
1357
  this.url = url;
1332
1358
  this.method = method;
1333
1359
  this.body = body;
1334
1360
  this.headers = headers;
1335
1361
  this.retries = retries;
1362
+ this.retryDelay = retryDelay;
1336
1363
  this.timeout = timeout;
1337
1364
  this.flowControl = flowControl;
1338
1365
  }
@@ -1407,6 +1434,9 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1407
1434
  getHeaders({ context, telemetry, invokeCount, step }) {
1408
1435
  const { headers, contentType } = super.getHeaders({ context, telemetry, invokeCount, step });
1409
1436
  headers["Upstash-Retries"] = this.retries.toString();
1437
+ if (this.retryDelay) {
1438
+ headers["Upstash-Retry-Delay"] = this.retryDelay;
1439
+ }
1410
1440
  headers[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
1411
1441
  if (this.flowControl) {
1412
1442
  const { flowControlKey, flowControlValue } = prepareFlowControl(this.flowControl);
@@ -1428,7 +1458,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1428
1458
  "Upstash-Callback-Workflow-CallType": "fromCallback",
1429
1459
  "Upstash-Callback-Workflow-Init": "false",
1430
1460
  "Upstash-Callback-Workflow-Url": context.url,
1431
- "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
1461
+ "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger",
1432
1462
  "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
1433
1463
  "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
1434
1464
  "Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
@@ -1446,7 +1476,10 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1446
1476
  headers,
1447
1477
  body: JSON.stringify(this.body),
1448
1478
  method: this.method,
1449
- url: this.url
1479
+ url: this.url,
1480
+ retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
1481
+ retryDelay: this.retryDelay,
1482
+ flowControl: this.flowControl
1450
1483
  }
1451
1484
  ]);
1452
1485
  }
@@ -1575,6 +1608,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1575
1608
  headers = {},
1576
1609
  workflowRunId,
1577
1610
  retries,
1611
+ retryDelay,
1578
1612
  flowControl
1579
1613
  }) {
1580
1614
  super(stepName);
@@ -1584,6 +1618,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1584
1618
  headers,
1585
1619
  workflowRunId: getWorkflowRunId(workflowRunId),
1586
1620
  retries,
1621
+ retryDelay,
1587
1622
  flowControl
1588
1623
  };
1589
1624
  const { workflowId } = workflow;
@@ -1628,6 +1663,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1628
1663
  workflowUrl: context.url,
1629
1664
  failureUrl: context.failureUrl,
1630
1665
  retries: context.retries,
1666
+ retryDelay: context.retryDelay,
1631
1667
  telemetry,
1632
1668
  flowControl: context.flowControl,
1633
1669
  useJSONContent: false
@@ -1653,11 +1689,13 @@ var LazyInvokeStep = class extends BaseLazyStep {
1653
1689
  headers = {},
1654
1690
  workflowRunId = getWorkflowRunId(),
1655
1691
  retries,
1692
+ retryDelay,
1656
1693
  flowControl
1657
1694
  } = this.params;
1658
1695
  const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1659
1696
  const {
1660
1697
  retries: workflowRetries,
1698
+ retryDelay: workflowRetryDelay,
1661
1699
  failureFunction,
1662
1700
  failureUrl,
1663
1701
  useJSONContent,
@@ -1669,6 +1707,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1669
1707
  workflowRunId,
1670
1708
  workflowUrl: newUrl,
1671
1709
  retries: retries ?? workflowRetries,
1710
+ retryDelay: retryDelay ?? workflowRetryDelay,
1672
1711
  telemetry,
1673
1712
  failureUrl: failureFunction ? newUrl : failureUrl,
1674
1713
  flowControl: flowControl ?? workflowFlowControl,
@@ -1721,6 +1760,7 @@ var WorkflowHeaders = class {
1721
1760
  getHeaders() {
1722
1761
  this.addBaseHeaders();
1723
1762
  this.addRetries();
1763
+ this.addRetryDelay();
1724
1764
  this.addFlowControl();
1725
1765
  this.addUserHeaders();
1726
1766
  this.addInvokeCount();
@@ -1734,7 +1774,7 @@ var WorkflowHeaders = class {
1734
1774
  [WORKFLOW_INIT_HEADER]: this.initHeaderValue,
1735
1775
  [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
1736
1776
  [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
1737
- [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1777
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger",
1738
1778
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1739
1779
  ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {},
1740
1780
  ...this.workflowConfig.telemetry && this.stepInfo?.lazyStep instanceof LazyCallStep && this.stepInfo.lazyStep.headers[AGENT_NAME_HEADER] ? { [TELEMETRY_HEADER_AGENT]: "true" } : {}
@@ -1766,6 +1806,16 @@ var WorkflowHeaders = class {
1766
1806
  this.headers.failureHeaders["Retries"] = retries;
1767
1807
  }
1768
1808
  }
1809
+ addRetryDelay() {
1810
+ if (this.workflowConfig.retryDelay === void 0 || this.workflowConfig.retryDelay === "") {
1811
+ return;
1812
+ }
1813
+ const retryDelay = this.workflowConfig.retryDelay.toString();
1814
+ this.headers.workflowHeaders["Retry-Delay"] = retryDelay;
1815
+ if (this.workflowConfig.failureUrl) {
1816
+ this.headers.failureHeaders["Retry-Delay"] = retryDelay;
1817
+ }
1818
+ }
1769
1819
  addFlowControl() {
1770
1820
  if (!this.workflowConfig.flowControl) {
1771
1821
  return;
@@ -1800,10 +1850,13 @@ var WorkflowHeaders = class {
1800
1850
  this.headers.failureHeaders["Workflow-Init"] = "false";
1801
1851
  this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
1802
1852
  this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
1803
- this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody";
1853
+ this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger";
1804
1854
  if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
1805
1855
  this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
1806
1856
  }
1857
+ if (this.workflowConfig.retryDelay !== void 0 && this.workflowConfig.retryDelay !== "") {
1858
+ this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
1859
+ }
1807
1860
  }
1808
1861
  addContentType() {
1809
1862
  if (this.workflowConfig.useJSONContent) {
@@ -1885,6 +1938,7 @@ var submitParallelSteps = async ({
1885
1938
  workflowUrl: context.url,
1886
1939
  failureUrl: context.failureUrl,
1887
1940
  retries: context.retries,
1941
+ retryDelay: context.retryDelay,
1888
1942
  flowControl: context.flowControl,
1889
1943
  telemetry
1890
1944
  },
@@ -2305,7 +2359,7 @@ var BaseWorkflowApi = class {
2305
2359
  */
2306
2360
  async callApi(stepName, settings) {
2307
2361
  const { url, appendHeaders, method } = getProviderInfo(settings.api);
2308
- const { method: userMethod, body, headers = {}, retries = 0, timeout } = settings;
2362
+ const { method: userMethod, body, headers = {}, retries = 0, retryDelay, timeout } = settings;
2309
2363
  return await this.context.call(stepName, {
2310
2364
  url,
2311
2365
  method: userMethod ?? method,
@@ -2315,6 +2369,7 @@ var BaseWorkflowApi = class {
2315
2369
  ...headers
2316
2370
  },
2317
2371
  retries,
2372
+ retryDelay,
2318
2373
  timeout
2319
2374
  });
2320
2375
  }
@@ -2726,11 +2781,58 @@ var WorkflowContext = class {
2726
2781
  * Number of retries
2727
2782
  */
2728
2783
  retries;
2784
+ /**
2785
+ * Delay between retries.
2786
+ *
2787
+ * By default, the `retryDelay` is exponential backoff.
2788
+ * More details can be found in: https://upstash.com/docs/qstash/features/retry.
2789
+ *
2790
+ * The `retryDelay` option allows you to customize the delay (in milliseconds) between retry attempts when message delivery fails.
2791
+ *
2792
+ * You can use mathematical expressions and the following built-in functions to calculate the delay dynamically.
2793
+ * The special variable `retried` represents the current retry attempt count (starting from 0).
2794
+ *
2795
+ * Supported functions:
2796
+ * - `pow`
2797
+ * - `sqrt`
2798
+ * - `abs`
2799
+ * - `exp`
2800
+ * - `floor`
2801
+ * - `ceil`
2802
+ * - `round`
2803
+ * - `min`
2804
+ * - `max`
2805
+ *
2806
+ * Examples of valid `retryDelay` values:
2807
+ * ```ts
2808
+ * 1000 // 1 second
2809
+ * 1000 * (1 + retried) // 1 second multiplied by the current retry attempt
2810
+ * pow(2, retried) // 2 to the power of the current retry attempt
2811
+ * max(10, pow(2, retried)) // The greater of 10 or 2^retried
2812
+ * ```
2813
+ */
2814
+ retryDelay;
2729
2815
  /**
2730
2816
  * Settings for controlling the number of active requests
2731
2817
  * and number of requests per second with the same key.
2732
2818
  */
2733
2819
  flowControl;
2820
+ /**
2821
+ * Label to apply to the workflow run.
2822
+ *
2823
+ * Can be used to filter the workflow run logs.
2824
+ *
2825
+ * Can be set by passing a `label` parameter when triggering the workflow
2826
+ * with `client.trigger`:
2827
+ *
2828
+ * ```ts
2829
+ * await client.trigger({
2830
+ * url: "https://workflow-endpoint.com",
2831
+ * label: "my-label"
2832
+ * });
2833
+ * ```
2834
+ */
2835
+ label;
2734
2836
  constructor({
2735
2837
  qstashClient,
2736
2838
  workflowRunId,
@@ -2742,9 +2844,11 @@ var WorkflowContext = class {
2742
2844
  initialPayload,
2743
2845
  env,
2744
2846
  retries,
2847
+ retryDelay,
2745
2848
  telemetry,
2746
2849
  invokeCount,
2747
- flowControl
2850
+ flowControl,
2851
+ label
2748
2852
  }) {
2749
2853
  this.qstashClient = qstashClient;
2750
2854
  this.workflowRunId = workflowRunId;
@@ -2755,7 +2859,9 @@ var WorkflowContext = class {
2755
2859
  this.requestPayload = initialPayload;
2756
2860
  this.env = env ?? {};
2757
2861
  this.retries = retries ?? DEFAULT_RETRIES;
2862
+ this.retryDelay = retryDelay;
2758
2863
  this.flowControl = flowControl;
2864
+ this.label = label;
2759
2865
  this.executor = new AutoExecutor(this, this.steps, telemetry, invokeCount, debug);
2760
2866
  }
2761
2867
  /**
@@ -2836,6 +2942,7 @@ var WorkflowContext = class {
2836
2942
  settings.body,
2837
2943
  settings.headers || {},
2838
2944
  settings.retries || 0,
2945
+ settings.retryDelay,
2839
2946
  settings.timeout,
2840
2947
  settings.flowControl ?? settings.workflow.options.flowControl
2841
2948
  );
@@ -2846,6 +2953,7 @@ var WorkflowContext = class {
2846
2953
  body,
2847
2954
  headers = {},
2848
2955
  retries = 0,
2956
+ retryDelay,
2849
2957
  timeout,
2850
2958
  flowControl
2851
2959
  } = settings;
@@ -2856,6 +2964,7 @@ var WorkflowContext = class {
2856
2964
  body,
2857
2965
  headers,
2858
2966
  retries,
2967
+ retryDelay,
2859
2968
  timeout,
2860
2969
  flowControl
2861
2970
  );
@@ -2866,7 +2975,7 @@ var WorkflowContext = class {
2866
2975
  * Pauses workflow execution until a specific event occurs or a timeout is reached.
2867
2976
  *
2868
2977
  *```ts
2869
- * const result = await workflow.waitForEvent("payment-confirmed", {
2978
+ * const result = await workflow.waitForEvent("payment-confirmed", "payment.confirmed", {
2870
2979
  * timeout: "5m"
2871
2980
  * });
2872
2981
  *```
@@ -2892,7 +3001,7 @@ var WorkflowContext = class {
2892
3001
  * @param stepName
2893
3002
  * @param eventId - Unique identifier for the event to wait for
2894
3003
  * @param options - Configuration options.
2895
- * @returns `{ timeout: boolean, eventData: unknown }`.
3004
+ * @returns `{ timeout: boolean, eventData: TEventData }`.
2896
3005
  * The `timeout` property specifies if the workflow has timed out. The `eventData`
2897
3006
  * is the data passed when notifying this workflow of an event.
2898
3007
  */
@@ -3052,7 +3161,9 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3052
3161
  initialPayload: context.requestPayload,
3053
3162
  env: context.env,
3054
3163
  retries: context.retries,
3055
- flowControl: context.flowControl
3164
+ retryDelay: context.retryDelay,
3165
+ flowControl: context.flowControl,
3166
+ label: context.label
3056
3167
  });
3057
3168
  try {
3058
3169
  await routeFunction(disabledContext);
@@ -3060,6 +3171,9 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3060
3171
  if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage || error instanceof WorkflowNonRetryableError) {
3061
3172
  return ok("step-found");
3062
3173
  }
3174
+ console.warn(
3175
+ "Upstash Workflow: Received an error while authorizing request. Please avoid throwing errors before the first step of your workflow."
3176
+ );
3063
3177
  return err(error);
3064
3178
  }
3065
3179
  return ok("run-ended");
@@ -3201,9 +3315,9 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
3201
3315
  };
3202
3316
  }
3203
3317
  };
3204
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
3318
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, retryDelay, flowControl, debug) => {
3205
3319
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
3206
- return ok("not-failure-callback");
3320
+ return ok({ result: "not-failure-callback" });
3207
3321
  }
3208
3322
  if (!failureFunction) {
3209
3323
  return err(
@@ -3226,20 +3340,23 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
3226
3340
  if (!errorMessage) {
3227
3341
  errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
3228
3342
  }
3343
+ const userHeaders = recreateUserHeaders(request.headers);
3229
3344
  const workflowContext = new WorkflowContext({
3230
3345
  qstashClient,
3231
3346
  workflowRunId,
3232
3347
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
3233
- headers: recreateUserHeaders(request.headers),
3348
+ headers: userHeaders,
3234
3349
  steps: [],
3235
3350
  url,
3236
3351
  failureUrl: url,
3237
3352
  debug,
3238
3353
  env,
3239
3354
  retries,
3355
+ retryDelay,
3240
3356
  flowControl,
3241
- telemetry: void 0
3357
+ telemetry: void 0,
3242
3358
  // not going to make requests in authentication check
3359
+ label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0
3243
3360
  });
3244
3361
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
3245
3362
  routeFunction,
@@ -3251,16 +3368,16 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
3251
3368
  } else if (authCheck.value === "run-ended") {
3252
3369
  return err(new WorkflowError("Not authorized to run the failure function."));
3253
3370
  }
3254
- await failureFunction({
3371
+ const failureResponse = await failureFunction({
3255
3372
  context: workflowContext,
3256
3373
  failStatus: status,
3257
3374
  failResponse: errorMessage,
3258
3375
  failHeaders: header
3259
3376
  });
3377
+ return ok({ result: "is-failure-callback", response: failureResponse });
3260
3378
  } catch (error) {
3261
3379
  return err(error);
3262
3380
  }
3263
- return ok("is-failure-callback");
3264
3381
  };
3265
3382
 
3266
3383
  // src/serve/options.ts
@@ -3276,8 +3393,8 @@ var processOptions = (options) => {
3276
3393
  baseUrl: environment.QSTASH_URL,
3277
3394
  token: environment.QSTASH_TOKEN
3278
3395
  }),
3279
- onStepFinish: (workflowRunId, finishCondition) => {
3280
- if (finishCondition === "auth-fail") {
3396
+ onStepFinish: (workflowRunId, _finishCondition, detailedFinishCondition) => {
3397
+ if (detailedFinishCondition?.condition === "auth-fail") {
3281
3398
  console.error(AUTH_FAIL_MESSAGE);
3282
3399
  return new Response(
3283
3400
  JSON.stringify({
@@ -3285,19 +3402,33 @@ var processOptions = (options) => {
3285
3402
  workflowRunId
3286
3403
  }),
3287
3404
  {
3288
- status: 400
3405
+ status: 400,
3406
+ headers: {
3407
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3408
+ }
3289
3409
  }
3290
3410
  );
3291
- } else if (finishCondition instanceof WorkflowNonRetryableError) {
3292
- return new Response(JSON.stringify(formatWorkflowError(finishCondition)), {
3411
+ } else if (detailedFinishCondition?.condition === "non-retryable-error") {
3412
+ return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
3293
3413
  headers: {
3294
- "Upstash-NonRetryable-Error": "true"
3414
+ "Upstash-NonRetryable-Error": "true",
3415
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3295
3416
  },
3296
3417
  status: 489
3297
3418
  });
3419
+ } else if (detailedFinishCondition?.condition === "failure-callback") {
3420
+ return new Response(detailedFinishCondition.result ?? void 0, {
3421
+ status: 200,
3422
+ headers: {
3423
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3424
+ }
3425
+ });
3298
3426
  }
3299
3427
  return new Response(JSON.stringify({ workflowRunId }), {
3300
- status: 200
3428
+ status: 200,
3429
+ headers: {
3430
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3431
+ }
3301
3432
  });
3302
3433
  },
3303
3434
  initialPayloadParser: (initialRequest) => {
@@ -3371,6 +3502,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3371
3502
  baseUrl,
3372
3503
  env,
3373
3504
  retries,
3505
+ retryDelay,
3374
3506
  useJSONContent,
3375
3507
  disableTelemetry,
3376
3508
  flowControl,
@@ -3401,10 +3533,14 @@ var serveBase = (routeFunction, telemetry, options) => {
3401
3533
  debug
3402
3534
  );
3403
3535
  if (workflowRunEnded) {
3404
- return onStepFinish(workflowRunId, "workflow-already-ended");
3536
+ return onStepFinish(workflowRunId, "workflow-already-ended", {
3537
+ condition: "workflow-already-ended"
3538
+ });
3405
3539
  }
3406
3540
  if (isLastDuplicate) {
3407
- return onStepFinish(workflowRunId, "duplicate-step");
3541
+ return onStepFinish(workflowRunId, "duplicate-step", {
3542
+ condition: "duplicate-step"
3543
+ });
3408
3544
  }
3409
3545
  const failureCheck = await handleFailure(
3410
3546
  request,
@@ -3415,16 +3551,21 @@ var serveBase = (routeFunction, telemetry, options) => {
3415
3551
  failureFunction,
3416
3552
  env,
3417
3553
  retries,
3554
+ retryDelay,
3418
3555
  flowControl,
3419
3556
  debug
3420
3557
  );
3421
3558
  if (failureCheck.isErr()) {
3422
3559
  throw failureCheck.error;
3423
- } else if (failureCheck.value === "is-failure-callback") {
3560
+ } else if (failureCheck.value.result === "is-failure-callback") {
3424
3561
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
3425
- return onStepFinish(workflowRunId, "failure-callback");
3562
+ return onStepFinish(workflowRunId, "failure-callback", {
3563
+ condition: "failure-callback",
3564
+ result: failureCheck.value.response
3565
+ });
3426
3566
  }
3427
3567
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3568
+ const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3428
3569
  const workflowContext = new WorkflowContext({
3429
3570
  qstashClient,
3430
3571
  workflowRunId,
@@ -3436,9 +3577,11 @@ var serveBase = (routeFunction, telemetry, options) => {
3436
3577
  debug,
3437
3578
  env,
3438
3579
  retries,
3580
+ retryDelay,
3439
3581
  telemetry,
3440
3582
  invokeCount,
3441
- flowControl
3583
+ flowControl,
3584
+ label
3442
3585
  });
3443
3586
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
3444
3587
  routeFunction,
@@ -3451,7 +3594,8 @@ var serveBase = (routeFunction, telemetry, options) => {
3451
3594
  await debug?.log("ERROR", "ERROR", { error: AUTH_FAIL_MESSAGE });
3452
3595
  return onStepFinish(
3453
3596
  isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId,
3454
- "auth-fail"
3597
+ "auth-fail",
3598
+ { condition: "auth-fail" }
3455
3599
  );
3456
3600
  }
3457
3601
  const callReturnCheck = await handleThirdPartyCallResult({
@@ -3461,6 +3605,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3461
3605
  workflowUrl,
3462
3606
  failureUrl: workflowFailureUrl,
3463
3607
  retries,
3608
+ retryDelay,
3464
3609
  flowControl,
3465
3610
  telemetry,
3466
3611
  debug
@@ -3488,19 +3633,28 @@ var serveBase = (routeFunction, telemetry, options) => {
3488
3633
  debug
3489
3634
  });
3490
3635
  if (result.isOk() && result.value instanceof WorkflowNonRetryableError) {
3491
- return onStepFinish(workflowRunId, result.value);
3636
+ return onStepFinish(workflowRunId, result.value, {
3637
+ condition: "non-retryable-error",
3638
+ result: result.value
3639
+ });
3492
3640
  }
3493
3641
  if (result.isErr()) {
3494
3642
  await debug?.log("ERROR", "ERROR", { error: result.error.message });
3495
3643
  throw result.error;
3496
3644
  }
3497
3645
  await debug?.log("INFO", "RESPONSE_WORKFLOW");
3498
- return onStepFinish(workflowContext.workflowRunId, "success");
3646
+ return onStepFinish(workflowContext.workflowRunId, "success", {
3647
+ condition: "success"
3648
+ });
3499
3649
  } else if (callReturnCheck.value === "workflow-ended") {
3500
- return onStepFinish(workflowContext.workflowRunId, "workflow-already-ended");
3650
+ return onStepFinish(workflowContext.workflowRunId, "workflow-already-ended", {
3651
+ condition: "workflow-already-ended"
3652
+ });
3501
3653
  }
3502
3654
  await debug?.log("INFO", "RESPONSE_DEFAULT");
3503
- return onStepFinish("no-workflow-id", "fromCallback");
3655
+ return onStepFinish("no-workflow-id", "fromCallback", {
3656
+ condition: "fromCallback"
3657
+ });
3504
3658
  };
3505
3659
  const safeHandler = async (request) => {
3506
3660
  try {
@@ -3515,11 +3669,17 @@ var serveBase = (routeFunction, telemetry, options) => {
3515
3669
  Original error: '${formattedError.message}'`;
3516
3670
  console.error(errorMessage);
3517
3671
  return new Response(errorMessage, {
3518
- status: 500
3672
+ status: 500,
3673
+ headers: {
3674
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3675
+ }
3519
3676
  });
3520
3677
  }
3521
3678
  return new Response(JSON.stringify(formattedError), {
3522
- status: 500
3679
+ status: 500,
3680
+ headers: {
3681
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3682
+ }
3523
3683
  });
3524
3684
  }
3525
3685
  };
@@ -3542,6 +3702,7 @@ export {
3542
3702
  __toESM,
3543
3703
  makeNotifyRequest,
3544
3704
  makeGetWaitersRequest,
3705
+ WORKFLOW_LABEL_HEADER,
3545
3706
  SDK_TELEMETRY,
3546
3707
  WorkflowError,
3547
3708
  WorkflowAbort,