@upstash/workflow 0.2.15 → 0.2.17

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
@@ -94,7 +94,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
94
94
  var DEFAULT_CONTENT_TYPE = "application/json";
95
95
  var NO_CONCURRENCY = 1;
96
96
  var DEFAULT_RETRIES = 3;
97
- var VERSION = "v0.2.15";
97
+ var VERSION = "v0.2.17";
98
98
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
99
99
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
100
100
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -624,6 +624,7 @@ var triggerFirstInvocation = async (params) => {
624
624
  workflowUrl: workflowContext.url,
625
625
  failureUrl: workflowContext.failureUrl,
626
626
  retries: workflowContext.retries,
627
+ retryDelay: workflowContext.retryDelay,
627
628
  telemetry,
628
629
  flowControl: workflowContext.flowControl,
629
630
  useJSONContent: useJSONContent ?? false
@@ -753,6 +754,7 @@ var handleThirdPartyCallResult = async ({
753
754
  workflowUrl,
754
755
  failureUrl,
755
756
  retries,
757
+ retryDelay,
756
758
  telemetry,
757
759
  flowControl,
758
760
  debug
@@ -823,6 +825,7 @@ ${atob(callbackMessage.body ?? "")}`
823
825
  workflowUrl,
824
826
  failureUrl,
825
827
  retries,
828
+ retryDelay,
826
829
  telemetry,
827
830
  flowControl
828
831
  },
@@ -973,7 +976,8 @@ var BaseLazyStep = class _BaseLazyStep {
973
976
  workflowRunId: context.workflowRunId,
974
977
  workflowUrl: context.url,
975
978
  failureUrl: context.failureUrl,
976
- retries: context.retries,
979
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
980
+ retryDelay: context.retryDelay,
977
981
  useJSONContent: false,
978
982
  telemetry,
979
983
  flowControl: context.flowControl
@@ -992,6 +996,9 @@ var BaseLazyStep = class _BaseLazyStep {
992
996
  body,
993
997
  headers,
994
998
  method: "POST",
999
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1000
+ retryDelay: context.retryDelay,
1001
+ flowControl: context.flowControl,
995
1002
  url: context.url
996
1003
  }
997
1004
  ]);
@@ -1062,6 +1069,9 @@ var LazySleepStep = class extends BaseLazyStep {
1062
1069
  headers,
1063
1070
  method: "POST",
1064
1071
  url: context.url,
1072
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1073
+ retryDelay: context.retryDelay,
1074
+ flowControl: context.flowControl,
1065
1075
  delay: isParallel ? void 0 : this.sleep
1066
1076
  }
1067
1077
  ]);
@@ -1104,6 +1114,9 @@ var LazySleepUntilStep = class extends BaseLazyStep {
1104
1114
  headers,
1105
1115
  method: "POST",
1106
1116
  url: context.url,
1117
+ retries: DEFAULT_RETRIES === context.retries ? void 0 : context.retries,
1118
+ retryDelay: context.retryDelay,
1119
+ flowControl: context.flowControl,
1107
1120
  notBefore: isParallel ? void 0 : this.sleepUntil
1108
1121
  }
1109
1122
  ]);
@@ -1115,17 +1128,19 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1115
1128
  body;
1116
1129
  headers;
1117
1130
  retries;
1131
+ retryDelay;
1118
1132
  timeout;
1119
1133
  flowControl;
1120
1134
  stepType = "Call";
1121
1135
  allowUndefinedOut = false;
1122
- constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
1136
+ constructor(stepName, url, method, body, headers, retries, retryDelay, timeout, flowControl) {
1123
1137
  super(stepName);
1124
1138
  this.url = url;
1125
1139
  this.method = method;
1126
1140
  this.body = body;
1127
1141
  this.headers = headers;
1128
1142
  this.retries = retries;
1143
+ this.retryDelay = retryDelay;
1129
1144
  this.timeout = timeout;
1130
1145
  this.flowControl = flowControl;
1131
1146
  }
@@ -1200,6 +1215,9 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1200
1215
  getHeaders({ context, telemetry, invokeCount, step }) {
1201
1216
  const { headers, contentType } = super.getHeaders({ context, telemetry, invokeCount, step });
1202
1217
  headers["Upstash-Retries"] = this.retries.toString();
1218
+ if (this.retryDelay) {
1219
+ headers["Upstash-Retry-Delay"] = this.retryDelay;
1220
+ }
1203
1221
  headers[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
1204
1222
  if (this.flowControl) {
1205
1223
  const { flowControlKey, flowControlValue } = prepareFlowControl(this.flowControl);
@@ -1221,7 +1239,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1221
1239
  "Upstash-Callback-Workflow-CallType": "fromCallback",
1222
1240
  "Upstash-Callback-Workflow-Init": "false",
1223
1241
  "Upstash-Callback-Workflow-Url": context.url,
1224
- "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
1242
+ "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger",
1225
1243
  "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
1226
1244
  "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
1227
1245
  "Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
@@ -1239,7 +1257,10 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1239
1257
  headers,
1240
1258
  body: JSON.stringify(this.body),
1241
1259
  method: this.method,
1242
- url: this.url
1260
+ url: this.url,
1261
+ retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
1262
+ retryDelay: this.retryDelay,
1263
+ flowControl: this.flowControl
1243
1264
  }
1244
1265
  ]);
1245
1266
  }
@@ -1368,6 +1389,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1368
1389
  headers = {},
1369
1390
  workflowRunId,
1370
1391
  retries,
1392
+ retryDelay,
1371
1393
  flowControl
1372
1394
  }) {
1373
1395
  super(stepName);
@@ -1377,6 +1399,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1377
1399
  headers,
1378
1400
  workflowRunId: getWorkflowRunId(workflowRunId),
1379
1401
  retries,
1402
+ retryDelay,
1380
1403
  flowControl
1381
1404
  };
1382
1405
  const { workflowId } = workflow;
@@ -1421,6 +1444,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1421
1444
  workflowUrl: context.url,
1422
1445
  failureUrl: context.failureUrl,
1423
1446
  retries: context.retries,
1447
+ retryDelay: context.retryDelay,
1424
1448
  telemetry,
1425
1449
  flowControl: context.flowControl,
1426
1450
  useJSONContent: false
@@ -1446,11 +1470,13 @@ var LazyInvokeStep = class extends BaseLazyStep {
1446
1470
  headers = {},
1447
1471
  workflowRunId = getWorkflowRunId(),
1448
1472
  retries,
1473
+ retryDelay,
1449
1474
  flowControl
1450
1475
  } = this.params;
1451
1476
  const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1452
1477
  const {
1453
1478
  retries: workflowRetries,
1479
+ retryDelay: workflowRetryDelay,
1454
1480
  failureFunction,
1455
1481
  failureUrl,
1456
1482
  useJSONContent,
@@ -1462,6 +1488,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1462
1488
  workflowRunId,
1463
1489
  workflowUrl: newUrl,
1464
1490
  retries: retries ?? workflowRetries,
1491
+ retryDelay: retryDelay ?? workflowRetryDelay,
1465
1492
  telemetry,
1466
1493
  failureUrl: failureFunction ? newUrl : failureUrl,
1467
1494
  flowControl: flowControl ?? workflowFlowControl,
@@ -1528,6 +1555,7 @@ var WorkflowHeaders = class {
1528
1555
  getHeaders() {
1529
1556
  this.addBaseHeaders();
1530
1557
  this.addRetries();
1558
+ this.addRetryDelay();
1531
1559
  this.addFlowControl();
1532
1560
  this.addUserHeaders();
1533
1561
  this.addInvokeCount();
@@ -1541,7 +1569,7 @@ var WorkflowHeaders = class {
1541
1569
  [WORKFLOW_INIT_HEADER]: this.initHeaderValue,
1542
1570
  [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
1543
1571
  [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
1544
- [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1572
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger",
1545
1573
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1546
1574
  ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {},
1547
1575
  ...this.workflowConfig.telemetry && this.stepInfo?.lazyStep instanceof LazyCallStep && this.stepInfo.lazyStep.headers[AGENT_NAME_HEADER] ? { [TELEMETRY_HEADER_AGENT]: "true" } : {}
@@ -1573,6 +1601,16 @@ var WorkflowHeaders = class {
1573
1601
  this.headers.failureHeaders["Retries"] = retries;
1574
1602
  }
1575
1603
  }
1604
+ addRetryDelay() {
1605
+ if (this.workflowConfig.retryDelay === void 0 || this.workflowConfig.retryDelay === "") {
1606
+ return;
1607
+ }
1608
+ const retryDelay = this.workflowConfig.retryDelay.toString();
1609
+ this.headers.workflowHeaders["Retry-Delay"] = retryDelay;
1610
+ if (this.workflowConfig.failureUrl) {
1611
+ this.headers.failureHeaders["Retry-Delay"] = retryDelay;
1612
+ }
1613
+ }
1576
1614
  addFlowControl() {
1577
1615
  if (!this.workflowConfig.flowControl) {
1578
1616
  return;
@@ -1607,10 +1645,13 @@ var WorkflowHeaders = class {
1607
1645
  this.headers.failureHeaders["Workflow-Init"] = "false";
1608
1646
  this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
1609
1647
  this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
1610
- this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody";
1648
+ this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger";
1611
1649
  if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
1612
1650
  this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
1613
1651
  }
1652
+ if (this.workflowConfig.retryDelay !== void 0 && this.workflowConfig.retryDelay !== "") {
1653
+ this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
1654
+ }
1614
1655
  }
1615
1656
  addContentType() {
1616
1657
  if (this.workflowConfig.useJSONContent) {
@@ -1692,6 +1733,7 @@ var submitParallelSteps = async ({
1692
1733
  workflowUrl: context.url,
1693
1734
  failureUrl: context.failureUrl,
1694
1735
  retries: context.retries,
1736
+ retryDelay: context.retryDelay,
1695
1737
  flowControl: context.flowControl,
1696
1738
  telemetry
1697
1739
  },
@@ -2112,7 +2154,7 @@ var BaseWorkflowApi = class {
2112
2154
  */
2113
2155
  async callApi(stepName, settings) {
2114
2156
  const { url, appendHeaders, method } = getProviderInfo(settings.api);
2115
- const { method: userMethod, body, headers = {}, retries = 0, timeout } = settings;
2157
+ const { method: userMethod, body, headers = {}, retries = 0, retryDelay, timeout } = settings;
2116
2158
  return await this.context.call(stepName, {
2117
2159
  url,
2118
2160
  method: userMethod ?? method,
@@ -2122,6 +2164,7 @@ var BaseWorkflowApi = class {
2122
2164
  ...headers
2123
2165
  },
2124
2166
  retries,
2167
+ retryDelay,
2125
2168
  timeout
2126
2169
  });
2127
2170
  }
@@ -2211,6 +2254,7 @@ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
2211
2254
  body,
2212
2255
  timeout: agentCallParams?.timeout,
2213
2256
  retries: agentCallParams?.retries,
2257
+ retryDelay: agentCallParams?.retryDelay,
2214
2258
  flowControl: agentCallParams?.flowControl
2215
2259
  });
2216
2260
  const responseHeaders = new Headers(
@@ -2539,7 +2583,10 @@ var serveManyBase = ({
2539
2583
  return new Response(
2540
2584
  `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
2541
2585
  {
2542
- status: 404
2586
+ status: 404,
2587
+ headers: {
2588
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
2589
+ }
2543
2590
  }
2544
2591
  );
2545
2592
  }
@@ -2548,7 +2595,10 @@ var serveManyBase = ({
2548
2595
  return new Response(
2549
2596
  `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
2550
2597
  {
2551
- status: 404
2598
+ status: 404,
2599
+ headers: {
2600
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
2601
+ }
2552
2602
  }
2553
2603
  );
2554
2604
  }
@@ -2685,6 +2735,37 @@ var WorkflowContext = class {
2685
2735
  * Number of retries
2686
2736
  */
2687
2737
  retries;
2738
+ /**
2739
+ * Delay between retries.
2740
+ *
2741
+ * By default, the `retryDelay` is exponential backoff.
2742
+ * More details can be found in: https://upstash.com/docs/qstash/features/retry.
2743
+ *
2744
+ * The `retryDelay` option allows you to customize the delay (in milliseconds) between retry attempts when message delivery fails.
2745
+ *
2746
+ * You can use mathematical expressions and the following built-in functions to calculate the delay dynamically.
2747
+ * The special variable `retried` represents the current retry attempt count (starting from 0).
2748
+ *
2749
+ * Supported functions:
2750
+ * - `pow`
2751
+ * - `sqrt`
2752
+ * - `abs`
2753
+ * - `exp`
2754
+ * - `floor`
2755
+ * - `ceil`
2756
+ * - `round`
2757
+ * - `min`
2758
+ * - `max`
2759
+ *
2760
+ * Examples of valid `retryDelay` values:
2761
+ * ```ts
2762
+ * 1000 // 1 second
2763
+ * 1000 * (1 + retried) // 1 second multiplied by the current retry attempt
2764
+ * pow(2, retried) // 2 to the power of the current retry attempt
2765
+ * max(10, pow(2, retried)) // The greater of 10 or 2^retried
2766
+ * ```
2767
+ */
2768
+ retryDelay;
2688
2769
  /**
2689
2770
  * Settings for controlling the number of active requests
2690
2771
  * and number of requests per second with the same key.
@@ -2701,6 +2782,7 @@ var WorkflowContext = class {
2701
2782
  initialPayload,
2702
2783
  env,
2703
2784
  retries,
2785
+ retryDelay,
2704
2786
  telemetry,
2705
2787
  invokeCount,
2706
2788
  flowControl
@@ -2714,6 +2796,7 @@ var WorkflowContext = class {
2714
2796
  this.requestPayload = initialPayload;
2715
2797
  this.env = env ?? {};
2716
2798
  this.retries = retries ?? DEFAULT_RETRIES;
2799
+ this.retryDelay = retryDelay;
2717
2800
  this.flowControl = flowControl;
2718
2801
  this.executor = new AutoExecutor(this, this.steps, telemetry, invokeCount, debug);
2719
2802
  }
@@ -2795,6 +2878,7 @@ var WorkflowContext = class {
2795
2878
  settings.body,
2796
2879
  settings.headers || {},
2797
2880
  settings.retries || 0,
2881
+ settings.retryDelay,
2798
2882
  settings.timeout,
2799
2883
  settings.flowControl ?? settings.workflow.options.flowControl
2800
2884
  );
@@ -2805,6 +2889,7 @@ var WorkflowContext = class {
2805
2889
  body,
2806
2890
  headers = {},
2807
2891
  retries = 0,
2892
+ retryDelay,
2808
2893
  timeout,
2809
2894
  flowControl
2810
2895
  } = settings;
@@ -2815,6 +2900,7 @@ var WorkflowContext = class {
2815
2900
  body,
2816
2901
  headers,
2817
2902
  retries,
2903
+ retryDelay,
2818
2904
  timeout,
2819
2905
  flowControl
2820
2906
  );
@@ -2825,7 +2911,7 @@ var WorkflowContext = class {
2825
2911
  * Pauses workflow execution until a specific event occurs or a timeout is reached.
2826
2912
  *
2827
2913
  *```ts
2828
- * const result = await workflow.waitForEvent("payment-confirmed", {
2914
+ * const result = await workflow.waitForEvent("payment-confirmed", "payment.confirmed", {
2829
2915
  * timeout: "5m"
2830
2916
  * });
2831
2917
  *```
@@ -2851,7 +2937,7 @@ var WorkflowContext = class {
2851
2937
  * @param stepName
2852
2938
  * @param eventId - Unique identifier for the event to wait for
2853
2939
  * @param options - Configuration options.
2854
- * @returns `{ timeout: boolean, eventData: unknown }`.
2940
+ * @returns `{ timeout: boolean, eventData: TEventData }`.
2855
2941
  * The `timeout` property specifies if the workflow has timed out. The `eventData`
2856
2942
  * is the data passed when notifying this workflow of an event.
2857
2943
  */
@@ -3011,6 +3097,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3011
3097
  initialPayload: context.requestPayload,
3012
3098
  env: context.env,
3013
3099
  retries: context.retries,
3100
+ retryDelay: context.retryDelay,
3014
3101
  flowControl: context.flowControl
3015
3102
  });
3016
3103
  try {
@@ -3019,6 +3106,9 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3019
3106
  if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage || error instanceof WorkflowNonRetryableError) {
3020
3107
  return ok("step-found");
3021
3108
  }
3109
+ console.warn(
3110
+ "Upstash Workflow: Received an error while authorizing request. Please avoid throwing errors before the first step of your workflow."
3111
+ );
3022
3112
  return err(error);
3023
3113
  }
3024
3114
  return ok("run-ended");
@@ -3160,9 +3250,9 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
3160
3250
  };
3161
3251
  }
3162
3252
  };
3163
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
3253
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, retryDelay, flowControl, debug) => {
3164
3254
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
3165
- return ok("not-failure-callback");
3255
+ return ok({ result: "not-failure-callback" });
3166
3256
  }
3167
3257
  if (!failureFunction) {
3168
3258
  return err(
@@ -3174,7 +3264,17 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
3174
3264
  try {
3175
3265
  const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
3176
3266
  const decodedBody = body ? decodeBase64(body) : "{}";
3177
- const errorPayload = JSON.parse(decodedBody);
3267
+ let errorMessage = "";
3268
+ try {
3269
+ const errorPayload = JSON.parse(decodedBody);
3270
+ if (errorPayload.message) {
3271
+ errorMessage = errorPayload.message;
3272
+ }
3273
+ } catch {
3274
+ }
3275
+ if (!errorMessage) {
3276
+ errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
3277
+ }
3178
3278
  const workflowContext = new WorkflowContext({
3179
3279
  qstashClient,
3180
3280
  workflowRunId,
@@ -3186,6 +3286,7 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
3186
3286
  debug,
3187
3287
  env,
3188
3288
  retries,
3289
+ retryDelay,
3189
3290
  flowControl,
3190
3291
  telemetry: void 0
3191
3292
  // not going to make requests in authentication check
@@ -3200,16 +3301,16 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
3200
3301
  } else if (authCheck.value === "run-ended") {
3201
3302
  return err(new WorkflowError("Not authorized to run the failure function."));
3202
3303
  }
3203
- await failureFunction({
3304
+ const failureResponse = await failureFunction({
3204
3305
  context: workflowContext,
3205
3306
  failStatus: status,
3206
- failResponse: errorPayload.message,
3307
+ failResponse: errorMessage,
3207
3308
  failHeaders: header
3208
3309
  });
3310
+ return ok({ result: "is-failure-callback", response: failureResponse });
3209
3311
  } catch (error) {
3210
3312
  return err(error);
3211
3313
  }
3212
- return ok("is-failure-callback");
3213
3314
  };
3214
3315
 
3215
3316
  // src/serve/options.ts
@@ -3225,8 +3326,8 @@ var processOptions = (options) => {
3225
3326
  baseUrl: environment.QSTASH_URL,
3226
3327
  token: environment.QSTASH_TOKEN
3227
3328
  }),
3228
- onStepFinish: (workflowRunId, finishCondition) => {
3229
- if (finishCondition === "auth-fail") {
3329
+ onStepFinish: (workflowRunId, _finishCondition, detailedFinishCondition) => {
3330
+ if (detailedFinishCondition?.condition === "auth-fail") {
3230
3331
  console.error(AUTH_FAIL_MESSAGE);
3231
3332
  return new Response(
3232
3333
  JSON.stringify({
@@ -3234,19 +3335,33 @@ var processOptions = (options) => {
3234
3335
  workflowRunId
3235
3336
  }),
3236
3337
  {
3237
- status: 400
3338
+ status: 400,
3339
+ headers: {
3340
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3341
+ }
3238
3342
  }
3239
3343
  );
3240
- } else if (finishCondition instanceof WorkflowNonRetryableError) {
3241
- return new Response(JSON.stringify(formatWorkflowError(finishCondition)), {
3344
+ } else if (detailedFinishCondition?.condition === "non-retryable-error") {
3345
+ return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
3242
3346
  headers: {
3243
- "Upstash-NonRetryable-Error": "true"
3347
+ "Upstash-NonRetryable-Error": "true",
3348
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3244
3349
  },
3245
3350
  status: 489
3246
3351
  });
3352
+ } else if (detailedFinishCondition?.condition === "failure-callback") {
3353
+ return new Response(detailedFinishCondition.result ?? void 0, {
3354
+ status: 200,
3355
+ headers: {
3356
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3357
+ }
3358
+ });
3247
3359
  }
3248
3360
  return new Response(JSON.stringify({ workflowRunId }), {
3249
- status: 200
3361
+ status: 200,
3362
+ headers: {
3363
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3364
+ }
3250
3365
  });
3251
3366
  },
3252
3367
  initialPayloadParser: (initialRequest) => {
@@ -3320,6 +3435,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3320
3435
  baseUrl,
3321
3436
  env,
3322
3437
  retries,
3438
+ retryDelay,
3323
3439
  useJSONContent,
3324
3440
  disableTelemetry,
3325
3441
  flowControl,
@@ -3350,10 +3466,14 @@ var serveBase = (routeFunction, telemetry, options) => {
3350
3466
  debug
3351
3467
  );
3352
3468
  if (workflowRunEnded) {
3353
- return onStepFinish(workflowRunId, "workflow-already-ended");
3469
+ return onStepFinish(workflowRunId, "workflow-already-ended", {
3470
+ condition: "workflow-already-ended"
3471
+ });
3354
3472
  }
3355
3473
  if (isLastDuplicate) {
3356
- return onStepFinish(workflowRunId, "duplicate-step");
3474
+ return onStepFinish(workflowRunId, "duplicate-step", {
3475
+ condition: "duplicate-step"
3476
+ });
3357
3477
  }
3358
3478
  const failureCheck = await handleFailure(
3359
3479
  request,
@@ -3364,14 +3484,18 @@ var serveBase = (routeFunction, telemetry, options) => {
3364
3484
  failureFunction,
3365
3485
  env,
3366
3486
  retries,
3487
+ retryDelay,
3367
3488
  flowControl,
3368
3489
  debug
3369
3490
  );
3370
3491
  if (failureCheck.isErr()) {
3371
3492
  throw failureCheck.error;
3372
- } else if (failureCheck.value === "is-failure-callback") {
3493
+ } else if (failureCheck.value.result === "is-failure-callback") {
3373
3494
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
3374
- return onStepFinish(workflowRunId, "failure-callback");
3495
+ return onStepFinish(workflowRunId, "failure-callback", {
3496
+ condition: "failure-callback",
3497
+ result: failureCheck.value.response
3498
+ });
3375
3499
  }
3376
3500
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3377
3501
  const workflowContext = new WorkflowContext({
@@ -3385,6 +3509,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3385
3509
  debug,
3386
3510
  env,
3387
3511
  retries,
3512
+ retryDelay,
3388
3513
  telemetry,
3389
3514
  invokeCount,
3390
3515
  flowControl
@@ -3400,7 +3525,8 @@ var serveBase = (routeFunction, telemetry, options) => {
3400
3525
  await debug?.log("ERROR", "ERROR", { error: AUTH_FAIL_MESSAGE });
3401
3526
  return onStepFinish(
3402
3527
  isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId,
3403
- "auth-fail"
3528
+ "auth-fail",
3529
+ { condition: "auth-fail" }
3404
3530
  );
3405
3531
  }
3406
3532
  const callReturnCheck = await handleThirdPartyCallResult({
@@ -3410,6 +3536,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3410
3536
  workflowUrl,
3411
3537
  failureUrl: workflowFailureUrl,
3412
3538
  retries,
3539
+ retryDelay,
3413
3540
  flowControl,
3414
3541
  telemetry,
3415
3542
  debug
@@ -3437,19 +3564,28 @@ var serveBase = (routeFunction, telemetry, options) => {
3437
3564
  debug
3438
3565
  });
3439
3566
  if (result.isOk() && result.value instanceof WorkflowNonRetryableError) {
3440
- return onStepFinish(workflowRunId, result.value);
3567
+ return onStepFinish(workflowRunId, result.value, {
3568
+ condition: "non-retryable-error",
3569
+ result: result.value
3570
+ });
3441
3571
  }
3442
3572
  if (result.isErr()) {
3443
3573
  await debug?.log("ERROR", "ERROR", { error: result.error.message });
3444
3574
  throw result.error;
3445
3575
  }
3446
3576
  await debug?.log("INFO", "RESPONSE_WORKFLOW");
3447
- return onStepFinish(workflowContext.workflowRunId, "success");
3577
+ return onStepFinish(workflowContext.workflowRunId, "success", {
3578
+ condition: "success"
3579
+ });
3448
3580
  } else if (callReturnCheck.value === "workflow-ended") {
3449
- return onStepFinish(workflowContext.workflowRunId, "workflow-already-ended");
3581
+ return onStepFinish(workflowContext.workflowRunId, "workflow-already-ended", {
3582
+ condition: "workflow-already-ended"
3583
+ });
3450
3584
  }
3451
3585
  await debug?.log("INFO", "RESPONSE_DEFAULT");
3452
- return onStepFinish("no-workflow-id", "fromCallback");
3586
+ return onStepFinish("no-workflow-id", "fromCallback", {
3587
+ condition: "fromCallback"
3588
+ });
3453
3589
  };
3454
3590
  const safeHandler = async (request) => {
3455
3591
  try {
@@ -3464,11 +3600,17 @@ var serveBase = (routeFunction, telemetry, options) => {
3464
3600
  Original error: '${formattedError.message}'`;
3465
3601
  console.error(errorMessage);
3466
3602
  return new Response(errorMessage, {
3467
- status: 500
3603
+ status: 500,
3604
+ headers: {
3605
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3606
+ }
3468
3607
  });
3469
3608
  }
3470
3609
  return new Response(JSON.stringify(formattedError), {
3471
- status: 500
3610
+ status: 500,
3611
+ headers: {
3612
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3613
+ }
3472
3614
  });
3473
3615
  }
3474
3616
  };
@@ -3536,6 +3678,9 @@ var servePagesRouter = (routeFunction, options) => {
3536
3678
  method: "POST"
3537
3679
  });
3538
3680
  const response = await serveHandler(request);
3681
+ for (const [key, value] of response.headers.entries()) {
3682
+ res.setHeader(key, value);
3683
+ }
3539
3684
  res.status(response.status).json(await response.json());
3540
3685
  };
3541
3686
  return {
package/nextjs.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-AC5CQCN3.mjs";
5
+ } from "./chunk-RP7G4UD5.mjs";
6
6
 
7
7
  // platforms/nextjs.ts
8
8
  var appTelemetry = {
@@ -65,6 +65,9 @@ var servePagesRouter = (routeFunction, options) => {
65
65
  method: "POST"
66
66
  });
67
67
  const response = await serveHandler(request);
68
+ for (const [key, value] of response.headers.entries()) {
69
+ res.setHeader(key, value);
70
+ }
68
71
  res.status(response.status).json(await response.json());
69
72
  };
