@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/nextjs.js CHANGED
@@ -89,12 +89,13 @@ var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
89
89
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
90
90
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
91
91
  var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
92
+ var WORKFLOW_LABEL_HEADER = "Upstash-Label";
92
93
  var WORKFLOW_PROTOCOL_VERSION = "1";
93
94
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
94
95
  var DEFAULT_CONTENT_TYPE = "application/json";
95
96
  var NO_CONCURRENCY = 1;
96
97
  var DEFAULT_RETRIES = 3;
97
- var VERSION = "v0.2.15";
98
+ var VERSION = "v0.2.18";
98
99
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
99
100
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
100
101
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -624,6 +625,7 @@ var triggerFirstInvocation = async (params) => {
624
625
  workflowUrl: workflowContext.url,
625
626
  failureUrl: workflowContext.failureUrl,
626
627
  retries: workflowContext.retries,
628
+ retryDelay: workflowContext.retryDelay,
627
629
  telemetry,
628
630
  flowControl: workflowContext.flowControl,
629
631
  useJSONContent: useJSONContent ?? false
@@ -637,6 +639,9 @@ var triggerFirstInvocation = async (params) => {
637
639
  if (useJSONContent) {
638
640
  headers["content-type"] = "application/json";
639
641
  }
642
+ if (workflowContext.label) {
643
+ headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
644
+ }
640
645
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
641
646
  return {
642
647
  headers,
@@ -737,10 +742,11 @@ var recreateUserHeaders = (headers) => {
737
742
  const pairs = headers.entries();
738
743
  for (const [header, value] of pairs) {
739
744
  const headerLowerCase = header.toLowerCase();
740
- if (!headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
745
+ const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
741
746
  !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
742
747
  headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
743
- headerLowerCase !== "render-proxy-ttl") {
748
+ headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
749
+ if (isUserHeader) {
744
750
  filteredHeaders.append(header, value);
745
751
  }
746
752
  }
@@ -753,6 +759,7 @@ var handleThirdPartyCallResult = async ({
753
759
  workflowUrl,
754
760
  failureUrl,
755
761
  retries,
762
+ retryDelay,
756
763
  telemetry,
757
764
  flowControl,
758
765
  debug
@@ -823,6 +830,7 @@ ${atob(callbackMessage.body ?? "")}`
823
830
  workflowUrl,
824
831
  failureUrl,
825
832
  retries,
833
+ retryDelay,
826
834
  telemetry,
827
835
  flowControl
828
836
  },
@@ -973,7 +981,8 @@ var BaseLazyStep = class _BaseLazyStep {
973
981
  workflowRunId: context.workflowRunId,
974
982
  workflowUrl: context.url,
975
983
  failureUrl: context.failureUrl,
976
- retries: context.retries,
984
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
985
+ retryDelay: context.retryDelay,
977
986
  useJSONContent: false,
978
987
  telemetry,
979
988
  flowControl: context.flowControl
@@ -992,6 +1001,9 @@ var BaseLazyStep = class _BaseLazyStep {
992
1001
  body,
993
1002
  headers,
994
1003
  method: "POST",
1004
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1005
+ retryDelay: context.retryDelay,
1006
+ flowControl: context.flowControl,
995
1007
  url: context.url
996
1008
  }
997
1009
  ]);
@@ -1062,6 +1074,9 @@ var LazySleepStep = class extends BaseLazyStep {
1062
1074
  headers,
1063
1075
  method: "POST",
1064
1076
  url: context.url,
1077
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1078
+ retryDelay: context.retryDelay,
1079
+ flowControl: context.flowControl,
1065
1080
  delay: isParallel ? void 0 : this.sleep
1066
1081
  }
1067
1082
  ]);
@@ -1104,6 +1119,9 @@ var LazySleepUntilStep = class extends BaseLazyStep {
1104
1119
  headers,
1105
1120
  method: "POST",
1106
1121
  url: context.url,
1122
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1123
+ retryDelay: context.retryDelay,
1124
+ flowControl: context.flowControl,
1107
1125
  notBefore: isParallel ? void 0 : this.sleepUntil
1108
1126
  }
1109
1127
  ]);
@@ -1115,17 +1133,19 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1115
1133
  body;
1116
1134
  headers;
1117
1135
  retries;
1136
+ retryDelay;
1118
1137
  timeout;
1119
1138
  flowControl;
1120
1139
  stepType = "Call";
1121
1140
  allowUndefinedOut = false;
1122
- constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
1141
+ constructor(stepName, url, method, body, headers, retries, retryDelay, timeout, flowControl) {
1123
1142
  super(stepName);
1124
1143
  this.url = url;
1125
1144
  this.method = method;
1126
1145
  this.body = body;
1127
1146
  this.headers = headers;
1128
1147
  this.retries = retries;
1148
+ this.retryDelay = retryDelay;
1129
1149
  this.timeout = timeout;
1130
1150
  this.flowControl = flowControl;
1131
1151
  }
@@ -1200,6 +1220,9 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1200
1220
  getHeaders({ context, telemetry, invokeCount, step }) {
1201
1221
  const { headers, contentType } = super.getHeaders({ context, telemetry, invokeCount, step });
1202
1222
  headers["Upstash-Retries"] = this.retries.toString();
1223
+ if (this.retryDelay) {
1224
+ headers["Upstash-Retry-Delay"] = this.retryDelay;
1225
+ }
1203
1226
  headers[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
1204
1227
  if (this.flowControl) {
1205
1228
  const { flowControlKey, flowControlValue } = prepareFlowControl(this.flowControl);
@@ -1221,7 +1244,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1221
1244
  "Upstash-Callback-Workflow-CallType": "fromCallback",
1222
1245
  "Upstash-Callback-Workflow-Init": "false",
1223
1246
  "Upstash-Callback-Workflow-Url": context.url,
1224
- "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
1247
+ "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger",
1225
1248
  "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
1226
1249
  "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
1227
1250
  "Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
@@ -1239,7 +1262,10 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1239
1262
  headers,
1240
1263
  body: JSON.stringify(this.body),
1241
1264
  method: this.method,
1242
- url: this.url
1265
+ url: this.url,
1266
+ retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
1267
+ retryDelay: this.retryDelay,
1268
+ flowControl: this.flowControl
1243
1269
  }
1244
1270
  ]);
1245
1271
  }
@@ -1368,6 +1394,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1368
1394
  headers = {},
1369
1395
  workflowRunId,
1370
1396
  retries,
1397
+ retryDelay,
1371
1398
  flowControl
1372
1399
  }) {
1373
1400
  super(stepName);
@@ -1377,6 +1404,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1377
1404
  headers,
1378
1405
  workflowRunId: getWorkflowRunId(workflowRunId),
1379
1406
  retries,
1407
+ retryDelay,
1380
1408
  flowControl
1381
1409
  };
1382
1410
  const { workflowId } = workflow;
@@ -1421,6 +1449,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1421
1449
  workflowUrl: context.url,
1422
1450
  failureUrl: context.failureUrl,
1423
1451
  retries: context.retries,
1452
+ retryDelay: context.retryDelay,
1424
1453
  telemetry,
1425
1454
  flowControl: context.flowControl,
1426
1455
  useJSONContent: false
@@ -1446,11 +1475,13 @@ var LazyInvokeStep = class extends BaseLazyStep {
1446
1475
  headers = {},
1447
1476
  workflowRunId = getWorkflowRunId(),
1448
1477
  retries,
1478
+ retryDelay,
1449
1479
  flowControl
1450
1480
  } = this.params;
1451
1481
  const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1452
1482
  const {
1453
1483
  retries: workflowRetries,
1484
+ retryDelay: workflowRetryDelay,
1454
1485
  failureFunction,
1455
1486
  failureUrl,
1456
1487
  useJSONContent,
@@ -1462,6 +1493,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1462
1493
  workflowRunId,
1463
1494
  workflowUrl: newUrl,
1464
1495
  retries: retries ?? workflowRetries,
1496
+ retryDelay: retryDelay ?? workflowRetryDelay,
1465
1497
  telemetry,
1466
1498
  failureUrl: failureFunction ? newUrl : failureUrl,
1467
1499
  flowControl: flowControl ?? workflowFlowControl,
@@ -1528,6 +1560,7 @@ var WorkflowHeaders = class {
1528
1560
  getHeaders() {
1529
1561
  this.addBaseHeaders();
1530
1562
  this.addRetries();
1563
+ this.addRetryDelay();
1531
1564
  this.addFlowControl();
1532
1565
  this.addUserHeaders();
1533
1566
  this.addInvokeCount();
@@ -1541,7 +1574,7 @@ var WorkflowHeaders = class {
1541
1574
  [WORKFLOW_INIT_HEADER]: this.initHeaderValue,
1542
1575
  [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
1543
1576
  [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
1544
- [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1577
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger",
1545
1578
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1546
1579
  ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {},
1547
1580
  ...this.workflowConfig.telemetry && this.stepInfo?.lazyStep instanceof LazyCallStep && this.stepInfo.lazyStep.headers[AGENT_NAME_HEADER] ? { [TELEMETRY_HEADER_AGENT]: "true" } : {}
@@ -1573,6 +1606,16 @@ var WorkflowHeaders = class {
1573
1606
  this.headers.failureHeaders["Retries"] = retries;
1574
1607
  }
1575
1608
  }
1609
+ addRetryDelay() {
1610
+ if (this.workflowConfig.retryDelay === void 0 || this.workflowConfig.retryDelay === "") {
1611
+ return;
1612
+ }
1613
+ const retryDelay = this.workflowConfig.retryDelay.toString();
1614
+ this.headers.workflowHeaders["Retry-Delay"] = retryDelay;
1615
+ if (this.workflowConfig.failureUrl) {
1616
+ this.headers.failureHeaders["Retry-Delay"] = retryDelay;
1617
+ }
1618
+ }
1576
1619
  addFlowControl() {
1577
1620
  if (!this.workflowConfig.flowControl) {
1578
1621
  return;
@@ -1607,10 +1650,13 @@ var WorkflowHeaders = class {
1607
1650
  this.headers.failureHeaders["Workflow-Init"] = "false";
1608
1651
  this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
1609
1652
  this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
1610
- this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody";
1653
+ this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger";
1611
1654
  if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
1612
1655
  this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
1613
1656
  }
1657
+ if (this.workflowConfig.retryDelay !== void 0 && this.workflowConfig.retryDelay !== "") {
1658
+ this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
1659
+ }
1614
1660
  }
1615
1661
  addContentType() {
1616
1662
  if (this.workflowConfig.useJSONContent) {
@@ -1692,6 +1738,7 @@ var submitParallelSteps = async ({
1692
1738
  workflowUrl: context.url,
1693
1739
  failureUrl: context.failureUrl,
1694
1740
  retries: context.retries,
1741
+ retryDelay: context.retryDelay,
1695
1742
  flowControl: context.flowControl,
1696
1743
  telemetry
1697
1744
  },
@@ -2112,7 +2159,7 @@ var BaseWorkflowApi = class {
2112
2159
  */
2113
2160
  async callApi(stepName, settings) {
2114
2161
  const { url, appendHeaders, method } = getProviderInfo(settings.api);
2115
- const { method: userMethod, body, headers = {}, retries = 0, timeout } = settings;
2162
+ const { method: userMethod, body, headers = {}, retries = 0, retryDelay, timeout } = settings;
2116
2163
  return await this.context.call(stepName, {
2117
2164
  url,
2118
2165
  method: userMethod ?? method,
@@ -2122,6 +2169,7 @@ var BaseWorkflowApi = class {
2122
2169
  ...headers
2123
2170
  },
2124
2171
  retries,
2172
+ retryDelay,
2125
2173
  timeout
2126
2174
  });
2127
2175
  }
@@ -2211,6 +2259,7 @@ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
2211
2259
  body,
2212
2260
  timeout: agentCallParams?.timeout,
2213
2261
  retries: agentCallParams?.retries,
2262
+ retryDelay: agentCallParams?.retryDelay,
2214
2263
  flowControl: agentCallParams?.flowControl
2215
2264
  });
2216
2265
  const responseHeaders = new Headers(
@@ -2539,7 +2588,10 @@ var serveManyBase = ({
2539
2588
  return new Response(
2540
2589
  `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
2541
2590
  {
2542
- status: 404
2591
+ status: 404,
2592
+ headers: {
2593
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
2594
+ }
2543
2595
  }
2544
2596
  );
2545
2597
  }
@@ -2548,7 +2600,10 @@ var serveManyBase = ({
2548
2600
  return new Response(
2549
2601
  `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
2550
2602
  {
2551
- status: 404
2603
+ status: 404,
2604
+ headers: {
2605
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
2606
+ }
2552
2607
  }
2553
2608
  );
2554
2609
  }
@@ -2685,11 +2740,58 @@ var WorkflowContext = class {
2685
2740
  * Number of retries
2686
2741
  */
2687
2742
  retries;
2743
+ /**
2744
+ * Delay between retries.
2745
+ *
2746
+ * By default, the `retryDelay` is exponential backoff.
2747
+ * More details can be found in: https://upstash.com/docs/qstash/features/retry.
2748
+ *
2749
+ * The `retryDelay` option allows you to customize the delay (in milliseconds) between retry attempts when message delivery fails.
2750
+ *
2751
+ * You can use mathematical expressions and the following built-in functions to calculate the delay dynamically.
2752
+ * The special variable `retried` represents the current retry attempt count (starting from 0).
2753
+ *
2754
+ * Supported functions:
2755
+ * - `pow`
2756
+ * - `sqrt`
2757
+ * - `abs`
2758
+ * - `exp`
2759
+ * - `floor`
2760
+ * - `ceil`
2761
+ * - `round`
2762
+ * - `min`
2763
+ * - `max`
2764
+ *
2765
+ * Examples of valid `retryDelay` values:
2766
+ * ```ts
2767
+ * 1000 // 1 second
2768
+ * 1000 * (1 + retried) // 1 second multiplied by the current retry attempt
2769
+ * pow(2, retried) // 2 to the power of the current retry attempt
2770
+ * max(10, pow(2, retried)) // The greater of 10 or 2^retried
2771
+ * ```
2772
+ */
2773
+ retryDelay;
2688
2774
  /**
2689
2775
  * Settings for controlling the number of active requests
2690
2776
  * and number of requests per second with the same key.
2691
2777
  */
2692
2778
  flowControl;
2779
+ /**
2780
+ * Label to apply to the workflow run.
2781
+ *
2782
+ * Can be used to filter the workflow run logs.
2783
+ *
2784
+ * Can be set by passing a `label` parameter when triggering the workflow
2785
+ * with `client.trigger`:
2786
+ *
2787
+ * ```ts
2788
+ * await client.trigger({
2789
+ * url: "https://workflow-endpoint.com",
2790
+ * label: "my-label"
2791
+ * });
2792
+ * ```
2793
+ */
2794
+ label;
2693
2795
  constructor({
2694
2796
  qstashClient,
2695
2797
  workflowRunId,
@@ -2701,9 +2803,11 @@ var WorkflowContext = class {
2701
2803
  initialPayload,
2702
2804
  env,
2703
2805
  retries,
2806
+ retryDelay,
2704
2807
  telemetry,
2705
2808
  invokeCount,
2706
- flowControl
2809
+ flowControl,
2810
+ label
2707
2811
  }) {
2708
2812
  this.qstashClient = qstashClient;
2709
2813
  this.workflowRunId = workflowRunId;
@@ -2714,7 +2818,9 @@ var WorkflowContext = class {
2714
2818
  this.requestPayload = initialPayload;
2715
2819
  this.env = env ?? {};
2716
2820
  this.retries = retries ?? DEFAULT_RETRIES;
2821
+ this.retryDelay = retryDelay;
2717
2822
  this.flowControl = flowControl;
2823
+ this.label = label;
2718
2824
  this.executor = new AutoExecutor(this, this.steps, telemetry, invokeCount, debug);
2719
2825
  }
2720
2826
  /**
@@ -2795,6 +2901,7 @@ var WorkflowContext = class {
2795
2901
  settings.body,
2796
2902
  settings.headers || {},
2797
2903
  settings.retries || 0,
2904
+ settings.retryDelay,
2798
2905
  settings.timeout,
2799
2906
  settings.flowControl ?? settings.workflow.options.flowControl
2800
2907
  );
@@ -2805,6 +2912,7 @@ var WorkflowContext = class {
2805
2912
  body,
2806
2913
  headers = {},
2807
2914
  retries = 0,
2915
+ retryDelay,
2808
2916
  timeout,
2809
2917
  flowControl
2810
2918
  } = settings;
@@ -2815,6 +2923,7 @@ var WorkflowContext = class {
2815
2923
  body,
2816
2924
  headers,
2817
2925
  retries,
2926
+ retryDelay,
2818
2927
  timeout,
2819
2928
  flowControl
2820
2929
  );
@@ -2825,7 +2934,7 @@ var WorkflowContext = class {
2825
2934
  * Pauses workflow execution until a specific event occurs or a timeout is reached.
2826
2935
  *
2827
2936
  *```ts
2828
- * const result = await workflow.waitForEvent("payment-confirmed", {
2937
+ * const result = await workflow.waitForEvent("payment-confirmed", "payment.confirmed", {
2829
2938
  * timeout: "5m"
2830
2939
  * });
2831
2940
  *```
@@ -2851,7 +2960,7 @@ var WorkflowContext = class {
2851
2960
  * @param stepName
2852
2961
  * @param eventId - Unique identifier for the event to wait for
2853
2962
  * @param options - Configuration options.
2854
- * @returns `{ timeout: boolean, eventData: unknown }`.
2963
+ * @returns `{ timeout: boolean, eventData: TEventData }`.
2855
2964
  * The `timeout` property specifies if the workflow has timed out. The `eventData`
2856
2965
  * is the data passed when notifying this workflow of an event.
2857
2966
  */
@@ -3011,7 +3120,9 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3011
3120
  initialPayload: context.requestPayload,
3012
3121
  env: context.env,
3013
3122
  retries: context.retries,
3014
- flowControl: context.flowControl
3123
+ retryDelay: context.retryDelay,
3124
+ flowControl: context.flowControl,
3125
+ label: context.label
3015
3126
  });
3016
3127
  try {
3017
3128
  await routeFunction(disabledContext);
@@ -3019,6 +3130,9 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3019
3130
  if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage || error instanceof WorkflowNonRetryableError) {
3020
3131
  return ok("step-found");
3021
3132
  }
3133
+ console.warn(
3134
+ "Upstash Workflow: Received an error while authorizing request. Please avoid throwing errors before the first step of your workflow."
3135
+ );
3022
3136
  return err(error);
3023
3137
  }
3024
3138
  return ok("run-ended");
@@ -3160,9 +3274,9 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
3160
3274
  };
3161
3275
  }
3162
3276
  };
3163
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
3277
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, retryDelay, flowControl, debug) => {
3164
3278
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
3165
- return ok("not-failure-callback");
3279
+ return ok({ result: "not-failure-callback" });
3166
3280
  }
3167
3281
  if (!failureFunction) {
3168
3282
  return err(
@@ -3185,20 +3299,23 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
3185
3299
  if (!errorMessage) {
3186
3300
  errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
3187
3301
  }
3302
+ const userHeaders = recreateUserHeaders(request.headers);
3188
3303
  const workflowContext = new WorkflowContext({
3189
3304
  qstashClient,
3190
3305
  workflowRunId,
3191
3306
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
3192
- headers: recreateUserHeaders(request.headers),
3307
+ headers: userHeaders,
3193
3308
  steps: [],
3194
3309
  url,
3195
3310
  failureUrl: url,
3196
3311
  debug,
3197
3312
  env,
3198
3313
  retries,
3314
+ retryDelay,
3199
3315
  flowControl,
3200
- telemetry: void 0
3316
+ telemetry: void 0,
3201
3317
  // not going to make requests in authentication check
3318
+ label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0
3202
3319
  });
3203
3320
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
3204
3321
  routeFunction,
@@ -3210,16 +3327,16 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
3210
3327
  } else if (authCheck.value === "run-ended") {
3211
3328
  return err(new WorkflowError("Not authorized to run the failure function."));
3212
3329
  }
3213
- await failureFunction({
3330
+ const failureResponse = await failureFunction({
3214
3331
  context: workflowContext,
3215
3332
  failStatus: status,
3216
3333
  failResponse: errorMessage,
3217
3334
  failHeaders: header
3218
3335
  });
3336
+ return ok({ result: "is-failure-callback", response: failureResponse });
3219
3337
  } catch (error) {
3220
3338
  return err(error);
3221
3339
  }
3222
- return ok("is-failure-callback");
3223
3340
  };
3224
3341
 
3225
3342
  // src/serve/options.ts
@@ -3235,8 +3352,8 @@ var processOptions = (options) => {
3235
3352
  baseUrl: environment.QSTASH_URL,
3236
3353
  token: environment.QSTASH_TOKEN
3237
3354
  }),
3238
- onStepFinish: (workflowRunId, finishCondition) => {
3239
- if (finishCondition === "auth-fail") {
3355
+ onStepFinish: (workflowRunId, _finishCondition, detailedFinishCondition) => {
3356
+ if (detailedFinishCondition?.condition === "auth-fail") {
3240
3357
  console.error(AUTH_FAIL_MESSAGE);
3241
3358
  return new Response(
3242
3359
  JSON.stringify({
@@ -3244,19 +3361,33 @@ var processOptions = (options) => {
3244
3361
  workflowRunId
3245
3362
  }),
3246
3363
  {
3247
- status: 400
3364
+ status: 400,
3365
+ headers: {
3366
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3367
+ }
3248
3368
  }
3249
3369
  );
3250
- } else if (finishCondition instanceof WorkflowNonRetryableError) {
3251
- return new Response(JSON.stringify(formatWorkflowError(finishCondition)), {
3370
+ } else if (detailedFinishCondition?.condition === "non-retryable-error") {
3371
+ return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
3252
3372
  headers: {
3253
- "Upstash-NonRetryable-Error": "true"
3373
+ "Upstash-NonRetryable-Error": "true",
3374
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3254
3375
  },
3255
3376
  status: 489
3256
3377
  });
3378
+ } else if (detailedFinishCondition?.condition === "failure-callback") {
3379
+ return new Response(detailedFinishCondition.result ?? void 0, {
3380
+ status: 200,
3381
+ headers: {
3382
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3383
+ }
3384
+ });
3257
3385
  }
3258
3386
  return new Response(JSON.stringify({ workflowRunId }), {
3259
- status: 200
3387
+ status: 200,
3388
+ headers: {
3389
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3390
+ }
3260
3391
  });
3261
3392
  },
3262
3393
  initialPayloadParser: (initialRequest) => {
@@ -3330,6 +3461,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3330
3461
  baseUrl,
3331
3462
  env,
3332
3463
  retries,
3464
+ retryDelay,
3333
3465
  useJSONContent,
3334
3466
  disableTelemetry,
3335
3467
  flowControl,
@@ -3360,10 +3492,14 @@ var serveBase = (routeFunction, telemetry, options) => {
3360
3492
  debug
3361
3493
  );
3362
3494
  if (workflowRunEnded) {
3363
- return onStepFinish(workflowRunId, "workflow-already-ended");
3495
+ return onStepFinish(workflowRunId, "workflow-already-ended", {
3496
+ condition: "workflow-already-ended"
3497
+ });
3364
3498
  }
3365
3499
  if (isLastDuplicate) {
3366
- return onStepFinish(workflowRunId, "duplicate-step");
3500
+ return onStepFinish(workflowRunId, "duplicate-step", {
3501
+ condition: "duplicate-step"
3502
+ });
3367
3503
  }
3368
3504
  const failureCheck = await handleFailure(
3369
3505
  request,
@@ -3374,16 +3510,21 @@ var serveBase = (routeFunction, telemetry, options) => {
3374
3510
  failureFunction,
3375
3511
  env,
3376
3512
  retries,
3513
+ retryDelay,
3377
3514
  flowControl,
3378
3515
  debug
3379
3516
  );
3380
3517
  if (failureCheck.isErr()) {
3381
3518
  throw failureCheck.error;
3382
- } else if (failureCheck.value === "is-failure-callback") {
3519
+ } else if (failureCheck.value.result === "is-failure-callback") {
3383
3520
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
3384
- return onStepFinish(workflowRunId, "failure-callback");
3521
+ return onStepFinish(workflowRunId, "failure-callback", {
3522
+ condition: "failure-callback",
3523
+ result: failureCheck.value.response
3524
+ });
3385
3525
  }
3386
3526
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3527
+ const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3387
3528
  const workflowContext = new WorkflowContext({
3388
3529
  qstashClient,
3389
3530
  workflowRunId,
@@ -3395,9 +3536,11 @@ var serveBase = (routeFunction, telemetry, options) => {
3395
3536
  debug,
3396
3537
  env,
3397
3538
  retries,
3539
+ retryDelay,
3398
3540
  telemetry,
3399
3541
  invokeCount,
3400
- flowControl
3542
+ flowControl,
3543
+ label
3401
3544
  });
3402
3545
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
3403
3546
  routeFunction,
@@ -3410,7 +3553,8 @@ var serveBase = (routeFunction, telemetry, options) => {
3410
3553
  await debug?.log("ERROR", "ERROR", { error: AUTH_FAIL_MESSAGE });
3411
3554
  return onStepFinish(
3412
3555
  isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId,
3413
- "auth-fail"
3556
+ "auth-fail",
3557
+ { condition: "auth-fail" }
3414
3558
  );
3415
3559
  }
3416
3560
  const callReturnCheck = await handleThirdPartyCallResult({
@@ -3420,6 +3564,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3420
3564
  workflowUrl,
3421
3565
  failureUrl: workflowFailureUrl,
3422
3566
  retries,
3567
+ retryDelay,
3423
3568
  flowControl,
3424
3569
  telemetry,
3425
3570
  debug
@@ -3447,19 +3592,28 @@ var serveBase = (routeFunction, telemetry, options) => {
3447
3592
  debug
3448
3593
  });
3449
3594
  if (result.isOk() && result.value instanceof WorkflowNonRetryableError) {
3450
- return onStepFinish(workflowRunId, result.value);
3595
+ return onStepFinish(workflowRunId, result.value, {
3596
+ condition: "non-retryable-error",
3597
+ result: result.value
3598
+ });
3451
3599
  }
3452
3600
  if (result.isErr()) {
3453
3601
  await debug?.log("ERROR", "ERROR", { error: result.error.message });
3454
3602
  throw result.error;
3455
3603
  }
3456
3604
  await debug?.log("INFO", "RESPONSE_WORKFLOW");
3457
- return onStepFinish(workflowContext.workflowRunId, "success");
3605
+ return onStepFinish(workflowContext.workflowRunId, "success", {
3606
+ condition: "success"
3607
+ });
3458
3608
  } else if (callReturnCheck.value === "workflow-ended") {
3459
- return onStepFinish(workflowContext.workflowRunId, "workflow-already-ended");
3609
+ return onStepFinish(workflowContext.workflowRunId, "workflow-already-ended", {
3610
+ condition: "workflow-already-ended"
3611
+ });
3460
3612
  }
3461
3613
  await debug?.log("INFO", "RESPONSE_DEFAULT");
3462
- return onStepFinish("no-workflow-id", "fromCallback");
3614
+ return onStepFinish("no-workflow-id", "fromCallback", {
3615
+ condition: "fromCallback"
3616
+ });
3463
3617
  };
3464
3618
  const safeHandler = async (request) => {
3465
3619
  try {
@@ -3474,11 +3628,17 @@ var serveBase = (routeFunction, telemetry, options) => {
3474
3628
  Original error: '${formattedError.message}'`;
3475
3629
  console.error(errorMessage);
3476
3630
  return new Response(errorMessage, {
3477
- status: 500
3631
+ status: 500,
3632
+ headers: {
3633
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3634
+ }
3478
3635
  });
3479
3636
  }
3480
3637
  return new Response(JSON.stringify(formattedError), {
3481
- status: 500
3638
+ status: 500,
3639
+ headers: {
3640
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3641
+ }
3482
3642
  });
3483
3643
  }
3484
3644
  };
@@ -3546,6 +3706,9 @@ var servePagesRouter = (routeFunction, options) => {
3546
3706
  method: "POST"
3547
3707
  });
3548
3708
  const response = await serveHandler(request);
3709
+ for (const [key, value] of response.headers.entries()) {
3710
+ res.setHeader(key, value);
3711
+ }
3549
3712
  res.status(response.status).json(await response.json());
3550
3713
  };
3551
3714
  return {