@upstash/workflow 1.4.0-rc → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/astro.d.mts +2 -3
  2. package/astro.d.ts +2 -3
  3. package/astro.js +164 -575
  4. package/astro.mjs +1 -1
  5. package/{chunk-UH6O5IJY.mjs → chunk-DRHEHWMS.mjs} +326 -737
  6. package/cloudflare.d.mts +2 -3
  7. package/cloudflare.d.ts +2 -3
  8. package/cloudflare.js +164 -575
  9. package/cloudflare.mjs +1 -1
  10. package/express.d.mts +2 -3
  11. package/express.d.ts +2 -3
  12. package/express.js +164 -575
  13. package/express.mjs +1 -1
  14. package/h3.d.mts +2 -3
  15. package/h3.d.ts +2 -3
  16. package/h3.js +164 -575
  17. package/h3.mjs +1 -1
  18. package/hono.d.mts +2 -3
  19. package/hono.d.ts +2 -3
  20. package/hono.js +164 -575
  21. package/hono.mjs +1 -1
  22. package/index.d.mts +2 -3
  23. package/index.d.ts +2 -3
  24. package/index.js +164 -575
  25. package/index.mjs +1 -1
  26. package/nextjs.d.mts +2 -3
  27. package/nextjs.d.ts +2 -3
  28. package/nextjs.js +164 -575
  29. package/nextjs.mjs +1 -1
  30. package/package.json +1 -1
  31. package/react-router.d.mts +2 -3
  32. package/react-router.d.ts +2 -3
  33. package/react-router.js +164 -575
  34. package/react-router.mjs +1 -1
  35. package/{serve-many-CbIV7145.d.mts → serve-many-CG3BFvO3.d.mts} +1 -1
  36. package/{serve-many-BCtZg31b.d.ts → serve-many-iJF1IUXk.d.ts} +1 -1
  37. package/solidjs.d.mts +1 -2
  38. package/solidjs.d.ts +1 -2
  39. package/solidjs.js +164 -575
  40. package/solidjs.mjs +1 -1
  41. package/svelte.d.mts +2 -3
  42. package/svelte.d.ts +2 -3
  43. package/svelte.js +164 -575
  44. package/svelte.mjs +1 -1
  45. package/tanstack.d.mts +2 -3
  46. package/tanstack.d.ts +2 -3
  47. package/tanstack.js +164 -575
  48. package/tanstack.mjs +1 -1
  49. package/{types-ffLPVG5_.d.ts → types-CekOpKvz.d.mts} +8 -248
  50. package/{types-ffLPVG5_.d.mts → types-CekOpKvz.d.ts} +8 -248
package/index.js CHANGED
@@ -304,14 +304,6 @@ var WORKFLOW_CREATED_AT_HEADER = "Upstash-Workflow-CreatedAt";
304
304
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
305
305
  var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
306
306
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
307
- var WORKFLOW_FEATURE_SET = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
308
- var WORKFLOW_STEP_CONFIG_CALL_TYPE = "stepConfig";
309
- var WORKFLOW_STEP_CONFIG_HEADER = "Upstash-Workflow-Step-Config";
310
- var FLOW_CONTROL_KEY_HEADER = "Upstash-Flow-Control-Key";
311
- var FLOW_CONTROL_VALUE_HEADER = "Upstash-Flow-Control-Value";
312
- var RETRIES_HEADER = "Upstash-Retries";
313
- var RETRY_DELAY_HEADER = "Upstash-Retry-Delay";
314
- var WORKFLOW_STEP_CONFIG_FEATURE = "WF_StepConfig";
315
307
  var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
316
308
  var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
317
309
  var WORKFLOW_LABEL_HEADER = "Upstash-Label";
@@ -329,6 +321,102 @@ var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
329
321
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
330
322
  var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
331
323
 
