@upstash/workflow 0.2.8-rc-invoke → 0.2.9

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/h3.js CHANGED
@@ -397,6 +397,7 @@ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
397
397
  var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
398
398
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
399
399
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
400
+ var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
400
401
  var WORKFLOW_PROTOCOL_VERSION = "1";
401
402
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
402
403
  var DEFAULT_CONTENT_TYPE = "application/json";
@@ -578,8 +579,9 @@ var LazyCallStep = class extends BaseLazyStep {
578
579
  headers;
579
580
  retries;
580
581
  timeout;
582
+ flowControl;
581
583
  stepType = "Call";
582
- constructor(stepName, url, method, body, headers, retries, timeout) {
584
+ constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
583
585
  super(stepName);
584
586
  this.url = url;
585
587
  this.method = method;
@@ -587,6 +589,7 @@ var LazyCallStep = class extends BaseLazyStep {
587
589
  this.headers = headers;
588
590
  this.retries = retries;
589
591
  this.timeout = timeout;
592
+ this.flowControl = flowControl;
590
593
  }
591
594
  getPlanStep(concurrent, targetStep) {
592
595
  return {
@@ -657,14 +660,22 @@ var LazyNotifyStep = class extends LazyFunctionStep {
657
660
  var LazyInvokeStep = class extends BaseLazyStep {
658
661
  stepType = "Invoke";
659
662
  params;
660
- constructor(stepName, { workflow, body, headers = {}, workflowRunId, retries }) {
663
+ constructor(stepName, {
664
+ workflow,
665
+ body,
666
+ headers = {},
667
+ workflowRunId,
668
+ retries,
669
+ flowControl
670
+ }) {
661
671
  super(stepName);
662
672
  this.params = {
663
673
  workflow,
664
674
  body,
665
675
  headers,
666
676
  workflowRunId: getWorkflowRunId(workflowRunId),
667
- retries
677
+ retries,
678
+ flowControl
668
679
  };
669
680
  }
670
681
  getPlanStep(concurrent, targetStep) {
@@ -1123,7 +1134,8 @@ var triggerFirstInvocation = async ({
1123
1134
  workflowContext,
1124
1135
  useJSONContent,
1125
1136
  telemetry: telemetry2,
1126
- debug
1137
+ debug,
1138
+ invokeCount
1127
1139
  }) => {
1128
1140
  const { headers } = getHeaders({
1129
1141
  initHeaderValue: "true",
@@ -1132,7 +1144,9 @@ var triggerFirstInvocation = async ({
1132
1144
  userHeaders: workflowContext.headers,
1133
1145
  failureUrl: workflowContext.failureUrl,
1134
1146
  retries: workflowContext.retries,
1135
- telemetry: telemetry2
1147
+ telemetry: telemetry2,
1148
+ invokeCount,
1149
+ flowControl: workflowContext.flowControl
1136
1150
  });
1137
1151
  if (workflowContext.headers.get("content-type")) {
1138
1152
  headers["content-type"] = workflowContext.headers.get("content-type");
@@ -1238,6 +1252,7 @@ var handleThirdPartyCallResult = async ({
1238
1252
  failureUrl,
1239
1253
  retries,
1240
1254
  telemetry: telemetry2,
1255
+ flowControl,
1241
1256
  debug
1242
1257
  }) => {
1243
1258
  try {
@@ -1285,6 +1300,7 @@ ${atob(callbackMessage.body ?? "")}`
1285
1300
  const stepType = request.headers.get("Upstash-Workflow-StepType");
1286
1301
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
1287
1302
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
1303
+ const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
1288
1304
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
1289
1305
  throw new Error(
1290
1306
  `Missing info in callback message source header: ${JSON.stringify({
@@ -1305,7 +1321,9 @@ ${atob(callbackMessage.body ?? "")}`
1305
1321
  userHeaders,
1306
1322
  failureUrl,
1307
1323
  retries,
1308
- telemetry: telemetry2
1324
+ telemetry: telemetry2,
1325
+ invokeCount: Number(invokeCount),
1326
+ flowControl
1309
1327
  });
1310
1328
  const callResponse = {
1311
1329
  status: callbackMessage.status,
@@ -1361,7 +1379,10 @@ var getHeaders = ({
1361
1379
  step,
1362
1380
  callRetries,
1363
1381
  callTimeout,
1364
- telemetry: telemetry2
1382
+ telemetry: telemetry2,
1383
+ invokeCount,
1384
+ flowControl,
1385
+ callFlowControl
1365
1386
  }) => {
1366
1387
  const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1367
1388
  const baseHeaders = {
@@ -1373,6 +1394,9 @@ var getHeaders = ({
1373
1394
  "content-type": contentType,
1374
1395
  ...telemetry2 ? getTelemetryHeaders(telemetry2) : {}
1375
1396
  };
1397
+ if (invokeCount !== void 0 && !step?.callUrl) {
1398
+ baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
1399
+ }
1376
1400
  if (!step?.callUrl) {
1377
1401
  baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1378
1402
  }
@@ -1381,13 +1405,23 @@ var getHeaders = ({
1381
1405
  }
1382
1406
  if (failureUrl) {
1383
1407
  baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
1408
+ baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
1409
+ baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
1410
+ baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
1411
+ baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
1412
+ baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
1413
+ if (retries !== void 0) {
1414
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1415
+ }
1416
+ if (flowControl) {
1417
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1418
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
1419
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
1420
+ }
1384
1421
  if (!step?.callUrl) {
1385
1422
  baseHeaders["Upstash-Failure-Callback"] = failureUrl;
1386
1423
  }
1387
1424
  }
1388
- if (step?.stepType === "Invoke") {
1389
- baseHeaders["upstash-workflow-invoke"] = "true";
1390
- }
1391
1425
  if (step?.callUrl) {
1392
1426
  baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
1393
1427
  baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
@@ -1395,9 +1429,26 @@ var getHeaders = ({
1395
1429
  baseHeaders["Upstash-Callback-Retries"] = retries.toString();
1396
1430
  baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1397
1431
  }
1398
- } else if (retries !== void 0) {
1399
- baseHeaders["Upstash-Retries"] = retries.toString();
1400
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1432
+ if (callFlowControl) {
1433
+ const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
1434
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1435
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1436
+ }
1437
+ if (flowControl) {
1438
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1439
+ baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
1440
+ baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
1441
+ }
1442
+ } else {
1443
+ if (flowControl) {
1444
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1445
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1446
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1447
+ }
1448
+ if (retries !== void 0) {
1449
+ baseHeaders["Upstash-Retries"] = retries.toString();
1450
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1451
+ }
1401
1452
  }
1402
1453
  if (userHeaders) {
1403
1454
  for (const header of userHeaders.keys()) {
@@ -1432,6 +1483,7 @@ var getHeaders = ({
1432
1483
  "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
1433
1484
  "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1434
1485
  "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1486
+ [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
1435
1487
  "Upstash-Workflow-CallType": "toCallback"
1436
1488
  }
1437
1489
  };
@@ -1489,9 +1541,159 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
1489
1541
  );
1490
1542
  }
1491
1543
  };
1544
+ var prepareFlowControl = (flowControl) => {
1545
+ const parallelism = flowControl.parallelism?.toString();
1546
+ const rate = flowControl.ratePerSecond?.toString();
1547
+ const controlValue = [
1548
+ parallelism ? `parallelism=${parallelism}` : void 0,
1549
+ rate ? `rate=${rate}` : void 0
1550
+ ].filter(Boolean);
1551
+ if (controlValue.length === 0) {
1552
+ throw new import_qstash3.QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
1553
+ }
1554
+ return {
1555
+ flowControlKey: flowControl.key,
1556
+ flowControlValue: controlValue.join(", ")
1557
+ };
1558
+ };
1492
1559
 
1493
1560
  // src/context/auto-executor.ts
1494
1561
  var import_qstash4 = require("@upstash/qstash");
1562
+
1563
+ // src/serve/serve-many.ts
1564
+ var getWorkflowId = (url) => {
1565
+ const components = url.split("/");
1566
+ const lastComponent = components[components.length - 1];
1567
+ return lastComponent.split("?")[0];
1568
+ };
1569
+ var serveManyBase = ({
1570
+ workflows,
1571
+ getUrl: getUrl2,
1572
+ serveMethod,
1573
+ options
1574
+ }) => {
1575
+ const workflowIds = [];
1576
+ const workflowMap = Object.fromEntries(
1577
+ Object.entries(workflows).map((workflow) => {
1578
+ const workflowId = workflow[0];
1579
+ if (workflowIds.includes(workflowId)) {
1580
+ throw new WorkflowError(
1581
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
1582
+ );
1583
+ }
1584
+ if (workflowId.includes("/")) {
1585
+ throw new WorkflowError(
1586
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
1587
+ );
1588
+ }
1589
+ workflowIds.push(workflowId);
1590
+ workflow[1].workflowId = workflowId;
1591
+ workflow[1].options = {
1592
+ ...options,
1593
+ ...workflow[1].options
1594
+ };
1595
+ const params = [workflow[1].routeFunction, workflow[1].options];
1596
+ const handler = serveMethod(...params);
1597
+ return [workflowId, handler];
1598
+ })
1599
+ );
1600
+ return {
1601
+ handler: async (...params) => {
1602
+ const url = getUrl2(...params);
1603
+ const pickedWorkflowId = getWorkflowId(url);
1604
+ if (!pickedWorkflowId) {
1605
+ return new Response(
1606
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
1607
+ {
1608
+ status: 404
1609
+ }
1610
+ );
1611
+ }
1612
+ const workflow = workflowMap[pickedWorkflowId];
1613
+ if (!workflow) {
1614
+ return new Response(
1615
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
1616
+ {
1617
+ status: 404
1618
+ }
1619
+ );
1620
+ }
1621
+ return await workflow(...params);
1622
+ }
1623
+ };
1624
+ };
1625
+ var invokeWorkflow = async ({
1626
+ settings,
1627
+ invokeStep,
1628
+ context,
1629
+ invokeCount,
1630
+ telemetry: telemetry2
1631
+ }) => {
1632
+ const {
1633
+ body,
1634
+ workflow,
1635
+ headers = {},
1636
+ workflowRunId = getWorkflowRunId(),
1637
+ retries,
1638
+ flowControl
1639
+ } = settings;
1640
+ const { workflowId } = workflow;
1641
+ const {
1642
+ retries: workflowRetries,
1643
+ failureFunction,
1644
+ failureUrl,
1645
+ useJSONContent,
1646
+ flowControl: workflowFlowControl
1647
+ } = workflow.options;
1648
+ if (!workflowId) {
1649
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1650
+ }
1651
+ const { headers: invokerHeaders } = getHeaders({
1652
+ initHeaderValue: "false",
1653
+ workflowRunId: context.workflowRunId,
1654
+ workflowUrl: context.url,
1655
+ userHeaders: context.headers,
1656
+ failureUrl: context.failureUrl,
1657
+ retries: context.retries,
1658
+ telemetry: telemetry2,
1659
+ invokeCount,
1660
+ flowControl: context.flowControl
1661
+ });
1662
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1663
+ const newUrl = context.url.replace(/[^/]+$/, workflowId);
1664
+ const { headers: triggerHeaders } = getHeaders({
1665
+ initHeaderValue: "true",
1666
+ workflowRunId,
1667
+ workflowUrl: newUrl,
1668
+ userHeaders: new Headers(headers),
1669
+ retries: retries ?? workflowRetries,
1670
+ telemetry: telemetry2,
1671
+ failureUrl: failureFunction ? newUrl : failureUrl,
1672
+ invokeCount: invokeCount + 1,
1673
+ flowControl: flowControl ?? workflowFlowControl
1674
+ });
1675
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1676
+ if (useJSONContent) {
1677
+ triggerHeaders["content-type"] = "application/json";
1678
+ }
1679
+ const request = {
1680
+ body: JSON.stringify(body),
1681
+ headers: Object.fromEntries(
1682
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1683
+ ),
1684
+ workflowRunId,
1685
+ workflowUrl: context.url,
1686
+ step: invokeStep
1687
+ };
1688
+ await context.qstashClient.publish({
1689
+ headers: triggerHeaders,
1690
+ method: "POST",
1691
+ body: JSON.stringify(request),
1692
+ url: newUrl
1693
+ });
1694
+ };
1695
+
1696
+ // src/context/auto-executor.ts
1495
1697
  var AutoExecutor = class _AutoExecutor {
1496
1698
  context;
1497
1699
  promises = /* @__PURE__ */ new WeakMap();
@@ -1500,14 +1702,16 @@ var AutoExecutor = class _AutoExecutor {
1500
1702
  nonPlanStepCount;
1501
1703
  steps;
1502
1704
  indexInCurrentList = 0;
1705
+ invokeCount;
1503
1706
  telemetry;
1504
1707
  stepCount = 0;
1505
1708
  planStepCount = 0;
1506
1709
  executingStep = false;
1507
- constructor(context, steps, telemetry2, debug) {
1710
+ constructor(context, steps, telemetry2, invokeCount, debug) {
1508
1711
  this.context = context;
1509
1712
  this.steps = steps;
1510
1713
  this.telemetry = telemetry2;
1714
+ this.invokeCount = invokeCount ?? 0;
1511
1715
  this.debug = debug;
1512
1716
  this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
1513
1717
  }
@@ -1730,7 +1934,9 @@ var AutoExecutor = class _AutoExecutor {
1730
1934
  step: waitStep,
1731
1935
  failureUrl: this.context.failureUrl,
1732
1936
  retries: this.context.retries,
1733
- telemetry: this.telemetry
1937
+ telemetry: this.telemetry,
1938
+ invokeCount: this.invokeCount,
1939
+ flowControl: this.context.flowControl
1734
1940
  });
1735
1941
  const waitBody = {
1736
1942
  url: this.context.url,
@@ -1758,17 +1964,13 @@ var AutoExecutor = class _AutoExecutor {
1758
1964
  if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1759
1965
  const invokeStep = steps[0];
1760
1966
  const lazyInvokeStep = lazySteps[0];
1761
- await lazyInvokeStep.params.workflow.callback(
1762
- {
1763
- body: lazyInvokeStep.params.body,
1764
- headers: lazyInvokeStep.params.headers,
1765
- workflowRunId: lazyInvokeStep.params.workflowRunId,
1766
- workflow: lazyInvokeStep.params.workflow,
1767
- retries: lazyInvokeStep.params.retries
1768
- },
1967
+ await invokeWorkflow({
1968
+ settings: lazyInvokeStep.params,
1769
1969
  invokeStep,
1770
- this.context
1771
- );
1970
+ context: this.context,
1971
+ invokeCount: this.invokeCount,
1972
+ telemetry: this.telemetry
1973
+ });
1772
1974
  throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1773
1975
  }
1774
1976
  const result = await this.context.qstashClient.batchJSON(
@@ -1784,11 +1986,14 @@ var AutoExecutor = class _AutoExecutor {
1784
1986
  retries: this.context.retries,
1785
1987
  callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
1786
1988
  callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
1787
- telemetry: this.telemetry
1989
+ telemetry: this.telemetry,
1990
+ invokeCount: this.invokeCount,
1991
+ flowControl: this.context.flowControl,
1992
+ callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
1788
1993
  });
1789
1994
  const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1790
1995
  singleStep.out = JSON.stringify(singleStep.out);
1791
- return singleStep.callUrl ? (
1996
+ return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
1792
1997
  // if the step is a third party call, we call the third party
1793
1998
  // url (singleStep.callUrl) and pass information about the workflow
1794
1999
  // in the headers (handled in getHeaders). QStash makes the request
@@ -2088,9 +2293,10 @@ var wrapTools = ({
2088
2293
  return Object.fromEntries(
2089
2294
  Object.entries(tools).map((toolInfo) => {
2090
2295
  const [toolName, tool3] = toolInfo;
2296
+ const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
2091
2297
  const aiSDKTool = convertToAISDKTool(tool3);
2092
2298
  const execute = aiSDKTool.execute;
2093
- if (execute) {
2299
+ if (execute && executeAsStep) {
2094
2300
  const wrappedExecute = (...params) => {
2095
2301
  return context.run(`Run tool ${toolName}`, () => execute(...params));
2096
2302
  };
@@ -2444,6 +2650,11 @@ var WorkflowContext = class {
2444
2650
  * Number of retries
2445
2651
  */
2446
2652
  retries;
2653
+ /**
2654
+ * Settings for controlling the number of active requests
2655
+ * and number of requests per second with the same key.
2656
+ */
2657
+ flowControl;
2447
2658
  constructor({
2448
2659
  qstashClient,
2449
2660
  workflowRunId,
@@ -2455,7 +2666,9 @@ var WorkflowContext = class {
2455
2666
  initialPayload,
2456
2667
  env,
2457
2668
  retries,
2458
- telemetry: telemetry2
2669
+ telemetry: telemetry2,
2670
+ invokeCount,
2671
+ flowControl
2459
2672
  }) {
2460
2673
  this.qstashClient = qstashClient;
2461
2674
  this.workflowRunId = workflowRunId;
@@ -2466,7 +2679,8 @@ var WorkflowContext = class {
2466
2679
  this.requestPayload = initialPayload;
2467
2680
  this.env = env ?? {};
2468
2681
  this.retries = retries ?? DEFAULT_RETRIES;
2469
- this.executor = new AutoExecutor(this, this.steps, telemetry2, debug);
2682
+ this.flowControl = flowControl;
2683
+ this.executor = new AutoExecutor(this, this.steps, telemetry2, invokeCount, debug);
2470
2684
  }
2471
2685
  /**
2472
2686
  * Executes a workflow step
@@ -2568,7 +2782,7 @@ var WorkflowContext = class {
2568
2782
  * }
2569
2783
  */
2570
2784
  async call(stepName, settings) {
2571
- const { url, method = "GET", body, headers = {}, retries = 0, timeout } = settings;
2785
+ const { url, method = "GET", body, headers = {}, retries = 0, timeout, flowControl } = settings;
2572
2786
  const result = await this.addStep(
2573
2787
  new LazyCallStep(
2574
2788
  stepName,
@@ -2577,7 +2791,8 @@ var WorkflowContext = class {
2577
2791
  body,
2578
2792
  headers,
2579
2793
  retries,
2580
- timeout
2794
+ timeout,
2795
+ flowControl
2581
2796
  )
2582
2797
  );
2583
2798
  if (typeof result === "string") {
@@ -2814,7 +3029,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
2814
3029
  failureUrl: context.failureUrl,
2815
3030
  initialPayload: context.requestPayload,
2816
3031
  env: context.env,
2817
- retries: context.retries
3032
+ retries: context.retries,
3033
+ flowControl: context.flowControl
2818
3034
  });
2819
3035
  try {
2820
3036
  await routeFunction(disabledContext);
@@ -2967,7 +3183,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
2967
3183
  };
2968
3184
  }
2969
3185
  };
2970
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, debug) => {
3186
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
2971
3187
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
2972
3188
  return ok("not-failure-callback");
2973
3189
  }
@@ -2979,22 +3195,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
2979
3195
  );
2980
3196
  }
2981
3197
  try {
2982
- const { status, header, body, url, sourceHeader, sourceBody, workflowRunId } = JSON.parse(
2983
- requestPayload
2984
- );
3198
+ const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
2985
3199
  const decodedBody = body ? decodeBase64(body) : "{}";
2986
3200
  const errorPayload = JSON.parse(decodedBody);
2987
3201
  const workflowContext = new WorkflowContext({
2988
3202
  qstashClient,
2989
3203
  workflowRunId,
2990
3204
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
2991
- headers: recreateUserHeaders(new Headers(sourceHeader)),
3205
+ headers: recreateUserHeaders(request.headers),
2992
3206
  steps: [],
2993
3207
  url,
2994
3208
  failureUrl: url,
2995
3209
  debug,
2996
3210
  env,
2997
3211
  retries,
3212
+ flowControl,
2998
3213
  telemetry: void 0
2999
3214
  // not going to make requests in authentication check
3000
3215
  });
@@ -3121,7 +3336,8 @@ var serveBase = (routeFunction, telemetry2, options) => {
3121
3336
  env,
3122
3337
  retries,
3123
3338
  useJSONContent,
3124
- disableTelemetry
3339
+ disableTelemetry,
3340
+ flowControl
3125
3341
  } = processOptions(options);
3126
3342
  telemetry2 = disableTelemetry ? void 0 : telemetry2;
3127
3343
  const debug = WorkflowLogger.getLogger(verbose);
@@ -3162,6 +3378,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
3162
3378
  failureFunction,
3163
3379
  env,
3164
3380
  retries,
3381
+ flowControl,
3165
3382
  debug
3166
3383
  );
3167
3384
  if (failureCheck.isErr()) {
@@ -3170,6 +3387,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
3170
3387
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
3171
3388
  return onStepFinish(workflowRunId, "failure-callback");
3172
3389
  }
3390
+ const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3173
3391
  const workflowContext = new WorkflowContext({
3174
3392
  qstashClient,
3175
3393
  workflowRunId,
@@ -3181,7 +3399,9 @@ var serveBase = (routeFunction, telemetry2, options) => {
3181
3399
  debug,
3182
3400
  env,
3183
3401
  retries,
3184
- telemetry: telemetry2
3402
+ telemetry: telemetry2,
3403
+ invokeCount,
3404
+ flowControl
3185
3405
  });
3186
3406
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
3187
3407
  routeFunction,
@@ -3204,6 +3424,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
3204
3424
  workflowUrl,
3205
3425
  failureUrl: workflowFailureUrl,
3206
3426
  retries,
3427
+ flowControl,
3207
3428
  telemetry: telemetry2,
3208
3429
  debug
3209
3430
  });
@@ -3213,7 +3434,13 @@ var serveBase = (routeFunction, telemetry2, options) => {
3213
3434
  });
3214
3435
  throw callReturnCheck.error;
3215
3436
  } else if (callReturnCheck.value === "continue-workflow") {
3216
- const result = isFirstInvocation ? await triggerFirstInvocation({ workflowContext, useJSONContent, telemetry: telemetry2, debug }) : await triggerRouteFunction({
3437
+ const result = isFirstInvocation ? await triggerFirstInvocation({
3438
+ workflowContext,
3439
+ useJSONContent,
3440
+ telemetry: telemetry2,
3441
+ debug,
3442
+ invokeCount
3443
+ }) : await triggerRouteFunction({
3217
3444
  onStep: async () => routeFunction(workflowContext),
3218
3445
  onCleanup: async (result2) => {
3219
3446
  await triggerWorkflowDelete(workflowContext, result2, debug);
@@ -3248,91 +3475,6 @@ var serveBase = (routeFunction, telemetry2, options) => {
3248
3475
  return { handler: safeHandler };
3249
3476
  };
3250
3477
 
3251
- // src/serve/serve-many.ts
3252
- var serveManyBase = ({
3253
- workflows,
3254
- getWorkflowId
3255
- }) => {
3256
- const workflowIds = [];
3257
- const workflowMap = Object.fromEntries(
3258
- Object.entries(workflows).map((workflow) => {
3259
- const workflowId = workflow[0];
3260
- if (workflowIds.includes(workflowId)) {
3261
- throw new WorkflowError(
3262
- `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
3263
- );
3264
- }
3265
- if (workflowId.includes("/")) {
3266
- throw new WorkflowError(
3267
- `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
3268
- );
3269
- }
3270
- workflowIds.push(workflowId);
3271
- workflow[1].workflowId = workflowId;
3272
- return [workflowId, workflow[1].handler];
3273
- })
3274
- );
3275
- return {
3276
- handler: async (...params) => {
3277
- const pickedWorkflowId = getWorkflowId(...params);
3278
- if (!pickedWorkflowId) {
3279
- throw new WorkflowError(`Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`);
3280
- }
3281
- const workflow = workflowMap[pickedWorkflowId];
3282
- if (!workflow) {
3283
- throw new WorkflowError(`No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`);
3284
- }
3285
- return await workflow(...params);
3286
- }
3287
- };
3288
- };
3289
- var createInvokeCallback = (telemetry2) => {
3290
- const invokeCallback = async (settings, invokeStep, context) => {
3291
- const { body, workflow, headers = {}, workflowRunId = getWorkflowRunId(), retries } = settings;
3292
- const { workflowId } = workflow;
3293
- if (!workflowId) {
3294
- throw new WorkflowError("You can only invoke workflow which has a workflowId");
3295
- }
3296
- const { headers: invokerHeaders } = getHeaders({
3297
- initHeaderValue: "false",
3298
- workflowRunId: context.workflowRunId,
3299
- workflowUrl: context.url,
3300
- userHeaders: context.headers,
3301
- failureUrl: context.failureUrl,
3302
- retries: context.retries,
3303
- telemetry: telemetry2
3304
- });
3305
- invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
3306
- const newUrl = context.url.replace(/[^/]+$/, workflowId);
3307
- const { headers: triggerHeaders } = getHeaders({
3308
- initHeaderValue: "true",
3309
- workflowRunId,
3310
- workflowUrl: newUrl,
3311
- userHeaders: new Headers(headers),
3312
- retries,
3313
- telemetry: telemetry2
3314
- });
3315
- triggerHeaders["Upstash-Workflow-Invoke"] = "true";
3316
- const request = {
3317
- body: JSON.stringify(body),
3318
- headers: Object.fromEntries(
3319
- Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
3320
- ),
3321
- workflowRunId,
3322
- workflowUrl: context.url,
3323
- step: invokeStep
3324
- };
3325
- await context.qstashClient.publish({
3326
- headers: triggerHeaders,
3327
- method: "POST",
3328
- body: JSON.stringify(request),
3329
- url: newUrl
3330
- });
3331
- return void 0;
3332
- };
3333
- return invokeCallback;
3334
- };
3335
-
3336
3478
  // platforms/h3.ts
3337
3479
  function transformHeaders(headers) {
3338
3480
  const formattedHeaders = Object.entries(headers).map(([key, value]) => [
@@ -3374,21 +3516,19 @@ var serve = (routeFunction, options) => {
3374
3516
  return { handler };
3375
3517
  };
3376
3518
  var createWorkflow = (...params) => {
3377
- const { handler } = serve(...params);
3519
+ const [routeFunction, options = {}] = params;
3378
3520
  return {
3379
- callback: createInvokeCallback(telemetry),
3380
- handler,
3521
+ routeFunction,
3522
+ options,
3381
3523
  workflowId: void 0
3382
3524
  };
3383
3525
  };
3384
- var serveMany = (workflows) => {
3526
+ var serveMany = (workflows, options) => {
3385
3527
  return serveManyBase({
3386
3528
  workflows,
3387
- getWorkflowId(event) {
3388
- const url = getUrl(event);
3389
- const components = url.split("/");
3390
- return components[components.length - 1];
3391
- }
3529
+ getUrl,
3530
+ serveMethod: (...params) => serve(...params).handler,
3531
+ options
3392
3532
  });
3393
3533
  };
3394
3534
  // Annotate the CommonJS export names for ESM import in node: