@upstash/workflow 0.2.8 → 0.2.10-hono-generics

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/solidjs.js CHANGED
@@ -83,12 +83,13 @@ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
83
83
  var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
84
84
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
85
85
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
86
+ var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
86
87
  var WORKFLOW_PROTOCOL_VERSION = "1";
87
88
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
88
89
  var DEFAULT_CONTENT_TYPE = "application/json";
89
90
  var NO_CONCURRENCY = 1;
90
91
  var DEFAULT_RETRIES = 3;
91
- var VERSION = "v0.2.3";
92
+ var VERSION = "v0.2.7";
92
93
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
93
94
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
94
95
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -135,6 +136,31 @@ var formatWorkflowError = (error) => {
135
136
  };
136
137
  };
137
138
 
139
+ // src/utils.ts
140
+ var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
141
+ var NANOID_LENGTH = 21;
142
+ function getRandomInt() {
143
+ return Math.floor(Math.random() * NANOID_CHARS.length);
144
+ }
145
+ function nanoid() {
146
+ return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
147
+ }
148
+ function getWorkflowRunId(id) {
149
+ return `wfr_${id ?? nanoid()}`;
150
+ }
151
+ function decodeBase64(base64) {
152
+ try {
153
+ const binString = atob(base64);
154
+ const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
155
+ return new TextDecoder().decode(intArray);
156
+ } catch (error) {
157
+ console.warn(
158
+ `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
159
+ );
160
+ return atob(base64);
161
+ }
162
+ }
163
+
138
164
  // src/context/steps.ts
139
165
  var BaseLazyStep = class {
140
166
  stepName;
@@ -239,8 +265,9 @@ var LazyCallStep = class extends BaseLazyStep {
239
265
  headers;
240
266
  retries;
241
267
  timeout;
268
+ flowControl;
242
269
  stepType = "Call";
243
- constructor(stepName, url, method, body, headers, retries, timeout) {
270
+ constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
244
271
  super(stepName);
245
272
  this.url = url;
246
273
  this.method = method;
@@ -248,6 +275,7 @@ var LazyCallStep = class extends BaseLazyStep {
248
275
  this.headers = headers;
249
276
  this.retries = retries;
250
277
  this.timeout = timeout;
278
+ this.flowControl = flowControl;
251
279
  }
252
280
  getPlanStep(concurrent, targetStep) {
253
281
  return {
@@ -315,6 +343,49 @@ var LazyNotifyStep = class extends LazyFunctionStep {
315
343
  });
316
344
  }
317
345
  };
346
+ var LazyInvokeStep = class extends BaseLazyStep {
347
+ stepType = "Invoke";
348
+ params;
349
+ constructor(stepName, {
350
+ workflow,
351
+ body,
352
+ headers = {},
353
+ workflowRunId,
354
+ retries,
355
+ flowControl
356
+ }) {
357
+ super(stepName);
358
+ this.params = {
359
+ workflow,
360
+ body,
361
+ headers,
362
+ workflowRunId: getWorkflowRunId(workflowRunId),
363
+ retries,
364
+ flowControl
365
+ };
366
+ }
367
+ getPlanStep(concurrent, targetStep) {
368
+ return {
369
+ stepId: 0,
370
+ stepName: this.stepName,
371
+ stepType: this.stepType,
372
+ concurrent,
373
+ targetStep
374
+ };
375
+ }
376
+ /**
377
+ * won't be used as it's the server who will add the result step
378
+ * in Invoke step.
379
+ */
380
+ getResultStep(concurrent, stepId) {
381
+ return Promise.resolve({
382
+ stepId,
383
+ stepName: this.stepName,
384
+ stepType: this.stepType,
385
+ concurrent
386
+ });
387
+ }
388
+ };
318
389
 
319
390
  // node_modules/neverthrow/dist/index.es.js
320
391
  var defaultErrorConfig = {
@@ -739,7 +810,8 @@ var StepTypes = [
739
810
  "SleepUntil",
740
811
  "Call",
741
812
  "Wait",
742
- "Notify"
813
+ "Notify",
814
+ "Invoke"
743
815
  ];
744
816
 
745
817
  // src/workflow-requests.ts
@@ -748,7 +820,8 @@ var triggerFirstInvocation = async ({
748
820
  workflowContext,
749
821
  useJSONContent,
750
822
  telemetry,
751
- debug
823
+ debug,
824
+ invokeCount
752
825
  }) => {
753
826
  const { headers } = getHeaders({
754
827
  initHeaderValue: "true",
@@ -757,7 +830,9 @@ var triggerFirstInvocation = async ({
757
830
  userHeaders: workflowContext.headers,
758
831
  failureUrl: workflowContext.failureUrl,
759
832
  retries: workflowContext.retries,
760
- telemetry
833
+ telemetry,
834
+ invokeCount,
835
+ flowControl: workflowContext.flowControl
761
836
  });
762
837
  if (workflowContext.headers.get("content-type")) {
763
838
  headers["content-type"] = workflowContext.headers.get("content-type");
@@ -803,8 +878,8 @@ var triggerRouteFunction = async ({
803
878
  debug
804
879
  }) => {
805
880
  try {
806
- await onStep();
807
- await onCleanup();
881
+ const result = await onStep();
882
+ await onCleanup(result);
808
883
  return ok("workflow-finished");
809
884
  } catch (error) {
810
885
  const error_ = error;
@@ -825,14 +900,15 @@ var triggerRouteFunction = async ({
825
900
  }
826
901
  }
827
902
  };
828
- var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
903
+ var triggerWorkflowDelete = async (workflowContext, result, debug, cancel = false) => {
829
904
  await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
830
905
  deletedWorkflowRunId: workflowContext.workflowRunId
831
906
  });
832
907
  await workflowContext.qstashClient.http.request({
833
908
  path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
834
909
  method: "DELETE",
835
- parseResponseAsJson: false
910
+ parseResponseAsJson: false,
911
+ body: JSON.stringify(result)
836
912
  });
837
913
  await debug?.log(
838
914
  "SUBMIT",
@@ -862,6 +938,7 @@ var handleThirdPartyCallResult = async ({
862
938
  failureUrl,
863
939
  retries,
864
940
  telemetry,
941
+ flowControl,
865
942
  debug
866
943
  }) => {
867
944
  try {
@@ -909,6 +986,7 @@ ${atob(callbackMessage.body ?? "")}`
909
986
  const stepType = request.headers.get("Upstash-Workflow-StepType");
910
987
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
911
988
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
989
+ const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
912
990
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
913
991
  throw new Error(
914
992
  `Missing info in callback message source header: ${JSON.stringify({
@@ -929,7 +1007,9 @@ ${atob(callbackMessage.body ?? "")}`
929
1007
  userHeaders,
930
1008
  failureUrl,
931
1009
  retries,
932
- telemetry
1010
+ telemetry,
1011
+ invokeCount: Number(invokeCount),
1012
+ flowControl
933
1013
  });
934
1014
  const callResponse = {
935
1015
  status: callbackMessage.status,
@@ -985,15 +1065,24 @@ var getHeaders = ({
985
1065
  step,
986
1066
  callRetries,
987
1067
  callTimeout,
988
- telemetry
1068
+ telemetry,
1069
+ invokeCount,
1070
+ flowControl,
1071
+ callFlowControl
989
1072
  }) => {
1073
+ const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
990
1074
  const baseHeaders = {
991
1075
  [WORKFLOW_INIT_HEADER]: initHeaderValue,
992
1076
  [WORKFLOW_ID_HEADER]: workflowRunId,
993
1077
  [WORKFLOW_URL_HEADER]: workflowUrl,
994
1078
  [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1079
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1080
+ "content-type": contentType,
995
1081
  ...telemetry ? getTelemetryHeaders(telemetry) : {}
996
1082
  };
1083
+ if (invokeCount !== void 0 && !step?.callUrl) {
1084
+ baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
1085
+ }
997
1086
  if (!step?.callUrl) {
998
1087
  baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
999
1088
  }
@@ -1010,6 +1099,11 @@ var getHeaders = ({
1010
1099
  if (retries !== void 0) {
1011
1100
  baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1012
1101
  }
1102
+ if (flowControl) {
1103
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1104
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
1105
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
1106
+ }
1013
1107
  if (!step?.callUrl) {
1014
1108
  baseHeaders["Upstash-Failure-Callback"] = failureUrl;
1015
1109
  }
@@ -1021,9 +1115,26 @@ var getHeaders = ({
1021
1115
  baseHeaders["Upstash-Callback-Retries"] = retries.toString();
1022
1116
  baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1023
1117
  }
1024
- } else if (retries !== void 0) {
1025
- baseHeaders["Upstash-Retries"] = retries.toString();
1026
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1118
+ if (callFlowControl) {
1119
+ const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
1120
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1121
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1122
+ }
1123
+ if (flowControl) {
1124
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1125
+ baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
1126
+ baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
1127
+ }
1128
+ } else {
1129
+ if (flowControl) {
1130
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
1131
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
1132
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
1133
+ }
1134
+ if (retries !== void 0) {
1135
+ baseHeaders["Upstash-Retries"] = retries.toString();
1136
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1137
+ }
1027
1138
  }
1028
1139
  if (userHeaders) {
1029
1140
  for (const header of userHeaders.keys()) {
@@ -1035,7 +1146,6 @@ var getHeaders = ({
1035
1146
  baseHeaders[`Upstash-Failure-Callback-Forward-${header}`] = userHeaders.get(header);
1036
1147
  }
1037
1148
  }
1038
- const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1039
1149
  if (step?.callHeaders) {
1040
1150
  const forwardedHeaders = Object.fromEntries(
1041
1151
  Object.entries(step.callHeaders).map(([header, value]) => [
@@ -1059,6 +1169,7 @@ var getHeaders = ({
1059
1169
  "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
1060
1170
  "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1061
1171
  "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1172
+ [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
1062
1173
  "Upstash-Workflow-CallType": "toCallback"
1063
1174
  }
1064
1175
  };
@@ -1085,8 +1196,7 @@ var getHeaders = ({
1085
1196
  "Upstash-Workflow-Runid": [workflowRunId],
1086
1197
  [WORKFLOW_INIT_HEADER]: ["false"],
1087
1198
  [WORKFLOW_URL_HEADER]: [workflowUrl],
1088
- "Upstash-Workflow-CallType": ["step"],
1089
- "Content-Type": [contentType]
1199
+ "Upstash-Workflow-CallType": ["step"]
1090
1200
  }
1091
1201
  };
1092
1202
  }
@@ -1117,9 +1227,98 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
1117
1227
  );
1118
1228
  }
1119
1229
  };
1230
+ var prepareFlowControl = (flowControl) => {
1231
+ const parallelism = flowControl.parallelism?.toString();
1232
+ const rate = flowControl.ratePerSecond?.toString();
1233
+ const controlValue = [
1234
+ parallelism ? `parallelism=${parallelism}` : void 0,
1235
+ rate ? `rate=${rate}` : void 0
1236
+ ].filter(Boolean);
1237
+ if (controlValue.length === 0) {
1238
+ throw new import_qstash3.QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
1239
+ }
1240
+ return {
1241
+ flowControlKey: flowControl.key,
1242
+ flowControlValue: controlValue.join(", ")
1243
+ };
1244
+ };
1120
1245
 
1121
1246
  // src/context/auto-executor.ts
1122
1247
  var import_qstash4 = require("@upstash/qstash");
1248
+
1249
+ // src/serve/serve-many.ts
1250
+ var invokeWorkflow = async ({
1251
+ settings,
1252
+ invokeStep,
1253
+ context,
1254
+ invokeCount,
1255
+ telemetry
1256
+ }) => {
1257
+ const {
1258
+ body,
1259
+ workflow,
1260
+ headers = {},
1261
+ workflowRunId = getWorkflowRunId(),
1262
+ retries,
1263
+ flowControl
1264
+ } = settings;
1265
+ const { workflowId } = workflow;
1266
+ const {
1267
+ retries: workflowRetries,
1268
+ failureFunction,
1269
+ failureUrl,
1270
+ useJSONContent,
1271
+ flowControl: workflowFlowControl
1272
+ } = workflow.options;
1273
+ if (!workflowId) {
1274
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1275
+ }
1276
+ const { headers: invokerHeaders } = getHeaders({
1277
+ initHeaderValue: "false",
1278
+ workflowRunId: context.workflowRunId,
1279
+ workflowUrl: context.url,
1280
+ userHeaders: context.headers,
1281
+ failureUrl: context.failureUrl,
1282
+ retries: context.retries,
1283
+ telemetry,
1284
+ invokeCount,
1285
+ flowControl: context.flowControl
1286
+ });
1287
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1288
+ const newUrl = context.url.replace(/[^/]+$/, workflowId);
1289
+ const { headers: triggerHeaders } = getHeaders({
1290
+ initHeaderValue: "true",
1291
+ workflowRunId,
1292
+ workflowUrl: newUrl,
1293
+ userHeaders: new Headers(headers),
1294
+ retries: retries ?? workflowRetries,
1295
+ telemetry,
1296
+ failureUrl: failureFunction ? newUrl : failureUrl,
1297
+ invokeCount: invokeCount + 1,
1298
+ flowControl: flowControl ?? workflowFlowControl
1299
+ });
1300
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1301
+ if (useJSONContent) {
1302
+ triggerHeaders["content-type"] = "application/json";
1303
+ }
1304
+ const request = {
1305
+ body: JSON.stringify(body),
1306
+ headers: Object.fromEntries(
1307
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1308
+ ),
1309
+ workflowRunId,
1310
+ workflowUrl: context.url,
1311
+ step: invokeStep
1312
+ };
1313
+ await context.qstashClient.publish({
1314
+ headers: triggerHeaders,
1315
+ method: "POST",
1316
+ body: JSON.stringify(request),
1317
+ url: newUrl
1318
+ });
1319
+ };
1320
+
1321
+ // src/context/auto-executor.ts
1123
1322
  var AutoExecutor = class _AutoExecutor {
1124
1323
  context;
1125
1324
  promises = /* @__PURE__ */ new WeakMap();
@@ -1128,14 +1327,16 @@ var AutoExecutor = class _AutoExecutor {
1128
1327
  nonPlanStepCount;
1129
1328
  steps;
1130
1329
  indexInCurrentList = 0;
1330
+ invokeCount;
1131
1331
  telemetry;
1132
1332
  stepCount = 0;
1133
1333
  planStepCount = 0;
1134
1334
  executingStep = false;
1135
- constructor(context, steps, telemetry, debug) {
1335
+ constructor(context, steps, telemetry, invokeCount, debug) {
1136
1336
  this.context = context;
1137
1337
  this.steps = steps;
1138
1338
  this.telemetry = telemetry;
1339
+ this.invokeCount = invokeCount ?? 0;
1139
1340
  this.debug = debug;
1140
1341
  this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
1141
1342
  }
@@ -1358,7 +1559,9 @@ var AutoExecutor = class _AutoExecutor {
1358
1559
  step: waitStep,
1359
1560
  failureUrl: this.context.failureUrl,
1360
1561
  retries: this.context.retries,
1361
- telemetry: this.telemetry
1562
+ telemetry: this.telemetry,
1563
+ invokeCount: this.invokeCount,
1564
+ flowControl: this.context.flowControl
1362
1565
  });
1363
1566
  const waitBody = {
1364
1567
  url: this.context.url,
@@ -1381,7 +1584,19 @@ var AutoExecutor = class _AutoExecutor {
1381
1584
  method: "POST",
1382
1585
  parseResponseAsJson: false
1383
1586
  });
1384
- throw new WorkflowAbort(steps[0].stepName, steps[0]);
1587
+ throw new WorkflowAbort(waitStep.stepName, waitStep);
1588
+ }
1589
+ if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1590
+ const invokeStep = steps[0];
1591
+ const lazyInvokeStep = lazySteps[0];
1592
+ await invokeWorkflow({
1593
+ settings: lazyInvokeStep.params,
1594
+ invokeStep,
1595
+ context: this.context,
1596
+ invokeCount: this.invokeCount,
1597
+ telemetry: this.telemetry
1598
+ });
1599
+ throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1385
1600
  }
1386
1601
  const result = await this.context.qstashClient.batchJSON(
1387
1602
  steps.map((singleStep, index) => {
@@ -1396,11 +1611,14 @@ var AutoExecutor = class _AutoExecutor {
1396
1611
  retries: this.context.retries,
1397
1612
  callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
1398
1613
  callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
1399
- telemetry: this.telemetry
1614
+ telemetry: this.telemetry,
1615
+ invokeCount: this.invokeCount,
1616
+ flowControl: this.context.flowControl,
1617
+ callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
1400
1618
  });
1401
1619
  const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1402
1620
  singleStep.out = JSON.stringify(singleStep.out);
1403
- return singleStep.callUrl ? (
1621
+ return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
1404
1622
  // if the step is a third party call, we call the third party
1405
1623
  // url (singleStep.callUrl) and pass information about the workflow
1406
1624
  // in the headers (handled in getHeaders). QStash makes the request
@@ -2057,6 +2275,11 @@ var WorkflowContext = class {
2057
2275
  * Number of retries
2058
2276
  */
2059
2277
  retries;
2278
+ /**
2279
+ * Settings for controlling the number of active requests
2280
+ * and number of requests per second with the same key.
2281
+ */
2282
+ flowControl;
2060
2283
  constructor({
2061
2284
  qstashClient,
2062
2285
  workflowRunId,
@@ -2068,7 +2291,9 @@ var WorkflowContext = class {
2068
2291
  initialPayload,
2069
2292
  env,
2070
2293
  retries,
2071
- telemetry
2294
+ telemetry,
2295
+ invokeCount,
2296
+ flowControl
2072
2297
  }) {
2073
2298
  this.qstashClient = qstashClient;
2074
2299
  this.workflowRunId = workflowRunId;
@@ -2079,7 +2304,8 @@ var WorkflowContext = class {
2079
2304
  this.requestPayload = initialPayload;
2080
2305
  this.env = env ?? {};
2081
2306
  this.retries = retries ?? DEFAULT_RETRIES;
2082
- this.executor = new AutoExecutor(this, this.steps, telemetry, debug);
2307
+ this.flowControl = flowControl;
2308
+ this.executor = new AutoExecutor(this, this.steps, telemetry, invokeCount, debug);
2083
2309
  }
2084
2310
  /**
2085
2311
  * Executes a workflow step
@@ -2181,7 +2407,7 @@ var WorkflowContext = class {
2181
2407
  * }
2182
2408
  */
2183
2409
  async call(stepName, settings) {
2184
- const { url, method = "GET", body, headers = {}, retries = 0, timeout } = settings;
2410
+ const { url, method = "GET", body, headers = {}, retries = 0, timeout, flowControl } = settings;
2185
2411
  const result = await this.addStep(
2186
2412
  new LazyCallStep(
2187
2413
  stepName,
@@ -2190,7 +2416,8 @@ var WorkflowContext = class {
2190
2416
  body,
2191
2417
  headers,
2192
2418
  retries,
2193
- timeout
2419
+ timeout,
2420
+ flowControl
2194
2421
  )
2195
2422
  );
2196
2423
  if (typeof result === "string") {
@@ -2299,6 +2526,13 @@ var WorkflowContext = class {
2299
2526
  return result;
2300
2527
  }
2301
2528
  }
2529
+ async invoke(stepName, settings) {
2530
+ const result = await this.addStep(new LazyInvokeStep(stepName, settings));
2531
+ return {
2532
+ ...result,
2533
+ body: result.body ? JSON.parse(result.body) : void 0
2534
+ };
2535
+ }
2302
2536
  /**
2303
2537
  * Cancel the current workflow run
2304
2538
  *
@@ -2376,31 +2610,6 @@ var WorkflowLogger = class _WorkflowLogger {
2376
2610
  }
2377
2611
  };
2378
2612
 
2379
- // src/utils.ts
2380
- var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
2381
- var NANOID_LENGTH = 21;
2382
- function getRandomInt() {
2383
- return Math.floor(Math.random() * NANOID_CHARS.length);
2384
- }
2385
- function nanoid() {
2386
- return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
2387
- }
2388
- function getWorkflowRunId(id) {
2389
- return `wfr_${id ?? nanoid()}`;
2390
- }
2391
- function decodeBase64(base64) {
2392
- try {
2393
- const binString = atob(base64);
2394
- const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
2395
- return new TextDecoder().decode(intArray);
2396
- } catch (error) {
2397
- console.warn(
2398
- `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
2399
- );
2400
- return atob(base64);
2401
- }
2402
- }
2403
-
2404
2613
  // src/serve/authorization.ts
2405
2614
  var import_qstash8 = require("@upstash/qstash");
2406
2615
  var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
@@ -2445,7 +2654,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
2445
2654
  failureUrl: context.failureUrl,
2446
2655
  initialPayload: context.requestPayload,
2447
2656
  env: context.env,
2448
- retries: context.retries
2657
+ retries: context.retries,
2658
+ flowControl: context.flowControl
2449
2659
  });
2450
2660
  try {
2451
2661
  await routeFunction(disabledContext);
@@ -2598,7 +2808,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
2598
2808
  };
2599
2809
  }
2600
2810
  };
2601
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, debug) => {
2811
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
2602
2812
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
2603
2813
  return ok("not-failure-callback");
2604
2814
  }
@@ -2610,22 +2820,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
2610
2820
  );
2611
2821
  }
2612
2822
  try {
2613
- const { status, header, body, url, sourceHeader, sourceBody, workflowRunId } = JSON.parse(
2614
- requestPayload
2615
- );
2823
+ const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
2616
2824
  const decodedBody = body ? decodeBase64(body) : "{}";
2617
2825
  const errorPayload = JSON.parse(decodedBody);
2618
2826
  const workflowContext = new WorkflowContext({
2619
2827
  qstashClient,
2620
2828
  workflowRunId,
2621
2829
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
2622
- headers: recreateUserHeaders(new Headers(sourceHeader)),
2830
+ headers: recreateUserHeaders(request.headers),
2623
2831
  steps: [],
2624
2832
  url,
2625
2833
  failureUrl: url,
2626
2834
  debug,
2627
2835
  env,
2628
2836
  retries,
2837
+ flowControl,
2629
2838
  telemetry: void 0
2630
2839
  // not going to make requests in authentication check
2631
2840
  });
@@ -2752,7 +2961,8 @@ var serveBase = (routeFunction, telemetry, options) => {
2752
2961
  env,
2753
2962
  retries,
2754
2963
  useJSONContent,
2755
- disableTelemetry
2964
+ disableTelemetry,
2965
+ flowControl
2756
2966
  } = processOptions(options);
2757
2967
  telemetry = disableTelemetry ? void 0 : telemetry;
2758
2968
  const debug = WorkflowLogger.getLogger(verbose);
@@ -2793,6 +3003,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2793
3003
  failureFunction,
2794
3004
  env,
2795
3005
  retries,
3006
+ flowControl,
2796
3007
  debug
2797
3008
  );
2798
3009
  if (failureCheck.isErr()) {
@@ -2801,6 +3012,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2801
3012
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
2802
3013
  return onStepFinish(workflowRunId, "failure-callback");
2803
3014
  }
3015
+ const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
2804
3016
  const workflowContext = new WorkflowContext({
2805
3017
  qstashClient,
2806
3018
  workflowRunId,
@@ -2812,7 +3024,9 @@ var serveBase = (routeFunction, telemetry, options) => {
2812
3024
  debug,
2813
3025
  env,
2814
3026
  retries,
2815
- telemetry
3027
+ telemetry,
3028
+ invokeCount,
3029
+ flowControl
2816
3030
  });
2817
3031
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
2818
3032
  routeFunction,
@@ -2835,6 +3049,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2835
3049
  workflowUrl,
2836
3050
  failureUrl: workflowFailureUrl,
2837
3051
  retries,
3052
+ flowControl,
2838
3053
  telemetry,
2839
3054
  debug
2840
3055
  });
@@ -2844,10 +3059,16 @@ var serveBase = (routeFunction, telemetry, options) => {
2844
3059
  });
2845
3060
  throw callReturnCheck.error;
2846
3061
  } else if (callReturnCheck.value === "continue-workflow") {
2847
- const result = isFirstInvocation ? await triggerFirstInvocation({ workflowContext, useJSONContent, telemetry, debug }) : await triggerRouteFunction({
3062
+ const result = isFirstInvocation ? await triggerFirstInvocation({
3063
+ workflowContext,
3064
+ useJSONContent,
3065
+ telemetry,
3066
+ debug,
3067
+ invokeCount
3068
+ }) : await triggerRouteFunction({
2848
3069
  onStep: async () => routeFunction(workflowContext),
2849
- onCleanup: async () => {
2850
- await triggerWorkflowDelete(workflowContext, debug);
3070
+ onCleanup: async (result2) => {
3071
+ await triggerWorkflowDelete(workflowContext, result2, debug);
2851
3072
  },
2852
3073
  onCancel: async () => {
2853
3074
  await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
@@ -2881,6 +3102,11 @@ var serveBase = (routeFunction, telemetry, options) => {
2881
3102
 
2882
3103
  // platforms/solidjs.ts
2883
3104
  var serve = (routeFunction, options) => {
3105
+ const telemetry = {
3106
+ sdk: SDK_TELEMETRY,
3107
+ framework: "solidjs",
3108
+ runtime: process.versions.bun ? `bun@${process.versions.bun}/node@${process.version}` : `node@${process.version}`
3109
+ };
2884
3110
  const handler = async (event) => {
2885
3111
  const method = event.request.method;
2886
3112
  if (method.toUpperCase() !== "POST") {
@@ -2888,15 +3114,7 @@ var serve = (routeFunction, options) => {
2888
3114
  status: 405
2889
3115
  });
2890
3116
  }
2891
- const { handler: serveHandler } = serveBase(
2892
- routeFunction,
2893
- {
2894
- sdk: SDK_TELEMETRY,
2895
- framework: "solidjs",
2896
- runtime: process.versions.bun ? `bun@${process.versions.bun}/node@${process.version}` : `node@${process.version}`
2897
- },
2898
- options
2899
- );
3117
+ const { handler: serveHandler } = serveBase(routeFunction, telemetry, options);
2900
3118
  return await serveHandler(event.request);
2901
3119
  };
2902
3120
  return { POST: handler };