@upstash/workflow 0.2.0 → 0.2.2

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
@@ -23556,52 +23556,12 @@ var require_express2 = __commonJS({
23556
23556
  // platforms/express.ts
23557
23557
  var express_exports = {};
23558
23558
  __export(express_exports, {
23559
- serve: () => serve2
23559
+ serve: () => serve
23560
23560
  });
23561
23561
  module.exports = __toCommonJS(express_exports);
23562
23562
 
23563
- // src/error.ts
23564
- var import_qstash = require("@upstash/qstash");
23565
- var WorkflowError = class extends import_qstash.QstashError {
23566
- constructor(message) {
23567
- super(message);
23568
- this.name = "WorkflowError";
23569
- }
23570
- };
23571
- var WorkflowAbort = class extends Error {
23572
- stepInfo;
23573
- stepName;
23574
- /**
23575
- * whether workflow is to be canceled on abort
23576
- */
23577
- cancelWorkflow;
23578
- /**
23579
- *
23580
- * @param stepName name of the aborting step
23581
- * @param stepInfo step information
23582
- * @param cancelWorkflow
23583
- */
23584
- constructor(stepName, stepInfo, cancelWorkflow = false) {
23585
- super(
23586
- `This is an Upstash Workflow error thrown after a step executes. It is expected to be raised. Make sure that you await for each step. Also, if you are using try/catch blocks, you should not wrap context.run/sleep/sleepUntil/call methods with try/catch. Aborting workflow after executing step '${stepName}'.`
23587
- );
23588
- this.name = "WorkflowAbort";
23589
- this.stepName = stepName;
23590
- this.stepInfo = stepInfo;
23591
- this.cancelWorkflow = cancelWorkflow;
23592
- }
23593
- };
23594
- var formatWorkflowError = (error) => {
23595
- return error instanceof Error ? {
23596
- error: error.name,
23597
- message: error.message
23598
- } : {
23599
- error: "Error",
23600
- message: "An error occured while executing workflow."
23601
- };
23602
- };
23603
-
23604
23563
  // src/client/utils.ts
23564
+ var import_qstash = require("@upstash/qstash");
23605
23565
  var makeNotifyRequest = async (requester, eventId, eventData) => {
23606
23566
  const result = await requester.request({
23607
23567
  path: ["v2", "notify", eventId],
@@ -23628,32 +23588,82 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
23628
23588
  await debug?.log("INFO", "ENDPOINT_START", {
23629
23589
  message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
23630
23590
  });
23631
- return steps;
23591
+ return { steps, workflowRunEnded: false };
23632
23592
  } else {
23633
23593
  const index = steps.findIndex((item) => item.messageId === messageId);
23634
23594
  if (index === -1) {
23635
- return [];
23595
+ return { steps: [], workflowRunEnded: false };
23636
23596
  }
23637
23597
  const filteredSteps = steps.slice(0, index + 1);
23638
23598
  await debug?.log("INFO", "ENDPOINT_START", {
23639
23599
  message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
23640
23600
  });
23641
- return filteredSteps;
23601
+ return { steps: filteredSteps, workflowRunEnded: false };
23642
23602
  }
23643
23603
  } catch (error) {
23644
- await debug?.log("ERROR", "ERROR", {
23645
- message: "failed while fetching steps.",
23646
- error
23647
- });
23648
- throw new WorkflowError(`Failed while pulling steps. ${error}`);
23604
+ if (error instanceof import_qstash.QstashError && error.status === 404) {
23605
+ await debug?.log("WARN", "ENDPOINT_START", {
23606
+ message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
23607
+ error
23608
+ });
23609
+ return { steps: void 0, workflowRunEnded: true };
23610
+ } else {
23611
+ throw error;
23612
+ }
23649
23613
  }
23650
23614
  };
23651
23615
 
23616
+ // src/error.ts
23617
+ var import_qstash2 = require("@upstash/qstash");
23618
+ var WorkflowError = class extends import_qstash2.QstashError {
23619
+ constructor(message) {
23620
+ super(message);
23621
+ this.name = "WorkflowError";
23622
+ }
23623
+ };
23624
+ var WorkflowAbort = class extends Error {
23625
+ stepInfo;
23626
+ stepName;
23627
+ /**
23628
+ * whether workflow is to be canceled on abort
23629
+ */
23630
+ cancelWorkflow;
23631
+ /**
23632
+ *
23633
+ * @param stepName name of the aborting step
23634
+ * @param stepInfo step information
23635
+ * @param cancelWorkflow
23636
+ */
23637
+ constructor(stepName, stepInfo, cancelWorkflow = false) {
23638
+ super(
23639
+ `This is an Upstash Workflow error thrown after a step executes. It is expected to be raised. Make sure that you await for each step. Also, if you are using try/catch blocks, you should not wrap context.run/sleep/sleepUntil/call methods with try/catch. Aborting workflow after executing step '${stepName}'.`
23640
+ );
23641
+ this.name = "WorkflowAbort";
23642
+ this.stepName = stepName;
23643
+ this.stepInfo = stepInfo;
23644
+ this.cancelWorkflow = cancelWorkflow;
23645
+ }
23646
+ };
23647
+ var formatWorkflowError = (error) => {
23648
+ return error instanceof Error ? {
23649
+ error: error.name,
23650
+ message: error.message
23651
+ } : {
23652
+ error: "Error",
23653
+ message: "An error occured while executing workflow."
23654
+ };
23655
+ };
23656
+
23652
23657
  // src/context/steps.ts
23653
23658
  var BaseLazyStep = class {
23654
23659
  stepName;
23655
23660
  // will be set in the subclasses
23656
23661
  constructor(stepName) {
23662
+ if (!stepName) {
23663
+ throw new WorkflowError(
23664
+ "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
23665
+ );
23666
+ }
23657
23667
  this.stepName = stepName;
23658
23668
  }
23659
23669
  };
@@ -23746,15 +23756,17 @@ var LazyCallStep = class extends BaseLazyStep {
23746
23756
  method;
23747
23757
  body;
23748
23758
  headers;
23749
- stepType = "Call";
23750
23759
  retries;
23751
- constructor(stepName, url, method, body, headers, retries) {
23760
+ timeout;
23761
+ stepType = "Call";
23762
+ constructor(stepName, url, method, body, headers, retries, timeout) {
23752
23763
  super(stepName);
23753
23764
  this.url = url;
23754
23765
  this.method = method;
23755
23766
  this.body = body;
23756
23767
  this.headers = headers;
23757
23768
  this.retries = retries;
23769
+ this.timeout = timeout;
23758
23770
  }
23759
23771
  getPlanStep(concurrent, targetStep) {
23760
23772
  return {
@@ -24262,8 +24274,8 @@ var StepTypes = [
24262
24274
  ];
24263
24275
 
24264
24276
  // src/workflow-requests.ts
24265
- var import_qstash2 = require("@upstash/qstash");
24266
- var triggerFirstInvocation = async (workflowContext, retries, debug) => {
24277
+ var import_qstash3 = require("@upstash/qstash");
24278
+ var triggerFirstInvocation = async (workflowContext, retries, useJSONContent, debug) => {
24267
24279
  const { headers } = getHeaders(
24268
24280
  "true",
24269
24281
  workflowContext.workflowRunId,
@@ -24273,6 +24285,9 @@ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
24273
24285
  workflowContext.failureUrl,
24274
24286
  retries
24275
24287
  );
24288
+ if (useJSONContent) {
24289
+ headers["content-type"] = "application/json";
24290
+ }
24276
24291
  try {
24277
24292
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
24278
24293
  const result = await workflowContext.qstashClient.publish({
@@ -24316,7 +24331,7 @@ var triggerRouteFunction = async ({
24316
24331
  return ok("workflow-finished");
24317
24332
  } catch (error) {
24318
24333
  const error_ = error;
24319
- if (error instanceof import_qstash2.QstashError && error.status === 400) {
24334
+ if (error instanceof import_qstash3.QstashError && error.status === 400) {
24320
24335
  await debug?.log("WARN", "RESPONSE_WORKFLOW", {
24321
24336
  message: `tried to append to a cancelled workflow. exiting without publishing.`,
24322
24337
  name: error.name,
@@ -24350,7 +24365,7 @@ var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
24350
24365
  );
24351
24366
  return { deleted: true };
24352
24367
  } catch (error) {
24353
- if (error instanceof import_qstash2.QstashError && error.status === 404) {
24368
+ if (error instanceof import_qstash3.QstashError && error.status === 404) {
24354
24369
  await debug?.log("WARN", "SUBMIT_CLEANUP", {
24355
24370
  message: `Failed to remove workflow run ${workflowContext.workflowRunId} as it doesn't exist.`,
24356
24371
  name: error.name,
@@ -24366,7 +24381,10 @@ var recreateUserHeaders = (headers) => {
24366
24381
  const pairs = headers.entries();
24367
24382
  for (const [header, value] of pairs) {
24368
24383
  const headerLowerCase = header.toLowerCase();
24369
- if (!headerLowerCase.startsWith("upstash-workflow-") && !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && headerLowerCase !== "cf-connecting-ip") {
24384
+ if (!headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
24385
+ !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
24386
+ headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
24387
+ headerLowerCase !== "render-proxy-ttl") {
24370
24388
  filteredHeaders.append(header, value);
24371
24389
  }
24372
24390
  }
@@ -24384,11 +24402,19 @@ var handleThirdPartyCallResult = async (request, requestPayload, client, workflo
24384
24402
  if (!workflowRunId2)
24385
24403
  throw new WorkflowError("workflow run id missing in context.call lazy fetch.");
24386
24404
  if (!messageId) throw new WorkflowError("message id missing in context.call lazy fetch.");
24387
- const steps = await getSteps(client.http, workflowRunId2, messageId, debug);
24405
+ const { steps, workflowRunEnded } = await getSteps(
24406
+ client.http,
24407
+ workflowRunId2,
24408
+ messageId,
24409
+ debug
24410
+ );
24411
+ if (workflowRunEnded) {
24412
+ return ok("workflow-ended");
24413
+ }
24388
24414
  const failingStep = steps.find((step) => step.messageId === messageId);
24389
24415
  if (!failingStep)
24390
24416
  throw new WorkflowError(
24391
- "Failed to submit the context.call." + (steps.length === 0 ? "No steps found." : `No step was found with matching messageId ${messageId} out of ${steps.length} steps.`)
24417
+ "Failed to submit the context.call. " + (steps.length === 0 ? "No steps found." : `No step was found with matching messageId ${messageId} out of ${steps.length} steps.`)
24392
24418
  );
24393
24419
  callbackPayload = atob(failingStep.body);
24394
24420
  }
@@ -24469,7 +24495,7 @@ ${atob(callbackMessage.body ?? "")}`
24469
24495
  );
24470
24496
  }
24471
24497
  };
24472
- var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step, failureUrl, retries, callRetries) => {
24498
+ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step, failureUrl, retries, callRetries, callTimeout) => {
24473
24499
  const baseHeaders = {
24474
24500
  [WORKFLOW_INIT_HEADER]: initHeaderValue,
24475
24501
  [WORKFLOW_ID_HEADER]: workflowRunId,
@@ -24479,6 +24505,9 @@ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step
24479
24505
  if (!step?.callUrl) {
24480
24506
  baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
24481
24507
  }
24508
+ if (callTimeout) {
24509
+ baseHeaders[`Upstash-Timeout`] = callTimeout.toString();
24510
+ }
24482
24511
  if (failureUrl) {
24483
24512
  baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
24484
24513
  if (!step?.callUrl) {
@@ -24854,7 +24883,8 @@ var AutoExecutor = class _AutoExecutor {
24854
24883
  singleStep,
24855
24884
  this.context.failureUrl,
24856
24885
  this.context.retries,
24857
- lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0
24886
+ lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
24887
+ lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0
24858
24888
  );
24859
24889
  const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
24860
24890
  singleStep.out = JSON.stringify(singleStep.out);
@@ -25204,6 +25234,7 @@ var WorkflowContext = class {
25204
25234
  * @param body call body
25205
25235
  * @param headers call headers
25206
25236
  * @param retries number of call retries. 0 by default
25237
+ * @param timeout max duration to wait for the endpoint to respond. in seconds.
25207
25238
  * @returns call result as {
25208
25239
  * status: number;
25209
25240
  * body: unknown;
@@ -25211,9 +25242,17 @@ var WorkflowContext = class {
25211
25242
  * }
25212
25243
  */
25213
25244
  async call(stepName, settings) {
25214
- const { url, method = "GET", body, headers = {}, retries = 0 } = settings;
25245
+ const { url, method = "GET", body, headers = {}, retries = 0, timeout } = settings;
25215
25246
  const result = await this.addStep(
25216
- new LazyCallStep(stepName, url, method, body, headers, retries)
25247
+ new LazyCallStep(
25248
+ stepName,
25249
+ url,
25250
+ method,
25251
+ body,
25252
+ headers,
25253
+ retries,
25254
+ timeout
25255
+ )
25217
25256
  );
25218
25257
  if (typeof result === "string") {
25219
25258
  try {
@@ -25414,7 +25453,7 @@ function decodeBase64(base64) {
25414
25453
  }
25415
25454
 
25416
25455
  // src/serve/authorization.ts
25417
- var import_qstash3 = require("@upstash/qstash");
25456
+ var import_qstash4 = require("@upstash/qstash");
25418
25457
  var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
25419
25458
  static disabledMessage = "disabled-qstash-worklfow-run";
25420
25459
  /**
@@ -25445,7 +25484,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
25445
25484
  */
25446
25485
  static async tryAuthentication(routeFunction, context) {
25447
25486
  const disabledContext = new _DisabledWorkflowContext({
25448
- qstashClient: new import_qstash3.Client({
25487
+ qstashClient: new import_qstash4.Client({
25449
25488
  baseUrl: "disabled-client",
25450
25489
  token: "disabled-client"
25451
25490
  }),
@@ -25569,7 +25608,8 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
25569
25608
  return {
25570
25609
  rawInitialPayload: requestPayload ?? "",
25571
25610
  steps: [],
25572
- isLastDuplicate: false
25611
+ isLastDuplicate: false,
25612
+ workflowRunEnded: false
25573
25613
  };
25574
25614
  } else {
25575
25615
  let rawSteps;
@@ -25579,7 +25619,21 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
25579
25619
  "ENDPOINT_START",
25580
25620
  "request payload is empty, steps will be fetched from QStash."
25581
25621
  );
25582
- rawSteps = await getSteps(requester, workflowRunId, messageId, debug);
25622
+ const { steps: fetchedSteps, workflowRunEnded } = await getSteps(
25623
+ requester,
25624
+ workflowRunId,
25625
+ messageId,
25626
+ debug
25627
+ );
25628
+ if (workflowRunEnded) {
25629
+ return {
25630
+ rawInitialPayload: void 0,
25631
+ steps: void 0,
25632
+ isLastDuplicate: void 0,
25633
+ workflowRunEnded: true
25634
+ };
25635
+ }
25636
+ rawSteps = fetchedSteps;
25583
25637
  } else {
25584
25638
  rawSteps = JSON.parse(requestPayload);
25585
25639
  }
@@ -25589,7 +25643,8 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
25589
25643
  return {
25590
25644
  rawInitialPayload,
25591
25645
  steps: deduplicatedSteps,
25592
- isLastDuplicate
25646
+ isLastDuplicate,
25647
+ workflowRunEnded: false
25593
25648
  };
25594
25649
  }
25595
25650
  };
@@ -25613,7 +25668,7 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
25613
25668
  const workflowContext = new WorkflowContext({
25614
25669
  qstashClient,
25615
25670
  workflowRunId,
25616
- initialPayload: initialPayloadParser(decodeBase64(sourceBody)),
25671
+ initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
25617
25672
  headers: recreateUserHeaders(new Headers(sourceHeader)),
25618
25673
  steps: [],
25619
25674
  url,
@@ -25643,22 +25698,35 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
25643
25698
  };
25644
25699
 
25645
25700
  // src/serve/options.ts
25646
- var import_qstash4 = require("@upstash/qstash");
25647
25701
  var import_qstash5 = require("@upstash/qstash");
25702
+ var import_qstash6 = require("@upstash/qstash");
25648
25703
  var processOptions = (options) => {
25649
25704
  const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
25650
25705
  const receiverEnvironmentVariablesSet = Boolean(
25651
25706
  environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
25652
25707
  );
25653
25708
  return {
25654
- qstashClient: new import_qstash5.Client({
25709
+ qstashClient: new import_qstash6.Client({
25655
25710
  baseUrl: environment.QSTASH_URL,
25656
25711
  token: environment.QSTASH_TOKEN
25657
25712
  }),
25658
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
25659
- onStepFinish: (workflowRunId, _finishCondition) => new Response(JSON.stringify({ workflowRunId }), {
25660
- status: 200
25661
- }),
25713
+ onStepFinish: (workflowRunId, finishCondition) => {
25714
+ if (finishCondition === "auth-fail") {
25715
+ console.error(AUTH_FAIL_MESSAGE);
25716
+ return new Response(
25717
+ JSON.stringify({
25718
+ message: AUTH_FAIL_MESSAGE,
25719
+ workflowRunId
25720
+ }),
25721
+ {
25722
+ status: 400
25723
+ }
25724
+ );
25725
+ }
25726
+ return new Response(JSON.stringify({ workflowRunId }), {
25727
+ status: 200
25728
+ });
25729
+ },
25662
25730
  initialPayloadParser: (initialRequest) => {
25663
25731
  if (!initialRequest) {
25664
25732
  return void 0;
@@ -25672,13 +25740,14 @@ var processOptions = (options) => {
25672
25740
  throw error;
25673
25741
  }
25674
25742
  },
25675
- receiver: receiverEnvironmentVariablesSet ? new import_qstash4.Receiver({
25743
+ receiver: receiverEnvironmentVariablesSet ? new import_qstash5.Receiver({
25676
25744
  currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
25677
25745
  nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
25678
25746
  }) : void 0,
25679
25747
  baseUrl: environment.UPSTASH_WORKFLOW_URL,
25680
25748
  env: environment,
25681
25749
  retries: DEFAULT_RETRIES,
25750
+ useJSONContent: false,
25682
25751
  ...options
25683
25752
  };
25684
25753
  };
@@ -25695,14 +25764,25 @@ var determineUrls = async (request, url, baseUrl, failureFunction, failureUrl, d
25695
25764
  });
25696
25765
  }
25697
25766
  const workflowFailureUrl = failureFunction ? workflowUrl : failureUrl;
25767
+ if (workflowUrl.includes("localhost")) {
25768
+ await debug?.log("WARN", "ENDPOINT_START", {
25769
+ message: `Workflow URL contains localhost. This can happen in local development, but shouldn't happen in production unless you have a route which contains localhost. Received: ${workflowUrl}`
25770
+ });
25771
+ }
25772
+ if (!(workflowUrl.startsWith("http://") || workflowUrl.startsWith("https://"))) {
25773
+ throw new WorkflowError(
25774
+ `Workflow URL should start with 'http://' or 'https://'. Recevied is '${workflowUrl}'`
25775
+ );
25776
+ }
25698
25777
  return {
25699
25778
  workflowUrl,
25700
25779
  workflowFailureUrl
25701
25780
  };
25702
25781
  };
25782
+ var AUTH_FAIL_MESSAGE = `Failed to authenticate Workflow request. If this is unexpected, see the caveat https://upstash.com/docs/workflow/basics/caveats#avoid-non-deterministic-code-outside-context-run`;
25703
25783
 
25704
25784
  // src/serve/index.ts
25705
- var serve = (routeFunction, options) => {
25785
+ var serveBase = (routeFunction, options) => {
25706
25786
  const {
25707
25787
  qstashClient,
25708
25788
  onStepFinish,
@@ -25714,7 +25794,8 @@ var serve = (routeFunction, options) => {
25714
25794
  failureFunction,
25715
25795
  baseUrl,
25716
25796
  env,
25717
- retries
25797
+ retries,
25798
+ useJSONContent
25718
25799
  } = processOptions(options);
25719
25800
  const debug = WorkflowLogger.getLogger(verbose);
25720
25801
  const handler = async (request) => {
@@ -25731,7 +25812,7 @@ var serve = (routeFunction, options) => {
25731
25812
  await verifyRequest(requestPayload, request.headers.get("upstash-signature"), receiver);
25732
25813
  const { isFirstInvocation, workflowRunId } = validateRequest(request);
25733
25814
  debug?.setWorkflowRunId(workflowRunId);
25734
- const { rawInitialPayload, steps, isLastDuplicate } = await parseRequest(
25815
+ const { rawInitialPayload, steps, isLastDuplicate, workflowRunEnded } = await parseRequest(
25735
25816
  requestPayload,
25736
25817
  isFirstInvocation,
25737
25818
  workflowRunId,
@@ -25739,8 +25820,11 @@ var serve = (routeFunction, options) => {
25739
25820
  request.headers.get("upstash-message-id"),
25740
25821
  debug
25741
25822
  );
25823
+ if (workflowRunEnded) {
25824
+ return onStepFinish(workflowRunId, "workflow-already-ended");
25825
+ }
25742
25826
  if (isLastDuplicate) {
25743
- return onStepFinish("no-workflow-id", "duplicate-step");
25827
+ return onStepFinish(workflowRunId, "duplicate-step");
25744
25828
  }
25745
25829
  const failureCheck = await handleFailure(
25746
25830
  request,
@@ -25754,7 +25838,7 @@ var serve = (routeFunction, options) => {
25754
25838
  throw failureCheck.error;
25755
25839
  } else if (failureCheck.value === "is-failure-callback") {
25756
25840
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
25757
- return onStepFinish("no-workflow-id", "failure-callback");
25841
+ return onStepFinish(workflowRunId, "failure-callback");
25758
25842
  }
25759
25843
  const workflowContext = new WorkflowContext({
25760
25844
  qstashClient,
@@ -25776,7 +25860,11 @@ var serve = (routeFunction, options) => {
25776
25860
  await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
25777
25861
  throw authCheck.error;
25778
25862
  } else if (authCheck.value === "run-ended") {
25779
- return onStepFinish("no-workflow-id", "auth-fail");
25863
+ await debug?.log("ERROR", "ERROR", { error: AUTH_FAIL_MESSAGE });
25864
+ return onStepFinish(
25865
+ isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId,
25866
+ "auth-fail"
25867
+ );
25780
25868
  }
25781
25869
  const callReturnCheck = await handleThirdPartyCallResult(
25782
25870
  request,
@@ -25793,7 +25881,7 @@ var serve = (routeFunction, options) => {
25793
25881
  });
25794
25882
  throw callReturnCheck.error;
25795
25883
  } else if (callReturnCheck.value === "continue-workflow") {
25796
- const result = isFirstInvocation ? await triggerFirstInvocation(workflowContext, retries, debug) : await triggerRouteFunction({
25884
+ const result = isFirstInvocation ? await triggerFirstInvocation(workflowContext, retries, useJSONContent, debug) : await triggerRouteFunction({
25797
25885
  onStep: async () => routeFunction(workflowContext),
25798
25886
  onCleanup: async () => {
25799
25887
  await triggerWorkflowDelete(workflowContext, debug);
@@ -25809,6 +25897,8 @@ var serve = (routeFunction, options) => {
25809
25897
  }
25810
25898
  await debug?.log("INFO", "RESPONSE_WORKFLOW");
25811
25899
  return onStepFinish(workflowContext.workflowRunId, "success");
25900
+ } else if (callReturnCheck.value === "workflow-ended") {
25901
+ return onStepFinish(workflowContext.workflowRunId, "workflow-already-ended");
25812
25902
  }
25813
25903
  await debug?.log("INFO", "RESPONSE_DEFAULT");
25814
25904
  return onStepFinish("no-workflow-id", "fromCallback");
@@ -25826,21 +25916,22 @@ var serve = (routeFunction, options) => {
25826
25916
  return { handler: safeHandler };
25827
25917
  };
25828
25918
 
25829
- // src/client/index.ts
25830
- var import_qstash6 = require("@upstash/qstash");
25831
-
25832
25919
  // platforms/express.ts
25833
25920
  var import_express = __toESM(require_express2());
25834
- function serve2(routeFunction, options) {
25921
+ function serve(routeFunction, options) {
25835
25922
  const router = (0, import_express.Router)();
25836
25923
  const handler = async (request_, res) => {
25837
25924
  if (request_.method.toUpperCase() !== "POST") {
25838
25925
  res.status(405).json("Only POST requests are allowed in workflows");
25839
25926
  return;
25840
25927
  }
25841
- if (request_.headers["content-type"] !== "application/json") {
25842
- res.status(400).json("Only application/json content type is allowed in express workflows");
25843
- return;
25928
+ let requestBody;
25929
+ if (request_.headers["content-type"]?.includes("text/plain")) {
25930
+ requestBody = request_.body;
25931
+ } else if (request_.headers["content-type"]?.includes("application/json")) {
25932
+ requestBody = JSON.stringify(request_.body);
25933
+ } else {
25934
+ requestBody = typeof request_.body === "string" ? request_.body : JSON.stringify(request_.body);
25844
25935
  }
25845
25936
  const protocol = request_.protocol;
25846
25937
  const host = request_.get("host") || "localhost";
@@ -25848,11 +25939,14 @@ function serve2(routeFunction, options) {
25848
25939
  const webRequest = new Request(url, {
25849
25940
  method: request_.method,
25850
25941
  headers: new Headers(request_.headers),
25851
- body: JSON.stringify(request_.body)
25942
+ body: requestBody
25852
25943
  });
25853
- const { handler: serveHandler } = serve(
25944
+ const { handler: serveHandler } = serveBase(
25854
25945
  (workflowContext) => routeFunction(workflowContext),
25855
- options
25946
+ {
25947
+ ...options,
25948
+ useJSONContent: true
25949
+ }
25856
25950
  );
25857
25951
  const response = await serveHandler(webRequest);
25858
25952
  res.status(response.status).json(await response.json());
package/express.mjs CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  __commonJS,
3
3
  __require,
4
4
  __toESM,
5
- serve
6
- } from "./chunk-5R2BFC3N.mjs";
5
+ serveBase
6
+ } from "./chunk-Z7WS5XIR.mjs";
7
7
 
8
8
  // node_modules/depd/index.js
9
9
  var require_depd = __commonJS({
@@ -23530,16 +23530,20 @@ var require_express2 = __commonJS({
23530
23530
 
23531
23531
  // platforms/express.ts
23532
23532
  var import_express = __toESM(require_express2());
23533
- function serve2(routeFunction, options) {
23533
+ function serve(routeFunction, options) {
23534
23534
  const router = (0, import_express.Router)();
23535
23535
  const handler = async (request_, res) => {
23536
23536
  if (request_.method.toUpperCase() !== "POST") {
23537
23537
  res.status(405).json("Only POST requests are allowed in workflows");
23538
23538
  return;
23539
23539
  }
23540
- if (request_.headers["content-type"] !== "application/json") {
23541
- res.status(400).json("Only application/json content type is allowed in express workflows");
23542
- return;
23540
+ let requestBody;
23541
+ if (request_.headers["content-type"]?.includes("text/plain")) {
23542
+ requestBody = request_.body;
23543
+ } else if (request_.headers["content-type"]?.includes("application/json")) {
23544
+ requestBody = JSON.stringify(request_.body);
23545
+ } else {
23546
+ requestBody = typeof request_.body === "string" ? request_.body : JSON.stringify(request_.body);
23543
23547
  }
23544
23548
  const protocol = request_.protocol;
23545
23549
  const host = request_.get("host") || "localhost";
@@ -23547,11 +23551,14 @@ function serve2(routeFunction, options) {
23547
23551
  const webRequest = new Request(url, {
23548
23552
  method: request_.method,
23549
23553
  headers: new Headers(request_.headers),
23550
- body: JSON.stringify(request_.body)
23554
+ body: requestBody
23551
23555
  });
23552
- const { handler: serveHandler } = serve(
23556
+ const { handler: serveHandler } = serveBase(
23553
23557
  (workflowContext) => routeFunction(workflowContext),
23554
- options
23558
+ {
23559
+ ...options,
23560
+ useJSONContent: true
23561
+ }
23555
23562
  );
23556
23563
  const response = await serveHandler(webRequest);
23557
23564
  res.status(response.status).json(await response.json());
@@ -23560,7 +23567,7 @@ function serve2(routeFunction, options) {
23560
23567
  return router;
23561
23568
  }
23562
23569
  export {
23563
- serve2 as serve
23570
+ serve
23564
23571
  };
23565
23572
  /*! Bundled license information:
23566
23573
 
package/h3.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as h3 from 'h3';
2
- import { R as RouteFunction, W as WorkflowServeOptions } from './types-Cki_MHrh.mjs';
2
+ import { R as RouteFunction, j as PublicServeOptions } from './types-APRap-aV.mjs';
3
3
  import '@upstash/qstash';
4
4
 
5
- declare const serve: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: Omit<WorkflowServeOptions<Response, TInitialPayload>, "onStepFinish">) => {
5
+ declare const serve: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: PublicServeOptions<TInitialPayload>) => {
6
6
  handler: h3.EventHandler<h3.EventHandlerRequest, Promise<Response | {
7
7
  status: number;
8
8
  body: string;
package/h3.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as h3 from 'h3';
2
- import { R as RouteFunction, W as WorkflowServeOptions } from './types-Cki_MHrh.js';
2
+ import { R as RouteFunction, j as PublicServeOptions } from './types-APRap-aV.js';
3
3
  import '@upstash/qstash';
4
4
 
5
- declare const serve: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: Omit<WorkflowServeOptions<Response, TInitialPayload>, "onStepFinish">) => {
5
+ declare const serve: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: PublicServeOptions<TInitialPayload>) => {
6
6
  handler: h3.EventHandler<h3.EventHandlerRequest, Promise<Response | {
7
7
  status: number;
8
8
  body: string;