324
+ // src/context/auto-executor.ts
325
+ var import_qstash5 = require("@upstash/qstash");
326
+
327
+ // src/qstash/headers.ts
328
+ var import_qstash4 = require("@upstash/qstash");
329
+
330
+ // src/utils.ts
331
+ var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
332
+ var NANOID_LENGTH = 21;
333
+ var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
334
+ function validateLabel(label) {
335
+ if (label === void 0) return;
336
+ const labels = Array.isArray(label) ? label : [label];
337
+ if (labels.length === 0) {
338
+ throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
339
+ }
340
+ for (const value of labels) {
341
+ if (!RESOURCE_NAME_PATTERN.test(value)) {
342
+ throw new WorkflowNonRetryableError(
343
+ `Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
344
+ );
345
+ }
346
+ }
347
+ }
348
+ function serializeLabel(label) {
349
+ return Array.isArray(label) ? label.join(",") : label;
350
+ }
351
+ function validateFlowControl(flowControl) {
352
+ if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
353
+ throw new WorkflowNonRetryableError(
354
+ `Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
355
+ );
356
+ }
357
+ }
358
+ function getRandomInt() {
359
+ return Math.floor(Math.random() * NANOID_CHARS.length);
360
+ }
361
+ function nanoid(length = NANOID_LENGTH) {
362
+ return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
363
+ }
364
+ function getWorkflowRunId(id) {
365
+ return `wfr_${id ?? nanoid()}`;
366
+ }
367
+ function decodeBase64(base64) {
368
+ const binString = atob(base64);
369
+ try {
370
+ const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
371
+ return new TextDecoder().decode(intArray);
372
+ } catch (error) {
373
+ console.warn(
374
+ `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
375
+ );
376
+ return binString;
377
+ }
378
+ }
379
+ function getUserIdFromToken(qstashClient) {
380
+ try {
381
+ const token = qstashClient.token;
382
+ const decodedToken = decodeBase64(token);
383
+ const tokenPayload = JSON.parse(decodedToken);
384
+ const userId = tokenPayload.UserID;
385
+ if (!userId) {
386
+ throw new WorkflowError("QStash token payload does not contain userId");
387
+ }
388
+ return userId;
389
+ } catch (error) {
390
+ throw new WorkflowError(
391
+ `Failed to decode QStash token while running create webhook step: ${error.message}`
392
+ );
393
+ }
394
+ }
395
+ function getQStashUrl(qstashClient) {
396
+ try {
397
+ const requester = qstashClient.http;
398
+ const baseUrl = requester.baseUrl;
399
+ if (!baseUrl) {
400
+ throw new WorkflowError("QStash client does not have a baseUrl");
401
+ }
402
+ return baseUrl;
403
+ } catch (error) {
404
+ throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
405
+ }
406
+ }
407
+ function getEventId() {
408
+ return `evt_${nanoid(15)}`;
409
+ }
410
+ function stringifyBody(body) {
411
+ if (body === void 0) {
412
+ return void 0;
413
+ }
414
+ if (typeof body === "string") {
415
+ return body;
416
+ }
417
+ return JSON.stringify(body);
418
+ }
419
+
332
420
  // node_modules/neverthrow/dist/index.es.js
333
421
  var defaultErrorConfig = {
334
422
  withStackTrace: false
@@ -744,102 +832,6 @@ var Err = class {
744
832
  };
745
833
  var fromThrowable = Result.fromThrowable;
746
834
 
747
- // src/context/auto-executor.ts
748
- var import_qstash5 = require("@upstash/qstash");
749
-
750
- // src/qstash/headers.ts
751
- var import_qstash4 = require("@upstash/qstash");
752
-
753
- // src/utils.ts
754
- var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
755
- var NANOID_LENGTH = 21;
756
- var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
757
- function validateLabel(label) {
758
- if (label === void 0) return;
759
- const labels = Array.isArray(label) ? label : [label];
760
- if (labels.length === 0) {
761
- throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
762
- }
763
- for (const value of labels) {
764
- if (!RESOURCE_NAME_PATTERN.test(value)) {
765
- throw new WorkflowNonRetryableError(
766
- `Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
767
- );
768
- }
769
- }
770
- }
771
- function serializeLabel(label) {
772
- return Array.isArray(label) ? label.join(",") : label;
773
- }
774
- function validateFlowControl(flowControl) {
775
- if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
776
- throw new WorkflowNonRetryableError(
777
- `Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
778
- );
779
- }
780
- }
781
- function getRandomInt() {
782
- return Math.floor(Math.random() * NANOID_CHARS.length);
783
- }
784
- function nanoid(length = NANOID_LENGTH) {
785
- return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
786
- }
787
- function getWorkflowRunId(id) {
788
- return `wfr_${id ?? nanoid()}`;
789
- }
790
- function decodeBase64(base64) {
791
- const binString = atob(base64);
792
- try {
793
- const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
794
- return new TextDecoder().decode(intArray);
795
- } catch (error) {
796
- console.warn(
797
- `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
798
- );
799
- return binString;
800
- }
801
- }
802
- function getUserIdFromToken(qstashClient) {
803
- try {
804
- const token = qstashClient.token;
805
- const decodedToken = decodeBase64(token);
806
- const tokenPayload = JSON.parse(decodedToken);
807
- const userId = tokenPayload.UserID;
808
- if (!userId) {
809
- throw new WorkflowError("QStash token payload does not contain userId");
810
- }
811
- return userId;
812
- } catch (error) {
813
- throw new WorkflowError(
814
- `Failed to decode QStash token while running create webhook step: ${error.message}`
815
- );
816
- }
817
- }
818
- function getQStashUrl(qstashClient) {
819
- try {
820
- const requester = qstashClient.http;
821
- const baseUrl = requester.baseUrl;
822
- if (!baseUrl) {
823
- throw new WorkflowError("QStash client does not have a baseUrl");
824
- }
825
- return baseUrl;
826
- } catch (error) {
827
- throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
828
- }
829
- }
830
- function getEventId() {
831
- return `evt_${nanoid(15)}`;
832
- }
833
- function stringifyBody(body) {
834
- if (body === void 0) {
835
- return void 0;
836
- }
837
- if (typeof body === "string") {
838
- return body;
839
- }
840
- return JSON.stringify(body);
841
- }
842
-
843
835
  // src/types.ts
844
836
  var StepTypes = [
845
837
  "Initial",
@@ -879,6 +871,10 @@ var triggerFirstInvocation = async (params) => {
879
871
  workflowConfig: {
880
872
  workflowRunId: workflowContext.workflowRunId,
881
873
  workflowUrl: workflowContext.url,
874
+ // the run doesn't exist yet, so the trigger time is sent when the creation time
875
+ // is not known. serve and client.trigger set it on the context already, so a
876
+ // retry of this request carries the same value and is deduplicated.
877
+ workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
882
878
  failureUrl,
883
879
  retries,
884
880
  retryDelay,
@@ -945,10 +941,6 @@ var triggerFirstInvocation = async (params) => {
945
941
  return err(error_);
946
942
  }
947
943
  };
948
- var flushPendingStep = async (workflowContext) => {
949
- const { executor } = workflowContext;
950
- return await executor.submitPendingStep();
951
- };
952
944
  var triggerRouteFunction = async ({
953
945
  onCleanup,
954
946
  onStep,
@@ -986,7 +978,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
986
978
  path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
987
979
  method: "DELETE",
988
980
  parseResponseAsJson: false,
989
- body: JSON.stringify(result)
981
+ body: JSON.stringify(result),
982
+ // with the creation time, QStash deletes the run only if it is the same run.
983
+ // a late delete of a previous run can't delete a new run with the same id.
984
+ headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
990
985
  });
991
986
  await dispatchDebug?.("onInfo", {
992
987
  info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
@@ -1063,6 +1058,7 @@ ${atob(callbackMessage.body ?? "")}`
1063
1058
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
1064
1059
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
1065
1060
  const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
1061
+ const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
1066
1062
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
1067
1063
  throw new Error(
1068
1064
  `Missing info in callback message source header: ${JSON.stringify({
@@ -1080,6 +1076,7 @@ ${atob(callbackMessage.body ?? "")}`
1080
1076
  workflowConfig: {
1081
1077
  workflowRunId,
1082
1078
  workflowUrl,
1079
+ workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
1083
1080
  telemetry
1084
1081
  },
1085
1082
  invokeCount: Number(invokeCount)
@@ -1156,29 +1153,8 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
1156
1153
  var BaseLazyStep = class _BaseLazyStep {
1157
1154
  stepName;
1158
1155
  context;
1159
- /**
1160
- * step-level settings (flow control, retries etc.) which override the
1161
- * settings the workflow run was triggered with, for this step only.
1162
- */
1163
- stepSettings;
1164
- /**
1165
- * whether this step's result can be submitted after the route function
1166
- * has moved on, instead of right away.
1167
- *
1168
- * Enabled for steps whose `getResultStep` produces the final `out` —
1169
- * which is not the same as "needs no server round trip":
1170
- * `LazyCreateWebhookStep` builds its result in `getBody` and returns
1171
- * `out: undefined` from `getResultStep`, so deferring it would hand
1172
- * `undefined` to the route function.
1173
- *
1174
- * Deferring lets the route function continue and reveal the next step,
1175
- * so that step's settings can ride on this step's submission instead
1176
- * of needing a step config request of their own.
1177
- */
1178
- supportsDeferredSubmission = false;
1179
- constructor(context, stepName, stepSettings) {
1156
+ constructor(context, stepName) {
1180
1157
  this.context = context;
1181
- this.stepSettings = stepSettings;
1182
1158
  if (!stepName) {
1183
1159
  throw new WorkflowError(
1184
1160
  "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
@@ -1242,18 +1218,13 @@ var BaseLazyStep = class _BaseLazyStep {
1242
1218
  step.out = JSON.stringify(step.out);
1243
1219
  return JSON.stringify(step);
1244
1220
  }
1245
- getHeaders({
1246
- context,
1247
- telemetry,
1248
- invokeCount,
1249
- step,
1250
- stepSettings
1251
- }) {
1221
+ getHeaders({ context, telemetry, invokeCount, step }) {
1252
1222
  return getHeaders({
1253
1223
  initHeaderValue: "false",
1254
1224
  workflowConfig: {
1255
1225
  workflowRunId: context.workflowRunId,
1256
1226
  workflowUrl: context.url,
1227
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1257
1228
  useJSONContent: false,
1258
1229
  telemetry
1259
1230
  },
@@ -1261,8 +1232,7 @@ var BaseLazyStep = class _BaseLazyStep {
1261
1232
  stepInfo: {
1262
1233
  step,
1263
1234
  lazyStep: this
1264
- },
1265
- stepSettings
1235
+ }
1266
1236
  });
1267
1237
  }
1268
1238
  async submitStep({ context, body, headers }) {
@@ -1280,9 +1250,8 @@ var LazyFunctionStep = class extends BaseLazyStep {
1280
1250
  stepFunction;
1281
1251
  stepType = "Run";
1282
1252
  allowUndefinedOut = true;
1283
- supportsDeferredSubmission = true;
1284
- constructor(context, stepName, stepFunction, stepSettings) {
1285
- super(context, stepName, stepSettings);
1253
+ constructor(context, stepName, stepFunction) {
1254
+ super(context, stepName);
1286
1255
  this.stepFunction = stepFunction;
1287
1256
  }
1288
1257
  getPlanStep(concurrent, targetStep) {
@@ -1312,7 +1281,6 @@ var LazySleepStep = class extends BaseLazyStep {
1312
1281
  sleep;
1313
1282
  stepType = "SleepFor";
1314
1283
  allowUndefinedOut = true;
1315
- supportsDeferredSubmission = true;
1316
1284
  constructor(context, stepName, sleep) {
1317
1285
  super(context, stepName);
1318
1286
  this.sleep = sleep;
@@ -1352,7 +1320,6 @@ var LazySleepUntilStep = class extends BaseLazyStep {
1352
1320
  sleepUntil;
1353
1321
  stepType = "SleepUntil";
1354
1322
  allowUndefinedOut = true;
1355
- supportsDeferredSubmission = true;
1356
1323
  constructor(context, stepName, sleepUntil) {
1357
1324
  super(context, stepName);
1358
1325
  this.sleepUntil = sleepUntil;
@@ -1585,6 +1552,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
1585
1552
  ) : {},
1586
1553
  // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1587
1554
  "Upstash-Workflow-Runid": [context.workflowRunId],
1555
+ // same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
1556
+ ...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
1588
1557
  [WORKFLOW_INIT_HEADER]: ["false"],
1589
1558
  [WORKFLOW_URL_HEADER]: [context.url],
1590
1559
  "Upstash-Workflow-CallType": ["step"]
@@ -1687,6 +1656,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1687
1656
  workflowConfig: {
1688
1657
  workflowRunId: context.workflowRunId,
1689
1658
  workflowUrl: context.url,
1659
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1690
1660
  telemetry,
1691
1661
  useJSONContent: false
1692
1662
  },
@@ -1696,6 +1666,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1696
1666
  invokerHeaders[key] = value;
1697
1667
  });
1698
1668
  invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1669
+ if (context.workflowRunCreatedAt) {
1670
+ invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
1671
+ }
1699
1672
  const request = {
1700
1673
  body: stringifyBody(this.params.body),
1701
1674
  headers: Object.fromEntries(
@@ -1724,6 +1697,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1724
1697
  workflowConfig: {
1725
1698
  workflowRunId: getWorkflowRunId(workflowRunId),
1726
1699
  workflowUrl: newUrl,
1700
+ // the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
1701
+ // it separates the invocations made by different runs of the invoker.
1702
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1727
1703
  retries,
1728
1704
  retryDelay,
1729
1705
  telemetry,
@@ -1845,7 +1821,6 @@ var WorkflowHeaders = class {
1845
1821
  invokeCount;
1846
1822
  initHeaderValue;
1847
1823
  stepInfo;
1848
- stepSettings;
1849
1824
  headers;
1850
1825
  /**
1851
1826
  * @param params workflow header parameters
@@ -1855,20 +1830,17 @@ var WorkflowHeaders = class {
1855
1830
  workflowConfig,
1856
1831
  invokeCount,
1857
1832
  initHeaderValue,
1858
- stepInfo,
1859
- stepSettings
1833
+ stepInfo
1860
1834
  }) {
1861
1835
  this.userHeaders = userHeaders;
1862
1836
  this.workflowConfig = workflowConfig;
1863
1837
  this.invokeCount = invokeCount;
1864
1838
  this.initHeaderValue = initHeaderValue;
1865
1839
  this.stepInfo = stepInfo;
1866
- this.stepSettings = stepSettings;
1867
1840
  this.headers = {
1868
1841
  rawHeaders: {},
1869
1842
  workflowHeaders: {},
1870
- failureHeaders: {},
1871
- stepSettingsHeaders: {}
1843
+ failureHeaders: {}
1872
1844
  };
1873
1845
  }
1874
1846
  getHeaders() {
@@ -1879,7 +1851,6 @@ var WorkflowHeaders = class {
1879
1851
  this.addUserHeaders();
1880
1852
  this.addInvokeCount();
1881
1853
  this.addFailureUrl();
1882
- this.addStepSettings();
1883
1854
  const contentType = this.addContentType();
1884
1855
  return this.prefixHeaders(contentType);
1885
1856
  }
@@ -1889,10 +1860,13 @@ var WorkflowHeaders = class {
1889
1860
  [WORKFLOW_INIT_HEADER]: this.initHeaderValue,
1890
1861
  [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
1891
1862
  [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
1892
- [WORKFLOW_FEATURE_HEADER]: WORKFLOW_FEATURE_SET,
1863
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
1893
1864
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1894
1865
  ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
1895
1866
  };
1867
+ if (this.workflowConfig.workflowRunCreatedAt) {
1868
+ this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
1869
+ }
1896
1870
  if (this.stepInfo?.lazyStep.stepType !== "Call") {
1897
1871
  this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1898
1872
  }
@@ -1967,7 +1941,7 @@ var WorkflowHeaders = class {
1967
1941
  this.headers.failureHeaders["Workflow-Init"] = "false";
1968
1942
  this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
1969
1943
  this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
1970
- this.headers.failureHeaders["Feature-Set"] = WORKFLOW_FEATURE_SET;
1944
+ this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
1971
1945
  if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
1972
1946
  this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
1973
1947
  }
@@ -1975,42 +1949,6 @@ var WorkflowHeaders = class {
1975
1949
  this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
1976
1950
  }
1977
1951
  }
1978
- /**
1979
- * Applies the step-level settings of a step to the message.
1980
- *
1981
- * These headers configure the message itself, so that QStash uses them
1982
- * instead of the settings the run was triggered with. The delivery of
1983
- * the message is what executes the step, which is how a step's
1984
- * settings reach it.
1985
- *
1986
- * When any setting is present the feature set is extended with
1987
- * `WF_StepConfig`, which is what tells QStash to keep them. QStash
1988
- * reports that back on the delivery as `Upstash-Workflow-Step-Config`.
1989
- */
1990
- addStepSettings() {
1991
- if (!this.stepSettings) {
1992
- return;
1993
- }
1994
- const headers = {};
1995
- if (this.stepSettings.flowControl) {
1996
- const { flowControlKey, flowControlValue } = prepareFlowControl(
1997
- this.stepSettings.flowControl
1998
- );
1999
- headers[FLOW_CONTROL_KEY_HEADER] = flowControlKey;
2000
- headers[FLOW_CONTROL_VALUE_HEADER] = flowControlValue;
2001
- }
2002
- if (this.stepSettings.retries !== void 0) {
2003
- headers[RETRIES_HEADER] = this.stepSettings.retries.toString();
2004
- }
2005
- if (this.stepSettings.retryDelay) {
2006
- headers[RETRY_DELAY_HEADER] = this.stepSettings.retryDelay;
2007
- }
2008
- if (Object.keys(headers).length === 0) {
2009
- return;
2010
- }
2011
- headers[WORKFLOW_FEATURE_HEADER] = `${WORKFLOW_FEATURE_SET},${WORKFLOW_STEP_CONFIG_FEATURE}`;
2012
- this.headers.stepSettingsHeaders = headers;
2013
- }
2014
1952
  addContentType() {
2015
1953
  if (this.workflowConfig.useJSONContent) {
2016
1954
  this.headers.rawHeaders["content-type"] = "application/json";
@@ -2024,17 +1962,14 @@ var WorkflowHeaders = class {
2024
1962
  return contentType;
2025
1963
  }
2026
1964
  prefixHeaders(contentType) {
2027
- const { rawHeaders, workflowHeaders, failureHeaders, stepSettingsHeaders } = this.headers;
1965
+ const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
2028
1966
  const isCall = this.stepInfo?.lazyStep.stepType === "Call";
2029
1967
  return {
2030
1968
  headers: {
2031
1969
  ...rawHeaders,
2032
1970
  ...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
2033
1971
  ...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
2034
- ...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {},
2035
- // last, so they override the run's settings above. Never
2036
- // prefixed: they configure this message, not its callback.
2037
- ...stepSettingsHeaders
1972
+ ...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
2038
1973
  },
2039
1974
  contentType
2040
1975
  };
@@ -2085,16 +2020,16 @@ var submitParallelSteps = async ({
2085
2020
  info: `Submitting ${planSteps.length} parallel steps.`
2086
2021
  });
2087
2022
  const result = await context.qstashClient.batch(
2088
- planSteps.map((planStep, index) => {
2023
+ planSteps.map((planStep) => {
2089
2024
  const { headers } = getHeaders({
2090
2025
  initHeaderValue: "false",
2091
2026
  workflowConfig: {
2092
2027
  workflowRunId: context.workflowRunId,
2093
2028
  workflowUrl: context.url,
2029
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
2094
2030
  telemetry
2095
2031
  },
2096
- invokeCount,
2097
- stepSettings: steps[index].stepSettings
2032
+ invokeCount
2098
2033
  });
2099
2034
  return {
2100
2035
  headers,
@@ -2113,38 +2048,31 @@ var submitParallelSteps = async ({
2113
2048
  }
2114
2049
  throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
2115
2050
  };
2116
- var executeStep = async ({
2051
+ var submitSingleStep = async ({
2052
+ context,
2117
2053
  lazyStep,
2118
2054
  stepId,
2055
+ invokeCount,
2119
2056
  concurrency,
2057
+ telemetry,
2058
+ dispatchDebug,
2120
2059
  dispatchLifecycle
2121
2060
  }) => {
2122
2061
  await dispatchLifecycle("beforeExecution", {
2123
2062
  stepName: lazyStep.stepName
2124
2063
  });
2064
+ let resultStep;
2125
2065
  try {
2126
- return await lazyStep.getResultStep(concurrency, stepId);
2066
+ resultStep = await lazyStep.getResultStep(concurrency, stepId);
2127
2067
  } catch (error) {
2128
2068
  attachStepNameToError(error, lazyStep.stepName);
2129
2069
  throw error;
2130
2070
  }
2131
- };
2132
- var submitStepResult = async ({
2133
- context,
2134
- lazyStep,
2135
- resultStep,
2136
- invokeCount,
2137
- concurrency,
2138
- telemetry,
2139
- dispatchDebug,
2140
- nextStepSettings
2141
- }) => {
2142
2071
  const { headers } = lazyStep.getHeaders({
2143
2072
  context,
2144
2073
  step: resultStep,
2145
2074
  invokeCount,
2146
- telemetry,
2147
- stepSettings: nextStepSettings
2075
+ telemetry
2148
2076
  });
2149
2077
  const body = lazyStep.getBody({
2150
2078
  context,
@@ -2169,138 +2097,6 @@ var submitStepResult = async ({
2169
2097
  }
2170
2098
  return resultStep;
2171
2099
  };
2172
- var publishStepConfigRequest = async ({
2173
- context,
2174
- lazyStep,
2175
- targetStep,
2176
- invokeCount,
2177
- telemetry,
2178
- dispatchDebug
2179
- }) => {
2180
- const { headers } = getHeaders({
2181
- initHeaderValue: "false",
2182
- workflowConfig: {
2183
- workflowRunId: context.workflowRunId,
2184
- workflowUrl: context.url,
2185
- telemetry
2186
- },
2187
- invokeCount,
2188
- stepSettings: lazyStep.stepSettings
2189
- });
2190
- await dispatchDebug("onInfo", {
2191
- info: `Step "${lazyStep.stepName}" (${targetStep}) has step-level settings which the current delivery does not carry. Requesting a delivery which does.`
2192
- });
2193
- const result = await context.qstashClient.publishJSON({
2194
- headers: {
2195
- ...headers,
2196
- "Upstash-Workflow-CallType": WORKFLOW_STEP_CONFIG_CALL_TYPE
2197
- },
2198
- method: "POST",
2199
- body: { targetStep, invokeCount },
2200
- url: context.url,
2201
- contentBasedDeduplication: true
2202
- });
2203
- if (result?.deduplicated) {
2204
- await dispatchDebug("onWarning", {
2205
- warning: `A step config request for step "${lazyStep.stepName}" (${targetStep}) was already published and this one was deduplicated. Expected when the delivery which published it is being retried; otherwise the run stops here.`
2206
- });
2207
- } else if (result?.messageId) {
2208
- await dispatchDebug("onInfo", {
2209
- info: `Requested a delivery under the settings of step "${lazyStep.stepName}" with messageId: ${result.messageId}.`
2210
- });
2211
- }
2212
- };
2213
-
2214
- // src/qstash/step-config.ts
2215
- var DEFAULT_PARALLELISM = 0;
2216
- var DEFAULT_RATE = 0;
2217
- var DEFAULT_PERIOD_SECONDS = 1;
2218
- var SECONDS_PER_UNIT = {
2219
- s: 1,
2220
- m: 60,
2221
- h: 60 * 60,
2222
- d: 24 * 60 * 60
2223
- };
2224
- var durationToSeconds = (duration) => {
2225
- if (typeof duration === "number") {
2226
- return Number.isFinite(duration) ? duration : void 0;
2227
- }
2228
- const match = /^(\d+)([smhd])$/.exec(duration.trim());
2229
- if (match) {
2230
- return Number(match[1]) * SECONDS_PER_UNIT[match[2]];
2231
- }
2232
- const seconds = Number(duration.trim());
2233
- return Number.isFinite(seconds) ? seconds : void 0;
2234
- };
2235
- var normalizeFlowControl = (flowControl) => {
2236
- const period = flowControl.period === void 0 ? void 0 : durationToSeconds(flowControl.period);
2237
- return {
2238
- key: flowControl.key,
2239
- parallelism: flowControl.parallelism ?? DEFAULT_PARALLELISM,
2240
- rate: flowControl.rate ?? flowControl.ratePerSecond ?? DEFAULT_RATE,
2241
- period: period ?? DEFAULT_PERIOD_SECONDS
2242
- };
2243
- };
2244
- var parseFlowControlHeaders = (key, value) => {
2245
- if (!key) {
2246
- return void 0;
2247
- }
2248
- const flowControl = {
2249
- key,
2250
- parallelism: DEFAULT_PARALLELISM,
2251
- rate: DEFAULT_RATE,
2252
- period: DEFAULT_PERIOD_SECONDS
2253
- };
2254
- for (const entry of (value ?? "").split(",")) {
2255
- const [name, rawValue] = entry.split("=").map((part) => part.trim());
2256
- if (!name || rawValue === void 0) {
2257
- continue;
2258
- }
2259
- switch (name) {
2260
- case "parallelism": {
2261
- flowControl.parallelism = Number(rawValue);
2262
- break;
2263
- }
2264
- case "rate": {
2265
- flowControl.rate = Number(rawValue);
2266
- break;
2267
- }
2268
- case "period": {
2269
- flowControl.period = durationToSeconds(rawValue) ?? DEFAULT_PERIOD_SECONDS;
2270
- break;
2271
- }
2272
- }
2273
- }
2274
- return flowControl;
2275
- };
2276
- var getEffectiveConfig = (headers) => {
2277
- const retriesHeader = headers.get(RETRIES_HEADER);
2278
- return {
2279
- flowControl: parseFlowControlHeaders(
2280
- headers.get(FLOW_CONTROL_KEY_HEADER),
2281
- headers.get(FLOW_CONTROL_VALUE_HEADER)
2282
- ),
2283
- retries: retriesHeader === null ? void 0 : Number(retriesHeader),
2284
- retryDelay: headers.get(RETRY_DELAY_HEADER) ?? void 0,
2285
- hasStepConfig: headers.get(WORKFLOW_STEP_CONFIG_HEADER) === "true"
2286
- };
2287
- };
2288
- var describeStepSettingsMismatch = (stepSettings, effectiveConfig) => {
2289
- if (stepSettings.flowControl) {
2290
- const wanted = normalizeFlowControl(stepSettings.flowControl);
2291
- const applied = effectiveConfig.flowControl;
2292
- if (!applied || applied.key !== wanted.key || applied.parallelism !== wanted.parallelism || applied.rate !== wanted.rate || applied.period !== wanted.period) {
2293
- return `flow control: expected ${JSON.stringify(wanted)}, delivery has ${JSON.stringify(applied)}`;
2294
- }
2295
- }
2296
- if (stepSettings.retries !== void 0 && effectiveConfig.retries !== void 0 && stepSettings.retries !== effectiveConfig.retries) {
2297
- return `retries: expected ${stepSettings.retries}, delivery has ${effectiveConfig.retries}`;
2298
- }
2299
- if (stepSettings.retryDelay !== void 0 && stepSettings.retryDelay !== effectiveConfig.retryDelay) {
2300
- return `retry delay: expected '${stepSettings.retryDelay}', delivery has '${effectiveConfig.retryDelay ?? ""}'`;
2301
- }
2302
- return void 0;
2303
- };
2304
2100
 
2305
2101
  // src/context/auto-executor.ts
2306
2102
  var AutoExecutor = class _AutoExecutor {
@@ -2317,25 +2113,6 @@ var AutoExecutor = class _AutoExecutor {
2317
2113
  stepCount = 0;
2318
2114
  planStepCount = 0;
2319
2115
  executingStep = false;
2320
- /**
2321
- * an executed step whose result hasn't been submitted to QStash yet.
2322
- *
2323
- * Set when a step which can produce its result in-process executes. The
2324
- * submission waits so that the route function can continue and reveal
2325
- * what comes next: if that is a single step with step-level settings,
2326
- * the settings ride on this submission and no step config request is
2327
- * needed, since the delivery of the submission is what executes it.
2328
- *
2329
- * Flushed when the next step is reached (see `addStep`) or when the
2330
- * route function ends (see `flushPendingStep` in `serve`).
2331
- */
2332
- pendingStep;
2333
- /**
2334
- * configuration QStash applied to the delivery being handled. A step
2335
- * with step-level settings is settled by comparing its settings against
2336
- * this.
2337
- */
2338
- effectiveConfig;
2339
2116
  /**
2340
2117
  * @param context workflow context
2341
2118
  * @param steps list of steps
@@ -2343,17 +2120,14 @@ var AutoExecutor = class _AutoExecutor {
2343
2120
  * @param dispatchLifecycle lifecycle event dispatcher
2344
2121
  * @param telemetry optional telemetry information
2345
2122
  * @param invokeCount optional invoke count
2346
- * @param effectiveConfig configuration QStash applied to the delivery
2347
- * being handled
2348
2123
  */
2349
- constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount, effectiveConfig) {
2124
+ constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount) {
2350
2125
  this.context = context;
2351
2126
  this.steps = steps;
2352
2127
  this.dispatchDebug = dispatchDebug;
2353
2128
  this.dispatchLifecycle = dispatchLifecycle;
2354
2129
  this.telemetry = telemetry;
2355
2130
  this.invokeCount = invokeCount ?? 0;
2356
- this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
2357
2131
  this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
2358
2132
  }
2359
2133
  /**
@@ -2388,15 +2162,6 @@ var AutoExecutor = class _AutoExecutor {
2388
2162
  lazyStepList.push(stepInfo);
2389
2163
  const index = this.indexInCurrentList++;
2390
2164
  const requestComplete = this.deferExecution().then(async () => {
2391
- const submitted = await this.submitPendingStep(
2392
- lazyStepList.length === 1 ? lazyStepList[0].stepSettings : void 0
2393
- );
2394
- if (submitted.isErr()) {
2395
- throw submitted.error;
2396
- }
2397
- if (submitted.value.result === "submitted-step") {
2398
- throw submitted.value.abort;
2399
- }
2400
2165
  if (!this.promises.has(lazyStepList)) {
2401
2166
  const promise2 = this.getExecutionPromise(lazyStepList);
2402
2167
  this.promises.set(lazyStepList, promise2);
@@ -2450,100 +2215,18 @@ var AutoExecutor = class _AutoExecutor {
2450
2215
  }
2451
2216
  return parsedOut;
2452
2217
  }
2453
- const mismatch = lazyStep.stepSettings ? describeStepSettingsMismatch(lazyStep.stepSettings, this.effectiveConfig) : void 0;
2454
- if (mismatch) {
2455
- if (!this.effectiveConfig.hasStepConfig) {
2456
- await publishStepConfigRequest({
2457
- context: this.context,
2458
- lazyStep,
2459
- targetStep: this.stepCount,
2460
- invokeCount: this.invokeCount,
2461
- telemetry: this.telemetry,
2462
- dispatchDebug: this.dispatchDebug
2463
- });
2464
- throw new WorkflowAbort(lazyStep.stepName);
2465
- } else {
2466
- await this.dispatchDebug("onWarning", {
2467
- warning: `The step-level settings of step '${lazyStep.stepName}' were published but are not recognized on the delivery which carries them (${mismatch}). Executing the step with the settings of the delivery instead. This is a bug in @upstash/workflow, please report it.`
2468
- });
2469
- }
2470
- }
2471
- const resultStep = await executeStep({
2472
- lazyStep,
2473
- stepId: this.stepCount,
2474
- concurrency: NO_CONCURRENCY,
2475
- dispatchLifecycle: this.dispatchLifecycle
2476
- });
2477
- if (lazyStep.supportsDeferredSubmission) {
2478
- this.pendingStep = { status: "held", lazyStep, resultStep };
2479
- return lazyStep.parseOut({
2480
- ...resultStep,
2481
- out: resultStep.out === void 0 ? void 0 : JSON.stringify(resultStep.out)
2482
- });
2483
- }
2484
- await submitStepResult({
2218
+ const resultStep = await submitSingleStep({
2485
2219
  context: this.context,
2486
2220
  lazyStep,
2487
- resultStep,
2221
+ stepId: this.stepCount,
2488
2222
  invokeCount: this.invokeCount,
2489
- concurrency: NO_CONCURRENCY,
2223
+ concurrency: 1,
2490
2224
  telemetry: this.telemetry,
2491
- dispatchDebug: this.dispatchDebug
2225
+ dispatchDebug: this.dispatchDebug,
2226
+ dispatchLifecycle: this.dispatchLifecycle
2492
2227
  });
2493
2228
  throw new WorkflowAbort(lazyStep.stepName, resultStep);
2494
2229
  }
2495
- /**
2496
- * Submits the result of the step being held, if there is one, and
2497
- * returns the abort the caller has to throw to end the invocation.
2498
- *
2499
- * Returns undefined when no step is being held, which is the caller's
2500
- * signal that there is nothing to end. Throws instead of returning
2501
- * when the submission itself fails, so that error reaches the caller
2502
- * rather than an abort claiming the step was submitted.
2503
- *
2504
- * Callers which arrive while a submission is already under way (the
2505
- * steps of a parallel group, added together) wait for that one and get
2506
- * the same abort, rather than submitting a second time.
2507
- *
2508
- * The next step's settings are attached whenever it has any, without
2509
- * checking them against the current delivery: the executor only knows
2510
- * the configuration of the delivery in hand, which inside a
2511
- * delivery is the *previous* step's, so such a check would be wrong
2512
- * exactly when two steps with settings follow each other.
2513
- *
2514
- * @param nextStepSettings step-level settings of the next step
2515
- * @returns `submitted-step` with the abort which ends this invocation,
2516
- * or `no-pending-step` when nothing was held and there is nothing to
2517
- * end
2518
- */
2519
- async submitPendingStep(nextStepSettings) {
2520
- if (!this.pendingStep) {
2521
- return ok({ result: "no-pending-step" });
2522
- }
2523
- try {
2524
- if (this.pendingStep.status === "submitting") {
2525
- return ok({ result: "submitted-step", abort: await this.pendingStep.submitted });
2526
- }
2527
- const { lazyStep, resultStep } = this.pendingStep;
2528
- const submitted = (async () => {
2529
- await submitStepResult({
2530
- context: this.context,
2531
- lazyStep,
2532
- resultStep,
2533
- invokeCount: this.invokeCount,
2534
- concurrency: NO_CONCURRENCY,
2535
- telemetry: this.telemetry,
2536
- dispatchDebug: this.dispatchDebug,
2537
- nextStepSettings
2538
- });
2539
- return new WorkflowAbort(lazyStep.stepName, resultStep);
2540
- })();
2541
- this.pendingStep = { status: "submitting", submitted };
2542
- return ok({ result: "submitted-step", abort: await submitted });
2543
- } catch (error) {
2544
- return err(error);
2545
- }
2546
- }
2547
2230
  /**
2548
2231
  * Runs steps in parallel.
2549
2232
  *
@@ -2592,20 +2275,15 @@ var AutoExecutor = class _AutoExecutor {
2592
2275
  validateStep(parallelSteps[stepIndex], planStep);
2593
2276
  try {
2594
2277
  const parallelStep = parallelSteps[stepIndex];
2595
- const resultStep = await executeStep({
2596
- lazyStep: parallelStep,
2597
- stepId: planStep.targetStep,
2598
- concurrency: parallelSteps.length,
2599
- dispatchLifecycle: this.dispatchLifecycle
2600
- });
2601
- await submitStepResult({
2278
+ const resultStep = await submitSingleStep({
2602
2279
  context: this.context,
2603
2280
  lazyStep: parallelStep,
2604
- resultStep,
2281
+ stepId: planStep.targetStep,
2605
2282
  invokeCount: this.invokeCount,
2606
2283
  concurrency: parallelSteps.length,
2607
2284
  telemetry: this.telemetry,
2608
- dispatchDebug: this.dispatchDebug
2285
+ dispatchDebug: this.dispatchDebug,
2286
+ dispatchLifecycle: this.dispatchLifecycle
2609
2287
  });
2610
2288
  throw new WorkflowAbort(parallelStep.stepName, resultStep);
2611
2289
  } catch (error) {
@@ -3154,46 +2832,6 @@ var WorkflowContext = class {
3154
2832
  * `1` on the first retry, `2` on the second, and so on.
3155
2833
  */
3156
2834
  retried;
3157
- /**
3158
- * Configuration QStash applied to the request being handled.
3159
- *
3160
- * This is the configuration of *this delivery*, not of the run: it is
3161
- * the configuration the run was triggered with, except inside the
3162
- * delivery which executes a step that has step-level settings, where
3163
- * it is that step's settings. The fields below expose it; the executor
3164
- * gets it directly rather than reaching through the context.
3165
- */
3166
- effectiveConfig;
3167
- /**
3168
- * Flow control QStash applied to the request being handled, if any.
3169
- *
3170
- * This is the flow control of the request in hand, which is the one the
3171
- * run was triggered with except inside a step carrying its own.
3172
- */
3173
- get flowControl() {
3174
- return this.effectiveConfig.flowControl;
3175
- }
3176
- /**
3177
- * Retry limit QStash applied to the request being handled, if it
3178
- * reported one.
3179
- *
3180
- * Not to be confused with {@link retried}, which is how many retries
3181
- * have already happened.
3182
- *
3183
- * @see {@link flowControl} for what "the request being handled" means
3184
- */
3185
- get retries() {
3186
- return this.effectiveConfig.retries;
3187
- }
3188
- /**
3189
- * Retry delay expression QStash applied to the request being handled,
3190
- * if any.
3191
- *
3192
- * @see {@link flowControl} for what "the request being handled" means
3193
- */
3194
- get retryDelay() {
3195
- return this.effectiveConfig.retryDelay;
3196
- }
3197
2835
  constructor({
3198
2836
  qstashClient,
3199
2837
  workflowRunId,
@@ -3207,8 +2845,7 @@ var WorkflowContext = class {
3207
2845
  invokeCount,
3208
2846
  label,
3209
2847
  retried,
3210
- middlewareManager,
3211
- effectiveConfig
2848
+ middlewareManager
3212
2849
  }) {
3213
2850
  this.qstashClient = qstashClient;
3214
2851
  this.workflowRunId = workflowRunId;
@@ -3220,7 +2857,6 @@ var WorkflowContext = class {
3220
2857
  this.env = env ?? {};
3221
2858
  this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
3222
2859
  this.retried = retried ?? 0;
3223
- this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
3224
2860
  const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
3225
2861
  middlewareManagerInstance.assignContext(this);
3226
2862
  this.executor = new AutoExecutor(
@@ -3229,8 +2865,7 @@ var WorkflowContext = class {
3229
2865
  middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
3230
2866
  middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
3231
2867
  telemetry,
3232
- invokeCount,
3233
- this.effectiveConfig
2868
+ invokeCount
3234
2869
  );
3235
2870
  }
3236
2871
  /**
@@ -3256,35 +2891,13 @@ var WorkflowContext = class {
3256
2891
  * ])
3257
2892
  * ```
3258
2893
  *
3259
- * Step-level settings can be passed as a third argument, overriding
3260
- * the settings the workflow run was triggered with for this step only:
3261
- *
3262
- * ```typescript
3263
- * const result = await context.run(
3264
- * "step 1",
3265
- * () => {
3266
- * return "result"
3267
- * },
3268
- * {
3269
- * flowControl: { key: "custom-key", parallelism: 3 },
3270
- * retries: 5,
3271
- * }
3272
- * )
3273
- * ```
3274
- *
3275
2894
  * @param stepName name of the step
3276
2895
  * @param stepFunction step function to be executed
3277
- * @param stepSettings step-level settings for this step
3278
2896
  * @returns result of the step function
3279
2897
  */
3280
- async run(stepName, stepFunction, stepSettings) {
3281
- if (stepSettings) {
3282
- validateFlowControl(stepSettings.flowControl);
3283
- }
2898
+ async run(stepName, stepFunction) {
3284
2899
  const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
3285
- return await this.addStep(
3286
- new LazyFunctionStep(this, stepName, wrappedStepFunction, stepSettings)
3287
- );
2900
+ return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
3288
2901
  }
3289
2902
  /**
3290
2903
  * Stops the execution for the duration provided.
@@ -3507,7 +3120,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3507
3120
  *
3508
3121
  * @param routeFunction
3509
3122
  */
3510
- static async tryAuthentication(routeFunction, context, effectiveConfig) {
3123
+ static async tryAuthentication(routeFunction, context) {
3511
3124
  const disabledContext = new _DisabledWorkflowContext({
3512
3125
  qstashClient: new import_qstash10.Client({
3513
3126
  baseUrl: "disabled-client",
@@ -3522,10 +3135,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3522
3135
  initialPayload: context.requestPayload,
3523
3136
  env: context.env,
3524
3137
  label: context.labels,
3525
- retried: context.retried,
3526
- // the route function runs for real here until it reaches a step, so
3527
- // it observes the same configuration as it will on the real context
3528
- effectiveConfig
3138
+ retried: context.retried
3529
3139
  });
3530
3140
  try {
3531
3141
  await routeFunction(disabledContext);
@@ -3742,7 +3352,6 @@ var handleFailure = async ({
3742
3352
  }
3743
3353
  const userHeaders = recreateUserHeaders(request.headers);
3744
3354
  const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
3745
- const effectiveConfig = getEffectiveConfig(request.headers);
3746
3355
  const workflowContext = new WorkflowContext({
3747
3356
  qstashClient,
3748
3357
  workflowRunId,
@@ -3756,13 +3365,11 @@ var handleFailure = async ({
3756
3365
  label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
3757
3366
  retried,
3758
3367
  workflowRunCreatedAt: workflowCreatedAt,
3759
- middlewareManager: void 0,
3760
- effectiveConfig
3368
+ middlewareManager: void 0
3761
3369
  });
3762
3370
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
3763
3371
  routeFunction,
3764
- workflowContext,
3765
- effectiveConfig
3372
+ workflowContext
3766
3373
  );
3767
3374
  if (authCheck.isErr()) {
3768
3375
  await dispatchDebug?.("onError", {
@@ -4251,28 +3858,26 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
4251
3858
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
4252
3859
  const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
4253
3860
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
4254
- const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
4255
- const effectiveConfig = getEffectiveConfig(request.headers);
3861
+ const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3862
+ const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
4256
3863
  const workflowContext = new WorkflowContext({
4257
3864
  qstashClient: regionalClient,
4258
3865
  workflowRunId,
4259
3866
  initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
4260
3867
  headers: recreateUserHeaders(request.headers),
4261
3868
  steps,
4262
- effectiveConfig,
4263
3869
  url: workflowUrl,
4264
3870
  env,
4265
3871
  telemetry,
4266
3872
  invokeCount,
4267
3873
  label,
4268
3874
  retried,
4269
- workflowRunCreatedAt: Number(workflowRunCreatedAt),
3875
+ workflowRunCreatedAt,
4270
3876
  middlewareManager
4271
3877
  });
4272
3878
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
4273
3879
  routeFunction,
4274
- workflowContext,
4275
- effectiveConfig
3880
+ workflowContext
4276
3881
  );
4277
3882
  if (authCheck.isErr()) {
4278
3883
  throw authCheck.error;
@@ -4309,23 +3914,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
4309
3914
  if (steps.length === 1) {
4310
3915
  await middlewareManager.dispatchLifecycle("runStarted", {});
4311
3916
  }
4312
- let outcome;
4313
- try {
4314
- outcome = { ran: true, result: await routeFunction(workflowContext) };
4315
- } catch (error) {
4316
- outcome = { ran: false, error };
4317
- }
4318
- const submitted = await flushPendingStep(workflowContext);
4319
- if (submitted.isErr()) {
4320
- throw submitted.error;
4321
- }
4322
- if (submitted.value.result === "submitted-step") {
4323
- throw submitted.value.abort;
4324
- }
4325
- if (!outcome.ran) {
4326
- throw outcome.error;
4327
- }
4328
- return outcome.result;
3917
+ return await routeFunction(workflowContext);
4329
3918
  },
4330
3919
  onCleanup: async (result2) => {
4331
3920
  await middlewareManager.dispatchLifecycle("runCompleted", {