@upstash/workflow 0.2.21 → 0.2.22

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
@@ -30,7 +30,95 @@ __export(nextjs_exports, {
30
30
  module.exports = __toCommonJS(nextjs_exports);
31
31
 
32
32
  // src/client/utils.ts
33
+ var import_qstash2 = require("@upstash/qstash");
34
+
35
+ // src/error.ts
33
36
  var import_qstash = require("@upstash/qstash");
37
+ var WorkflowError = class extends import_qstash.QstashError {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "WorkflowError";
41
+ }
42
+ };
43
+ var WorkflowAbort = class extends Error {
44
+ stepInfo;
45
+ stepName;
46
+ /**
47
+ * whether workflow is to be canceled on abort
48
+ */
49
+ cancelWorkflow;
50
+ /**
51
+ *
52
+ * @param stepName name of the aborting step
53
+ * @param stepInfo step information
54
+ * @param cancelWorkflow
55
+ */
56
+ constructor(stepName, stepInfo, cancelWorkflow = false) {
57
+ super(
58
+ `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}'.`
59
+ );
60
+ this.name = "WorkflowAbort";
61
+ this.stepName = stepName;
62
+ this.stepInfo = stepInfo;
63
+ this.cancelWorkflow = cancelWorkflow;
64
+ }
65
+ };
66
+ var WorkflowNonRetryableError = class extends WorkflowAbort {
67
+ /**
68
+ * @param message error message to be displayed
69
+ */
70
+ constructor(message) {
71
+ super("fail", void 0, false);
72
+ this.name = "WorkflowNonRetryableError";
73
+ if (message) this.message = message;
74
+ }
75
+ };
76
+ var WorkflowRetryAfterError = class extends WorkflowAbort {
77
+ retryAfter;
78
+ /**
79
+ * @param retryAfter time in seconds after which the workflow should be retried
80
+ * @param message error message to be displayed
81
+ */
82
+ constructor(message, retryAfter) {
83
+ super("retry", void 0, false);
84
+ this.name = "WorkflowRetryAfterError";
85
+ this.retryAfter = retryAfter;
86
+ if (message) this.message = message;
87
+ }
88
+ };
89
+ var formatWorkflowError = (error) => {
90
+ return error instanceof Error ? {
91
+ error: error.name,
92
+ message: error.message,
93
+ stack: error.stack
94
+ } : {
95
+ error: "Error",
96
+ message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
97
+ };
98
+ };
99
+ function getConstructorName(obj) {
100
+ if (obj === null || obj === void 0) {
101
+ return null;
102
+ }
103
+ const ctor = obj.constructor;
104
+ if (!ctor || ctor.name === "Object") {
105
+ return null;
106
+ }
107
+ return ctor.name;
108
+ }
109
+ function getConstructorNames(obj) {
110
+ const proto = Object.getPrototypeOf(obj);
111
+ const name = getConstructorName(proto);
112
+ if (name === null) {
113
+ return [];
114
+ }
115
+ return [name, ...getConstructorNames(proto)];
116
+ }
117
+ function isInstanceOf(v, ctor) {
118
+ return getConstructorNames(v).includes(ctor.name);
119
+ }
120
+
121
+ // src/client/utils.ts
34
122
  var makeNotifyRequest = async (requester, eventId, eventData) => {
35
123
  const result = await requester.request({
36
124
  path: ["v2", "notify", eventId],
@@ -70,7 +158,7 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
70
158
  return { steps: filteredSteps, workflowRunEnded: false };
71
159
  }
72
160
  } catch (error) {
73
- if (error instanceof import_qstash.QstashError && error.status === 404) {
161
+ if (isInstanceOf(error, import_qstash2.QstashError) && error.status === 404) {
74
162
  await debug?.log("WARN", "ENDPOINT_START", {
75
163
  message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
76
164
  error
@@ -95,65 +183,13 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
95
183
  var DEFAULT_CONTENT_TYPE = "application/json";
96
184
  var NO_CONCURRENCY = 1;
97
185
  var DEFAULT_RETRIES = 3;
98
- var VERSION = "v0.2.21";
186
+ var VERSION = "v0.2.22";
99
187
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
100
188
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
101
189
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
102
190
  var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
103
191
  var TELEMETRY_HEADER_AGENT = "Upstash-Telemetry-Agent";
104
192
 
105
- // src/error.ts
106
- var import_qstash2 = require("@upstash/qstash");
107
- var WorkflowError = class extends import_qstash2.QstashError {
108
- constructor(message) {
109
- super(message);
110
- this.name = "WorkflowError";
111
- }
112
- };
113
- var WorkflowAbort = class extends Error {
114
- stepInfo;
115
- stepName;
116
- /**
117
- * whether workflow is to be canceled on abort
118
- */
119
- cancelWorkflow;
120
- /**
121
- *
122
- * @param stepName name of the aborting step
123
- * @param stepInfo step information
124
- * @param cancelWorkflow
125
- */
126
- constructor(stepName, stepInfo, cancelWorkflow = false) {
127
- super(
128
- `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}'.`
129
- );
130
- this.name = "WorkflowAbort";
131
- this.stepName = stepName;
132
- this.stepInfo = stepInfo;
133
- this.cancelWorkflow = cancelWorkflow;
134
- }
135
- };
136
- var WorkflowNonRetryableError = class extends WorkflowAbort {
137
- /**
138
- * @param message error message to be displayed
139
- */
140
- constructor(message) {
141
- super("fail", void 0, false);
142
- this.name = "WorkflowNonRetryableError";
143
- if (message) this.message = message;
144
- }
145
- };
146
- var formatWorkflowError = (error) => {
147
- return error instanceof Error ? {
148
- error: error.name,
149
- message: error.message,
150
- stack: error.stack
151
- } : {
152
- error: "Error",
153
- message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
154
- };
155
- };
156
-
157
193
  // src/context/auto-executor.ts
158
194
  var import_qstash5 = require("@upstash/qstash");
159
195
 
@@ -713,17 +749,17 @@ var triggerRouteFunction = async ({
713
749
  return ok("workflow-finished");
714
750
  } catch (error) {
715
751
  const error_ = error;
716
- if (error instanceof import_qstash3.QstashError && error.status === 400) {
752
+ if (isInstanceOf(error, import_qstash3.QstashError) && error.status === 400) {
717
753
  await debug?.log("WARN", "RESPONSE_WORKFLOW", {
718
754
  message: `tried to append to a cancelled workflow. exiting without publishing.`,
719
755
  name: error.name,
720
756
  errorMessage: error.message
721
757
  });
722
758
  return ok("workflow-was-finished");
723
- } else if (!(error_ instanceof WorkflowAbort)) {
724
- return err(error_);
725
- } else if (error_ instanceof WorkflowNonRetryableError) {
759
+ } else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
726
760
  return ok(error_);
761
+ } else if (!isInstanceOf(error_, WorkflowAbort)) {
762
+ return err(error_);
727
763
  } else if (error_.cancelWorkflow) {
728
764
  await onCancel();
729
765
  return ok("workflow-finished");
@@ -2030,7 +2066,7 @@ var AutoExecutor = class _AutoExecutor {
2030
2066
  });
2031
2067
  throw new WorkflowAbort(parallelStep.stepName, resultStep);
2032
2068
  } catch (error) {
2033
- if (error instanceof WorkflowAbort || error instanceof import_qstash5.QstashError && error.status === 400) {
2069
+ if (isInstanceOf(error, WorkflowAbort) || isInstanceOf(error, import_qstash5.QstashError) && error.status === 400) {
2034
2070
  throw error;
2035
2071
  }
2036
2072
  throw new WorkflowError(
@@ -2137,7 +2173,7 @@ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
2137
2173
  validateStep(lazySteps[index], stepFromRequest);
2138
2174
  }
2139
2175
  } catch (error) {
2140
- if (error instanceof WorkflowError) {
2176
+ if (isInstanceOf(error, WorkflowError)) {
2141
2177
  const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
2142
2178
  const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
2143
2179
  const requestStepNames = stepsFromRequest.map((step) => step.stepName);
@@ -2318,7 +2354,7 @@ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
2318
2354
  headers: responseHeaders
2319
2355
  });
2320
2356
  } catch (error) {
2321
- if (error instanceof Error && error.name === "WorkflowAbort") {
2357
+ if (error instanceof Error && isInstanceOf(error, WorkflowAbort)) {
2322
2358
  throw error;
2323
2359
  } else {
2324
2360
  console.error("Error in fetch implementation:", error);
@@ -2420,10 +2456,10 @@ var Agent = class {
2420
2456
  });
2421
2457
  return { text: result.text };
2422
2458
  } catch (error) {
2423
- if (error instanceof import_ai2.ToolExecutionError) {
2424
- if (error.cause instanceof Error && error.cause.name === "WorkflowAbort") {
2459
+ if (isInstanceOf(error, import_ai2.ToolExecutionError)) {
2460
+ if (error.cause instanceof Error && isInstanceOf(error.cause, WorkflowAbort)) {
2425
2461
  throw error.cause;
2426
- } else if (error.cause instanceof import_ai2.ToolExecutionError && error.cause.cause instanceof Error && error.cause.cause.name === "WorkflowAbort") {
2462
+ } else if (isInstanceOf(error.cause, import_ai2.ToolExecutionError) && isInstanceOf(error.cause.cause, WorkflowAbort)) {
2427
2463
  throw error.cause.cause;
2428
2464
  } else {
2429
2465
  throw error;
@@ -3172,7 +3208,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3172
3208
  try {
3173
3209
  await routeFunction(disabledContext);
3174
3210
  } catch (error) {
3175
- if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage || error instanceof WorkflowNonRetryableError) {
3211
+ if (isInstanceOf(error, WorkflowAbort) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
3176
3212
  return ok("step-found");
3177
3213
  }
3178
3214
  console.warn(
@@ -3425,13 +3461,24 @@ var processOptions = (options) => {
3425
3461
  },
3426
3462
  status: 489
3427
3463
  });
3428
- } else if (detailedFinishCondition?.condition === "failure-callback") {
3429
- return new Response(detailedFinishCondition.result ?? void 0, {
3430
- status: 200,
3464
+ } else if (detailedFinishCondition?.condition === "retry-after-error") {
3465
+ return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
3431
3466
  headers: {
3467
+ "Retry-After": detailedFinishCondition.result.retryAfter.toString(),
3432
3468
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3433
- }
3469
+ },
3470
+ status: 429
3434
3471
  });
3472
+ } else if (detailedFinishCondition?.condition === "failure-callback") {
3473
+ return new Response(
3474
+ JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
3475
+ {
3476
+ status: 200,
3477
+ headers: {
3478
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3479
+ }
3480
+ }
3481
+ );
3435
3482
  }
3436
3483
  return new Response(JSON.stringify({ workflowRunId }), {
3437
3484
  status: 200,
@@ -3641,12 +3688,18 @@ var serveBase = (routeFunction, telemetry, options) => {
3641
3688
  },
3642
3689
  debug
3643
3690
  });
3644
- if (result.isOk() && result.value instanceof WorkflowNonRetryableError) {
3691
+ if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
3645
3692
  return onStepFinish(workflowRunId, result.value, {
3646
3693
  condition: "non-retryable-error",
3647
3694
  result: result.value
3648
3695
  });
3649
3696
  }
3697
+ if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
3698
+ return onStepFinish(workflowRunId, result.value, {
3699
+ condition: "retry-after-error",
3700
+ result: result.value
3701
+ });
3702
+ }
3650
3703
  if (result.isErr()) {
3651
3704
  await debug?.log("ERROR", "ERROR", { error: result.error.message });
3652
3705
  throw result.error;
@@ -3677,7 +3730,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3677
3730
  const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
3678
3731
  Original error: '${formattedError.message}'`;
3679
3732
  console.error(errorMessage);
3680
- return new Response(errorMessage, {
3733
+ return new Response(JSON.stringify({ error: errorMessage }), {
3681
3734
  status: 500,
3682
3735
  headers: {
3683
3736
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
@@ -3759,7 +3812,8 @@ var servePagesRouter = (routeFunction, options) => {
3759
3812
  for (const [key, value] of response.headers.entries()) {
3760
3813
  res.setHeader(key, value);
3761
3814
  }
3762
- res.status(response.status).json(await response.json());
3815
+ const responseData = await response.json();
3816
+ res.status(response.status).json(responseData);
3763
3817
  };
3764
3818
  return {
3765
3819
  handler
package/nextjs.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-NQDNC5P4.mjs";
5
+ } from "./chunk-BON2RKOR.mjs";
6
6
 
7
7
  // platforms/nextjs.ts
8
8
  var appTelemetry = {
@@ -68,7 +68,8 @@ var servePagesRouter = (routeFunction, options) => {
68
68
  for (const [key, value] of response.headers.entries()) {
69
69
  res.setHeader(key, value);
70
70
  }
71
- res.status(response.status).json(await response.json());
71
+ const responseData = await response.json();
72
+ res.status(response.status).json(responseData);
72
73
  };
73
74
  return {
74
75
  handler
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@upstash/workflow","version":"v0.2.21","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"},"./tanstack":{"import":"./tanstack.mjs","require":"./tanstack.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git+https://github.com/upstash/workflow-ts.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@ai-sdk/anthropic":"^1.1.15","@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@tanstack/react-start":"^1.132.48","@types/bun":"^1.1.10","@types/express":"^5.0.3","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^5.1.0","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@ai-sdk/openai":"^1.2.1","@upstash/qstash":"^2.8.2","ai":"^4.1.54","zod":"^3.24.1"},"directories":{"example":"examples"}}
1
+ {"name":"@upstash/workflow","version":"v0.2.22","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"},"./tanstack":{"import":"./tanstack.mjs","require":"./tanstack.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git+https://github.com/upstash/workflow-ts.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@ai-sdk/anthropic":"^1.1.15","@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@tanstack/react-start":"^1.132.48","@types/bun":"^1.1.10","@types/express":"^5.0.3","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^5.1.0","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@ai-sdk/openai":"^1.2.1","@upstash/qstash":"^2.8.2","ai":"^4.1.54","zod":"^3.24.1"},"directories":{"example":"examples"}}
@@ -1,4 +1,4 @@
1
- import { n as PublicServeOptions, R as RouteFunction, y as InvokableWorkflow } from './types-Q3dM0UlR.js';
1
+ import { o as PublicServeOptions, R as RouteFunction, z as InvokableWorkflow } from './types-9nCq6bRP.js';
2
2
 
3
3
  type OmitOptionsInServeMany<TOptions> = Omit<TOptions, "env" | "url" | "schema" | "initialPayloadParser">;
4
4
  declare const serveManyBase: <THandler extends (...params: any[]) => any, TOptions extends OmitOptionsInServeMany<PublicServeOptions> = OmitOptionsInServeMany<PublicServeOptions>, TServeParams extends [routeFunction: RouteFunction<any, any>, options: TOptions] = [routeFunction: RouteFunction<any, any>, options: TOptions]>({ workflows, getUrl, serveMethod, options, }: {
@@ -1,4 +1,4 @@
1
- import { n as PublicServeOptions, R as RouteFunction, y as InvokableWorkflow } from './types-Q3dM0UlR.mjs';
1
+ import { o as PublicServeOptions, R as RouteFunction, z as InvokableWorkflow } from './types-9nCq6bRP.mjs';
2
2
 
3
3
  type OmitOptionsInServeMany<TOptions> = Omit<TOptions, "env" | "url" | "schema" | "initialPayloadParser">;
4
4
  declare const serveManyBase: <THandler extends (...params: any[]) => any, TOptions extends OmitOptionsInServeMany<PublicServeOptions> = OmitOptionsInServeMany<PublicServeOptions>, TServeParams extends [routeFunction: RouteFunction<any, any>, options: TOptions] = [routeFunction: RouteFunction<any, any>, options: TOptions]>({ workflows, getUrl, serveMethod, options, }: {
package/solidjs.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { APIEvent } from '@solidjs/start/server';
2
- import { R as RouteFunction, n as PublicServeOptions } from './types-Q3dM0UlR.mjs';
2
+ import { R as RouteFunction, o as PublicServeOptions } from './types-9nCq6bRP.mjs';
3
3
  import '@upstash/qstash';
4
4
  import 'zod';
5
5
  import 'ai';
package/solidjs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { APIEvent } from '@solidjs/start/server';
2
- import { R as RouteFunction, n as PublicServeOptions } from './types-Q3dM0UlR.js';
2
+ import { R as RouteFunction, o as PublicServeOptions } from './types-9nCq6bRP.js';
3
3
  import '@upstash/qstash';
4
4
  import 'zod';
5
5
  import 'ai';
package/solidjs.js CHANGED
@@ -25,7 +25,95 @@ __export(solidjs_exports, {
25
25
  module.exports = __toCommonJS(solidjs_exports);
26
26
 
27
27
  // src/client/utils.ts
28
+ var import_qstash2 = require("@upstash/qstash");
29
+
30
+ // src/error.ts
28
31
  var import_qstash = require("@upstash/qstash");
32
+ var WorkflowError = class extends import_qstash.QstashError {
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = "WorkflowError";
36
+ }
37
+ };
38
+ var WorkflowAbort = class extends Error {
39
+ stepInfo;
40
+ stepName;
41
+ /**
42
+ * whether workflow is to be canceled on abort
43
+ */
44
+ cancelWorkflow;
45
+ /**
46
+ *
47
+ * @param stepName name of the aborting step
48
+ * @param stepInfo step information
49
+ * @param cancelWorkflow
50
+ */
51
+ constructor(stepName, stepInfo, cancelWorkflow = false) {
52
+ super(
53
+ `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}'.`
54
+ );
55
+ this.name = "WorkflowAbort";
56
+ this.stepName = stepName;
57
+ this.stepInfo = stepInfo;
58
+ this.cancelWorkflow = cancelWorkflow;
59
+ }
60
+ };
61
+ var WorkflowNonRetryableError = class extends WorkflowAbort {
62
+ /**
63
+ * @param message error message to be displayed
64
+ */
65
+ constructor(message) {
66
+ super("fail", void 0, false);
67
+ this.name = "WorkflowNonRetryableError";
68
+ if (message) this.message = message;
69
+ }
70
+ };
71
+ var WorkflowRetryAfterError = class extends WorkflowAbort {
72
+ retryAfter;
73
+ /**
74
+ * @param retryAfter time in seconds after which the workflow should be retried
75
+ * @param message error message to be displayed
76
+ */
77
+ constructor(message, retryAfter) {
78
+ super("retry", void 0, false);
79
+ this.name = "WorkflowRetryAfterError";
80
+ this.retryAfter = retryAfter;
81
+ if (message) this.message = message;
82
+ }
83
+ };
84
+ var formatWorkflowError = (error) => {
85
+ return error instanceof Error ? {
86
+ error: error.name,
87
+ message: error.message,
88
+ stack: error.stack
89
+ } : {
90
+ error: "Error",
91
+ message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
92
+ };
93
+ };
94
+ function getConstructorName(obj) {
95
+ if (obj === null || obj === void 0) {
96
+ return null;
97
+ }
98
+ const ctor = obj.constructor;
99
+ if (!ctor || ctor.name === "Object") {
100
+ return null;
101
+ }
102
+ return ctor.name;
103
+ }
104
+ function getConstructorNames(obj) {
105
+ const proto = Object.getPrototypeOf(obj);
106
+ const name = getConstructorName(proto);
107
+ if (name === null) {
108
+ return [];
109
+ }
110
+ return [name, ...getConstructorNames(proto)];
111
+ }
112
+ function isInstanceOf(v, ctor) {
113
+ return getConstructorNames(v).includes(ctor.name);
114
+ }
115
+
116
+ // src/client/utils.ts
29
117
  var makeNotifyRequest = async (requester, eventId, eventData) => {
30
118
  const result = await requester.request({
31
119
  path: ["v2", "notify", eventId],
@@ -65,7 +153,7 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
65
153
  return { steps: filteredSteps, workflowRunEnded: false };
66
154
  }
67
155
  } catch (error) {
68
- if (error instanceof import_qstash.QstashError && error.status === 404) {
156
+ if (isInstanceOf(error, import_qstash2.QstashError) && error.status === 404) {
69
157
  await debug?.log("WARN", "ENDPOINT_START", {
70
158
  message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
71
159
  error
@@ -90,65 +178,13 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
90
178
  var DEFAULT_CONTENT_TYPE = "application/json";
91
179
  var NO_CONCURRENCY = 1;
92
180
  var DEFAULT_RETRIES = 3;
93
- var VERSION = "v0.2.21";
181
+ var VERSION = "v0.2.22";
94
182
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
95
183
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
96
184
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
97
185
  var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
98
186
  var TELEMETRY_HEADER_AGENT = "Upstash-Telemetry-Agent";
99
187
 
100
- // src/error.ts
101
- var import_qstash2 = require("@upstash/qstash");
102
- var WorkflowError = class extends import_qstash2.QstashError {
103
- constructor(message) {
104
- super(message);
105
- this.name = "WorkflowError";
106
- }
107
- };
108
- var WorkflowAbort = class extends Error {
109
- stepInfo;
110
- stepName;
111
- /**
112
- * whether workflow is to be canceled on abort
113
- */
114
- cancelWorkflow;
115
- /**
116
- *
117
- * @param stepName name of the aborting step
118
- * @param stepInfo step information
119
- * @param cancelWorkflow
120
- */
121
- constructor(stepName, stepInfo, cancelWorkflow = false) {
122
- super(
123
- `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}'.`
124
- );
125
- this.name = "WorkflowAbort";
126
- this.stepName = stepName;
127
- this.stepInfo = stepInfo;
128
- this.cancelWorkflow = cancelWorkflow;
129
- }
130
- };
131
- var WorkflowNonRetryableError = class extends WorkflowAbort {
132
- /**
133
- * @param message error message to be displayed
134
- */
135
- constructor(message) {
136
- super("fail", void 0, false);
137
- this.name = "WorkflowNonRetryableError";
138
- if (message) this.message = message;
139
- }
140
- };
141
- var formatWorkflowError = (error) => {
142
- return error instanceof Error ? {
143
- error: error.name,
144
- message: error.message,
145
- stack: error.stack
146
- } : {
147
- error: "Error",
148
- message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
149
- };
150
- };
151
-
152
188
  // src/context/auto-executor.ts
153
189
  var import_qstash5 = require("@upstash/qstash");
154
190
 
@@ -708,17 +744,17 @@ var triggerRouteFunction = async ({
708
744
  return ok("workflow-finished");
709
745
  } catch (error) {
710
746
  const error_ = error;
711
- if (error instanceof import_qstash3.QstashError && error.status === 400) {
747
+ if (isInstanceOf(error, import_qstash3.QstashError) && error.status === 400) {
712
748
  await debug?.log("WARN", "RESPONSE_WORKFLOW", {
713
749
  message: `tried to append to a cancelled workflow. exiting without publishing.`,
714
750
  name: error.name,
715
751
  errorMessage: error.message
716
752
  });
717
753
  return ok("workflow-was-finished");
718
- } else if (!(error_ instanceof WorkflowAbort)) {
719
- return err(error_);
720
- } else if (error_ instanceof WorkflowNonRetryableError) {
754
+ } else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
721
755
  return ok(error_);
756
+ } else if (!isInstanceOf(error_, WorkflowAbort)) {
757
+ return err(error_);
722
758
  } else if (error_.cancelWorkflow) {
723
759
  await onCancel();
724
760
  return ok("workflow-finished");
@@ -2025,7 +2061,7 @@ var AutoExecutor = class _AutoExecutor {
2025
2061
  });
2026
2062
  throw new WorkflowAbort(parallelStep.stepName, resultStep);
2027
2063
  } catch (error) {
2028
- if (error instanceof WorkflowAbort || error instanceof import_qstash5.QstashError && error.status === 400) {
2064
+ if (isInstanceOf(error, WorkflowAbort) || isInstanceOf(error, import_qstash5.QstashError) && error.status === 400) {
2029
2065
  throw error;
2030
2066
  }
2031
2067
  throw new WorkflowError(
@@ -2132,7 +2168,7 @@ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
2132
2168
  validateStep(lazySteps[index], stepFromRequest);
2133
2169
  }
2134
2170
  } catch (error) {
2135
- if (error instanceof WorkflowError) {
2171
+ if (isInstanceOf(error, WorkflowError)) {
2136
2172
  const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
2137
2173
  const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
2138
2174
  const requestStepNames = stepsFromRequest.map((step) => step.stepName);
@@ -2313,7 +2349,7 @@ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
2313
2349
  headers: responseHeaders
2314
2350
  });
2315
2351
  } catch (error) {
2316
- if (error instanceof Error && error.name === "WorkflowAbort") {
2352
+ if (error instanceof Error && isInstanceOf(error, WorkflowAbort)) {
2317
2353
  throw error;
2318
2354
  } else {
2319
2355
  console.error("Error in fetch implementation:", error);
@@ -2415,10 +2451,10 @@ var Agent = class {
2415
2451
  });
2416
2452
  return { text: result.text };
2417
2453
  } catch (error) {
2418
- if (error instanceof import_ai2.ToolExecutionError) {
2419
- if (error.cause instanceof Error && error.cause.name === "WorkflowAbort") {
2454
+ if (isInstanceOf(error, import_ai2.ToolExecutionError)) {
2455
+ if (error.cause instanceof Error && isInstanceOf(error.cause, WorkflowAbort)) {
2420
2456
  throw error.cause;
2421
- } else if (error.cause instanceof import_ai2.ToolExecutionError && error.cause.cause instanceof Error && error.cause.cause.name === "WorkflowAbort") {
2457
+ } else if (isInstanceOf(error.cause, import_ai2.ToolExecutionError) && isInstanceOf(error.cause.cause, WorkflowAbort)) {
2422
2458
  throw error.cause.cause;
2423
2459
  } else {
2424
2460
  throw error;
@@ -3100,7 +3136,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3100
3136
  try {
3101
3137
  await routeFunction(disabledContext);
3102
3138
  } catch (error) {
3103
- if (error instanceof WorkflowAbort && error.stepName === this.disabledMessage || error instanceof WorkflowNonRetryableError) {
3139
+ if (isInstanceOf(error, WorkflowAbort) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
3104
3140
  return ok("step-found");
3105
3141
  }
3106
3142
  console.warn(
@@ -3353,13 +3389,24 @@ var processOptions = (options) => {
3353
3389
  },
3354
3390
  status: 489
3355
3391
  });
3356
- } else if (detailedFinishCondition?.condition === "failure-callback") {
3357
- return new Response(detailedFinishCondition.result ?? void 0, {
3358
- status: 200,
3392
+ } else if (detailedFinishCondition?.condition === "retry-after-error") {
3393
+ return new Response(JSON.stringify(formatWorkflowError(detailedFinishCondition.result)), {
3359
3394
  headers: {
3395
+ "Retry-After": detailedFinishCondition.result.retryAfter.toString(),
3360
3396
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3361
- }
3397
+ },
3398
+ status: 429
3362
3399
  });
3400
+ } else if (detailedFinishCondition?.condition === "failure-callback") {
3401
+ return new Response(
3402
+ JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
3403
+ {
3404
+ status: 200,
3405
+ headers: {
3406
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3407
+ }
3408
+ }
3409
+ );
3363
3410
  }
3364
3411
  return new Response(JSON.stringify({ workflowRunId }), {
3365
3412
  status: 200,
@@ -3569,12 +3616,18 @@ var serveBase = (routeFunction, telemetry, options) => {
3569
3616
  },
3570
3617
  debug
3571
3618
  });
3572
- if (result.isOk() && result.value instanceof WorkflowNonRetryableError) {
3619
+ if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
3573
3620
  return onStepFinish(workflowRunId, result.value, {
3574
3621
  condition: "non-retryable-error",
3575
3622
  result: result.value
3576
3623
  });
3577
3624
  }
3625
+ if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
3626
+ return onStepFinish(workflowRunId, result.value, {
3627
+ condition: "retry-after-error",
3628
+ result: result.value
3629
+ });
3630
+ }
3578
3631
  if (result.isErr()) {
3579
3632
  await debug?.log("ERROR", "ERROR", { error: result.error.message });
3580
3633
  throw result.error;
@@ -3605,7 +3658,7 @@ var serveBase = (routeFunction, telemetry, options) => {
3605
3658
  const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
3606
3659
  Original error: '${formattedError.message}'`;
3607
3660
  console.error(errorMessage);
3608
- return new Response(errorMessage, {
3661
+ return new Response(JSON.stringify({ error: errorMessage }), {
3609
3662
  status: 500,
3610
3663
  headers: {
3611
3664
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION