@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/express.js CHANGED
@@ -23704,6 +23704,7 @@ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
23704
23704
  var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
23705
23705
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
23706
23706
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
23707
+ var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
23707
23708
  var WORKFLOW_PROTOCOL_VERSION = "1";
23708
23709
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
23709
23710
  var DEFAULT_CONTENT_TYPE = "application/json";
@@ -23938,8 +23939,9 @@ var LazyCallStep = class extends BaseLazyStep {
23938
23939
  headers;
23939
23940
  retries;
23940
23941
  timeout;
23942
+ flowControl;
23941
23943
  stepType = "Call";
23942
- constructor(stepName, url, method, body, headers, retries, timeout) {
23944
+ constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
23943
23945
  super(stepName);
23944
23946
  this.url = url;
23945
23947
  this.method = method;
@@ -23947,6 +23949,7 @@ var LazyCallStep = class extends BaseLazyStep {
23947
23949
  this.headers = headers;
23948
23950
  this.retries = retries;
23949
23951
  this.timeout = timeout;
23952
+ this.flowControl = flowControl;
23950
23953
  }
23951
23954
  getPlanStep(concurrent, targetStep) {
23952
23955
  return {
@@ -24017,14 +24020,22 @@ var LazyNotifyStep = class extends LazyFunctionStep {
24017
24020
  var LazyInvokeStep = class extends BaseLazyStep {
24018
24021
  stepType = "Invoke";
24019
24022
  params;
24020
- constructor(stepName, { workflow, body, headers = {}, workflowRunId, retries }) {
24023
+ constructor(stepName, {
24024
+ workflow,
24025
+ body,
24026
+ headers = {},
24027
+ workflowRunId,
24028
+ retries,
24029
+ flowControl
24030
+ }) {
24021
24031
  super(stepName);
24022
24032
  this.params = {
24023
24033
  workflow,
24024
24034
  body,
24025
24035
  headers,
24026
24036
  workflowRunId: getWorkflowRunId(workflowRunId),
24027
- retries
24037
+ retries,
24038
+ flowControl
24028
24039
  };
24029
24040
  }
24030
24041
  getPlanStep(concurrent, targetStep) {
@@ -24483,7 +24494,8 @@ var triggerFirstInvocation = async ({
24483
24494
  workflowContext,
24484
24495
  useJSONContent,
24485
24496
  telemetry: telemetry2,
24486
- debug
24497
+ debug,
24498
+ invokeCount
24487
24499
  }) => {
24488
24500
  const { headers } = getHeaders({
24489
24501
  initHeaderValue: "true",
@@ -24492,7 +24504,9 @@ var triggerFirstInvocation = async ({
24492
24504
  userHeaders: workflowContext.headers,
24493
24505
  failureUrl: workflowContext.failureUrl,
24494
24506
  retries: workflowContext.retries,
24495
- telemetry: telemetry2
24507
+ telemetry: telemetry2,
24508
+ invokeCount,
24509
+ flowControl: workflowContext.flowControl
24496
24510
  });
24497
24511
  if (workflowContext.headers.get("content-type")) {
24498
24512
  headers["content-type"] = workflowContext.headers.get("content-type");
@@ -24598,6 +24612,7 @@ var handleThirdPartyCallResult = async ({
24598
24612
  failureUrl,
24599
24613
  retries,
24600
24614
  telemetry: telemetry2,
24615
+ flowControl,
24601
24616
  debug
24602
24617
  }) => {
24603
24618
  try {
@@ -24645,6 +24660,7 @@ ${atob(callbackMessage.body ?? "")}`
24645
24660
  const stepType = request.headers.get("Upstash-Workflow-StepType");
24646
24661
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
24647
24662
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
24663
+ const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
24648
24664
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
24649
24665
  throw new Error(
24650
24666
  `Missing info in callback message source header: ${JSON.stringify({
@@ -24665,7 +24681,9 @@ ${atob(callbackMessage.body ?? "")}`
24665
24681
  userHeaders,
24666
24682
  failureUrl,
24667
24683
  retries,
24668
- telemetry: telemetry2
24684
+ telemetry: telemetry2,
24685
+ invokeCount: Number(invokeCount),
24686
+ flowControl
24669
24687
  });
24670
24688
  const callResponse = {
24671
24689
  status: callbackMessage.status,
@@ -24721,7 +24739,10 @@ var getHeaders = ({
24721
24739
  step,
24722
24740
  callRetries,
24723
24741
  callTimeout,
24724
- telemetry: telemetry2
24742
+ telemetry: telemetry2,
24743
+ invokeCount,
24744
+ flowControl,
24745
+ callFlowControl
24725
24746
  }) => {
24726
24747
  const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
24727
24748
  const baseHeaders = {
@@ -24733,6 +24754,9 @@ var getHeaders = ({
24733
24754
  "content-type": contentType,
24734
24755
  ...telemetry2 ? getTelemetryHeaders(telemetry2) : {}
24735
24756
  };
24757
+ if (invokeCount !== void 0 && !step?.callUrl) {
24758
+ baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
24759
+ }
24736
24760
  if (!step?.callUrl) {
24737
24761
  baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
24738
24762
  }
@@ -24741,13 +24765,23 @@ var getHeaders = ({
24741
24765
  }
24742
24766
  if (failureUrl) {
24743
24767
  baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
24768
+ baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
24769
+ baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
24770
+ baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
24771
+ baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
24772
+ baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
24773
+ if (retries !== void 0) {
24774
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
24775
+ }
24776
+ if (flowControl) {
24777
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
24778
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
24779
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
24780
+ }
24744
24781
  if (!step?.callUrl) {
24745
24782
  baseHeaders["Upstash-Failure-Callback"] = failureUrl;
24746
24783
  }
24747
24784
  }
24748
- if (step?.stepType === "Invoke") {
24749
- baseHeaders["upstash-workflow-invoke"] = "true";
24750
- }
24751
24785
  if (step?.callUrl) {
24752
24786
  baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
24753
24787
  baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
@@ -24755,9 +24789,26 @@ var getHeaders = ({
24755
24789
  baseHeaders["Upstash-Callback-Retries"] = retries.toString();
24756
24790
  baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
24757
24791
  }
24758
- } else if (retries !== void 0) {
24759
- baseHeaders["Upstash-Retries"] = retries.toString();
24760
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
24792
+ if (callFlowControl) {
24793
+ const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
24794
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
24795
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
24796
+ }
24797
+ if (flowControl) {
24798
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
24799
+ baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
24800
+ baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
24801
+ }
24802
+ } else {
24803
+ if (flowControl) {
24804
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
24805
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
24806
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
24807
+ }
24808
+ if (retries !== void 0) {
24809
+ baseHeaders["Upstash-Retries"] = retries.toString();
24810
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
24811
+ }
24761
24812
  }
24762
24813
  if (userHeaders) {
24763
24814
  for (const header of userHeaders.keys()) {
@@ -24792,6 +24843,7 @@ var getHeaders = ({
24792
24843
  "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
24793
24844
  "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
24794
24845
  "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
24846
+ [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
24795
24847
  "Upstash-Workflow-CallType": "toCallback"
24796
24848
  }
24797
24849
  };
@@ -24849,9 +24901,159 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
24849
24901
  );
24850
24902
  }
24851
24903
  };
24904
+ var prepareFlowControl = (flowControl) => {
24905
+ const parallelism = flowControl.parallelism?.toString();
24906
+ const rate = flowControl.ratePerSecond?.toString();
24907
+ const controlValue = [
24908
+ parallelism ? `parallelism=${parallelism}` : void 0,
24909
+ rate ? `rate=${rate}` : void 0
24910
+ ].filter(Boolean);
24911
+ if (controlValue.length === 0) {
24912
+ throw new import_qstash3.QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
24913
+ }
24914
+ return {
24915
+ flowControlKey: flowControl.key,
24916
+ flowControlValue: controlValue.join(", ")
24917
+ };
24918
+ };
24852
24919
 
24853
24920
  // src/context/auto-executor.ts
24854
24921
  var import_qstash4 = require("@upstash/qstash");
24922
+
24923
+ // src/serve/serve-many.ts
24924
+ var getWorkflowId = (url) => {
24925
+ const components = url.split("/");
24926
+ const lastComponent = components[components.length - 1];
24927
+ return lastComponent.split("?")[0];
24928
+ };
24929
+ var serveManyBase = ({
24930
+ workflows,
24931
+ getUrl,
24932
+ serveMethod,
24933
+ options
24934
+ }) => {
24935
+ const workflowIds = [];
24936
+ const workflowMap = Object.fromEntries(
24937
+ Object.entries(workflows).map((workflow) => {
24938
+ const workflowId = workflow[0];
24939
+ if (workflowIds.includes(workflowId)) {
24940
+ throw new WorkflowError(
24941
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
24942
+ );
24943
+ }
24944
+ if (workflowId.includes("/")) {
24945
+ throw new WorkflowError(
24946
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
24947
+ );
24948
+ }
24949
+ workflowIds.push(workflowId);
24950
+ workflow[1].workflowId = workflowId;
24951
+ workflow[1].options = {
24952
+ ...options,
24953
+ ...workflow[1].options
24954
+ };
24955
+ const params = [workflow[1].routeFunction, workflow[1].options];
24956
+ const handler = serveMethod(...params);
24957
+ return [workflowId, handler];
24958
+ })
24959
+ );
24960
+ return {
24961
+ handler: async (...params) => {
24962
+ const url = getUrl(...params);
24963
+ const pickedWorkflowId = getWorkflowId(url);
24964
+ if (!pickedWorkflowId) {
24965
+ return new Response(
24966
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
24967
+ {
24968
+ status: 404
24969
+ }
24970
+ );
24971
+ }
24972
+ const workflow = workflowMap[pickedWorkflowId];
24973
+ if (!workflow) {
24974
+ return new Response(
24975
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
24976
+ {
24977
+ status: 404
24978
+ }
24979
+ );
24980
+ }
24981
+ return await workflow(...params);
24982
+ }
24983
+ };
24984
+ };
24985
+ var invokeWorkflow = async ({
24986
+ settings,
24987
+ invokeStep,
24988
+ context,
24989
+ invokeCount,
24990
+ telemetry: telemetry2
24991
+ }) => {
24992
+ const {
24993
+ body,
24994
+ workflow,
24995
+ headers = {},
24996
+ workflowRunId = getWorkflowRunId(),
24997
+ retries,
24998
+ flowControl
24999
+ } = settings;
25000
+ const { workflowId } = workflow;
25001
+ const {
25002
+ retries: workflowRetries,
25003
+ failureFunction,
25004
+ failureUrl,
25005
+ useJSONContent,
25006
+ flowControl: workflowFlowControl
25007
+ } = workflow.options;
25008
+ if (!workflowId) {
25009
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
25010
+ }
25011
+ const { headers: invokerHeaders } = getHeaders({
25012
+ initHeaderValue: "false",
25013
+ workflowRunId: context.workflowRunId,
25014
+ workflowUrl: context.url,
25015
+ userHeaders: context.headers,
25016
+ failureUrl: context.failureUrl,
25017
+ retries: context.retries,
25018
+ telemetry: telemetry2,
25019
+ invokeCount,
25020
+ flowControl: context.flowControl
25021
+ });
25022
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
25023
+ const newUrl = context.url.replace(/[^/]+$/, workflowId);
25024
+ const { headers: triggerHeaders } = getHeaders({
25025
+ initHeaderValue: "true",
25026
+ workflowRunId,
25027
+ workflowUrl: newUrl,
25028
+ userHeaders: new Headers(headers),
25029
+ retries: retries ?? workflowRetries,
25030
+ telemetry: telemetry2,
25031
+ failureUrl: failureFunction ? newUrl : failureUrl,
25032
+ invokeCount: invokeCount + 1,
25033
+ flowControl: flowControl ?? workflowFlowControl
25034
+ });
25035
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
25036
+ if (useJSONContent) {
25037
+ triggerHeaders["content-type"] = "application/json";
25038
+ }
25039
+ const request = {
25040
+ body: JSON.stringify(body),
25041
+ headers: Object.fromEntries(
25042
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
25043
+ ),
25044
+ workflowRunId,
25045
+ workflowUrl: context.url,
25046
+ step: invokeStep
25047
+ };
25048
+ await context.qstashClient.publish({
25049
+ headers: triggerHeaders,
25050
+ method: "POST",
25051
+ body: JSON.stringify(request),
25052
+ url: newUrl
25053
+ });
25054
+ };
25055
+
25056
+ // src/context/auto-executor.ts
24855
25057
  var AutoExecutor = class _AutoExecutor {
24856
25058
  context;
24857
25059
  promises = /* @__PURE__ */ new WeakMap();
@@ -24860,14 +25062,16 @@ var AutoExecutor = class _AutoExecutor {
24860
25062
  nonPlanStepCount;
24861
25063
  steps;
24862
25064
  indexInCurrentList = 0;
25065
+ invokeCount;
24863
25066
  telemetry;
24864
25067
  stepCount = 0;
24865
25068
  planStepCount = 0;
24866
25069
  executingStep = false;
24867
- constructor(context, steps, telemetry2, debug) {
25070
+ constructor(context, steps, telemetry2, invokeCount, debug) {
24868
25071
  this.context = context;
24869
25072
  this.steps = steps;
24870
25073
  this.telemetry = telemetry2;
25074
+ this.invokeCount = invokeCount ?? 0;
24871
25075
  this.debug = debug;
24872
25076
  this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
24873
25077
  }
@@ -25090,7 +25294,9 @@ var AutoExecutor = class _AutoExecutor {
25090
25294
  step: waitStep,
25091
25295
  failureUrl: this.context.failureUrl,
25092
25296
  retries: this.context.retries,
25093
- telemetry: this.telemetry
25297
+ telemetry: this.telemetry,
25298
+ invokeCount: this.invokeCount,
25299
+ flowControl: this.context.flowControl
25094
25300
  });
25095
25301
  const waitBody = {
25096
25302
  url: this.context.url,
@@ -25118,17 +25324,13 @@ var AutoExecutor = class _AutoExecutor {
25118
25324
  if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
25119
25325
  const invokeStep = steps[0];
25120
25326
  const lazyInvokeStep = lazySteps[0];
25121
- await lazyInvokeStep.params.workflow.callback(
25122
- {
25123
- body: lazyInvokeStep.params.body,
25124
- headers: lazyInvokeStep.params.headers,
25125
- workflowRunId: lazyInvokeStep.params.workflowRunId,
25126
- workflow: lazyInvokeStep.params.workflow,
25127
- retries: lazyInvokeStep.params.retries
25128
- },
25327
+ await invokeWorkflow({
25328
+ settings: lazyInvokeStep.params,
25129
25329
  invokeStep,
25130
- this.context
25131
- );
25330
+ context: this.context,
25331
+ invokeCount: this.invokeCount,
25332
+ telemetry: this.telemetry
25333
+ });
25132
25334
  throw new WorkflowAbort(invokeStep.stepName, invokeStep);
25133
25335
  }
25134
25336
  const result = await this.context.qstashClient.batchJSON(
@@ -25144,11 +25346,14 @@ var AutoExecutor = class _AutoExecutor {
25144
25346
  retries: this.context.retries,
25145
25347
  callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
25146
25348
  callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
25147
- telemetry: this.telemetry
25349
+ telemetry: this.telemetry,
25350
+ invokeCount: this.invokeCount,
25351
+ flowControl: this.context.flowControl,
25352
+ callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
25148
25353
  });
25149
25354
  const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
25150
25355
  singleStep.out = JSON.stringify(singleStep.out);
25151
- return singleStep.callUrl ? (
25356
+ return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
25152
25357
  // if the step is a third party call, we call the third party
25153
25358
  // url (singleStep.callUrl) and pass information about the workflow
25154
25359
  // in the headers (handled in getHeaders). QStash makes the request
@@ -25448,9 +25653,10 @@ var wrapTools = ({
25448
25653
  return Object.fromEntries(
25449
25654
  Object.entries(tools).map((toolInfo) => {
25450
25655
  const [toolName, tool3] = toolInfo;
25656
+ const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
25451
25657
  const aiSDKTool = convertToAISDKTool(tool3);
25452
25658
  const execute = aiSDKTool.execute;
25453
- if (execute) {
25659
+ if (execute && executeAsStep) {
25454
25660
  const wrappedExecute = (...params) => {
25455
25661
  return context.run(`Run tool ${toolName}`, () => execute(...params));
25456
25662
  };
@@ -25804,6 +26010,11 @@ var WorkflowContext = class {
25804
26010
  * Number of retries
25805
26011
  */
25806
26012
  retries;
26013
+ /**
26014
+ * Settings for controlling the number of active requests
26015
+ * and number of requests per second with the same key.
26016
+ */
26017
+ flowControl;
25807
26018
  constructor({
25808
26019
  qstashClient,
25809
26020
  workflowRunId,
@@ -25815,7 +26026,9 @@ var WorkflowContext = class {
25815
26026
  initialPayload,
25816
26027
  env,
25817
26028
  retries,
25818
- telemetry: telemetry2
26029
+ telemetry: telemetry2,
26030
+ invokeCount,
26031
+ flowControl
25819
26032
  }) {
25820
26033
  this.qstashClient = qstashClient;
25821
26034
  this.workflowRunId = workflowRunId;
@@ -25826,7 +26039,8 @@ var WorkflowContext = class {
25826
26039
  this.requestPayload = initialPayload;
25827
26040
  this.env = env ?? {};
25828
26041
  this.retries = retries ?? DEFAULT_RETRIES;
25829
- this.executor = new AutoExecutor(this, this.steps, telemetry2, debug);
26042
+ this.flowControl = flowControl;
26043
+ this.executor = new AutoExecutor(this, this.steps, telemetry2, invokeCount, debug);
25830
26044
  }
25831
26045
  /**
25832
26046
  * Executes a workflow step
@@ -25928,7 +26142,7 @@ var WorkflowContext = class {
25928
26142
  * }
25929
26143
  */
25930
26144
  async call(stepName, settings) {
25931
- const { url, method = "GET", body, headers = {}, retries = 0, timeout } = settings;
26145
+ const { url, method = "GET", body, headers = {}, retries = 0, timeout, flowControl } = settings;
25932
26146
  const result = await this.addStep(
25933
26147
  new LazyCallStep(
25934
26148
  stepName,
@@ -25937,7 +26151,8 @@ var WorkflowContext = class {
25937
26151
  body,
25938
26152
  headers,
25939
26153
  retries,
25940
- timeout
26154
+ timeout,
26155
+ flowControl
25941
26156
  )
25942
26157
  );
25943
26158
  if (typeof result === "string") {
@@ -26174,7 +26389,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
26174
26389
  failureUrl: context.failureUrl,
26175
26390
  initialPayload: context.requestPayload,
26176
26391
  env: context.env,
26177
- retries: context.retries
26392
+ retries: context.retries,
26393
+ flowControl: context.flowControl
26178
26394
  });
26179
26395
  try {
26180
26396
  await routeFunction(disabledContext);
@@ -26327,7 +26543,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
26327
26543
  };
26328
26544
  }
26329
26545
  };
26330
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, debug) => {
26546
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
26331
26547
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
26332
26548
  return ok("not-failure-callback");
26333
26549
  }
@@ -26339,22 +26555,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
26339
26555
  );
26340
26556
  }
26341
26557
  try {
26342
- const { status, header, body, url, sourceHeader, sourceBody, workflowRunId } = JSON.parse(
26343
- requestPayload
26344
- );
26558
+ const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
26345
26559
  const decodedBody = body ? decodeBase64(body) : "{}";
26346
26560
  const errorPayload = JSON.parse(decodedBody);
26347
26561
  const workflowContext = new WorkflowContext({
26348
26562
  qstashClient,
26349
26563
  workflowRunId,
26350
26564
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
26351
- headers: recreateUserHeaders(new Headers(sourceHeader)),
26565
+ headers: recreateUserHeaders(request.headers),
26352
26566
  steps: [],
26353
26567
  url,
26354
26568
  failureUrl: url,
26355
26569
  debug,
26356
26570
  env,
26357
26571
  retries,
26572
+ flowControl,
26358
26573
  telemetry: void 0
26359
26574
  // not going to make requests in authentication check
26360
26575
  });
@@ -26481,7 +26696,8 @@ var serveBase = (routeFunction, telemetry2, options) => {
26481
26696
  env,
26482
26697
  retries,
26483
26698
  useJSONContent,
26484
- disableTelemetry
26699
+ disableTelemetry,
26700
+ flowControl
26485
26701
  } = processOptions(options);
26486
26702
  telemetry2 = disableTelemetry ? void 0 : telemetry2;
26487
26703
  const debug = WorkflowLogger.getLogger(verbose);
@@ -26522,6 +26738,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
26522
26738
  failureFunction,
26523
26739
  env,
26524
26740
  retries,
26741
+ flowControl,
26525
26742
  debug
26526
26743
  );
26527
26744
  if (failureCheck.isErr()) {
@@ -26530,6 +26747,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
26530
26747
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
26531
26748
  return onStepFinish(workflowRunId, "failure-callback");
26532
26749
  }
26750
+ const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
26533
26751
  const workflowContext = new WorkflowContext({
26534
26752
  qstashClient,
26535
26753
  workflowRunId,
@@ -26541,7 +26759,9 @@ var serveBase = (routeFunction, telemetry2, options) => {
26541
26759
  debug,
26542
26760
  env,
26543
26761
  retries,
26544
- telemetry: telemetry2
26762
+ telemetry: telemetry2,
26763
+ invokeCount,
26764
+ flowControl
26545
26765
  });
26546
26766
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
26547
26767
  routeFunction,
@@ -26564,6 +26784,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
26564
26784
  workflowUrl,
26565
26785
  failureUrl: workflowFailureUrl,
26566
26786
  retries,
26787
+ flowControl,
26567
26788
  telemetry: telemetry2,
26568
26789
  debug
26569
26790
  });
@@ -26573,7 +26794,13 @@ var serveBase = (routeFunction, telemetry2, options) => {
26573
26794
  });
26574
26795
  throw callReturnCheck.error;
26575
26796
  } else if (callReturnCheck.value === "continue-workflow") {
26576
- const result = isFirstInvocation ? await triggerFirstInvocation({ workflowContext, useJSONContent, telemetry: telemetry2, debug }) : await triggerRouteFunction({
26797
+ const result = isFirstInvocation ? await triggerFirstInvocation({
26798
+ workflowContext,
26799
+ useJSONContent,
26800
+ telemetry: telemetry2,
26801
+ debug,
26802
+ invokeCount
26803
+ }) : await triggerRouteFunction({
26577
26804
  onStep: async () => routeFunction(workflowContext),
26578
26805
  onCleanup: async (result2) => {
26579
26806
  await triggerWorkflowDelete(workflowContext, result2, debug);
@@ -26610,93 +26837,6 @@ var serveBase = (routeFunction, telemetry2, options) => {
26610
26837
 
26611
26838
  // platforms/express.ts
26612
26839
  var import_express = __toESM(require_express2());
26613
-
26614
- // src/serve/serve-many.ts
26615
- var serveManyBase = ({
26616
- workflows,
26617
- getWorkflowId
26618
- }) => {
26619
- const workflowIds = [];
26620
- const workflowMap = Object.fromEntries(
26621
- Object.entries(workflows).map((workflow) => {
26622
- const workflowId = workflow[0];
26623
- if (workflowIds.includes(workflowId)) {
26624
- throw new WorkflowError(
26625
- `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
26626
- );
26627
- }
26628
- if (workflowId.includes("/")) {
26629
- throw new WorkflowError(
26630
- `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
26631
- );
26632
- }
26633
- workflowIds.push(workflowId);
26634
- workflow[1].workflowId = workflowId;
26635
- return [workflowId, workflow[1].handler];
26636
- })
26637
- );
26638
- return {
26639
- handler: async (...params) => {
26640
- const pickedWorkflowId = getWorkflowId(...params);
26641
- if (!pickedWorkflowId) {
26642
- throw new WorkflowError(`Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`);
26643
- }
26644
- const workflow = workflowMap[pickedWorkflowId];
26645
- if (!workflow) {
26646
- throw new WorkflowError(`No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`);
26647
- }
26648
- return await workflow(...params);
26649
- }
26650
- };
26651
- };
26652
- var createInvokeCallback = (telemetry2) => {
26653
- const invokeCallback = async (settings, invokeStep, context) => {
26654
- const { body, workflow, headers = {}, workflowRunId = getWorkflowRunId(), retries } = settings;
26655
- const { workflowId } = workflow;
26656
- if (!workflowId) {
26657
- throw new WorkflowError("You can only invoke workflow which has a workflowId");
26658
- }
26659
- const { headers: invokerHeaders } = getHeaders({
26660
- initHeaderValue: "false",
26661
- workflowRunId: context.workflowRunId,
26662
- workflowUrl: context.url,
26663
- userHeaders: context.headers,
26664
- failureUrl: context.failureUrl,
26665
- retries: context.retries,
26666
- telemetry: telemetry2
26667
- });
26668
- invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
26669
- const newUrl = context.url.replace(/[^/]+$/, workflowId);
26670
- const { headers: triggerHeaders } = getHeaders({
26671
- initHeaderValue: "true",
26672
- workflowRunId,
26673
- workflowUrl: newUrl,
26674
- userHeaders: new Headers(headers),
26675
- retries,
26676
- telemetry: telemetry2
26677
- });
26678
- triggerHeaders["Upstash-Workflow-Invoke"] = "true";
26679
- const request = {
26680
- body: JSON.stringify(body),
26681
- headers: Object.fromEntries(
26682
- Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
26683
- ),
26684
- workflowRunId,
26685
- workflowUrl: context.url,
26686
- step: invokeStep
26687
- };
26688
- await context.qstashClient.publish({
26689
- headers: triggerHeaders,
26690
- method: "POST",
26691
- body: JSON.stringify(request),
26692
- url: newUrl
26693
- });
26694
- return void 0;
26695
- };
26696
- return invokeCallback;
26697
- };
26698
-
26699
- // platforms/express.ts
26700
26840
  var isEmptyRequest = (req) => {
26701
26841
  return req.headers["content-type"] === "application/json" && req.headers["content-length"] === "0";
26702
26842
  };
@@ -26730,14 +26870,10 @@ function createExpressHandler(params) {
26730
26870
  headers: new Headers(request_.headers),
26731
26871
  body: requestBody
26732
26872
  });
26733
- const { handler: serveHandler } = serveBase(
26734
- routeFunction,
26735
- telemetry,
26736
- {
26737
- ...options,
26738
- useJSONContent: true
26739
- }
26740
- );
26873
+ const { handler: serveHandler } = serveBase(routeFunction, telemetry, {
26874
+ ...options,
26875
+ useJSONContent: true
26876
+ });
26741
26877
  const response = await serveHandler(webRequest);
26742
26878
  res.status(response.status).json(await response.json());
26743
26879
  };
@@ -26749,21 +26885,22 @@ function serve(routeFunction, options) {
26749
26885
  return router;
26750
26886
  }
26751
26887
  var createWorkflow = (...params) => {
26752
- const handler = createExpressHandler(params);
26888
+ const [routeFunction, options = {}] = params;
26753
26889
  return {
26754
- callback: createInvokeCallback(telemetry),
26755
- handler,
26890
+ routeFunction,
26891
+ options,
26756
26892
  workflowId: void 0
26757
26893
  };
26758
26894
  };
26759
- var serveMany = (workflows) => {
26895
+ var serveMany = (workflows, options) => {
26760
26896
  const router = (0, import_express.Router)();
26761
26897
  const { handler } = serveManyBase({
26762
26898
  workflows,
26763
- getWorkflowId(...params) {
26764
- const components = params[0].url.split("/");
26765
- return components[components.length - 1];
26766
- }
26899
+ getUrl(...params) {
26900
+ return params[0].url;
26901
+ },
26902
+ serveMethod: (...params) => createExpressHandler(params),
26903
+ options
26767
26904
  });
26768
26905
  router.all("*", handler);
26769
26906
  return router;