70
73
  return {
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@upstash/workflow","version":"v0.2.15","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git+https://github.com/upstash/workflow-ts.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@ai-sdk/anthropic":"^1.1.15","@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@types/bun":"^1.1.10","@types/express":"^5.0.1","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^4.21.1","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@ai-sdk/openai":"^1.2.1","@upstash/qstash":"^2.8.1","ai":"^4.1.54","zod":"^3.24.1"},"directories":{"example":"examples"}}
1
+ {"name":"@upstash/workflow","version":"v0.2.17","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git+https://github.com/upstash/workflow-ts.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@ai-sdk/anthropic":"^1.1.15","@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@types/bun":"^1.1.10","@types/express":"^5.0.3","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^5.1.0","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@ai-sdk/openai":"^1.2.1","@upstash/qstash":"^2.8.2","ai":"^4.1.54","zod":"^3.24.1"},"directories":{"example":"examples"}}
@@ -1,4 +1,4 @@
1
- import { n as PublicServeOptions, R as RouteFunction, w as InvokableWorkflow } from './types-Dd-3bPoU.js';
1
+ import { n as PublicServeOptions, R as RouteFunction, x as InvokableWorkflow } from './types--R_3XZXz.js';
2
2
 
3
3
  type OmitOptionsInServeMany<TOptions> = Omit<TOptions, "env" | "url" | "schema" | "initialPayloadParser">;
4
4
  declare const serveManyBase: <THandler extends (...params: any[]) => any, TOptions extends OmitOptionsInServeMany<PublicServeOptions> = OmitOptionsInServeMany<PublicServeOptions>, TServeParams extends [routeFunction: RouteFunction<any, any>, options: TOptions] = [routeFunction: RouteFunction<any, any>, options: TOptions]>({ workflows, getUrl, serveMethod, options, }: {
@@ -1,4 +1,4 @@
1
- import { n as PublicServeOptions, R as RouteFunction, w as InvokableWorkflow } from './types-Dd-3bPoU.mjs';
1
+ import { n as PublicServeOptions, R as RouteFunction, x as InvokableWorkflow } from './types--R_3XZXz.mjs';
2
2
 
3
3
  type OmitOptionsInServeMany<TOptions> = Omit<TOptions, "env" | "url" | "schema" | "initialPayloadParser">;
4
4
  declare const serveManyBase: <THandler extends (...params: any[]) => any, TOptions extends OmitOptionsInServeMany<PublicServeOptions> = OmitOptionsInServeMany<PublicServeOptions>, TServeParams extends [routeFunction: RouteFunction<any, any>, options: TOptions] = [routeFunction: RouteFunction<any, any>, options: TOptions]>({ workflows, getUrl, serveMethod, options, }: {
package/solidjs.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { APIEvent } from '@solidjs/start/server';
2
- import { R as RouteFunction, n as PublicServeOptions } from './types-Dd-3bPoU.mjs';
2
+ import { R as RouteFunction, n as PublicServeOptions } from './types--R_3XZXz.mjs';
3
3
  import '@upstash/qstash';
4
4
  import 'zod';
5
5
  import 'ai';