@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/astro.js CHANGED
@@ -85,6 +85,7 @@ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
85
85
  var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
86
86
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
87
87
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
88
+ var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
88
89
  var WORKFLOW_PROTOCOL_VERSION = "1";
89
90
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
90
91
  var DEFAULT_CONTENT_TYPE = "application/json";
@@ -266,8 +267,9 @@ var LazyCallStep = class extends BaseLazyStep {
266
267
  headers;
267
268
  retries;
268
269
  timeout;
270
+ flowControl;
269
271
  stepType = "Call";
270
- constructor(stepName, url, method, body, headers, retries, timeout) {
272
+ constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
271
273
  super(stepName);
272
274
  this.url = url;
273
275
  this.method = method;
@@ -275,6 +277,7 @@ var LazyCallStep = class extends BaseLazyStep {
275
277
  this.headers = headers;
276
278
  this.retries = retries;
277
279
  this.timeout = timeout;
280
+ this.flowControl = flowControl;
278
281
  }
279
282
  getPlanStep(concurrent, targetStep) {
280
283
  return {
@@ -345,14 +348,22 @@ var LazyNotifyStep = class extends LazyFunctionStep {
345
348
  var LazyInvokeStep = class extends BaseLazyStep {
346
349
  stepType = "Invoke";
347
350
  params;
348
- constructor(stepName, { workflow, body, headers = {}, workflowRunId, retries }) {
351
+ constructor(stepName, {
352
+ workflow,
353
+ body,
354
+ headers = {},
355
+ workflowRunId,
356
+ retries,
357
+ flowControl
358
+ }) {
349
359
  super(stepName);
350
360
  this.params = {
351
361
  workflow,
352
362
  body,
353
363
  headers,
354
364
  workflowRunId: getWorkflowRunId(workflowRunId),
355
- retries
365
+ retries,
366
+ flowControl
356
367
  };
357
368
  }
358
369
  getPlanStep(concurrent, targetStep) {
@@ -811,7 +822,8 @@ var triggerFirstInvocation = async ({
811
822
  workflowContext,
812
823
  useJSONContent,
813
824
  telemetry: telemetry2,
814
- debug
825
+ debug,
826
+ invokeCount
815
827
  }) => {
816
828
  const { headers } = getHeaders({
817
829
  initHeaderValue: "true",
@@ -820,7 +832,9 @@ var triggerFirstInvocation = async ({
820
832
  userHeaders: workflowContext.headers,
821
833
  failureUrl: workflowContext.failureUrl,
822
834
  retries: workflowContext.retries,
823
- telemetry: telemetry2
835
+ telemetry: telemetry2,
836
+ invokeCount,
837
+ flowControl: workflowContext.flowControl
824
838
  });
825
839
  if (workflowContext.headers.get("content-type")) {
826
840
  headers["content-type"] = workflowContext.headers.get("content-type");
@@ -926,6 +940,7 @@ var handleThirdPartyCallResult = async ({
926
940
  failureUrl,
927
941
  retries,
928
942
  telemetry: telemetry2,
943
+ flowControl,
929
944
  debug
930
945
  }) => {
931
946
  try {
@@ -973,6 +988,7 @@ ${atob(callbackMessage.body ?? "")}`
973
988
  const stepType = request.headers.get("Upstash-Workflow-StepType");
974
989
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
975
990
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
991
+ const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
976
992
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
977
993
  throw new Error(
978
994
  `Missing info in callback message source header: ${JSON.stringify({
@@ -993,7 +1009,9 @@ ${atob(callbackMessage.body ?? "")}`
993
1009
  userHeaders,
994
1010
  failureUrl,
995
1011
  retries,
996
- telemetry: telemetry2
1012
+ telemetry: telemetry2,
1013
+ invokeCount: Number(invokeCount),
1014
+ flowControl
997
1015
  });
998
1016
  const callResponse = {
999
1017
  status: callbackMessage.status,
@@ -1049,7 +1067,10 @@ var getHeaders = ({
1049
1067
  step,
1050
1068
  callRetries,
1051
1069
  callTimeout,
1052
- telemetry: telemetry2
1070
+ telemetry: telemetry2,
1071
+ invokeCount,
1072
+ flowControl,
1073
+ callFlowControl
1053
1074
  }) => {
1054
1075
  const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1055
1076
  const baseHeaders = {
@@ -1061,6 +1082,9 @@ var getHeaders = ({
1061
1082
  "content-type": contentType,
1062
1083
  ...telemetry2 ? getTelemetryHeaders(telemetry2) : {}
1063
1084
  };
1085
+ if (invokeCount !== void 0 && !step?.callUrl) {
1086
+ baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
1087
+ }
1064
1088
  if (!step?.callUrl) {
1065
1089
  baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1066
1090
  }
@@ -1069,13 +1093,23 @@ var getHeaders = ({
1069
1093
  }
1070
1094
  if (failureUrl) {
1071
1095
  baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
1096
+ baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
1097
+ baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
1098
+ baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
1099
+ baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
1100
+ baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
1101
+ if (retries !== void 0) {
1102
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1103
+ }
1104
+ if (flowControl) {
1105
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1106
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
1107
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
1108
+ }
1072
1109
  if (!step?.callUrl) {
1073
1110
  baseHeaders["Upstash-Failure-Callback"] = failureUrl;
1074
1111
  }
1075
1112
  }
1076
- if (step?.stepType === "Invoke") {
1077
- baseHeaders["upstash-workflow-invoke"] = "true";
1078
- }
1079
1113
  if (step?.callUrl) {
1080
1114
  baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
1081
1115
  baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
@@ -1083,9 +1117,26 @@ var getHeaders = ({
1083
1117
  baseHeaders["Upstash-Callback-Retries"] = retries.toString();
1084
1118
  baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1085
1119
  }
1086
- } else if (retries !== void 0) {
1087
- baseHeaders["Upstash-Retries"] = retries.toString();
1088
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1120
+ if (callFlowControl) {
1121
+ const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
1122
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1123
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1124
+ }
1125
+ if (flowControl) {
1126
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1127
+ baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
1128
+ baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
1129
+ }
1130
+ } else {
1131
+ if (flowControl) {
1132
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1133
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1134
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1135
+ }
1136
+ if (retries !== void 0) {
1137
+ baseHeaders["Upstash-Retries"] = retries.toString();
1138
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1139
+ }
1089
1140
  }
1090
1141
  if (userHeaders) {
1091
1142
  for (const header of userHeaders.keys()) {
@@ -1120,6 +1171,7 @@ var getHeaders = ({
1120
1171
  "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
1121
1172
  "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1122
1173
  "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1174
+ [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
1123
1175
  "Upstash-Workflow-CallType": "toCallback"
1124
1176
  }
1125
1177
  };
@@ -1177,9 +1229,159 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
1177
1229
  );
1178
1230
  }
1179
1231
  };
1232
+ var prepareFlowControl = (flowControl) => {
1233
+ const parallelism = flowControl.parallelism?.toString();
1234
+ const rate = flowControl.ratePerSecond?.toString();
1235
+ const controlValue = [
1236
+ parallelism ? `parallelism=${parallelism}` : void 0,
1237
+ rate ? `rate=${rate}` : void 0
1238
+ ].filter(Boolean);
1239
+ if (controlValue.length === 0) {
1240
+ throw new import_qstash3.QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
1241
+ }
1242
+ return {
1243
+ flowControlKey: flowControl.key,
1244
+ flowControlValue: controlValue.join(", ")
1245
+ };
1246
+ };
1180
1247
 
1181
1248
  // src/context/auto-executor.ts
1182
1249
  var import_qstash4 = require("@upstash/qstash");
1250
+
1251
+ // src/serve/serve-many.ts
1252
+ var getWorkflowId = (url) => {
1253
+ const components = url.split("/");
1254
+ const lastComponent = components[components.length - 1];
1255
+ return lastComponent.split("?")[0];
1256
+ };
1257
+ var serveManyBase = ({
1258
+ workflows,
1259
+ getUrl,
1260
+ serveMethod,
1261
+ options
1262
+ }) => {
1263
+ const workflowIds = [];
1264
+ const workflowMap = Object.fromEntries(
1265
+ Object.entries(workflows).map((workflow) => {
1266
+ const workflowId = workflow[0];
1267
+ if (workflowIds.includes(workflowId)) {
1268
+ throw new WorkflowError(
1269
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
1270
+ );
1271
+ }
1272
+ if (workflowId.includes("/")) {
1273
+ throw new WorkflowError(
1274
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
1275
+ );
1276
+ }
1277
+ workflowIds.push(workflowId);
1278
+ workflow[1].workflowId = workflowId;
1279
+ workflow[1].options = {
1280
+ ...options,
1281
+ ...workflow[1].options
1282
+ };
1283
+ const params = [workflow[1].routeFunction, workflow[1].options];
1284
+ const handler = serveMethod(...params);
1285
+ return [workflowId, handler];
1286
+ })
1287
+ );
1288
+ return {
1289
+ handler: async (...params) => {
1290
+ const url = getUrl(...params);
1291
+ const pickedWorkflowId = getWorkflowId(url);
1292
+ if (!pickedWorkflowId) {
1293
+ return new Response(
1294
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
1295
+ {
1296
+ status: 404
1297
+ }
1298
+ );
1299
+ }
1300
+ const workflow = workflowMap[pickedWorkflowId];
1301
+ if (!workflow) {
1302
+ return new Response(
1303
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
1304
+ {
1305
+ status: 404
1306
+ }
1307
+ );
1308
+ }
1309
+ return await workflow(...params);
1310
+ }
1311
+ };
1312
+ };
1313
+ var invokeWorkflow = async ({
1314
+ settings,
1315
+ invokeStep,
1316
+ context,
1317
+ invokeCount,
1318
+ telemetry: telemetry2
1319
+ }) => {
1320
+ const {
1321
+ body,
1322
+ workflow,
1323
+ headers = {},
1324
+ workflowRunId = getWorkflowRunId(),
1325
+ retries,
1326
+ flowControl
1327
+ } = settings;
1328
+ const { workflowId } = workflow;
1329
+ const {
1330
+ retries: workflowRetries,
1331
+ failureFunction,
1332
+ failureUrl,
1333
+ useJSONContent,
1334
+ flowControl: workflowFlowControl
1335
+ } = workflow.options;
1336
+ if (!workflowId) {
1337
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1338
+ }
1339
+ const { headers: invokerHeaders } = getHeaders({
1340
+ initHeaderValue: "false",
1341
+ workflowRunId: context.workflowRunId,
1342
+ workflowUrl: context.url,
1343
+ userHeaders: context.headers,
1344
+ failureUrl: context.failureUrl,
1345
+ retries: context.retries,
1346
+ telemetry: telemetry2,
1347
+ invokeCount,
1348
+ flowControl: context.flowControl
1349
+ });
1350
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1351
+ const newUrl = context.url.replace(/[^/]+$/, workflowId);
1352
+ const { headers: triggerHeaders } = getHeaders({
1353
+ initHeaderValue: "true",
1354
+ workflowRunId,
1355
+ workflowUrl: newUrl,
1356
+ userHeaders: new Headers(headers),
1357
+ retries: retries ?? workflowRetries,
1358
+ telemetry: telemetry2,
1359
+ failureUrl: failureFunction ? newUrl : failureUrl,
1360
+ invokeCount: invokeCount + 1,
1361
+ flowControl: flowControl ?? workflowFlowControl
1362
+ });
1363
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1364
+ if (useJSONContent) {
1365
+ triggerHeaders["content-type"] = "application/json";
1366
+ }
1367
+ const request = {
1368
+ body: JSON.stringify(body),
1369
+ headers: Object.fromEntries(
1370
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1371
+ ),
1372
+ workflowRunId,
1373
+ workflowUrl: context.url,
1374
+ step: invokeStep
1375
+ };
1376
+ await context.qstashClient.publish({
1377
+ headers: triggerHeaders,
1378
+ method: "POST",
1379
+ body: JSON.stringify(request),
1380
+ url: newUrl
1381
+ });
1382
+ };
1383
+
1384
+ // src/context/auto-executor.ts
1183
1385
  var AutoExecutor = class _AutoExecutor {
1184
1386
  context;
1185
1387
  promises = /* @__PURE__ */ new WeakMap();
@@ -1188,14 +1390,16 @@ var AutoExecutor = class _AutoExecutor {
1188
1390
  nonPlanStepCount;
1189
1391
  steps;
1190
1392
  indexInCurrentList = 0;
1393
+ invokeCount;
1191
1394
  telemetry;
1192
1395
  stepCount = 0;
1193
1396
  planStepCount = 0;
1194
1397
  executingStep = false;
1195
- constructor(context, steps, telemetry2, debug) {
1398
+ constructor(context, steps, telemetry2, invokeCount, debug) {
1196
1399
  this.context = context;
1197
1400
  this.steps = steps;
1198
1401
  this.telemetry = telemetry2;
1402
+ this.invokeCount = invokeCount ?? 0;
1199
1403
  this.debug = debug;
1200
1404
  this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
1201
1405
  }
@@ -1418,7 +1622,9 @@ var AutoExecutor = class _AutoExecutor {
1418
1622
  step: waitStep,
1419
1623
  failureUrl: this.context.failureUrl,
1420
1624
  retries: this.context.retries,
1421
- telemetry: this.telemetry
1625
+ telemetry: this.telemetry,
1626
+ invokeCount: this.invokeCount,
1627
+ flowControl: this.context.flowControl
1422
1628
  });
1423
1629
  const waitBody = {
1424
1630
  url: this.context.url,
@@ -1446,17 +1652,13 @@ var AutoExecutor = class _AutoExecutor {
1446
1652
  if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1447
1653
  const invokeStep = steps[0];
1448
1654
  const lazyInvokeStep = lazySteps[0];
1449
- await lazyInvokeStep.params.workflow.callback(
1450
- {
1451
- body: lazyInvokeStep.params.body,
1452
- headers: lazyInvokeStep.params.headers,
1453
- workflowRunId: lazyInvokeStep.params.workflowRunId,
1454
- workflow: lazyInvokeStep.params.workflow,
1455
- retries: lazyInvokeStep.params.retries
1456
- },
1655
+ await invokeWorkflow({
1656
+ settings: lazyInvokeStep.params,
1457
1657
  invokeStep,
1458
- this.context
1459
- );
1658
+ context: this.context,
1659
+ invokeCount: this.invokeCount,
1660
+ telemetry: this.telemetry
1661
+ });
1460
1662
  throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1461
1663
  }
1462
1664
  const result = await this.context.qstashClient.batchJSON(
@@ -1472,11 +1674,14 @@ var AutoExecutor = class _AutoExecutor {
1472
1674
  retries: this.context.retries,
1473
1675
  callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
1474
1676
  callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
1475
- telemetry: this.telemetry
1677
+ telemetry: this.telemetry,
1678
+ invokeCount: this.invokeCount,
1679
+ flowControl: this.context.flowControl,
1680
+ callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
1476
1681
  });
1477
1682
  const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1478
1683
  singleStep.out = JSON.stringify(singleStep.out);
1479
- return singleStep.callUrl ? (
1684
+ return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
1480
1685
  // if the step is a third party call, we call the third party
1481
1686
  // url (singleStep.callUrl) and pass information about the workflow
1482
1687
  // in the headers (handled in getHeaders). QStash makes the request
@@ -1776,9 +1981,10 @@ var wrapTools = ({
1776
1981
  return Object.fromEntries(
1777
1982
  Object.entries(tools).map((toolInfo) => {
1778
1983
  const [toolName, tool3] = toolInfo;
1984
+ const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
1779
1985
  const aiSDKTool = convertToAISDKTool(tool3);
1780
1986
  const execute = aiSDKTool.execute;
1781
- if (execute) {
1987
+ if (execute && executeAsStep) {
1782
1988
  const wrappedExecute = (...params) => {
1783
1989
  return context.run(`Run tool ${toolName}`, () => execute(...params));
1784
1990
  };
@@ -2132,6 +2338,11 @@ var WorkflowContext = class {
2132
2338
  * Number of retries
2133
2339
  */
2134
2340
  retries;
2341
+ /**
2342
+ * Settings for controlling the number of active requests
2343
+ * and number of requests per second with the same key.
2344
+ */
2345
+ flowControl;
2135
2346
  constructor({
2136
2347
  qstashClient,
2137
2348
  workflowRunId,
@@ -2143,7 +2354,9 @@ var WorkflowContext = class {
2143
2354
  initialPayload,
2144
2355
  env,
2145
2356
  retries,
2146
- telemetry: telemetry2
2357
+ telemetry: telemetry2,
2358
+ invokeCount,
2359
+ flowControl
2147
2360
  }) {
2148
2361
  this.qstashClient = qstashClient;
2149
2362
  this.workflowRunId = workflowRunId;
@@ -2154,7 +2367,8 @@ var WorkflowContext = class {
2154
2367
  this.requestPayload = initialPayload;
2155
2368
  this.env = env ?? {};
2156
2369
  this.retries = retries ?? DEFAULT_RETRIES;
2157
- this.executor = new AutoExecutor(this, this.steps, telemetry2, debug);
2370
+ this.flowControl = flowControl;
2371
+ this.executor = new AutoExecutor(this, this.steps, telemetry2, invokeCount, debug);
2158
2372
  }
2159
2373
  /**
2160
2374
  * Executes a workflow step
@@ -2256,7 +2470,7 @@ var WorkflowContext = class {
2256
2470
  * }
2257
2471
  */
2258
2472
  async call(stepName, settings) {
2259
- const { url, method = "GET", body, headers = {}, retries = 0, timeout } = settings;
2473
+ const { url, method = "GET", body, headers = {}, retries = 0, timeout, flowControl } = settings;
2260
2474
  const result = await this.addStep(
2261
2475
  new LazyCallStep(
2262
2476
  stepName,
@@ -2265,7 +2479,8 @@ var WorkflowContext = class {
2265
2479
  body,
2266
2480
  headers,
2267
2481
  retries,
2268
- timeout
2482
+ timeout,
2483
+ flowControl
2269
2484
  )
2270
2485
  );
2271
2486
  if (typeof result === "string") {
@@ -2502,7 +2717,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
2502
2717
  failureUrl: context.failureUrl,
2503
2718
  initialPayload: context.requestPayload,
2504
2719
  env: context.env,
2505
- retries: context.retries
2720
+ retries: context.retries,
2721
+ flowControl: context.flowControl
2506
2722
  });
2507
2723
  try {
2508
2724
  await routeFunction(disabledContext);
@@ -2655,7 +2871,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
2655
2871
  };
2656
2872
  }
2657
2873
  };
2658
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, debug) => {
2874
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
2659
2875
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
2660
2876
  return ok("not-failure-callback");
2661
2877
  }
@@ -2667,22 +2883,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
2667
2883
  );
2668
2884
  }
2669
2885
  try {
2670
- const { status, header, body, url, sourceHeader, sourceBody, workflowRunId } = JSON.parse(
2671
- requestPayload
2672
- );
2886
+ const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
2673
2887
  const decodedBody = body ? decodeBase64(body) : "{}";
2674
2888
  const errorPayload = JSON.parse(decodedBody);
2675
2889
  const workflowContext = new WorkflowContext({
2676
2890
  qstashClient,
2677
2891
  workflowRunId,
2678
2892
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
2679
- headers: recreateUserHeaders(new Headers(sourceHeader)),
2893
+ headers: recreateUserHeaders(request.headers),
2680
2894
  steps: [],
2681
2895
  url,
2682
2896
  failureUrl: url,
2683
2897
  debug,
2684
2898
  env,
2685
2899
  retries,
2900
+ flowControl,
2686
2901
  telemetry: void 0
2687
2902
  // not going to make requests in authentication check
2688
2903
  });
@@ -2809,7 +3024,8 @@ var serveBase = (routeFunction, telemetry2, options) => {
2809
3024
  env,
2810
3025
  retries,
2811
3026
  useJSONContent,
2812
- disableTelemetry
3027
+ disableTelemetry,
3028
+ flowControl
2813
3029
  } = processOptions(options);
2814
3030
  telemetry2 = disableTelemetry ? void 0 : telemetry2;
2815
3031
  const debug = WorkflowLogger.getLogger(verbose);
@@ -2850,6 +3066,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
2850
3066
  failureFunction,
2851
3067
  env,
2852
3068
  retries,
3069
+ flowControl,
2853
3070
  debug
2854
3071
  );
2855
3072
  if (failureCheck.isErr()) {
@@ -2858,6 +3075,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
2858
3075
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
2859
3076
  return onStepFinish(workflowRunId, "failure-callback");
2860
3077
  }
3078
+ const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
2861
3079
  const workflowContext = new WorkflowContext({
2862
3080
  qstashClient,
2863
3081
  workflowRunId,
@@ -2869,7 +3087,9 @@ var serveBase = (routeFunction, telemetry2, options) => {
2869
3087
  debug,
2870
3088
  env,
2871
3089
  retries,
2872
- telemetry: telemetry2
3090
+ telemetry: telemetry2,
3091
+ invokeCount,
3092
+ flowControl
2873
3093
  });
2874
3094
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
2875
3095
  routeFunction,
@@ -2892,6 +3112,7 @@ var serveBase = (routeFunction, telemetry2, options) => {
2892
3112
  workflowUrl,
2893
3113
  failureUrl: workflowFailureUrl,
2894
3114
  retries,
3115
+ flowControl,
2895
3116
  telemetry: telemetry2,
2896
3117
  debug
2897
3118
  });
@@ -2901,7 +3122,13 @@ var serveBase = (routeFunction, telemetry2, options) => {
2901
3122
  });
2902
3123
  throw callReturnCheck.error;
2903
3124
  } else if (callReturnCheck.value === "continue-workflow") {
2904
- const result = isFirstInvocation ? await triggerFirstInvocation({ workflowContext, useJSONContent, telemetry: telemetry2, debug }) : await triggerRouteFunction({
3125
+ const result = isFirstInvocation ? await triggerFirstInvocation({
3126
+ workflowContext,
3127
+ useJSONContent,
3128
+ telemetry: telemetry2,
3129
+ debug,
3130
+ invokeCount
3131
+ }) : await triggerRouteFunction({
2905
3132
  onStep: async () => routeFunction(workflowContext),
2906
3133
  onCleanup: async (result2) => {
2907
3134
  await triggerWorkflowDelete(workflowContext, result2, debug);
@@ -2936,91 +3163,6 @@ var serveBase = (routeFunction, telemetry2, options) => {
2936
3163
  return { handler: safeHandler };
2937
3164
  };
2938
3165
 
2939
- // src/serve/serve-many.ts
2940
- var serveManyBase = ({
2941
- workflows,
2942
- getWorkflowId
2943
- }) => {
2944
- const workflowIds = [];
2945
- const workflowMap = Object.fromEntries(
2946
- Object.entries(workflows).map((workflow) => {
2947
- const workflowId = workflow[0];
2948
- if (workflowIds.includes(workflowId)) {
2949
- throw new WorkflowError(
2950
- `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
2951
- );
2952
- }
2953
- if (workflowId.includes("/")) {
2954
- throw new WorkflowError(
2955
- `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
2956
- );
2957
- }
2958
- workflowIds.push(workflowId);
2959
- workflow[1].workflowId = workflowId;
2960
- return [workflowId, workflow[1].handler];
2961
- })
2962
- );
2963
- return {
2964
- handler: async (...params) => {
2965
- const pickedWorkflowId = getWorkflowId(...params);
2966
- if (!pickedWorkflowId) {
2967
- throw new WorkflowError(`Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`);
2968
- }
2969
- const workflow = workflowMap[pickedWorkflowId];
2970
- if (!workflow) {
2971
- throw new WorkflowError(`No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`);
2972
- }
2973
- return await workflow(...params);
2974
- }
2975
- };
2976
- };
2977
- var createInvokeCallback = (telemetry2) => {
2978
- const invokeCallback = async (settings, invokeStep, context) => {
2979
- const { body, workflow, headers = {}, workflowRunId = getWorkflowRunId(), retries } = settings;
2980
- const { workflowId } = workflow;
2981
- if (!workflowId) {
2982
- throw new WorkflowError("You can only invoke workflow which has a workflowId");
2983
- }
2984
- const { headers: invokerHeaders } = getHeaders({
2985
- initHeaderValue: "false",
2986
- workflowRunId: context.workflowRunId,
2987
- workflowUrl: context.url,
2988
- userHeaders: context.headers,
2989
- failureUrl: context.failureUrl,
2990
- retries: context.retries,
2991
- telemetry: telemetry2
2992
- });
2993
- invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
2994
- const newUrl = context.url.replace(/[^/]+$/, workflowId);
2995
- const { headers: triggerHeaders } = getHeaders({
2996
- initHeaderValue: "true",
2997
- workflowRunId,
2998
- workflowUrl: newUrl,
2999
- userHeaders: new Headers(headers),
3000
- retries,
3001
- telemetry: telemetry2
3002
- });
3003
- triggerHeaders["Upstash-Workflow-Invoke"] = "true";
3004
- const request = {
3005
- body: JSON.stringify(body),
3006
- headers: Object.fromEntries(
3007
- Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
3008
- ),
3009
- workflowRunId,
3010
- workflowUrl: context.url,
3011
- step: invokeStep
3012
- };
3013
- await context.qstashClient.publish({
3014
- headers: triggerHeaders,
3015
- method: "POST",
3016
- body: JSON.stringify(request),
3017
- url: newUrl
3018
- });
3019
- return void 0;
3020
- };
3021
- return invokeCallback;
3022
- };
3023
-
3024
3166
  // platforms/astro.ts
3025
3167
  var telemetry = {
3026
3168
  sdk: SDK_TELEMETRY,
@@ -3039,21 +3181,26 @@ function serve(routeFunction, options) {
3039
3181
  return { POST };
3040
3182
  }
3041
3183
  var createWorkflow = (...params) => {
3042
- const { POST: handler } = serve(...params);
3184
+ const [routeFunction, options = {}] = params;
3043
3185
  return {
3044
- callback: createInvokeCallback(telemetry),
3045
- handler,
3046
- workflowId: void 0
3186
+ workflowId: void 0,
3187
+ // @ts-expect-error because astro route function has another parameeter,
3188
+ // the RouteFunction type can't cover this. We need to make RouteFunction
3189
+ // accept more variables than simply the context. Until then, ignoring the
3190
+ // error here. Tested the usage in astro project and it's fine. TODO.
3191
+ routeFunction,
3192
+ options
3047
3193
  };
3048
3194
  };
3049
- var serveMany = (workflows) => {
3195
+ var serveMany = (workflows, options) => {
3050
3196
  return {
3051
3197
  POST: serveManyBase({
3052
3198
  workflows,
3053
- getWorkflowId(...params) {
3054
- const components = params[0].request.url.split("/");
3055
- return components[components.length - 1];
3056
- }
3199
+ getUrl(...params) {
3200
+ return params[0].request.url;
3201
+ },
3202
+ serveMethod: (...params) => serve(...params).POST,
3203
+ options
3057
3204
  }).handler
3058
3205
  };
3059
3206
  };