@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/nextjs.js CHANGED
@@ -88,6 +88,7 @@ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
88
88
  var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
89
89
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
90
90
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
91
+ var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
91
92
  var WORKFLOW_PROTOCOL_VERSION = "1";
92
93
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
93
94
  var DEFAULT_CONTENT_TYPE = "application/json";
@@ -269,8 +270,9 @@ var LazyCallStep = class extends BaseLazyStep {
269
270
  headers;
270
271
  retries;
271
272
  timeout;
273
+ flowControl;
272
274
  stepType = "Call";
273
- constructor(stepName, url, method, body, headers, retries, timeout) {
275
+ constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
274
276
  super(stepName);
275
277
  this.url = url;
276
278
  this.method = method;
@@ -278,6 +280,7 @@ var LazyCallStep = class extends BaseLazyStep {
278
280
  this.headers = headers;
279
281
  this.retries = retries;
280
282
  this.timeout = timeout;
283
+ this.flowControl = flowControl;
281
284
  }
282
285
  getPlanStep(concurrent, targetStep) {
283
286
  return {
@@ -348,14 +351,22 @@ var LazyNotifyStep = class extends LazyFunctionStep {
348
351
  var LazyInvokeStep = class extends BaseLazyStep {
349
352
  stepType = "Invoke";
350
353
  params;
351
- constructor(stepName, { workflow, body, headers = {}, workflowRunId, retries }) {
354
+ constructor(stepName, {
355
+ workflow,
356
+ body,
357
+ headers = {},
358
+ workflowRunId,
359
+ retries,
360
+ flowControl
361
+ }) {
352
362
  super(stepName);
353
363
  this.params = {
354
364
  workflow,
355
365
  body,
356
366
  headers,
357
367
  workflowRunId: getWorkflowRunId(workflowRunId),
358
- retries
368
+ retries,
369
+ flowControl
359
370
  };
360
371
  }
361
372
  getPlanStep(concurrent, targetStep) {
@@ -814,7 +825,8 @@ var triggerFirstInvocation = async ({
814
825
  workflowContext,
815
826
  useJSONContent,
816
827
  telemetry,
817
- debug
828
+ debug,
829
+ invokeCount
818
830
  }) => {
819
831
  const { headers } = getHeaders({
820
832
  initHeaderValue: "true",
@@ -823,7 +835,9 @@ var triggerFirstInvocation = async ({
823
835
  userHeaders: workflowContext.headers,
824
836
  failureUrl: workflowContext.failureUrl,
825
837
  retries: workflowContext.retries,
826
- telemetry
838
+ telemetry,
839
+ invokeCount,
840
+ flowControl: workflowContext.flowControl
827
841
  });
828
842
  if (workflowContext.headers.get("content-type")) {
829
843
  headers["content-type"] = workflowContext.headers.get("content-type");
@@ -929,6 +943,7 @@ var handleThirdPartyCallResult = async ({
929
943
  failureUrl,
930
944
  retries,
931
945
  telemetry,
946
+ flowControl,
932
947
  debug
933
948
  }) => {
934
949
  try {
@@ -976,6 +991,7 @@ ${atob(callbackMessage.body ?? "")}`
976
991
  const stepType = request.headers.get("Upstash-Workflow-StepType");
977
992
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
978
993
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
994
+ const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
979
995
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
980
996
  throw new Error(
981
997
  `Missing info in callback message source header: ${JSON.stringify({
@@ -996,7 +1012,9 @@ ${atob(callbackMessage.body ?? "")}`
996
1012
  userHeaders,
997
1013
  failureUrl,
998
1014
  retries,
999
- telemetry
1015
+ telemetry,
1016
+ invokeCount: Number(invokeCount),
1017
+ flowControl
1000
1018
  });
1001
1019
  const callResponse = {
1002
1020
  status: callbackMessage.status,
@@ -1052,7 +1070,10 @@ var getHeaders = ({
1052
1070
  step,
1053
1071
  callRetries,
1054
1072
  callTimeout,
1055
- telemetry
1073
+ telemetry,
1074
+ invokeCount,
1075
+ flowControl,
1076
+ callFlowControl
1056
1077
  }) => {
1057
1078
  const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1058
1079
  const baseHeaders = {
@@ -1064,6 +1085,9 @@ var getHeaders = ({
1064
1085
  "content-type": contentType,
1065
1086
  ...telemetry ? getTelemetryHeaders(telemetry) : {}
1066
1087
  };
1088
+ if (invokeCount !== void 0 && !step?.callUrl) {
1089
+ baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
1090
+ }
1067
1091
  if (!step?.callUrl) {
1068
1092
  baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1069
1093
  }
@@ -1072,13 +1096,23 @@ var getHeaders = ({
1072
1096
  }
1073
1097
  if (failureUrl) {
1074
1098
  baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
1099
+ baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
1100
+ baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
1101
+ baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
1102
+ baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
1103
+ baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
1104
+ if (retries !== void 0) {
1105
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1106
+ }
1107
+ if (flowControl) {
1108
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1109
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
1110
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
1111
+ }
1075
1112
  if (!step?.callUrl) {
1076
1113
  baseHeaders["Upstash-Failure-Callback"] = failureUrl;
1077
1114
  }
1078
1115
  }
1079
- if (step?.stepType === "Invoke") {
1080
- baseHeaders["upstash-workflow-invoke"] = "true";
1081
- }
1082
1116
  if (step?.callUrl) {
1083
1117
  baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
1084
1118
  baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
@@ -1086,9 +1120,26 @@ var getHeaders = ({
1086
1120
  baseHeaders["Upstash-Callback-Retries"] = retries.toString();
1087
1121
  baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1088
1122
  }
1089
- } else if (retries !== void 0) {
1090
- baseHeaders["Upstash-Retries"] = retries.toString();
1091
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1123
+ if (callFlowControl) {
1124
+ const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
1125
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1126
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1127
+ }
1128
+ if (flowControl) {
1129
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1130
+ baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
1131
+ baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
1132
+ }
1133
+ } else {
1134
+ if (flowControl) {
1135
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1136
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1137
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1138
+ }
1139
+ if (retries !== void 0) {
1140
+ baseHeaders["Upstash-Retries"] = retries.toString();
1141
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1142
+ }
1092
1143
  }
1093
1144
  if (userHeaders) {
1094
1145
  for (const header of userHeaders.keys()) {
@@ -1123,6 +1174,7 @@ var getHeaders = ({
1123
1174
  "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
1124
1175
  "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1125
1176
  "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1177
+ [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
1126
1178
  "Upstash-Workflow-CallType": "toCallback"
1127
1179
  }
1128
1180
  };
@@ -1180,9 +1232,159 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
1180
1232
  );
1181
1233
  }
1182
1234
  };
1235
+ var prepareFlowControl = (flowControl) => {
1236
+ const parallelism = flowControl.parallelism?.toString();
1237
+ const rate = flowControl.ratePerSecond?.toString();
1238
+ const controlValue = [
1239
+ parallelism ? `parallelism=${parallelism}` : void 0,
1240
+ rate ? `rate=${rate}` : void 0
1241
+ ].filter(Boolean);
1242
+ if (controlValue.length === 0) {
1243
+ throw new import_qstash3.QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
1244
+ }
1245
+ return {
1246
+ flowControlKey: flowControl.key,
1247
+ flowControlValue: controlValue.join(", ")
1248
+ };
1249
+ };
1183
1250
 
1184
1251
  // src/context/auto-executor.ts
1185
1252
  var import_qstash4 = require("@upstash/qstash");
1253
+
1254
+ // src/serve/serve-many.ts
1255
+ var getWorkflowId = (url) => {
1256
+ const components = url.split("/");
1257
+ const lastComponent = components[components.length - 1];
1258
+ return lastComponent.split("?")[0];
1259
+ };
1260
+ var serveManyBase = ({
1261
+ workflows,
1262
+ getUrl,
1263
+ serveMethod,
1264
+ options
1265
+ }) => {
1266
+ const workflowIds = [];
1267
+ const workflowMap = Object.fromEntries(
1268
+ Object.entries(workflows).map((workflow) => {
1269
+ const workflowId = workflow[0];
1270
+ if (workflowIds.includes(workflowId)) {
1271
+ throw new WorkflowError(
1272
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
1273
+ );
1274
+ }
1275
+ if (workflowId.includes("/")) {
1276
+ throw new WorkflowError(
1277
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
1278
+ );
1279
+ }
1280
+ workflowIds.push(workflowId);
1281
+ workflow[1].workflowId = workflowId;
1282
+ workflow[1].options = {
1283
+ ...options,
1284
+ ...workflow[1].options
1285
+ };
1286
+ const params = [workflow[1].routeFunction, workflow[1].options];
1287
+ const handler = serveMethod(...params);
1288
+ return [workflowId, handler];
1289
+ })
1290
+ );
1291
+ return {
1292
+ handler: async (...params) => {
1293
+ const url = getUrl(...params);
1294
+ const pickedWorkflowId = getWorkflowId(url);
1295
+ if (!pickedWorkflowId) {
1296
+ return new Response(
1297
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
1298
+ {
1299
+ status: 404
1300
+ }
1301
+ );
1302
+ }
1303
+ const workflow = workflowMap[pickedWorkflowId];
1304
+ if (!workflow) {
1305
+ return new Response(
1306
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
1307
+ {
1308
+ status: 404
1309
+ }
1310
+ );
1311
+ }
1312
+ return await workflow(...params);
1313
+ }
1314
+ };
1315
+ };
1316
+ var invokeWorkflow = async ({
1317
+ settings,
1318
+ invokeStep,
1319
+ context,
1320
+ invokeCount,
1321
+ telemetry
1322
+ }) => {
1323
+ const {
1324
+ body,
1325
+ workflow,
1326
+ headers = {},
1327
+ workflowRunId = getWorkflowRunId(),
1328
+ retries,
1329
+ flowControl
1330
+ } = settings;
1331
+ const { workflowId } = workflow;
1332
+ const {
1333
+ retries: workflowRetries,
1334
+ failureFunction,
1335
+ failureUrl,
1336
+ useJSONContent,
1337
+ flowControl: workflowFlowControl
1338
+ } = workflow.options;
1339
+ if (!workflowId) {
1340
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1341
+ }
1342
+ const { headers: invokerHeaders } = getHeaders({
1343
+ initHeaderValue: "false",
1344
+ workflowRunId: context.workflowRunId,
1345
+ workflowUrl: context.url,
1346
+ userHeaders: context.headers,
1347
+ failureUrl: context.failureUrl,
1348
+ retries: context.retries,
1349
+ telemetry,
1350
+ invokeCount,
1351
+ flowControl: context.flowControl
1352
+ });
1353
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1354
+ const newUrl = context.url.replace(/[^/]+$/, workflowId);
1355
+ const { headers: triggerHeaders } = getHeaders({
1356
+ initHeaderValue: "true",
1357
+ workflowRunId,
1358
+ workflowUrl: newUrl,
1359
+ userHeaders: new Headers(headers),
1360
+ retries: retries ?? workflowRetries,
1361
+ telemetry,
1362
+ failureUrl: failureFunction ? newUrl : failureUrl,
1363
+ invokeCount: invokeCount + 1,
1364
+ flowControl: flowControl ?? workflowFlowControl
1365
+ });
1366
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1367
+ if (useJSONContent) {
1368
+ triggerHeaders["content-type"] = "application/json";
1369
+ }
1370
+ const request = {
1371
+ body: JSON.stringify(body),
1372
+ headers: Object.fromEntries(
1373
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1374
+ ),
1375
+ workflowRunId,
1376
+ workflowUrl: context.url,
1377
+ step: invokeStep
1378
+ };
1379
+ await context.qstashClient.publish({
1380
+ headers: triggerHeaders,
1381
+ method: "POST",
1382
+ body: JSON.stringify(request),
1383
+ url: newUrl
1384
+ });
1385
+ };
1386
+
1387
+ // src/context/auto-executor.ts
1186
1388
  var AutoExecutor = class _AutoExecutor {
1187
1389
  context;
1188
1390
  promises = /* @__PURE__ */ new WeakMap();
@@ -1191,14 +1393,16 @@ var AutoExecutor = class _AutoExecutor {
1191
1393
  nonPlanStepCount;
1192
1394
  steps;
1193
1395
  indexInCurrentList = 0;
1396
+ invokeCount;
1194
1397
  telemetry;
1195
1398
  stepCount = 0;
1196
1399
  planStepCount = 0;
1197
1400
  executingStep = false;
1198
- constructor(context, steps, telemetry, debug) {
1401
+ constructor(context, steps, telemetry, invokeCount, debug) {
1199
1402
  this.context = context;
1200
1403
  this.steps = steps;
1201
1404
  this.telemetry = telemetry;
1405
+ this.invokeCount = invokeCount ?? 0;
1202
1406
  this.debug = debug;
1203
1407
  this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
1204
1408
  }
@@ -1421,7 +1625,9 @@ var AutoExecutor = class _AutoExecutor {
1421
1625
  step: waitStep,
1422
1626
  failureUrl: this.context.failureUrl,
1423
1627
  retries: this.context.retries,
1424
- telemetry: this.telemetry
1628
+ telemetry: this.telemetry,
1629
+ invokeCount: this.invokeCount,
1630
+ flowControl: this.context.flowControl
1425
1631
  });
1426
1632
  const waitBody = {
1427
1633
  url: this.context.url,
@@ -1449,17 +1655,13 @@ var AutoExecutor = class _AutoExecutor {
1449
1655
  if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1450
1656
  const invokeStep = steps[0];
1451
1657
  const lazyInvokeStep = lazySteps[0];
1452
- await lazyInvokeStep.params.workflow.callback(
1453
- {
1454
- body: lazyInvokeStep.params.body,
1455
- headers: lazyInvokeStep.params.headers,
1456
- workflowRunId: lazyInvokeStep.params.workflowRunId,
1457
- workflow: lazyInvokeStep.params.workflow,
1458
- retries: lazyInvokeStep.params.retries
1459
- },
1658
+ await invokeWorkflow({
1659
+ settings: lazyInvokeStep.params,
1460
1660
  invokeStep,
1461
- this.context
1462
- );
1661
+ context: this.context,
1662
+ invokeCount: this.invokeCount,
1663
+ telemetry: this.telemetry
1664
+ });
1463
1665
  throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1464
1666
  }
1465
1667
  const result = await this.context.qstashClient.batchJSON(
@@ -1475,11 +1677,14 @@ var AutoExecutor = class _AutoExecutor {
1475
1677
  retries: this.context.retries,
1476
1678
  callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
1477
1679
  callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
1478
- telemetry: this.telemetry
1680
+ telemetry: this.telemetry,
1681
+ invokeCount: this.invokeCount,
1682
+ flowControl: this.context.flowControl,
1683
+ callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
1479
1684
  });
1480
1685
  const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1481
1686
  singleStep.out = JSON.stringify(singleStep.out);
1482
- return singleStep.callUrl ? (
1687
+ return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
1483
1688
  // if the step is a third party call, we call the third party
1484
1689
  // url (singleStep.callUrl) and pass information about the workflow
1485
1690
  // in the headers (handled in getHeaders). QStash makes the request
@@ -1779,9 +1984,10 @@ var wrapTools = ({
1779
1984
  return Object.fromEntries(
1780
1985
  Object.entries(tools).map((toolInfo) => {
1781
1986
  const [toolName, tool3] = toolInfo;
1987
+ const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
1782
1988
  const aiSDKTool = convertToAISDKTool(tool3);
1783
1989
  const execute = aiSDKTool.execute;
1784
- if (execute) {
1990
+ if (execute && executeAsStep) {
1785
1991
  const wrappedExecute = (...params) => {
1786
1992
  return context.run(`Run tool ${toolName}`, () => execute(...params));
1787
1993
  };
@@ -2135,6 +2341,11 @@ var WorkflowContext = class {
2135
2341
  * Number of retries
2136
2342
  */
2137
2343
  retries;
2344
+ /**
2345
+ * Settings for controlling the number of active requests
2346
+ * and number of requests per second with the same key.
2347
+ */
2348
+ flowControl;
2138
2349
  constructor({
2139
2350
  qstashClient,
2140
2351
  workflowRunId,
@@ -2146,7 +2357,9 @@ var WorkflowContext = class {
2146
2357
  initialPayload,
2147
2358
  env,
2148
2359
  retries,
2149
- telemetry
2360
+ telemetry,
2361
+ invokeCount,
2362
+ flowControl
2150
2363
  }) {
2151
2364
  this.qstashClient = qstashClient;
2152
2365
  this.workflowRunId = workflowRunId;
@@ -2157,7 +2370,8 @@ var WorkflowContext = class {
2157
2370
  this.requestPayload = initialPayload;
2158
2371
  this.env = env ?? {};
2159
2372
  this.retries = retries ?? DEFAULT_RETRIES;
2160
- this.executor = new AutoExecutor(this, this.steps, telemetry, debug);
2373
+ this.flowControl = flowControl;
2374
+ this.executor = new AutoExecutor(this, this.steps, telemetry, invokeCount, debug);
2161
2375
  }
2162
2376
  /**
2163
2377
  * Executes a workflow step
@@ -2259,7 +2473,7 @@ var WorkflowContext = class {
2259
2473
  * }
2260
2474
  */
2261
2475
  async call(stepName, settings) {
2262
- const { url, method = "GET", body, headers = {}, retries = 0, timeout } = settings;
2476
+ const { url, method = "GET", body, headers = {}, retries = 0, timeout, flowControl } = settings;
2263
2477
  const result = await this.addStep(
2264
2478
  new LazyCallStep(
2265
2479
  stepName,
@@ -2268,7 +2482,8 @@ var WorkflowContext = class {
2268
2482
  body,
2269
2483
  headers,
2270
2484
  retries,
2271
- timeout
2485
+ timeout,
2486
+ flowControl
2272
2487
  )
2273
2488
  );
2274
2489
  if (typeof result === "string") {
@@ -2505,7 +2720,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
2505
2720
  failureUrl: context.failureUrl,
2506
2721
  initialPayload: context.requestPayload,
2507
2722
  env: context.env,
2508
- retries: context.retries
2723
+ retries: context.retries,
2724
+ flowControl: context.flowControl
2509
2725
  });
2510
2726
  try {
2511
2727
  await routeFunction(disabledContext);
@@ -2658,7 +2874,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
2658
2874
  };
2659
2875
  }
2660
2876
  };
2661
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, debug) => {
2877
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
2662
2878
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
2663
2879
  return ok("not-failure-callback");
2664
2880
  }
@@ -2670,22 +2886,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
2670
2886
  );
2671
2887
  }
2672
2888
  try {
2673
- const { status, header, body, url, sourceHeader, sourceBody, workflowRunId } = JSON.parse(
2674
- requestPayload
2675
- );
2889
+ const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
2676
2890
  const decodedBody = body ? decodeBase64(body) : "{}";
2677
2891
  const errorPayload = JSON.parse(decodedBody);
2678
2892
  const workflowContext = new WorkflowContext({
2679
2893
  qstashClient,
2680
2894
  workflowRunId,
2681
2895
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
2682
- headers: recreateUserHeaders(new Headers(sourceHeader)),
2896
+ headers: recreateUserHeaders(request.headers),
2683
2897
  steps: [],
2684
2898
  url,
2685
2899
  failureUrl: url,
2686
2900
  debug,
2687
2901
  env,
2688
2902
  retries,
2903
+ flowControl,
2689
2904
  telemetry: void 0
2690
2905
  // not going to make requests in authentication check
2691
2906
  });
@@ -2812,7 +3027,8 @@ var serveBase = (routeFunction, telemetry, options) => {
2812
3027
  env,
2813
3028
  retries,
2814
3029
  useJSONContent,
2815
- disableTelemetry
3030
+ disableTelemetry,
3031
+ flowControl
2816
3032
  } = processOptions(options);
2817
3033
  telemetry = disableTelemetry ? void 0 : telemetry;
2818
3034
  const debug = WorkflowLogger.getLogger(verbose);
@@ -2853,6 +3069,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2853
3069
  failureFunction,
2854
3070
  env,
2855
3071
  retries,
3072
+ flowControl,
2856
3073
  debug
2857
3074
  );
2858
3075
  if (failureCheck.isErr()) {
@@ -2861,6 +3078,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2861
3078
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
2862
3079
  return onStepFinish(workflowRunId, "failure-callback");
2863
3080
  }
3081
+ const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
2864
3082
  const workflowContext = new WorkflowContext({
2865
3083
  qstashClient,
2866
3084
  workflowRunId,
@@ -2872,7 +3090,9 @@ var serveBase = (routeFunction, telemetry, options) => {
2872
3090
  debug,
2873
3091
  env,
2874
3092
  retries,
2875
- telemetry
3093
+ telemetry,
3094
+ invokeCount,
3095
+ flowControl
2876
3096
  });
2877
3097
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
2878
3098
  routeFunction,
@@ -2895,6 +3115,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2895
3115
  workflowUrl,
2896
3116
  failureUrl: workflowFailureUrl,
2897
3117
  retries,
3118
+ flowControl,
2898
3119
  telemetry,
2899
3120
  debug
2900
3121
  });
@@ -2904,7 +3125,13 @@ var serveBase = (routeFunction, telemetry, options) => {
2904
3125
  });
2905
3126
  throw callReturnCheck.error;
2906
3127
  } else if (callReturnCheck.value === "continue-workflow") {
2907
- const result = isFirstInvocation ? await triggerFirstInvocation({ workflowContext, useJSONContent, telemetry, debug }) : await triggerRouteFunction({
3128
+ const result = isFirstInvocation ? await triggerFirstInvocation({
3129
+ workflowContext,
3130
+ useJSONContent,
3131
+ telemetry,
3132
+ debug,
3133
+ invokeCount
3134
+ }) : await triggerRouteFunction({
2908
3135
  onStep: async () => routeFunction(workflowContext),
2909
3136
  onCleanup: async (result2) => {
2910
3137
  await triggerWorkflowDelete(workflowContext, result2, debug);
@@ -2939,91 +3166,6 @@ var serveBase = (routeFunction, telemetry, options) => {
2939
3166
  return { handler: safeHandler };
2940
3167
  };
2941
3168
 
2942
- // src/serve/serve-many.ts
2943
- var serveManyBase = ({
2944
- workflows,
2945
- getWorkflowId
2946
- }) => {
2947
- const workflowIds = [];
2948
- const workflowMap = Object.fromEntries(
2949
- Object.entries(workflows).map((workflow) => {
2950
- const workflowId = workflow[0];
2951
- if (workflowIds.includes(workflowId)) {
2952
- throw new WorkflowError(
2953
- `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
2954
- );
2955
- }
2956
- if (workflowId.includes("/")) {
2957
- throw new WorkflowError(
2958
- `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
2959
- );
2960
- }
2961
- workflowIds.push(workflowId);
2962
- workflow[1].workflowId = workflowId;
2963
- return [workflowId, workflow[1].handler];
2964
- })
2965
- );
2966
- return {
2967
- handler: async (...params) => {
2968
- const pickedWorkflowId = getWorkflowId(...params);
2969
- if (!pickedWorkflowId) {
2970
- throw new WorkflowError(`Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`);
2971
- }
2972
- const workflow = workflowMap[pickedWorkflowId];
2973
- if (!workflow) {
2974
- throw new WorkflowError(`No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`);
2975
- }
2976
- return await workflow(...params);
2977
- }
2978
- };
2979
- };
2980
- var createInvokeCallback = (telemetry) => {
2981
- const invokeCallback = async (settings, invokeStep, context) => {
2982
- const { body, workflow, headers = {}, workflowRunId = getWorkflowRunId(), retries } = settings;
2983
- const { workflowId } = workflow;
2984
- if (!workflowId) {
2985
- throw new WorkflowError("You can only invoke workflow which has a workflowId");
2986
- }
2987
- const { headers: invokerHeaders } = getHeaders({
2988
- initHeaderValue: "false",
2989
- workflowRunId: context.workflowRunId,
2990
- workflowUrl: context.url,
2991
- userHeaders: context.headers,
2992
- failureUrl: context.failureUrl,
2993
- retries: context.retries,
2994
- telemetry
2995
- });
2996
- invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
2997
- const newUrl = context.url.replace(/[^/]+$/, workflowId);
2998
- const { headers: triggerHeaders } = getHeaders({
2999
- initHeaderValue: "true",
3000
- workflowRunId,
3001
- workflowUrl: newUrl,
3002
- userHeaders: new Headers(headers),
3003
- retries,
3004
- telemetry
3005
- });
3006
- triggerHeaders["Upstash-Workflow-Invoke"] = "true";
3007
- const request = {
3008
- body: JSON.stringify(body),
3009
- headers: Object.fromEntries(
3010
- Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
3011
- ),
3012
- workflowRunId,
3013
- workflowUrl: context.url,
3014
- step: invokeStep
3015
- };
3016
- await context.qstashClient.publish({
3017
- headers: triggerHeaders,
3018
- method: "POST",
3019
- body: JSON.stringify(request),
3020
- url: newUrl
3021
- });
3022
- return void 0;
3023
- };
3024
- return invokeCallback;
3025
- };
3026
-
3027
3169
  // platforms/nextjs.ts
3028
3170
  var appTelemetry = {
3029
3171
  sdk: SDK_TELEMETRY,
@@ -3048,21 +3190,22 @@ var serve = (routeFunction, options) => {
3048
3190
  };
3049
3191
  };
3050
3192
  var createWorkflow = (...params) => {
3051
- const { POST } = serve(...params);
3193
+ const [routeFunction, options = {}] = params;
3052
3194
  return {
3053
- callback: createInvokeCallback(appTelemetry),
3054
- handler: POST,
3195
+ routeFunction,
3196
+ options,
3055
3197
  workflowId: void 0
3056
3198
  };
3057
3199
  };
3058
- var serveMany = (workflows) => {
3200
+ var serveMany = (workflows, options) => {
3059
3201
  return {
3060
3202
  POST: serveManyBase({
3061
3203
  workflows,
3062
- getWorkflowId(params) {
3063
- const components = params.url.split("/");
3064
- return components[components.length - 1];
3065
- }
3204
+ getUrl(params) {
3205
+ return params.url;
3206
+ },
3207
+ serveMethod: (...params) => serve(...params).POST,
3208
+ options
3066
3209
  }).handler
3067
3210
  };
3068
3211
  };
@@ -3091,24 +3234,24 @@ var servePagesRouter = (routeFunction, options) => {
3091
3234
  };
3092
3235
  };
3093
3236
  var createWorkflowPagesRouter = (...params) => {
3094
- const { handler } = servePagesRouter(...params);
3237
+ const [routeFunction, options = {}] = params;
3095
3238
  return {
3096
- callback: createInvokeCallback(pagesTelemetry),
3097
- handler,
3239
+ routeFunction,
3240
+ options,
3098
3241
  workflowId: void 0
3099
3242
  };
3100
3243
  };
3101
- var serveManyPagesRouter = (workflows) => {
3244
+ var serveManyPagesRouter = (workflows, options) => {
3102
3245
  return serveManyBase({
3103
3246
  workflows,
3104
- getWorkflowId(request_) {
3247
+ getUrl(request_) {
3105
3248
  const protocol = request_.headers["x-forwarded-proto"];
3106
3249
  const host = request_.headers.host;
3107
3250
  const baseUrl = `${protocol}://${host}`;
3108
- const url = `${baseUrl}${request_.url}`;
3109
- const components = url.split("/");
3110
- return components[components.length - 1];
3111
- }
3251
+ return `${baseUrl}${request_.url}`;
3252
+ },
3253
+ serveMethod: (...params) => servePagesRouter(...params).handler,
3254
+ options
3112
3255
  });
3113
3256
  };
3114
3257
  // Annotate the CommonJS export names for ESM import in node: