@upstash/workflow 1.1.0 → 1.1.1

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.
Files changed (50) hide show
  1. package/astro.d.mts +2 -2
  2. package/astro.d.ts +2 -2
  3. package/astro.js +28 -2
  4. package/astro.mjs +1 -1
  5. package/{chunk-C5HFGF7Q.mjs → chunk-QRCGBBFJ.mjs} +28 -2
  6. package/cloudflare.d.mts +2 -2
  7. package/cloudflare.d.ts +2 -2
  8. package/cloudflare.js +28 -2
  9. package/cloudflare.mjs +1 -1
  10. package/express.d.mts +2 -2
  11. package/express.d.ts +2 -2
  12. package/express.js +28 -2
  13. package/express.mjs +1 -1
  14. package/h3.d.mts +2 -2
  15. package/h3.d.ts +2 -2
  16. package/h3.js +32 -6
  17. package/h3.mjs +5 -5
  18. package/hono.d.mts +2 -2
  19. package/hono.d.ts +2 -2
  20. package/hono.js +28 -2
  21. package/hono.mjs +1 -1
  22. package/index.d.mts +3 -3
  23. package/index.d.ts +3 -3
  24. package/index.js +31 -3
  25. package/index.mjs +4 -2
  26. package/nextjs.d.mts +2 -2
  27. package/nextjs.d.ts +2 -2
  28. package/nextjs.js +28 -2
  29. package/nextjs.mjs +1 -1
  30. package/package.json +1 -1
  31. package/react-router.d.mts +38 -0
  32. package/react-router.d.ts +38 -0
  33. package/react-router.js +3861 -0
  34. package/react-router.mjs +45 -0
  35. package/{serve-many-qnfynN1x.d.ts → serve-many-BP-8Ytbc.d.ts} +1 -1
  36. package/{serve-many-DhB8-zPD.d.mts → serve-many-BsycEL_d.d.mts} +1 -1
  37. package/solidjs.d.mts +1 -1
  38. package/solidjs.d.ts +1 -1
  39. package/solidjs.js +28 -2
  40. package/solidjs.mjs +1 -1
  41. package/svelte.d.mts +2 -2
  42. package/svelte.d.ts +2 -2
  43. package/svelte.js +28 -2
  44. package/svelte.mjs +1 -1
  45. package/tanstack.d.mts +2 -2
  46. package/tanstack.d.ts +2 -2
  47. package/tanstack.js +28 -2
  48. package/tanstack.mjs +1 -1
  49. package/{types-pEje3VEB.d.ts → types-B90SJYZV.d.mts} +9 -2
  50. package/{types-pEje3VEB.d.mts → types-B90SJYZV.d.ts} +9 -2
@@ -0,0 +1,3861 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // platforms/react-router.ts
21
+ var react_router_exports = {};
22
+ __export(react_router_exports, {
23
+ createWorkflow: () => createWorkflow,
24
+ serve: () => serve,
25
+ serveMany: () => serveMany
26
+ });
27
+ module.exports = __toCommonJS(react_router_exports);
28
+
29
+ // src/constants.ts
30
+ var WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId";
31
+ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
32
+ var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
33
+ var WORKFLOW_CREATED_AT_HEADER = "Upstash-Workflow-CreatedAt";
34
+ var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
35
+ var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
36
+ var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
37
+ var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
38
+ var WORKFLOW_LABEL_HEADER = "Upstash-Label";
39
+ var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
40
+ var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
41
+ var WORKFLOW_PROTOCOL_VERSION = "1";
42
+ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
43
+ var DEFAULT_CONTENT_TYPE = "application/json";
44
+ var NO_CONCURRENCY = 1;
45
+ var DEFAULT_RETRIES = 3;
46
+ var VERSION = "v1.0.0";
47
+ var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
48
+ var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
49
+ var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
50
+ var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
51
+
52
+ // src/client/utils.ts
53
+ var import_qstash2 = require("@upstash/qstash");
54
+
55
+ // src/error.ts
56
+ var import_qstash = require("@upstash/qstash");
57
+ var WorkflowError = class extends import_qstash.QstashError {
58
+ constructor(message) {
59
+ super(message);
60
+ this.name = "WorkflowError";
61
+ }
62
+ };
63
+ var WorkflowAbort = class extends Error {
64
+ stepName;
65
+ stepInfo;
66
+ /**
67
+ * @param stepName name of the aborting step
68
+ * @param stepInfo step information
69
+ */
70
+ constructor(stepName, stepInfo) {
71
+ super(
72
+ `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}'.`
73
+ );
74
+ this.name = "WorkflowAbort";
75
+ this.stepName = stepName;
76
+ this.stepInfo = stepInfo;
77
+ }
78
+ };
79
+ var WorkflowAuthError = class extends WorkflowAbort {
80
+ /**
81
+ * @param stepName name of the step found during authorization
82
+ */
83
+ constructor(stepName) {
84
+ super(stepName);
85
+ this.name = "WorkflowAuthError";
86
+ this.message = `This is an Upstash Workflow error thrown during authorization check. Found step '${stepName}' during dry-run.`;
87
+ }
88
+ };
89
+ var WorkflowCancelAbort = class extends WorkflowAbort {
90
+ constructor() {
91
+ super("cancel");
92
+ this.name = "WorkflowCancelAbort";
93
+ this.message = "Workflow has been canceled by user via context.cancel().";
94
+ }
95
+ };
96
+ var WorkflowNonRetryableError = class extends WorkflowAbort {
97
+ /**
98
+ * @param message error message to be displayed
99
+ */
100
+ constructor(message) {
101
+ super("non-retryable-error");
102
+ this.name = "WorkflowNonRetryableError";
103
+ this.message = message ?? "Workflow failed with non-retryable error.";
104
+ }
105
+ };
106
+ var WorkflowRetryAfterError = class extends WorkflowAbort {
107
+ retryAfter;
108
+ /**
109
+ * @param message error message to be displayed
110
+ * @param retryAfter time in seconds after which the workflow should be retried
111
+ */
112
+ constructor(message, retryAfter) {
113
+ super("retry-after-error");
114
+ this.name = "WorkflowRetryAfterError";
115
+ this.message = message;
116
+ this.retryAfter = retryAfter;
117
+ }
118
+ };
119
+ var formatWorkflowError = (error) => {
120
+ return error instanceof Error ? {
121
+ error: error.name,
122
+ message: error.message,
123
+ stack: error.stack
124
+ } : {
125
+ error: "Error",
126
+ message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
127
+ };
128
+ };
129
+ function getConstructorName(obj) {
130
+ if (obj === null || obj === void 0) {
131
+ return null;
132
+ }
133
+ const ctor = obj.constructor;
134
+ if (!ctor || ctor.name === "Object") {
135
+ return null;
136
+ }
137
+ return ctor.name;
138
+ }
139
+ function getConstructorNames(obj) {
140
+ const proto = Object.getPrototypeOf(obj);
141
+ const name = getConstructorName(proto);
142
+ if (name === null) {
143
+ return [];
144
+ }
145
+ return [name, ...getConstructorNames(proto)];
146
+ }
147
+ function isInstanceOf(v, ctor) {
148
+ return getConstructorNames(v).includes(ctor.name);
149
+ }
150
+
151
+ // src/client/utils.ts
152
+ var makeNotifyRequest = async (requester, eventId, eventData) => {
153
+ const result = await requester.request({
154
+ path: ["v2", "notify", eventId],
155
+ method: "POST",
156
+ body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
157
+ });
158
+ return result;
159
+ };
160
+ var makeCancelRequest = async (requester, workflowRunId) => {
161
+ await requester.request({
162
+ path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
163
+ method: "DELETE",
164
+ parseResponseAsJson: false
165
+ });
166
+ return true;
167
+ };
168
+ var getSteps = async (requester, workflowRunId, messageId, dispatchDebug) => {
169
+ try {
170
+ const steps = await requester.request({
171
+ path: ["v2", "workflows", "runs", workflowRunId],
172
+ parseResponseAsJson: true
173
+ });
174
+ if (steps.length === 1) {
175
+ return {
176
+ steps,
177
+ workflowRunEnded: false
178
+ };
179
+ }
180
+ if (!messageId) {
181
+ await dispatchDebug?.("onInfo", {
182
+ info: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
183
+ });
184
+ return { steps, workflowRunEnded: false };
185
+ } else {
186
+ const index = steps.findIndex((item) => item.messageId === messageId);
187
+ if (index === -1) {
188
+ return { steps: [], workflowRunEnded: false };
189
+ }
190
+ const filteredSteps = steps.slice(0, index + 1);
191
+ await dispatchDebug?.("onInfo", {
192
+ info: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
193
+ });
194
+ return { steps: filteredSteps, workflowRunEnded: false };
195
+ }
196
+ } catch (error) {
197
+ if (isInstanceOf(error, import_qstash2.QstashError) && error.status === 404) {
198
+ await dispatchDebug?.("onWarning", {
199
+ warning: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed."
200
+ });
201
+ return { steps: void 0, workflowRunEnded: true };
202
+ } else {
203
+ throw error;
204
+ }
205
+ }
206
+ };
207
+
208
+ // src/context/auto-executor.ts
209
+ var import_qstash5 = require("@upstash/qstash");
210
+
211
+ // src/qstash/headers.ts
212
+ var import_qstash4 = require("@upstash/qstash");
213
+
214
+ // src/utils.ts
215
+ var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
216
+ var NANOID_LENGTH = 21;
217
+ function getRandomInt() {
218
+ return Math.floor(Math.random() * NANOID_CHARS.length);
219
+ }
220
+ function nanoid(length = NANOID_LENGTH) {
221
+ return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
222
+ }
223
+ function getWorkflowRunId(id) {
224
+ return `wfr_${id ?? nanoid()}`;
225
+ }
226
+ function decodeBase64(base64) {
227
+ const binString = atob(base64);
228
+ try {
229
+ const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
230
+ return new TextDecoder().decode(intArray);
231
+ } catch (error) {
232
+ console.warn(
233
+ `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
234
+ );
235
+ return binString;
236
+ }
237
+ }
238
+ function getUserIdFromToken(qstashClient) {
239
+ try {
240
+ const token = qstashClient.token;
241
+ const decodedToken = decodeBase64(token);
242
+ const tokenPayload = JSON.parse(decodedToken);
243
+ const userId = tokenPayload.UserID;
244
+ if (!userId) {
245
+ throw new WorkflowError("QStash token payload does not contain userId");
246
+ }
247
+ return userId;
248
+ } catch (error) {
249
+ throw new WorkflowError(
250
+ `Failed to decode QStash token while running create webhook step: ${error.message}`
251
+ );
252
+ }
253
+ }
254
+ function getQStashUrl(qstashClient) {
255
+ try {
256
+ const requester = qstashClient.http;
257
+ const baseUrl = requester.baseUrl;
258
+ if (!baseUrl) {
259
+ throw new WorkflowError("QStash client does not have a baseUrl");
260
+ }
261
+ return baseUrl;
262
+ } catch (error) {
263
+ throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
264
+ }
265
+ }
266
+ function getEventId() {
267
+ return `evt_${nanoid(15)}`;
268
+ }
269
+ function stringifyBody(body) {
270
+ if (body === void 0) {
271
+ return void 0;
272
+ }
273
+ if (typeof body === "string") {
274
+ return body;
275
+ }
276
+ return JSON.stringify(body);
277
+ }
278
+
279
+ // node_modules/neverthrow/dist/index.es.js
280
+ var defaultErrorConfig = {
281
+ withStackTrace: false
282
+ };
283
+ var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
284
+ const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
285
+ const maybeStack = config.withStackTrace ? new Error().stack : void 0;
286
+ return {
287
+ data,
288
+ message,
289
+ stack: maybeStack
290
+ };
291
+ };
292
+ function __awaiter(thisArg, _arguments, P, generator) {
293
+ function adopt(value) {
294
+ return value instanceof P ? value : new P(function(resolve) {
295
+ resolve(value);
296
+ });
297
+ }
298
+ return new (P || (P = Promise))(function(resolve, reject) {
299
+ function fulfilled(value) {
300
+ try {
301
+ step(generator.next(value));
302
+ } catch (e) {
303
+ reject(e);
304
+ }
305
+ }
306
+ function rejected(value) {
307
+ try {
308
+ step(generator["throw"](value));
309
+ } catch (e) {
310
+ reject(e);
311
+ }
312
+ }
313
+ function step(result) {
314
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
315
+ }
316
+ step((generator = generator.apply(thisArg, [])).next());
317
+ });
318
+ }
319
+ function __values(o) {
320
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
321
+ if (m) return m.call(o);
322
+ if (o && typeof o.length === "number") return {
323
+ next: function() {
324
+ if (o && i >= o.length) o = void 0;
325
+ return { value: o && o[i++], done: !o };
326
+ }
327
+ };
328
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
329
+ }
330
+ function __await(v) {
331
+ return this instanceof __await ? (this.v = v, this) : new __await(v);
332
+ }
333
+ function __asyncGenerator(thisArg, _arguments, generator) {
334
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
335
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
336
+ return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
337
+ return this;
338
+ }, i;
339
+ function verb(n) {
340
+ if (g[n]) i[n] = function(v) {
341
+ return new Promise(function(a, b) {
342
+ q.push([n, v, a, b]) > 1 || resume(n, v);
343
+ });
344
+ };
345
+ }
346
+ function resume(n, v) {
347
+ try {
348
+ step(g[n](v));
349
+ } catch (e) {
350
+ settle(q[0][3], e);
351
+ }
352
+ }
353
+ function step(r) {
354
+ r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
355
+ }
356
+ function fulfill(value) {
357
+ resume("next", value);
358
+ }
359
+ function reject(value) {
360
+ resume("throw", value);
361
+ }
362
+ function settle(f, v) {
363
+ if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]);
364
+ }
365
+ }
366
+ function __asyncDelegator(o) {
367
+ var i, p;
368
+ return i = {}, verb("next"), verb("throw", function(e) {
369
+ throw e;
370
+ }), verb("return"), i[Symbol.iterator] = function() {
371
+ return this;
372
+ }, i;
373
+ function verb(n, f) {
374
+ i[n] = o[n] ? function(v) {
375
+ return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v;
376
+ } : f;
377
+ }
378
+ }
379
+ function __asyncValues(o) {
380
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
381
+ var m = o[Symbol.asyncIterator], i;
382
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
383
+ return this;
384
+ }, i);
385
+ function verb(n) {
386
+ i[n] = o[n] && function(v) {
387
+ return new Promise(function(resolve, reject) {
388
+ v = o[n](v), settle(resolve, reject, v.done, v.value);
389
+ });
390
+ };
391
+ }
392
+ function settle(resolve, reject, d, v) {
393
+ Promise.resolve(v).then(function(v2) {
394
+ resolve({ value: v2, done: d });
395
+ }, reject);
396
+ }
397
+ }
398
+ var ResultAsync = class _ResultAsync {
399
+ constructor(res) {
400
+ this._promise = res;
401
+ }
402
+ static fromSafePromise(promise) {
403
+ const newPromise = promise.then((value) => new Ok(value));
404
+ return new _ResultAsync(newPromise);
405
+ }
406
+ static fromPromise(promise, errorFn) {
407
+ const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
408
+ return new _ResultAsync(newPromise);
409
+ }
410
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
411
+ static fromThrowable(fn, errorFn) {
412
+ return (...args) => {
413
+ return new _ResultAsync((() => __awaiter(this, void 0, void 0, function* () {
414
+ try {
415
+ return new Ok(yield fn(...args));
416
+ } catch (error) {
417
+ return new Err(errorFn ? errorFn(error) : error);
418
+ }
419
+ }))());
420
+ };
421
+ }
422
+ static combine(asyncResultList) {
423
+ return combineResultAsyncList(asyncResultList);
424
+ }
425
+ static combineWithAllErrors(asyncResultList) {
426
+ return combineResultAsyncListWithAllErrors(asyncResultList);
427
+ }
428
+ map(f) {
429
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
430
+ if (res.isErr()) {
431
+ return new Err(res.error);
432
+ }
433
+ return new Ok(yield f(res.value));
434
+ })));
435
+ }
436
+ andThrough(f) {
437
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
438
+ if (res.isErr()) {
439
+ return new Err(res.error);
440
+ }
441
+ const newRes = yield f(res.value);
442
+ if (newRes.isErr()) {
443
+ return new Err(newRes.error);
444
+ }
445
+ return new Ok(res.value);
446
+ })));
447
+ }
448
+ andTee(f) {
449
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
450
+ if (res.isErr()) {
451
+ return new Err(res.error);
452
+ }
453
+ try {
454
+ yield f(res.value);
455
+ } catch (e) {
456
+ }
457
+ return new Ok(res.value);
458
+ })));
459
+ }
460
+ mapErr(f) {
461
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
462
+ if (res.isOk()) {
463
+ return new Ok(res.value);
464
+ }
465
+ return new Err(yield f(res.error));
466
+ })));
467
+ }
468
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
469
+ andThen(f) {
470
+ return new _ResultAsync(this._promise.then((res) => {
471
+ if (res.isErr()) {
472
+ return new Err(res.error);
473
+ }
474
+ const newValue = f(res.value);
475
+ return newValue instanceof _ResultAsync ? newValue._promise : newValue;
476
+ }));
477
+ }
478
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
479
+ orElse(f) {
480
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
481
+ if (res.isErr()) {
482
+ return f(res.error);
483
+ }
484
+ return new Ok(res.value);
485
+ })));
486
+ }
487
+ match(ok2, _err) {
488
+ return this._promise.then((res) => res.match(ok2, _err));
489
+ }
490
+ unwrapOr(t) {
491
+ return this._promise.then((res) => res.unwrapOr(t));
492
+ }
493
+ /**
494
+ * Emulates Rust's `?` operator in `safeTry`'s body. See also `safeTry`.
495
+ */
496
+ safeUnwrap() {
497
+ return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
498
+ return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
499
+ });
500
+ }
501
+ // Makes ResultAsync implement PromiseLike<Result>
502
+ then(successCallback, failureCallback) {
503
+ return this._promise.then(successCallback, failureCallback);
504
+ }
505
+ };
506
+ var errAsync = (err2) => new ResultAsync(Promise.resolve(new Err(err2)));
507
+ var fromPromise = ResultAsync.fromPromise;
508
+ var fromSafePromise = ResultAsync.fromSafePromise;
509
+ var fromAsyncThrowable = ResultAsync.fromThrowable;
510
+ var combineResultList = (resultList) => {
511
+ let acc = ok([]);
512
+ for (const result of resultList) {
513
+ if (result.isErr()) {
514
+ acc = err(result.error);
515
+ break;
516
+ } else {
517
+ acc.map((list) => list.push(result.value));
518
+ }
519
+ }
520
+ return acc;
521
+ };
522
+ var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
523
+ var combineResultListWithAllErrors = (resultList) => {
524
+ let acc = ok([]);
525
+ for (const result of resultList) {
526
+ if (result.isErr() && acc.isErr()) {
527
+ acc.error.push(result.error);
528
+ } else if (result.isErr() && acc.isOk()) {
529
+ acc = err([result.error]);
530
+ } else if (result.isOk() && acc.isOk()) {
531
+ acc.value.push(result.value);
532
+ }
533
+ }
534
+ return acc;
535
+ };
536
+ var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
537
+ var Result;
538
+ (function(Result2) {
539
+ function fromThrowable2(fn, errorFn) {
540
+ return (...args) => {
541
+ try {
542
+ const result = fn(...args);
543
+ return ok(result);
544
+ } catch (e) {
545
+ return err(errorFn ? errorFn(e) : e);
546
+ }
547
+ };
548
+ }
549
+ Result2.fromThrowable = fromThrowable2;
550
+ function combine(resultList) {
551
+ return combineResultList(resultList);
552
+ }
553
+ Result2.combine = combine;
554
+ function combineWithAllErrors(resultList) {
555
+ return combineResultListWithAllErrors(resultList);
556
+ }
557
+ Result2.combineWithAllErrors = combineWithAllErrors;
558
+ })(Result || (Result = {}));
559
+ var ok = (value) => new Ok(value);
560
+ function err(err2) {
561
+ return new Err(err2);
562
+ }
563
+ var Ok = class {
564
+ constructor(value) {
565
+ this.value = value;
566
+ }
567
+ isOk() {
568
+ return true;
569
+ }
570
+ isErr() {
571
+ return !this.isOk();
572
+ }
573
+ map(f) {
574
+ return ok(f(this.value));
575
+ }
576
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
577
+ mapErr(_f) {
578
+ return ok(this.value);
579
+ }
580
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
581
+ andThen(f) {
582
+ return f(this.value);
583
+ }
584
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
585
+ andThrough(f) {
586
+ return f(this.value).map((_value) => this.value);
587
+ }
588
+ andTee(f) {
589
+ try {
590
+ f(this.value);
591
+ } catch (e) {
592
+ }
593
+ return ok(this.value);
594
+ }
595
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
596
+ orElse(_f) {
597
+ return ok(this.value);
598
+ }
599
+ asyncAndThen(f) {
600
+ return f(this.value);
601
+ }
602
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
603
+ asyncAndThrough(f) {
604
+ return f(this.value).map(() => this.value);
605
+ }
606
+ asyncMap(f) {
607
+ return ResultAsync.fromSafePromise(f(this.value));
608
+ }
609
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
610
+ unwrapOr(_v) {
611
+ return this.value;
612
+ }
613
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
614
+ match(ok2, _err) {
615
+ return ok2(this.value);
616
+ }
617
+ safeUnwrap() {
618
+ const value = this.value;
619
+ return (function* () {
620
+ return value;
621
+ })();
622
+ }
623
+ _unsafeUnwrap(_) {
624
+ return this.value;
625
+ }
626
+ _unsafeUnwrapErr(config) {
627
+ throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
628
+ }
629
+ };
630
+ var Err = class {
631
+ constructor(error) {
632
+ this.error = error;
633
+ }
634
+ isOk() {
635
+ return false;
636
+ }
637
+ isErr() {
638
+ return !this.isOk();
639
+ }
640
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
641
+ map(_f) {
642
+ return err(this.error);
643
+ }
644
+ mapErr(f) {
645
+ return err(f(this.error));
646
+ }
647
+ andThrough(_f) {
648
+ return err(this.error);
649
+ }
650
+ andTee(_f) {
651
+ return err(this.error);
652
+ }
653
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
654
+ andThen(_f) {
655
+ return err(this.error);
656
+ }
657
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
658
+ orElse(f) {
659
+ return f(this.error);
660
+ }
661
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
662
+ asyncAndThen(_f) {
663
+ return errAsync(this.error);
664
+ }
665
+ asyncAndThrough(_f) {
666
+ return errAsync(this.error);
667
+ }
668
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
669
+ asyncMap(_f) {
670
+ return errAsync(this.error);
671
+ }
672
+ unwrapOr(v) {
673
+ return v;
674
+ }
675
+ match(_ok, err2) {
676
+ return err2(this.error);
677
+ }
678
+ safeUnwrap() {
679
+ const error = this.error;
680
+ return (function* () {
681
+ yield err(error);
682
+ throw new Error("Do not use this generator out of `safeTry`");
683
+ })();
684
+ }
685
+ _unsafeUnwrap(config) {
686
+ throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
687
+ }
688
+ _unsafeUnwrapErr(_) {
689
+ return this.error;
690
+ }
691
+ };
692
+ var fromThrowable = Result.fromThrowable;
693
+
694
+ // src/types.ts
695
+ var StepTypes = [
696
+ "Initial",
697
+ "Run",
698
+ "SleepFor",
699
+ "SleepUntil",
700
+ "Call",
701
+ "Wait",
702
+ "Notify",
703
+ "Invoke",
704
+ "CreateWebhook",
705
+ "WaitForWebhook"
706
+ ];
707
+
708
+ // src/workflow-requests.ts
709
+ var import_qstash3 = require("@upstash/qstash");
710
+ var triggerFirstInvocation = async (params) => {
711
+ const firstInvocationParams = Array.isArray(params) ? params : [params];
712
+ const workflowContextClient = firstInvocationParams[0].workflowContext.qstashClient;
713
+ const invocationBatch = firstInvocationParams.map(
714
+ ({
715
+ workflowContext,
716
+ useJSONContent,
717
+ telemetry: telemetry2,
718
+ invokeCount,
719
+ delay,
720
+ notBefore,
721
+ failureUrl,
722
+ retries,
723
+ retryDelay,
724
+ flowControl,
725
+ unknownSdk
726
+ }) => {
727
+ const { headers } = getHeaders({
728
+ initHeaderValue: "true",
729
+ workflowConfig: {
730
+ workflowRunId: workflowContext.workflowRunId,
731
+ workflowUrl: workflowContext.url,
732
+ failureUrl,
733
+ retries,
734
+ retryDelay,
735
+ telemetry: telemetry2,
736
+ flowControl,
737
+ useJSONContent: useJSONContent ?? false
738
+ },
739
+ invokeCount: invokeCount ?? 0,
740
+ userHeaders: workflowContext.headers
741
+ });
742
+ if (workflowContext.headers.get("content-type")) {
743
+ headers["content-type"] = workflowContext.headers.get("content-type");
744
+ }
745
+ if (useJSONContent) {
746
+ headers["content-type"] = "application/json";
747
+ }
748
+ if (unknownSdk) {
749
+ headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
750
+ }
751
+ if (workflowContext.label) {
752
+ headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
753
+ }
754
+ const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
755
+ return {
756
+ headers,
757
+ method: "POST",
758
+ body,
759
+ url: workflowContext.url,
760
+ delay,
761
+ notBefore
762
+ };
763
+ }
764
+ );
765
+ try {
766
+ const results = await workflowContextClient.batch(invocationBatch);
767
+ const invocationStatuses = [];
768
+ for (let i = 0; i < results.length; i++) {
769
+ const result = results[i];
770
+ const invocationParams = firstInvocationParams[i];
771
+ invocationParams.middlewareManager?.assignContext(invocationParams.workflowContext);
772
+ if (result.deduplicated) {
773
+ await invocationParams.middlewareManager?.dispatchDebug("onWarning", {
774
+ warning: `Workflow run ${invocationParams.workflowContext.workflowRunId} already exists. A new one isn't created.`
775
+ });
776
+ invocationStatuses.push("workflow-run-already-exists");
777
+ } else {
778
+ await invocationParams.middlewareManager?.dispatchDebug("onInfo", {
779
+ info: `Workflow run started successfully with URL ${invocationParams.workflowContext.url}.`
780
+ });
781
+ invocationStatuses.push("success");
782
+ }
783
+ }
784
+ const hasAnyDeduplicated = invocationStatuses.some(
785
+ (status) => status === "workflow-run-already-exists"
786
+ );
787
+ if (hasAnyDeduplicated) {
788
+ return ok("workflow-run-already-exists");
789
+ } else {
790
+ return ok("success");
791
+ }
792
+ } catch (error) {
793
+ const error_ = error;
794
+ return err(error_);
795
+ }
796
+ };
797
+ var triggerRouteFunction = async ({
798
+ onCleanup,
799
+ onStep,
800
+ onCancel,
801
+ middlewareManager
802
+ }) => {
803
+ try {
804
+ const result = await onStep();
805
+ await onCleanup(result);
806
+ return ok("workflow-finished");
807
+ } catch (error) {
808
+ const error_ = error;
809
+ if (isInstanceOf(error, import_qstash3.QstashError) && error.status === 400) {
810
+ await middlewareManager?.dispatchDebug("onWarning", {
811
+ warning: `Tried to append to a cancelled workflow. Exiting without publishing. Error: ${error.message}`
812
+ });
813
+ return ok("workflow-was-finished");
814
+ } else if (isInstanceOf(error_, WorkflowNonRetryableError) || isInstanceOf(error_, WorkflowRetryAfterError)) {
815
+ return ok(error_);
816
+ } else if (isInstanceOf(error_, WorkflowCancelAbort)) {
817
+ await onCancel();
818
+ return ok("workflow-finished");
819
+ } else if (isInstanceOf(error_, WorkflowAbort)) {
820
+ return ok("step-finished");
821
+ } else {
822
+ return err(error_);
823
+ }
824
+ }
825
+ };
826
+ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, dispatchDebug) => {
827
+ await dispatchDebug?.("onInfo", {
828
+ info: `Deleting workflow run ${workflowContext.workflowRunId} from QStash` + (cancel ? " with cancel=true." : ".")
829
+ });
830
+ await workflowContext.qstashClient.http.request({
831
+ path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
832
+ method: "DELETE",
833
+ parseResponseAsJson: false,
834
+ body: JSON.stringify(result)
835
+ });
836
+ await dispatchDebug?.("onInfo", {
837
+ info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
838
+ });
839
+ };
840
+ var recreateUserHeaders = (headers) => {
841
+ const filteredHeaders = new Headers();
842
+ const pairs = headers.entries();
843
+ for (const [header, value] of pairs) {
844
+ const headerLowerCase = header.toLowerCase();
845
+ const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
846
+ !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
847
+ headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
848
+ headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
849
+ if (isUserHeader) {
850
+ filteredHeaders.append(header, value);
851
+ }
852
+ }
853
+ return filteredHeaders;
854
+ };
855
+ var handleThirdPartyCallResult = async ({
856
+ request,
857
+ requestPayload,
858
+ client,
859
+ workflowUrl,
860
+ telemetry: telemetry2,
861
+ middlewareManager
862
+ }) => {
863
+ try {
864
+ if (request.headers.get("Upstash-Workflow-Callback")) {
865
+ let callbackPayload;
866
+ if (requestPayload) {
867
+ callbackPayload = requestPayload;
868
+ } else {
869
+ const workflowRunId2 = request.headers.get("upstash-workflow-runid");
870
+ const messageId = request.headers.get("upstash-message-id");
871
+ if (!workflowRunId2)
872
+ throw new WorkflowError("workflow run id missing in context.call lazy fetch.");
873
+ if (!messageId) throw new WorkflowError("message id missing in context.call lazy fetch.");
874
+ const { steps, workflowRunEnded } = await getSteps(
875
+ client.http,
876
+ workflowRunId2,
877
+ messageId,
878
+ middlewareManager?.dispatchDebug.bind(middlewareManager)
879
+ );
880
+ if (workflowRunEnded) {
881
+ return ok("workflow-ended");
882
+ }
883
+ const failingStep = steps.find((step) => step.messageId === messageId);
884
+ if (!failingStep)
885
+ throw new WorkflowError(
886
+ "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.`)
887
+ );
888
+ callbackPayload = atob(failingStep.body);
889
+ }
890
+ const callbackMessage = JSON.parse(callbackPayload);
891
+ if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
892
+ await middlewareManager?.dispatchDebug("onWarning", {
893
+ warning: `Third party call returned status ${callbackMessage.status}. Retrying (${callbackMessage.retried} out of ${callbackMessage.maxRetries}).`
894
+ });
895
+ console.warn(
896
+ `Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
897
+ ${atob(callbackMessage.body ?? "")}`
898
+ );
899
+ return ok("call-will-retry");
900
+ }
901
+ const workflowRunId = request.headers.get(WORKFLOW_ID_HEADER);
902
+ const stepIdString = request.headers.get("Upstash-Workflow-StepId");
903
+ const stepName = request.headers.get("Upstash-Workflow-StepName");
904
+ const stepType = request.headers.get("Upstash-Workflow-StepType");
905
+ const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
906
+ const contentType = request.headers.get("Upstash-Workflow-ContentType");
907
+ const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
908
+ if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
909
+ throw new Error(
910
+ `Missing info in callback message source header: ${JSON.stringify({
911
+ workflowRunId,
912
+ stepIdString,
913
+ stepName,
914
+ stepType,
915
+ concurrentString,
916
+ contentType
917
+ })}`
918
+ );
919
+ }
920
+ const userHeaders = recreateUserHeaders(request.headers);
921
+ const { headers: requestHeaders } = getHeaders({
922
+ initHeaderValue: "false",
923
+ workflowConfig: {
924
+ workflowRunId,
925
+ workflowUrl,
926
+ telemetry: telemetry2
927
+ },
928
+ userHeaders,
929
+ invokeCount: Number(invokeCount)
930
+ });
931
+ const callResponse = {
932
+ status: callbackMessage.status,
933
+ body: atob(callbackMessage.body ?? ""),
934
+ header: callbackMessage.header
935
+ };
936
+ const callResultStep = {
937
+ stepId: Number(stepIdString),
938
+ stepName,
939
+ stepType,
940
+ out: JSON.stringify(callResponse),
941
+ concurrent: Number(concurrentString)
942
+ };
943
+ await middlewareManager?.dispatchDebug("onInfo", {
944
+ info: `Submitting third party call result, step ${stepName} (${stepIdString}).`
945
+ });
946
+ await client.publishJSON({
947
+ headers: requestHeaders,
948
+ method: "POST",
949
+ body: callResultStep,
950
+ url: workflowUrl
951
+ });
952
+ await middlewareManager?.dispatchDebug("onInfo", {
953
+ info: `Third party call result submitted successfully, step ${stepName} (${stepIdString}).`
954
+ });
955
+ return ok("is-call-return");
956
+ } else {
957
+ return ok("continue-workflow");
958
+ }
959
+ } catch (error) {
960
+ const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
961
+ return err(
962
+ new WorkflowError(`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`)
963
+ );
964
+ }
965
+ };
966
+ var getTelemetryHeaders = (telemetry2) => {
967
+ return {
968
+ [TELEMETRY_HEADER_SDK]: telemetry2.sdk,
969
+ [TELEMETRY_HEADER_FRAMEWORK]: telemetry2.framework ?? "unknown",
970
+ [TELEMETRY_HEADER_RUNTIME]: telemetry2.runtime ?? "unknown"
971
+ };
972
+ };
973
+ var verifyRequest = async (body, signature, verifier) => {
974
+ if (!verifier) {
975
+ return;
976
+ }
977
+ try {
978
+ if (!signature) {
979
+ throw new Error("`Upstash-Signature` header is not passed.");
980
+ }
981
+ const isValid = await verifier.verify({
982
+ body,
983
+ signature
984
+ });
985
+ if (!isValid) {
986
+ throw new Error("Signature in `Upstash-Signature` header is not valid");
987
+ }
988
+ } catch (error) {
989
+ throw new WorkflowError(
990
+ `Failed to verify that the Workflow request comes from QStash: ${error}
991
+
992
+ If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
993
+
994
+ If you want to disable QStash Verification, you should clear env variables QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY`
995
+ );
996
+ }
997
+ };
998
+
999
+ // src/context/steps.ts
1000
+ var BaseLazyStep = class _BaseLazyStep {
1001
+ stepName;
1002
+ context;
1003
+ constructor(context, stepName) {
1004
+ this.context = context;
1005
+ if (!stepName) {
1006
+ throw new WorkflowError(
1007
+ "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
1008
+ );
1009
+ }
1010
+ if (typeof stepName !== "string") {
1011
+ throw new WorkflowError(
1012
+ `A workflow step name must be a string. Received "${stepName}" (${typeof stepName}).`
1013
+ );
1014
+ }
1015
+ this.stepName = stepName;
1016
+ }
1017
+ /**
1018
+ * parse the out field of a step result.
1019
+ *
1020
+ * will be called when returning the steps to the context from auto executor
1021
+ *
1022
+ * @param step step
1023
+ * @returns parsed out field
1024
+ */
1025
+ parseOut(step) {
1026
+ const out = step.out;
1027
+ if (out === void 0) {
1028
+ if (this.allowUndefinedOut) {
1029
+ return this.handleUndefinedOut(step);
1030
+ } else {
1031
+ throw new WorkflowError(
1032
+ `Error while parsing output of ${this.stepType} step. Expected a string, but got: undefined`
1033
+ );
1034
+ }
1035
+ }
1036
+ if (typeof out === "object") {
1037
+ console.warn(
1038
+ `Error while parsing ${this.stepType} step output. Expected a string, but got object. Please reach out to Upstash Support.`
1039
+ );
1040
+ return out;
1041
+ }
1042
+ if (typeof out !== "string") {
1043
+ throw new WorkflowError(
1044
+ `Error while parsing output of ${this.stepType} step. Expected a string or undefined, but got: ${typeof out}`
1045
+ );
1046
+ }
1047
+ return this.safeParseOut(out, step);
1048
+ }
1049
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1050
+ safeParseOut(out, step) {
1051
+ return _BaseLazyStep.tryParsing(out);
1052
+ }
1053
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1054
+ handleUndefinedOut(step) {
1055
+ return void 0;
1056
+ }
1057
+ static tryParsing(stepOut) {
1058
+ try {
1059
+ return JSON.parse(stepOut);
1060
+ } catch {
1061
+ return stepOut;
1062
+ }
1063
+ }
1064
+ getBody({ step }) {
1065
+ step.out = JSON.stringify(step.out);
1066
+ return JSON.stringify(step);
1067
+ }
1068
+ getHeaders({ context, telemetry: telemetry2, invokeCount, step }) {
1069
+ return getHeaders({
1070
+ initHeaderValue: "false",
1071
+ workflowConfig: {
1072
+ workflowRunId: context.workflowRunId,
1073
+ workflowUrl: context.url,
1074
+ useJSONContent: false,
1075
+ telemetry: telemetry2
1076
+ },
1077
+ userHeaders: context.headers,
1078
+ invokeCount,
1079
+ stepInfo: {
1080
+ step,
1081
+ lazyStep: this
1082
+ }
1083
+ });
1084
+ }
1085
+ async submitStep({ context, body, headers }) {
1086
+ return await context.qstashClient.batch([
1087
+ {
1088
+ body,
1089
+ headers,
1090
+ method: "POST",
1091
+ url: context.url
1092
+ }
1093
+ ]);
1094
+ }
1095
+ };
1096
+ var LazyFunctionStep = class extends BaseLazyStep {
1097
+ stepFunction;
1098
+ stepType = "Run";
1099
+ allowUndefinedOut = true;
1100
+ constructor(context, stepName, stepFunction) {
1101
+ super(context, stepName);
1102
+ this.stepFunction = stepFunction;
1103
+ }
1104
+ getPlanStep(concurrent, targetStep) {
1105
+ return {
1106
+ stepId: 0,
1107
+ stepName: this.stepName,
1108
+ stepType: this.stepType,
1109
+ concurrent,
1110
+ targetStep
1111
+ };
1112
+ }
1113
+ async getResultStep(concurrent, stepId) {
1114
+ let result = this.stepFunction();
1115
+ if (result instanceof Promise) {
1116
+ result = await result;
1117
+ }
1118
+ return {
1119
+ stepId,
1120
+ stepName: this.stepName,
1121
+ stepType: this.stepType,
1122
+ out: result,
1123
+ concurrent
1124
+ };
1125
+ }
1126
+ };
1127
+ var LazySleepStep = class extends BaseLazyStep {
1128
+ sleep;
1129
+ stepType = "SleepFor";
1130
+ allowUndefinedOut = true;
1131
+ constructor(context, stepName, sleep) {
1132
+ super(context, stepName);
1133
+ this.sleep = sleep;
1134
+ }
1135
+ getPlanStep(concurrent, targetStep) {
1136
+ return {
1137
+ stepId: 0,
1138
+ stepName: this.stepName,
1139
+ stepType: this.stepType,
1140
+ sleepFor: this.sleep,
1141
+ concurrent,
1142
+ targetStep
1143
+ };
1144
+ }
1145
+ async getResultStep(concurrent, stepId) {
1146
+ return await Promise.resolve({
1147
+ stepId,
1148
+ stepName: this.stepName,
1149
+ stepType: this.stepType,
1150
+ sleepFor: this.sleep,
1151
+ concurrent
1152
+ });
1153
+ }
1154
+ async submitStep({ context, body, headers, isParallel }) {
1155
+ return await context.qstashClient.batch([
1156
+ {
1157
+ body,
1158
+ headers,
1159
+ method: "POST",
1160
+ url: context.url,
1161
+ delay: isParallel ? void 0 : this.sleep
1162
+ }
1163
+ ]);
1164
+ }
1165
+ };
1166
+ var LazySleepUntilStep = class extends BaseLazyStep {
1167
+ sleepUntil;
1168
+ stepType = "SleepUntil";
1169
+ allowUndefinedOut = true;
1170
+ constructor(context, stepName, sleepUntil) {
1171
+ super(context, stepName);
1172
+ this.sleepUntil = sleepUntil;
1173
+ }
1174
+ getPlanStep(concurrent, targetStep) {
1175
+ return {
1176
+ stepId: 0,
1177
+ stepName: this.stepName,
1178
+ stepType: this.stepType,
1179
+ sleepUntil: this.sleepUntil,
1180
+ concurrent,
1181
+ targetStep
1182
+ };
1183
+ }
1184
+ async getResultStep(concurrent, stepId) {
1185
+ return await Promise.resolve({
1186
+ stepId,
1187
+ stepName: this.stepName,
1188
+ stepType: this.stepType,
1189
+ sleepUntil: this.sleepUntil,
1190
+ concurrent
1191
+ });
1192
+ }
1193
+ safeParseOut() {
1194
+ return void 0;
1195
+ }
1196
+ async submitStep({ context, body, headers, isParallel }) {
1197
+ return await context.qstashClient.batch([
1198
+ {
1199
+ body,
1200
+ headers,
1201
+ method: "POST",
1202
+ url: context.url,
1203
+ notBefore: isParallel ? void 0 : this.sleepUntil
1204
+ }
1205
+ ]);
1206
+ }
1207
+ };
1208
+ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1209
+ url;
1210
+ method;
1211
+ body;
1212
+ headers;
1213
+ retries;
1214
+ retryDelay;
1215
+ timeout;
1216
+ flowControl;
1217
+ stepType = "Call";
1218
+ allowUndefinedOut = false;
1219
+ constructor(params) {
1220
+ super(params.context, params.stepName);
1221
+ this.url = params.url;
1222
+ this.method = params.method ?? "GET";
1223
+ this.body = params.body;
1224
+ this.headers = params.headers ?? {};
1225
+ this.retries = params.retries ?? 0;
1226
+ this.retryDelay = params.retryDelay;
1227
+ this.timeout = params.timeout;
1228
+ this.flowControl = params.flowControl;
1229
+ }
1230
+ getPlanStep(concurrent, targetStep) {
1231
+ return {
1232
+ stepId: 0,
1233
+ stepName: this.stepName,
1234
+ stepType: this.stepType,
1235
+ concurrent,
1236
+ targetStep
1237
+ };
1238
+ }
1239
+ async getResultStep(concurrent, stepId) {
1240
+ return await Promise.resolve({
1241
+ stepId,
1242
+ stepName: this.stepName,
1243
+ stepType: this.stepType,
1244
+ concurrent,
1245
+ callUrl: this.url,
1246
+ callMethod: this.method,
1247
+ callBody: this.body,
1248
+ callHeaders: this.headers
1249
+ });
1250
+ }
1251
+ safeParseOut(out) {
1252
+ const { header, status, body } = JSON.parse(out);
1253
+ const responseHeaders = new Headers(header);
1254
+ if (_LazyCallStep.isText(responseHeaders.get("content-type"))) {
1255
+ const bytes = new Uint8Array(out.length);
1256
+ for (let i = 0; i < out.length; i++) {
1257
+ bytes[i] = out.charCodeAt(i);
1258
+ }
1259
+ const processedResult = new TextDecoder().decode(bytes);
1260
+ const newBody = JSON.parse(processedResult).body;
1261
+ return {
1262
+ status,
1263
+ header,
1264
+ body: BaseLazyStep.tryParsing(newBody)
1265
+ };
1266
+ } else {
1267
+ return { header, status, body };
1268
+ }
1269
+ }
1270
+ static applicationContentTypes = [
1271
+ "application/json",
1272
+ "application/xml",
1273
+ "application/javascript",
1274
+ "application/x-www-form-urlencoded",
1275
+ "application/xhtml+xml",
1276
+ "application/ld+json",
1277
+ "application/rss+xml",
1278
+ "application/atom+xml"
1279
+ ];
1280
+ static isText = (contentTypeHeader) => {
1281
+ if (!contentTypeHeader) {
1282
+ return false;
1283
+ }
1284
+ if (_LazyCallStep.applicationContentTypes.some((type) => contentTypeHeader.includes(type))) {
1285
+ return true;
1286
+ }
1287
+ if (contentTypeHeader.startsWith("text/")) {
1288
+ return true;
1289
+ }
1290
+ return false;
1291
+ };
1292
+ getBody({ step }) {
1293
+ if (!step.callUrl) {
1294
+ throw new WorkflowError("Incompatible step received in LazyCallStep.getBody");
1295
+ }
1296
+ return JSON.stringify(step.callBody);
1297
+ }
1298
+ getHeaders({ context, telemetry: telemetry2, invokeCount, step }) {
1299
+ const { headers, contentType } = super.getHeaders({ context, telemetry: telemetry2, invokeCount, step });
1300
+ headers["Upstash-Retries"] = this.retries.toString();
1301
+ if (this.retryDelay) {
1302
+ headers["Upstash-Retry-Delay"] = this.retryDelay;
1303
+ }
1304
+ headers[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
1305
+ if (this.flowControl) {
1306
+ const { flowControlKey, flowControlValue } = prepareFlowControl(this.flowControl);
1307
+ headers["Upstash-Flow-Control-Key"] = flowControlKey;
1308
+ headers["Upstash-Flow-Control-Value"] = flowControlValue;
1309
+ }
1310
+ if (this.timeout) {
1311
+ headers["Upstash-Timeout"] = this.timeout.toString();
1312
+ }
1313
+ const forwardedHeaders = Object.fromEntries(
1314
+ Object.entries(this.headers).map(([header, value]) => [`Upstash-Forward-${header}`, value])
1315
+ );
1316
+ return {
1317
+ headers: {
1318
+ ...headers,
1319
+ ...forwardedHeaders,
1320
+ "Upstash-Callback": context.url,
1321
+ "Upstash-Callback-Workflow-RunId": context.workflowRunId,
1322
+ "Upstash-Callback-Workflow-CallType": "fromCallback",
1323
+ "Upstash-Callback-Workflow-Init": "false",
1324
+ "Upstash-Callback-Workflow-Url": context.url,
1325
+ "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
1326
+ "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
1327
+ "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
1328
+ "Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
1329
+ "Upstash-Callback-Forward-Upstash-Workflow-StepType": this.stepType,
1330
+ "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1331
+ "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1332
+ "Upstash-Workflow-CallType": "toCallback"
1333
+ },
1334
+ contentType
1335
+ };
1336
+ }
1337
+ async submitStep({ context, headers }) {
1338
+ return await context.qstashClient.batch([
1339
+ {
1340
+ headers,
1341
+ body: this.body,
1342
+ method: this.method,
1343
+ url: this.url,
1344
+ retries: DEFAULT_RETRIES === this.retries ? void 0 : this.retries,
1345
+ retryDelay: this.retryDelay,
1346
+ flowControl: this.flowControl
1347
+ }
1348
+ ]);
1349
+ }
1350
+ };
1351
+ var LazyWaitEventStep = class extends BaseLazyStep {
1352
+ eventId;
1353
+ timeout;
1354
+ allowUndefinedOut = false;
1355
+ constructor(context, stepName, eventId, timeout) {
1356
+ super(context, stepName);
1357
+ this.eventId = eventId;
1358
+ this.timeout = timeout;
1359
+ }
1360
+ getPlanStep(concurrent, targetStep) {
1361
+ return {
1362
+ stepId: 0,
1363
+ stepName: this.stepName,
1364
+ stepType: this.stepType,
1365
+ waitEventId: this.eventId,
1366
+ timeout: this.timeout,
1367
+ concurrent,
1368
+ targetStep
1369
+ };
1370
+ }
1371
+ async getResultStep(concurrent, stepId) {
1372
+ return await Promise.resolve({
1373
+ stepId,
1374
+ stepName: this.stepName,
1375
+ stepType: this.stepType,
1376
+ waitEventId: this.eventId,
1377
+ timeout: this.timeout,
1378
+ concurrent
1379
+ });
1380
+ }
1381
+ getHeaders({ context, telemetry: telemetry2, invokeCount, step }) {
1382
+ const headers = super.getHeaders({ context, telemetry: telemetry2, invokeCount, step });
1383
+ headers.headers["Upstash-Workflow-CallType"] = "step";
1384
+ return headers;
1385
+ }
1386
+ getBody({ context, step, headers, telemetry: telemetry2 }) {
1387
+ if (!step.waitEventId) {
1388
+ throw new WorkflowError("Incompatible step received in LazyWaitForEventStep.getBody");
1389
+ }
1390
+ const timeoutHeaders = {
1391
+ // to include user headers:
1392
+ ...Object.fromEntries(Object.entries(headers).map(([header, value]) => [header, [value]])),
1393
+ // to include telemetry headers:
1394
+ ...telemetry2 ? Object.fromEntries(
1395
+ Object.entries(getTelemetryHeaders(telemetry2)).map(([header, value]) => [
1396
+ header,
1397
+ [value]
1398
+ ])
1399
+ ) : {},
1400
+ // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1401
+ "Upstash-Workflow-Runid": [context.workflowRunId],
1402
+ [WORKFLOW_INIT_HEADER]: ["false"],
1403
+ [WORKFLOW_URL_HEADER]: [context.url],
1404
+ "Upstash-Workflow-CallType": ["step"]
1405
+ };
1406
+ const waitBody = {
1407
+ url: context.url,
1408
+ timeout: step.timeout,
1409
+ timeoutBody: void 0,
1410
+ timeoutUrl: context.url,
1411
+ timeoutHeaders,
1412
+ step: {
1413
+ stepId: step.stepId,
1414
+ stepType: this.stepType,
1415
+ stepName: step.stepName,
1416
+ concurrent: step.concurrent,
1417
+ targetStep: step.targetStep
1418
+ }
1419
+ };
1420
+ return JSON.stringify(waitBody);
1421
+ }
1422
+ async submitStep({ context, body, headers }) {
1423
+ const result = await context.qstashClient.http.request({
1424
+ path: ["v2", "wait", this.eventId],
1425
+ body,
1426
+ headers,
1427
+ method: "POST",
1428
+ parseResponseAsJson: false
1429
+ });
1430
+ return [result];
1431
+ }
1432
+ };
1433
+ var LazyNotifyStep = class extends LazyFunctionStep {
1434
+ stepType = "Notify";
1435
+ constructor(context, stepName, eventId, eventData, requester) {
1436
+ super(context, stepName, async () => {
1437
+ const notifyResponse = await makeNotifyRequest(requester, eventId, eventData);
1438
+ return {
1439
+ eventId,
1440
+ eventData,
1441
+ notifyResponse
1442
+ };
1443
+ });
1444
+ }
1445
+ safeParseOut(out) {
1446
+ const result = JSON.parse(out);
1447
+ return {
1448
+ ...result,
1449
+ eventData: BaseLazyStep.tryParsing(result.eventData)
1450
+ };
1451
+ }
1452
+ };
1453
+ var LazyInvokeStep = class extends BaseLazyStep {
1454
+ stepType = "Invoke";
1455
+ params;
1456
+ allowUndefinedOut = false;
1457
+ /**
1458
+ * workflow id of the invoked workflow
1459
+ */
1460
+ workflowId;
1461
+ constructor(context, stepName, params) {
1462
+ super(context, stepName);
1463
+ this.params = params;
1464
+ const { workflowId } = params.workflow;
1465
+ if (!workflowId) {
1466
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1467
+ }
1468
+ this.workflowId = workflowId;
1469
+ }
1470
+ getPlanStep(concurrent, targetStep) {
1471
+ return {
1472
+ stepId: 0,
1473
+ stepName: this.stepName,
1474
+ stepType: this.stepType,
1475
+ concurrent,
1476
+ targetStep
1477
+ };
1478
+ }
1479
+ /**
1480
+ * won't be used as it's the server who will add the result step
1481
+ * in Invoke step.
1482
+ */
1483
+ getResultStep(concurrent, stepId) {
1484
+ return Promise.resolve({
1485
+ stepId,
1486
+ stepName: this.stepName,
1487
+ stepType: this.stepType,
1488
+ concurrent
1489
+ });
1490
+ }
1491
+ safeParseOut(out) {
1492
+ const result = JSON.parse(out);
1493
+ return {
1494
+ ...result,
1495
+ body: BaseLazyStep.tryParsing(result.body)
1496
+ };
1497
+ }
1498
+ getBody({ context, step, telemetry: telemetry2, invokeCount }) {
1499
+ const { headers: invokerHeaders } = getHeaders({
1500
+ initHeaderValue: "false",
1501
+ workflowConfig: {
1502
+ workflowRunId: context.workflowRunId,
1503
+ workflowUrl: context.url,
1504
+ telemetry: telemetry2,
1505
+ useJSONContent: false
1506
+ },
1507
+ userHeaders: context.headers,
1508
+ invokeCount
1509
+ });
1510
+ context.qstashClient.http.headers?.forEach((value, key) => {
1511
+ invokerHeaders[key] = value;
1512
+ });
1513
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1514
+ const request = {
1515
+ body: stringifyBody(this.params.body),
1516
+ headers: Object.fromEntries(
1517
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1518
+ ),
1519
+ workflowRunId: context.workflowRunId,
1520
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1521
+ workflowUrl: context.url,
1522
+ step
1523
+ };
1524
+ return JSON.stringify(request);
1525
+ }
1526
+ getHeaders({ context, telemetry: telemetry2, invokeCount }) {
1527
+ const {
1528
+ workflow,
1529
+ headers = {},
1530
+ workflowRunId,
1531
+ retries,
1532
+ retryDelay,
1533
+ flowControl,
1534
+ label
1535
+ } = this.params;
1536
+ const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1537
+ const { headers: triggerHeaders, contentType } = getHeaders({
1538
+ initHeaderValue: "true",
1539
+ workflowConfig: {
1540
+ workflowRunId: getWorkflowRunId(workflowRunId),
1541
+ workflowUrl: newUrl,
1542
+ retries,
1543
+ retryDelay,
1544
+ telemetry: telemetry2,
1545
+ failureUrl: newUrl,
1546
+ flowControl,
1547
+ useJSONContent: workflow.useJSONContent ?? false
1548
+ },
1549
+ invokeCount: invokeCount + 1,
1550
+ userHeaders: new Headers(headers)
1551
+ });
1552
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1553
+ if (label) {
1554
+ triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1555
+ triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = label;
1556
+ }
1557
+ return { headers: triggerHeaders, contentType };
1558
+ }
1559
+ async submitStep({ context, body, headers }) {
1560
+ const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1561
+ const result = await context.qstashClient.publish({
1562
+ headers,
1563
+ method: "POST",
1564
+ body,
1565
+ url: newUrl
1566
+ });
1567
+ return [result];
1568
+ }
1569
+ };
1570
+ var LazyCreateWebhookStep = class extends BaseLazyStep {
1571
+ stepType = "CreateWebhook";
1572
+ allowUndefinedOut = false;
1573
+ getPlanStep(concurrent, targetStep) {
1574
+ return {
1575
+ stepId: 0,
1576
+ stepName: this.stepName,
1577
+ stepType: this.stepType,
1578
+ concurrent,
1579
+ targetStep
1580
+ };
1581
+ }
1582
+ async getResultStep(concurrent, stepId) {
1583
+ return {
1584
+ stepId,
1585
+ stepName: this.stepName,
1586
+ stepType: this.stepType,
1587
+ out: void 0,
1588
+ concurrent
1589
+ };
1590
+ }
1591
+ getBody({ step, context }) {
1592
+ const userId = getUserIdFromToken(context.qstashClient);
1593
+ const workflowRunId = context.workflowRunId;
1594
+ const eventId = getEventId();
1595
+ const qstashUrl = getQStashUrl(this.context.qstashClient);
1596
+ return JSON.stringify({
1597
+ ...step,
1598
+ out: JSON.stringify({
1599
+ webhookUrl: `${qstashUrl}/v2/workflows/hooks/${userId}/${workflowRunId}/${eventId}`,
1600
+ eventId
1601
+ })
1602
+ });
1603
+ }
1604
+ };
1605
+ var LazyWaitForWebhookStep = class extends LazyWaitEventStep {
1606
+ stepType = "WaitForWebhook";
1607
+ allowUndefinedOut = true;
1608
+ constructor(context, stepName, webhook, timeout) {
1609
+ super(context, stepName, webhook.eventId, timeout);
1610
+ }
1611
+ safeParseOut(out) {
1612
+ const eventData = decodeBase64(out);
1613
+ const parsedEventData = BaseLazyStep.tryParsing(eventData);
1614
+ const body = parsedEventData.body;
1615
+ const parsedBody = typeof body === "string" ? decodeBase64(body) : void 0;
1616
+ const request = new Request(
1617
+ `${parsedEventData.proto}://${parsedEventData.host}${parsedEventData.url}`,
1618
+ {
1619
+ method: parsedEventData.method,
1620
+ headers: parsedEventData.header,
1621
+ body: parsedBody
1622
+ }
1623
+ );
1624
+ return {
1625
+ request,
1626
+ timeout: false
1627
+ };
1628
+ }
1629
+ handleUndefinedOut() {
1630
+ return {
1631
+ timeout: true,
1632
+ request: void 0
1633
+ };
1634
+ }
1635
+ };
1636
+ var LazyWaitForEventStep = class extends LazyWaitEventStep {
1637
+ stepType = "Wait";
1638
+ allowUndefinedOut = true;
1639
+ parseWaitForEventOut(out, waitTimeout) {
1640
+ return {
1641
+ eventData: out ? BaseLazyStep.tryParsing(decodeBase64(out)) : void 0,
1642
+ timeout: waitTimeout ?? false
1643
+ };
1644
+ }
1645
+ safeParseOut(out, step) {
1646
+ return this.parseWaitForEventOut(out, step.waitTimeout);
1647
+ }
1648
+ handleUndefinedOut(step) {
1649
+ return this.parseWaitForEventOut(void 0, step.waitTimeout);
1650
+ }
1651
+ };
1652
+
1653
+ // src/qstash/headers.ts
1654
+ var WorkflowHeaders = class {
1655
+ userHeaders;
1656
+ workflowConfig;
1657
+ invokeCount;
1658
+ initHeaderValue;
1659
+ stepInfo;
1660
+ headers;
1661
+ /**
1662
+ * @param params workflow header parameters
1663
+ */
1664
+ constructor({
1665
+ userHeaders,
1666
+ workflowConfig,
1667
+ invokeCount,
1668
+ initHeaderValue,
1669
+ stepInfo
1670
+ }) {
1671
+ this.userHeaders = userHeaders;
1672
+ this.workflowConfig = workflowConfig;
1673
+ this.invokeCount = invokeCount;
1674
+ this.initHeaderValue = initHeaderValue;
1675
+ this.stepInfo = stepInfo;
1676
+ this.headers = {
1677
+ rawHeaders: {},
1678
+ workflowHeaders: {},
1679
+ failureHeaders: {}
1680
+ };
1681
+ }
1682
+ getHeaders() {
1683
+ this.addBaseHeaders();
1684
+ this.addRetries();
1685
+ this.addRetryDelay();
1686
+ this.addFlowControl();
1687
+ this.addUserHeaders();
1688
+ this.addInvokeCount();
1689
+ this.addFailureUrl();
1690
+ const contentType = this.addContentType();
1691
+ return this.prefixHeaders(contentType);
1692
+ }
1693
+ addBaseHeaders() {
1694
+ this.headers.rawHeaders = {
1695
+ ...this.headers.rawHeaders,
1696
+ [WORKFLOW_INIT_HEADER]: this.initHeaderValue,
1697
+ [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
1698
+ [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
1699
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig",
1700
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1701
+ ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
1702
+ };
1703
+ if (this.stepInfo?.lazyStep.stepType !== "Call") {
1704
+ this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1705
+ }
1706
+ }
1707
+ addInvokeCount() {
1708
+ if (this.invokeCount === void 0 || this.invokeCount === 0) {
1709
+ return;
1710
+ }
1711
+ const invokeCount = this.invokeCount.toString();
1712
+ this.headers.workflowHeaders[`Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1713
+ if (this.workflowConfig.failureUrl) {
1714
+ this.headers.failureHeaders[`Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1715
+ }
1716
+ if (this.stepInfo?.lazyStep instanceof LazyCallStep) {
1717
+ this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1718
+ }
1719
+ }
1720
+ addRetries() {
1721
+ if (this.workflowConfig.retries === void 0 || this.workflowConfig.retries === DEFAULT_RETRIES) {
1722
+ return;
1723
+ }
1724
+ const retries = this.workflowConfig.retries.toString();
1725
+ this.headers.workflowHeaders["Retries"] = retries;
1726
+ if (this.workflowConfig.failureUrl) {
1727
+ this.headers.failureHeaders["Retries"] = retries;
1728
+ }
1729
+ }
1730
+ addRetryDelay() {
1731
+ if (this.workflowConfig.retryDelay === void 0 || this.workflowConfig.retryDelay === "") {
1732
+ return;
1733
+ }
1734
+ const retryDelay = this.workflowConfig.retryDelay.toString();
1735
+ this.headers.workflowHeaders["Retry-Delay"] = retryDelay;
1736
+ if (this.workflowConfig.failureUrl) {
1737
+ this.headers.failureHeaders["Retry-Delay"] = retryDelay;
1738
+ }
1739
+ }
1740
+ addFlowControl() {
1741
+ if (!this.workflowConfig.flowControl) {
1742
+ return;
1743
+ }
1744
+ const { flowControlKey, flowControlValue } = prepareFlowControl(
1745
+ this.workflowConfig.flowControl
1746
+ );
1747
+ this.headers.workflowHeaders["Flow-Control-Key"] = flowControlKey;
1748
+ this.headers.workflowHeaders["Flow-Control-Value"] = flowControlValue;
1749
+ if (this.workflowConfig.failureUrl) {
1750
+ this.headers.failureHeaders["Flow-Control-Key"] = flowControlKey;
1751
+ this.headers.failureHeaders["Flow-Control-Value"] = flowControlValue;
1752
+ }
1753
+ }
1754
+ addUserHeaders() {
1755
+ for (const [key, value] of this.userHeaders.entries()) {
1756
+ const forwardKey = `Forward-${key}`;
1757
+ this.headers.workflowHeaders[forwardKey] = value;
1758
+ if (this.workflowConfig.failureUrl) {
1759
+ this.headers.failureHeaders[forwardKey] = value;
1760
+ }
1761
+ }
1762
+ }
1763
+ addFailureUrl() {
1764
+ if (!this.workflowConfig.failureUrl) {
1765
+ return;
1766
+ }
1767
+ this.headers.workflowHeaders["Failure-Callback"] = this.workflowConfig.failureUrl;
1768
+ this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
1769
+ this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_CALLBACK_HEADER}`] = "true";
1770
+ this.headers.failureHeaders["Workflow-Runid"] = this.workflowConfig.workflowRunId;
1771
+ this.headers.failureHeaders["Workflow-Init"] = "false";
1772
+ this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
1773
+ this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
1774
+ this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
1775
+ if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
1776
+ this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
1777
+ }
1778
+ if (this.workflowConfig.retryDelay !== void 0 && this.workflowConfig.retryDelay !== "") {
1779
+ this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
1780
+ }
1781
+ }
1782
+ addContentType() {
1783
+ if (this.workflowConfig.useJSONContent) {
1784
+ this.headers.rawHeaders["content-type"] = "application/json";
1785
+ return "application/json";
1786
+ }
1787
+ const callHeaders = new Headers(
1788
+ this.stepInfo?.lazyStep instanceof LazyCallStep ? this.stepInfo.lazyStep.headers : {}
1789
+ );
1790
+ const contentType = (callHeaders.get("content-type") ? callHeaders.get("content-type") : this.userHeaders?.get("Content-Type") ? this.userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1791
+ this.headers.rawHeaders["content-type"] = contentType;
1792
+ return contentType;
1793
+ }
1794
+ prefixHeaders(contentType) {
1795
+ const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
1796
+ const isCall = this.stepInfo?.lazyStep.stepType === "Call";
1797
+ return {
1798
+ headers: {
1799
+ ...rawHeaders,
1800
+ ...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
1801
+ ...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
1802
+ ...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
1803
+ },
1804
+ contentType
1805
+ };
1806
+ }
1807
+ };
1808
+ function addPrefixToHeaders(headers, prefix) {
1809
+ const prefixedHeaders = {};
1810
+ for (const [key, value] of Object.entries(headers)) {
1811
+ prefixedHeaders[`${prefix}${key}`] = value;
1812
+ }
1813
+ return prefixedHeaders;
1814
+ }
1815
+ var prepareFlowControl = (flowControl) => {
1816
+ const parallelism = flowControl.parallelism?.toString();
1817
+ const rate = (flowControl.rate ?? flowControl.ratePerSecond)?.toString();
1818
+ const period = typeof flowControl.period === "number" ? `${flowControl.period}s` : flowControl.period;
1819
+ const controlValue = [
1820
+ parallelism ? `parallelism=${parallelism}` : void 0,
1821
+ rate ? `rate=${rate}` : void 0,
1822
+ period ? `period=${period}` : void 0
1823
+ ].filter(Boolean);
1824
+ if (controlValue.length === 0) {
1825
+ throw new import_qstash4.QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
1826
+ }
1827
+ return {
1828
+ flowControlKey: flowControl.key,
1829
+ flowControlValue: controlValue.join(", ")
1830
+ };
1831
+ };
1832
+ var getHeaders = (params) => {
1833
+ const workflowHeaders = new WorkflowHeaders(params);
1834
+ return workflowHeaders.getHeaders();
1835
+ };
1836
+
1837
+ // src/qstash/submit-steps.ts
1838
+ var submitParallelSteps = async ({
1839
+ context,
1840
+ steps,
1841
+ initialStepCount,
1842
+ invokeCount,
1843
+ telemetry: telemetry2,
1844
+ dispatchDebug
1845
+ }) => {
1846
+ const planSteps = steps.map(
1847
+ (step, index) => step.getPlanStep(steps.length, initialStepCount + index)
1848
+ );
1849
+ await dispatchDebug("onInfo", {
1850
+ info: `Submitting ${planSteps.length} parallel steps.`
1851
+ });
1852
+ const result = await context.qstashClient.batch(
1853
+ planSteps.map((planStep) => {
1854
+ const { headers } = getHeaders({
1855
+ initHeaderValue: "false",
1856
+ workflowConfig: {
1857
+ workflowRunId: context.workflowRunId,
1858
+ workflowUrl: context.url,
1859
+ telemetry: telemetry2
1860
+ },
1861
+ userHeaders: context.headers,
1862
+ invokeCount
1863
+ });
1864
+ return {
1865
+ headers,
1866
+ method: "POST",
1867
+ url: context.url,
1868
+ body: JSON.stringify(planStep),
1869
+ notBefore: planStep.sleepUntil,
1870
+ delay: planStep.sleepFor
1871
+ };
1872
+ })
1873
+ );
1874
+ if (result && result.length > 0) {
1875
+ await dispatchDebug("onInfo", {
1876
+ info: `Submitted ${planSteps.length} parallel steps. messageIds: ${result.filter((r) => r).map((r) => r.messageId).join(", ")}.`
1877
+ });
1878
+ }
1879
+ throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
1880
+ };
1881
+ var submitSingleStep = async ({
1882
+ context,
1883
+ lazyStep,
1884
+ stepId,
1885
+ invokeCount,
1886
+ concurrency,
1887
+ telemetry: telemetry2,
1888
+ dispatchDebug,
1889
+ dispatchLifecycle
1890
+ }) => {
1891
+ await dispatchLifecycle("beforeExecution", {
1892
+ stepName: lazyStep.stepName
1893
+ });
1894
+ const resultStep = await lazyStep.getResultStep(concurrency, stepId);
1895
+ const { headers } = lazyStep.getHeaders({
1896
+ context,
1897
+ step: resultStep,
1898
+ invokeCount,
1899
+ telemetry: telemetry2
1900
+ });
1901
+ const body = lazyStep.getBody({
1902
+ context,
1903
+ step: resultStep,
1904
+ headers,
1905
+ invokeCount,
1906
+ telemetry: telemetry2
1907
+ });
1908
+ const submitResult = await lazyStep.submitStep({
1909
+ context,
1910
+ body,
1911
+ headers,
1912
+ isParallel: concurrency !== NO_CONCURRENCY,
1913
+ invokeCount,
1914
+ step: resultStep,
1915
+ telemetry: telemetry2
1916
+ });
1917
+ if (submitResult && submitResult[0]) {
1918
+ await dispatchDebug("onInfo", {
1919
+ info: `Submitted step "${resultStep.stepName}" with messageId: ${submitResult[0].messageId}.`
1920
+ });
1921
+ }
1922
+ return resultStep;
1923
+ };
1924
+
1925
+ // src/context/auto-executor.ts
1926
+ var AutoExecutor = class _AutoExecutor {
1927
+ context;
1928
+ promises = /* @__PURE__ */ new WeakMap();
1929
+ activeLazyStepList;
1930
+ nonPlanStepCount;
1931
+ steps;
1932
+ indexInCurrentList = 0;
1933
+ invokeCount;
1934
+ telemetry;
1935
+ dispatchDebug;
1936
+ dispatchLifecycle;
1937
+ stepCount = 0;
1938
+ planStepCount = 0;
1939
+ executingStep = false;
1940
+ /**
1941
+ * @param context workflow context
1942
+ * @param steps list of steps
1943
+ * @param dispatchDebug debug event dispatcher
1944
+ * @param dispatchLifecycle lifecycle event dispatcher
1945
+ * @param telemetry optional telemetry information
1946
+ * @param invokeCount optional invoke count
1947
+ */
1948
+ constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry2, invokeCount) {
1949
+ this.context = context;
1950
+ this.steps = steps;
1951
+ this.dispatchDebug = dispatchDebug;
1952
+ this.dispatchLifecycle = dispatchLifecycle;
1953
+ this.telemetry = telemetry2;
1954
+ this.invokeCount = invokeCount ?? 0;
1955
+ this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
1956
+ }
1957
+ /**
1958
+ * Adds the step function to the list of step functions to run in
1959
+ * parallel. After adding the function, defers the execution, so
1960
+ * that if there is another step function to be added, it's also
1961
+ * added.
1962
+ *
1963
+ * After all functions are added, list of functions are executed.
1964
+ * If there is a single function, it's executed by itself. If there
1965
+ * are multiple, they are run in parallel.
1966
+ *
1967
+ * If a function is already executing (this.executingStep), this
1968
+ * means that there is a nested step which is not allowed. In this
1969
+ * case, addStep throws WorkflowError.
1970
+ *
1971
+ * @param stepInfo step plan to add
1972
+ * @returns result of the step function
1973
+ */
1974
+ async addStep(stepInfo) {
1975
+ if (this.executingStep) {
1976
+ throw new WorkflowError(
1977
+ `A step can not be run inside another step. Tried to run '${stepInfo.stepName}' inside '${this.executingStep}'`
1978
+ );
1979
+ }
1980
+ this.stepCount += 1;
1981
+ const lazyStepList = this.activeLazyStepList ?? [];
1982
+ if (!this.activeLazyStepList) {
1983
+ this.activeLazyStepList = lazyStepList;
1984
+ this.indexInCurrentList = 0;
1985
+ }
1986
+ lazyStepList.push(stepInfo);
1987
+ const index = this.indexInCurrentList++;
1988
+ const requestComplete = this.deferExecution().then(async () => {
1989
+ if (!this.promises.has(lazyStepList)) {
1990
+ const promise2 = this.getExecutionPromise(lazyStepList);
1991
+ this.promises.set(lazyStepList, promise2);
1992
+ this.activeLazyStepList = void 0;
1993
+ this.planStepCount += lazyStepList.length > 1 ? lazyStepList.length : 0;
1994
+ }
1995
+ const promise = this.promises.get(lazyStepList);
1996
+ return promise;
1997
+ });
1998
+ const result = await requestComplete;
1999
+ return _AutoExecutor.getResult(lazyStepList, result, index);
2000
+ }
2001
+ /**
2002
+ * Wraps a step function to set this.executingStep to step name
2003
+ * before running and set this.executingStep to False after execution
2004
+ * ends.
2005
+ *
2006
+ * this.executingStep allows us to detect nested steps which are not
2007
+ * allowed.
2008
+ *
2009
+ * @param stepName name of the step being wrapped
2010
+ * @param stepFunction step function to wrap
2011
+ * @returns wrapped step function
2012
+ */
2013
+ wrapStep(stepName, stepFunction) {
2014
+ this.executingStep = stepName;
2015
+ const result = stepFunction();
2016
+ this.executingStep = false;
2017
+ return result;
2018
+ }
2019
+ /**
2020
+ * Executes a step:
2021
+ * - If the step result is available in the steps, returns the result
2022
+ * - If the result is not available, runs the function
2023
+ * - Sends the result to QStash
2024
+ *
2025
+ * @param lazyStep lazy step to execute
2026
+ * @returns step result
2027
+ */
2028
+ async runSingle(lazyStep) {
2029
+ if (this.stepCount < this.nonPlanStepCount) {
2030
+ const step = this.steps[this.stepCount + this.planStepCount];
2031
+ validateStep(lazyStep, step);
2032
+ const parsedOut = lazyStep.parseOut(step);
2033
+ const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
2034
+ if (isLastMemoized) {
2035
+ await this.dispatchLifecycle("afterExecution", {
2036
+ stepName: lazyStep.stepName,
2037
+ result: parsedOut
2038
+ });
2039
+ }
2040
+ return parsedOut;
2041
+ }
2042
+ const resultStep = await submitSingleStep({
2043
+ context: this.context,
2044
+ lazyStep,
2045
+ stepId: this.stepCount,
2046
+ invokeCount: this.invokeCount,
2047
+ concurrency: 1,
2048
+ telemetry: this.telemetry,
2049
+ dispatchDebug: this.dispatchDebug,
2050
+ dispatchLifecycle: this.dispatchLifecycle
2051
+ });
2052
+ throw new WorkflowAbort(lazyStep.stepName, resultStep);
2053
+ }
2054
+ /**
2055
+ * Runs steps in parallel.
2056
+ *
2057
+ * @param parallelSteps list of lazy steps to run in parallel
2058
+ * @returns results of the functions run in parallel
2059
+ */
2060
+ async runParallel(parallelSteps) {
2061
+ const initialStepCount = this.stepCount - (parallelSteps.length - 1);
2062
+ const parallelCallState = this.getParallelCallState(parallelSteps.length, initialStepCount);
2063
+ const sortedSteps = sortSteps(this.steps);
2064
+ const plannedParallelStepCount = sortedSteps[initialStepCount + this.planStepCount]?.concurrent;
2065
+ if (parallelCallState !== "first" && plannedParallelStepCount !== parallelSteps.length) {
2066
+ throw new WorkflowError(
2067
+ `Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
2068
+ );
2069
+ }
2070
+ await this.dispatchDebug("onInfo", {
2071
+ info: `Executing parallel steps with: ` + JSON.stringify({
2072
+ parallelCallState,
2073
+ initialStepCount,
2074
+ plannedParallelStepCount,
2075
+ stepCount: this.stepCount,
2076
+ planStepCount: this.planStepCount
2077
+ })
2078
+ });
2079
+ switch (parallelCallState) {
2080
+ case "first": {
2081
+ await submitParallelSteps({
2082
+ context: this.context,
2083
+ steps: parallelSteps,
2084
+ initialStepCount,
2085
+ invokeCount: this.invokeCount,
2086
+ telemetry: this.telemetry,
2087
+ dispatchDebug: this.dispatchDebug
2088
+ });
2089
+ break;
2090
+ }
2091
+ case "partial": {
2092
+ const planStep = this.steps.at(-1);
2093
+ if (!planStep || planStep.targetStep === void 0) {
2094
+ throw new WorkflowError(
2095
+ `There must be a last step and it should have targetStep larger than 0.Received: ${JSON.stringify(planStep)}`
2096
+ );
2097
+ }
2098
+ const stepIndex = planStep.targetStep - initialStepCount;
2099
+ validateStep(parallelSteps[stepIndex], planStep);
2100
+ try {
2101
+ const parallelStep = parallelSteps[stepIndex];
2102
+ const resultStep = await submitSingleStep({
2103
+ context: this.context,
2104
+ lazyStep: parallelStep,
2105
+ stepId: planStep.targetStep,
2106
+ invokeCount: this.invokeCount,
2107
+ concurrency: parallelSteps.length,
2108
+ telemetry: this.telemetry,
2109
+ dispatchDebug: this.dispatchDebug,
2110
+ dispatchLifecycle: this.dispatchLifecycle
2111
+ });
2112
+ throw new WorkflowAbort(parallelStep.stepName, resultStep);
2113
+ } catch (error) {
2114
+ if (isInstanceOf(error, WorkflowAbort) || isInstanceOf(error, import_qstash5.QstashError) && error.status === 400) {
2115
+ throw error;
2116
+ }
2117
+ throw new WorkflowError(
2118
+ `Error submitting steps to QStash in partial parallel step execution: ${error}`
2119
+ );
2120
+ }
2121
+ break;
2122
+ }
2123
+ case "discard": {
2124
+ const resultStep = this.steps.at(-1);
2125
+ const lazyStep = parallelSteps.find(
2126
+ (planStep, index) => resultStep.stepId - index === initialStepCount
2127
+ );
2128
+ if (lazyStep) {
2129
+ await this.dispatchLifecycle("afterExecution", {
2130
+ stepName: lazyStep.stepName,
2131
+ result: lazyStep.parseOut(resultStep)
2132
+ });
2133
+ }
2134
+ throw new WorkflowAbort("discarded parallel");
2135
+ }
2136
+ case "last": {
2137
+ const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
2138
+ validateParallelSteps(parallelSteps, parallelResultSteps);
2139
+ const isLastMemoized = this.stepCount + 1 === this.nonPlanStepCount && this.steps.at(-1).stepId !== 0;
2140
+ if (isLastMemoized) {
2141
+ const resultStep = this.steps.at(-1);
2142
+ const lazyStep = parallelSteps.find(
2143
+ (planStep, index) => resultStep.stepId - index === initialStepCount
2144
+ );
2145
+ await this.dispatchLifecycle("afterExecution", {
2146
+ stepName: lazyStep.stepName,
2147
+ result: lazyStep.parseOut(resultStep)
2148
+ });
2149
+ }
2150
+ return parallelResultSteps.map(
2151
+ (step, index) => parallelSteps[index].parseOut(step)
2152
+ );
2153
+ }
2154
+ }
2155
+ const fillValue = void 0;
2156
+ return Array.from({ length: parallelSteps.length }).fill(fillValue);
2157
+ }
2158
+ /**
2159
+ * Determines the parallel call state
2160
+ *
2161
+ * First filters the steps to get the steps which are after `initialStepCount` parameter.
2162
+ *
2163
+ * Depending on the remaining steps, decides the parallel state:
2164
+ * - "first": If there are no steps
2165
+ * - "last" If there are equal to or more than `2 * parallelStepCount`. We multiply by two
2166
+ * because each step in a parallel execution will have 2 steps: a plan step and a result
2167
+ * step.
2168
+ * - "partial": If the last step is a plan step
2169
+ * - "discard": If the last step is not a plan step. This means that the parallel execution
2170
+ * is in progress (there are still steps to run) and one step has finished and submitted
2171
+ * its result to QStash
2172
+ *
2173
+ * @param parallelStepCount number of steps to run in parallel
2174
+ * @param initialStepCount steps after the parallel invocation
2175
+ * @returns parallel call state
2176
+ */
2177
+ getParallelCallState(parallelStepCount, initialStepCount) {
2178
+ const remainingSteps = this.steps.filter(
2179
+ (step) => (step.targetStep || step.stepId) >= initialStepCount
2180
+ );
2181
+ if (remainingSteps.length === 0) {
2182
+ return "first";
2183
+ } else if (remainingSteps.length >= 2 * parallelStepCount) {
2184
+ return "last";
2185
+ } else if (remainingSteps.at(-1)?.targetStep) {
2186
+ return "partial";
2187
+ } else {
2188
+ return "discard";
2189
+ }
2190
+ }
2191
+ /**
2192
+ * Get the promise by executing the lazt steps list. If there is a single
2193
+ * step, we call `runSingle`. Otherwise `runParallel` is called.
2194
+ *
2195
+ * @param lazyStepList steps list to execute
2196
+ * @returns promise corresponding to the execution
2197
+ */
2198
+ getExecutionPromise(lazyStepList) {
2199
+ return lazyStepList.length === 1 ? this.runSingle(lazyStepList[0]) : this.runParallel(lazyStepList);
2200
+ }
2201
+ /**
2202
+ * @param lazyStepList steps we executed
2203
+ * @param result result of the promise from `getExecutionPromise`
2204
+ * @param index index of the current step
2205
+ * @returns result[index] if lazyStepList > 1, otherwise result
2206
+ */
2207
+ static getResult(lazyStepList, result, index) {
2208
+ if (lazyStepList.length === 1) {
2209
+ return result;
2210
+ } else if (Array.isArray(result) && lazyStepList.length === result.length && index < lazyStepList.length) {
2211
+ return result[index];
2212
+ } else {
2213
+ throw new WorkflowError(
2214
+ `Unexpected parallel call result while executing step ${index}: '${result}'. Expected ${lazyStepList.length} many items`
2215
+ );
2216
+ }
2217
+ }
2218
+ async deferExecution() {
2219
+ await Promise.resolve();
2220
+ await Promise.resolve();
2221
+ }
2222
+ };
2223
+ var validateStep = (lazyStep, stepFromRequest) => {
2224
+ if (lazyStep.stepName !== stepFromRequest.stepName) {
2225
+ throw new WorkflowError(
2226
+ `Incompatible step name. Expected '${lazyStep.stepName}', got '${stepFromRequest.stepName}' from the request`
2227
+ );
2228
+ }
2229
+ if (lazyStep.stepType !== stepFromRequest.stepType) {
2230
+ throw new WorkflowError(
2231
+ `Incompatible step type. Expected '${lazyStep.stepType}', got '${stepFromRequest.stepType}' from the request`
2232
+ );
2233
+ }
2234
+ };
2235
+ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
2236
+ try {
2237
+ for (const [index, stepFromRequest] of stepsFromRequest.entries()) {
2238
+ validateStep(lazySteps[index], stepFromRequest);
2239
+ }
2240
+ } catch (error) {
2241
+ if (isInstanceOf(error, WorkflowError)) {
2242
+ const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
2243
+ const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
2244
+ const requestStepNames = stepsFromRequest.map((step) => step.stepName);
2245
+ const requestStepTypes = stepsFromRequest.map((step) => step.stepType);
2246
+ throw new WorkflowError(
2247
+ `Incompatible steps detected in parallel execution: ${error.message}
2248
+ > Step Names from the request: ${JSON.stringify(requestStepNames)}
2249
+ Step Types from the request: ${JSON.stringify(requestStepTypes)}
2250
+ > Step Names expected: ${JSON.stringify(lazyStepNames)}
2251
+ Step Types expected: ${JSON.stringify(lazyStepTypes)}`
2252
+ );
2253
+ }
2254
+ throw error;
2255
+ }
2256
+ };
2257
+ var sortSteps = (steps) => {
2258
+ const getStepId = (step) => step.targetStep || step.stepId;
2259
+ return [...steps].sort((step, stepOther) => getStepId(step) - getStepId(stepOther));
2260
+ };
2261
+
2262
+ // src/context/api/anthropic.ts
2263
+ var import_qstash6 = require("@upstash/qstash");
2264
+
2265
+ // src/context/provider.ts
2266
+ var getProviderInfo = (api) => {
2267
+ if (!api.provider) {
2268
+ throw new WorkflowError("A Provider must be provided.");
2269
+ }
2270
+ if (api.provider.owner === "upstash") {
2271
+ throw new WorkflowError("Upstash provider isn't supported.");
2272
+ }
2273
+ const { name, provider, ...parameters } = api;
2274
+ if (!provider.baseUrl) throw new TypeError("baseUrl cannot be empty or undefined!");
2275
+ if (!provider.token) throw new TypeError("token cannot be empty or undefined!");
2276
+ if (provider.apiKind !== name) {
2277
+ throw new TypeError(`Unexpected api name. Expected '${provider.apiKind}', received ${name}`);
2278
+ }
2279
+ const providerInfo = {
2280
+ url: provider.getUrl(),
2281
+ baseUrl: provider.baseUrl,
2282
+ route: provider.getRoute(),
2283
+ appendHeaders: provider.getHeaders(parameters),
2284
+ owner: provider.owner,
2285
+ method: provider.method
2286
+ };
2287
+ return provider.onFinish(providerInfo, parameters);
2288
+ };
2289
+
2290
+ // src/context/api/base.ts
2291
+ var BaseWorkflowApi = class {
2292
+ context;
2293
+ /**
2294
+ * @param context workflow context
2295
+ */
2296
+ constructor({ context }) {
2297
+ this.context = context;
2298
+ }
2299
+ /**
2300
+ * context.call which uses a QStash API
2301
+ *
2302
+ * @param stepName name of the step
2303
+ * @param settings call settings including api configuration
2304
+ * @returns call response
2305
+ */
2306
+ async callApi(stepName, settings) {
2307
+ const { url, appendHeaders, method } = getProviderInfo(settings.api);
2308
+ const { method: userMethod, body, headers = {}, retries = 0, retryDelay, timeout } = settings;
2309
+ return await this.context.call(stepName, {
2310
+ url,
2311
+ method: userMethod ?? method,
2312
+ body: typeof body === "string" ? body : JSON.stringify(body),
2313
+ headers: {
2314
+ ...appendHeaders,
2315
+ ...headers
2316
+ },
2317
+ retries,
2318
+ retryDelay,
2319
+ timeout
2320
+ });
2321
+ }
2322
+ };
2323
+
2324
+ // src/context/api/anthropic.ts
2325
+ var AnthropicAPI = class extends BaseWorkflowApi {
2326
+ async call(stepName, settings) {
2327
+ const { token, operation, ...parameters } = settings;
2328
+ return await this.callApi(stepName, {
2329
+ api: {
2330
+ name: "llm",
2331
+ provider: (0, import_qstash6.anthropic)({ token })
2332
+ },
2333
+ ...parameters
2334
+ });
2335
+ }
2336
+ };
2337
+
2338
+ // src/context/api/openai.ts
2339
+ var import_qstash7 = require("@upstash/qstash");
2340
+ var OpenAIAPI = class extends BaseWorkflowApi {
2341
+ async call(stepName, settings) {
2342
+ const { token, organization, operation, baseURL, ...parameters } = settings;
2343
+ const useOpenAI = baseURL === void 0;
2344
+ const provider = useOpenAI ? (0, import_qstash7.openai)({ token, organization }) : (0, import_qstash7.custom)({ baseUrl: baseURL, token });
2345
+ return await this.callApi(stepName, {
2346
+ api: {
2347
+ name: "llm",
2348
+ provider
2349
+ },
2350
+ ...parameters
2351
+ });
2352
+ }
2353
+ };
2354
+
2355
+ // src/context/api/resend.ts
2356
+ var import_qstash8 = require("@upstash/qstash");
2357
+ var ResendAPI = class extends BaseWorkflowApi {
2358
+ async call(stepName, settings) {
2359
+ const { token, batch = false, ...parameters } = settings;
2360
+ return await this.callApi(stepName, {
2361
+ api: {
2362
+ name: "email",
2363
+ provider: (0, import_qstash8.resend)({ token, batch })
2364
+ },
2365
+ ...parameters
2366
+ });
2367
+ }
2368
+ };
2369
+
2370
+ // src/context/api/index.ts
2371
+ var WorkflowApi = class extends BaseWorkflowApi {
2372
+ get openai() {
2373
+ return new OpenAIAPI({
2374
+ context: this.context
2375
+ });
2376
+ }
2377
+ get resend() {
2378
+ return new ResendAPI({
2379
+ context: this.context
2380
+ });
2381
+ }
2382
+ get anthropic() {
2383
+ return new AnthropicAPI({
2384
+ context: this.context
2385
+ });
2386
+ }
2387
+ };
2388
+
2389
+ // src/serve/serve-many.ts
2390
+ var getWorkflowId = (url) => {
2391
+ const components = url.split("/");
2392
+ const lastComponent = components[components.length - 1];
2393
+ return lastComponent.split("?")[0];
2394
+ };
2395
+ var serveManyBase = ({
2396
+ workflows,
2397
+ getUrl,
2398
+ serveMethod,
2399
+ options
2400
+ }) => {
2401
+ const workflowIds = [];
2402
+ const workflowMap = Object.fromEntries(
2403
+ Object.entries(workflows).map((workflow) => {
2404
+ const workflowId = workflow[0];
2405
+ if (workflowIds.includes(workflowId)) {
2406
+ throw new WorkflowError(
2407
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
2408
+ );
2409
+ }
2410
+ if (workflowId.includes("/")) {
2411
+ throw new WorkflowError(
2412
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
2413
+ );
2414
+ }
2415
+ workflowIds.push(workflowId);
2416
+ workflow[1].workflowId = workflowId;
2417
+ workflow[1].options = {
2418
+ ...options,
2419
+ ...workflow[1].options
2420
+ };
2421
+ const params = [workflow[1].routeFunction, workflow[1].options];
2422
+ const handler = serveMethod(...params);
2423
+ return [workflowId, handler];
2424
+ })
2425
+ );
2426
+ return {
2427
+ handler: async (...params) => {
2428
+ const url = getUrl(...params);
2429
+ const pickedWorkflowId = getWorkflowId(url);
2430
+ if (!pickedWorkflowId) {
2431
+ return new Response(
2432
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
2433
+ {
2434
+ status: 404,
2435
+ headers: {
2436
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
2437
+ }
2438
+ }
2439
+ );
2440
+ }
2441
+ const workflow = workflowMap[pickedWorkflowId];
2442
+ if (!workflow) {
2443
+ return new Response(
2444
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
2445
+ {
2446
+ status: 404,
2447
+ headers: {
2448
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
2449
+ }
2450
+ }
2451
+ );
2452
+ }
2453
+ return await workflow(...params);
2454
+ }
2455
+ };
2456
+ };
2457
+ var getNewUrlFromWorkflowId = (url, workflowId) => {
2458
+ if (!workflowId) {
2459
+ throw new WorkflowError("You can only call workflow which has a workflowId");
2460
+ }
2461
+ return url.replace(/[^/]+$/, workflowId);
2462
+ };
2463
+
2464
+ // src/middleware/default-callbacks.ts
2465
+ var onErrorWithConsole = async ({ workflowRunId, error }) => {
2466
+ console.error(` [Upstash Workflow]: Error in workflow run ${workflowRunId}: ` + error);
2467
+ };
2468
+ var onWarningWithConsole = async ({ workflowRunId, warning }) => {
2469
+ console.warn(` [Upstash Workflow]: Warning in workflow run ${workflowRunId}: ` + warning);
2470
+ };
2471
+ var onInfoWithConsole = async ({
2472
+ workflowRunId,
2473
+ info
2474
+ }) => {
2475
+ console.info(` [Upstash Workflow]: Info in workflow run ${workflowRunId}: ` + info);
2476
+ };
2477
+
2478
+ // src/middleware/manager.ts
2479
+ var MiddlewareManager = class {
2480
+ middlewares;
2481
+ workflowRunId;
2482
+ context;
2483
+ /**
2484
+ * @param middlewares list of workflow middlewares
2485
+ */
2486
+ constructor(middlewares = []) {
2487
+ this.middlewares = middlewares;
2488
+ }
2489
+ /**
2490
+ * Assign workflow run ID - will be passed to debug events
2491
+ *
2492
+ * @param workflowRunId workflow run id to assign
2493
+ */
2494
+ assignWorkflowRunId(workflowRunId) {
2495
+ this.workflowRunId = workflowRunId;
2496
+ }
2497
+ /**
2498
+ * Assign context - required for lifecycle events
2499
+ *
2500
+ * also assigns workflowRunId from context
2501
+ *
2502
+ * @param context workflow context to assign
2503
+ */
2504
+ assignContext(context) {
2505
+ this.context = context;
2506
+ this.workflowRunId = context.workflowRunId;
2507
+ }
2508
+ /**
2509
+ * Internal method to execute middlewares with common error handling logic
2510
+ *
2511
+ * @param event event name to dispatch
2512
+ * @param params event parameters
2513
+ */
2514
+ async executeMiddlewares(event, params) {
2515
+ await Promise.all(this.middlewares.map((m) => m.ensureInit()));
2516
+ await Promise.all(
2517
+ this.middlewares.map(async (middleware) => {
2518
+ const callback = middleware.getCallback(event);
2519
+ if (callback) {
2520
+ try {
2521
+ await callback(params);
2522
+ } catch (error) {
2523
+ try {
2524
+ const onErrorCallback = middleware.getCallback("onError") ?? onErrorWithConsole;
2525
+ await onErrorCallback({
2526
+ workflowRunId: this.workflowRunId,
2527
+ error
2528
+ });
2529
+ } catch (onErrorError) {
2530
+ console.error(
2531
+ `Failed while executing "onError" of middleware "${middleware.name}", falling back to logging the error to console. Error: ${onErrorError}`
2532
+ );
2533
+ onErrorWithConsole({
2534
+ workflowRunId: this.workflowRunId,
2535
+ error
2536
+ });
2537
+ }
2538
+ }
2539
+ }
2540
+ })
2541
+ );
2542
+ if (event === "onError") {
2543
+ onErrorWithConsole({
2544
+ workflowRunId: this.workflowRunId,
2545
+ ...params
2546
+ });
2547
+ } else if (event === "onWarning") {
2548
+ onWarningWithConsole({
2549
+ workflowRunId: this.workflowRunId,
2550
+ ...params
2551
+ });
2552
+ }
2553
+ }
2554
+ /**
2555
+ * Dispatch a debug event (onError, onWarning, onInfo)
2556
+ *
2557
+ * @param event debug event name
2558
+ * @param params event parameters
2559
+ */
2560
+ async dispatchDebug(event, params) {
2561
+ const paramsWithRunId = {
2562
+ ...params,
2563
+ workflowRunId: this.workflowRunId
2564
+ };
2565
+ await this.executeMiddlewares(event, paramsWithRunId);
2566
+ }
2567
+ /**
2568
+ * Dispatch a lifecycle event (beforeExecution, afterExecution, runStarted, runCompleted)
2569
+ *
2570
+ * @param event lifecycle event name
2571
+ * @param params event parameters
2572
+ */
2573
+ async dispatchLifecycle(event, params) {
2574
+ if (!this.context) {
2575
+ throw new WorkflowError(
2576
+ `Something went wrong while calling middlewares. Lifecycle event "${event}" was called before assignContext.`
2577
+ );
2578
+ }
2579
+ const paramsWithContext = {
2580
+ ...params,
2581
+ context: this.context
2582
+ };
2583
+ await this.executeMiddlewares(event, paramsWithContext);
2584
+ }
2585
+ };
2586
+
2587
+ // src/context/context.ts
2588
+ var WorkflowContext = class {
2589
+ executor;
2590
+ steps;
2591
+ /**
2592
+ * QStash client of the workflow
2593
+ *
2594
+ * Can be overwritten by passing `qstashClient` parameter in `serve`:
2595
+ *
2596
+ * ```ts
2597
+ * import { Client } from "@upstash/qstash"
2598
+ *
2599
+ * export const POST = serve(
2600
+ * async (context) => {
2601
+ * ...
2602
+ * },
2603
+ * {
2604
+ * qstashClient: new Client({...})
2605
+ * }
2606
+ * )
2607
+ * ```
2608
+ */
2609
+ qstashClient;
2610
+ /**
2611
+ * Run id of the workflow
2612
+ */
2613
+ workflowRunId;
2614
+ /**
2615
+ * Creation time of the workflow run
2616
+ */
2617
+ workflowRunCreatedAt;
2618
+ /**
2619
+ * URL of the workflow
2620
+ *
2621
+ * Can be overwritten by passing a `url` parameter in `serve`:
2622
+ *
2623
+ * ```ts
2624
+ * export const POST = serve(
2625
+ * async (context) => {
2626
+ * ...
2627
+ * },
2628
+ * {
2629
+ * url: "new-url-value"
2630
+ * }
2631
+ * )
2632
+ * ```
2633
+ */
2634
+ url;
2635
+ /**
2636
+ * Payload of the request which started the workflow.
2637
+ *
2638
+ * To specify its type, you can define `serve` as follows:
2639
+ *
2640
+ * ```ts
2641
+ * // set requestPayload type to MyPayload:
2642
+ * export const POST = serve<MyPayload>(
2643
+ * async (context) => {
2644
+ * ...
2645
+ * }
2646
+ * )
2647
+ * ```
2648
+ *
2649
+ * By default, `serve` tries to apply `JSON.parse` to the request payload.
2650
+ * If your payload is encoded in a format other than JSON, you can utilize
2651
+ * the `initialPayloadParser` parameter:
2652
+ *
2653
+ * ```ts
2654
+ * export const POST = serve<MyPayload>(
2655
+ * async (context) => {
2656
+ * ...
2657
+ * },
2658
+ * {
2659
+ * initialPayloadParser: (initialPayload) => {return doSomething(initialPayload)}
2660
+ * }
2661
+ * )
2662
+ * ```
2663
+ */
2664
+ requestPayload;
2665
+ /**
2666
+ * headers of the initial request
2667
+ */
2668
+ headers;
2669
+ /**
2670
+ * Map of environment variables and their values.
2671
+ *
2672
+ * Can be set using the `env` option of serve:
2673
+ *
2674
+ * ```ts
2675
+ * export const POST = serve<MyPayload>(
2676
+ * async (context) => {
2677
+ * const key = context.env["API_KEY"];
2678
+ * },
2679
+ * {
2680
+ * env: {
2681
+ * "API_KEY": "*****";
2682
+ * }
2683
+ * }
2684
+ * )
2685
+ * ```
2686
+ *
2687
+ * Default value is set to `process.env`.
2688
+ */
2689
+ env;
2690
+ /**
2691
+ * Label to apply to the workflow run.
2692
+ *
2693
+ * Can be used to filter the workflow run logs.
2694
+ *
2695
+ * Can be set by passing a `label` parameter when triggering the workflow
2696
+ * with `client.trigger`:
2697
+ *
2698
+ * ```ts
2699
+ * await client.trigger({
2700
+ * url: "https://workflow-endpoint.com",
2701
+ * label: "my-label"
2702
+ * });
2703
+ * ```
2704
+ */
2705
+ label;
2706
+ constructor({
2707
+ qstashClient,
2708
+ workflowRunId,
2709
+ workflowRunCreatedAt,
2710
+ headers,
2711
+ steps,
2712
+ url,
2713
+ initialPayload,
2714
+ env,
2715
+ telemetry: telemetry2,
2716
+ invokeCount,
2717
+ label,
2718
+ middlewareManager
2719
+ }) {
2720
+ this.qstashClient = qstashClient;
2721
+ this.workflowRunId = workflowRunId;
2722
+ this.workflowRunCreatedAt = workflowRunCreatedAt;
2723
+ this.steps = steps;
2724
+ this.url = url;
2725
+ this.headers = headers;
2726
+ this.requestPayload = initialPayload;
2727
+ this.env = env ?? {};
2728
+ this.label = label;
2729
+ const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
2730
+ middlewareManagerInstance.assignContext(this);
2731
+ this.executor = new AutoExecutor(
2732
+ this,
2733
+ this.steps,
2734
+ middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
2735
+ middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
2736
+ telemetry2,
2737
+ invokeCount
2738
+ );
2739
+ }
2740
+ /**
2741
+ * Executes a workflow step
2742
+ *
2743
+ * ```typescript
2744
+ * const result = await context.run("step 1", () => {
2745
+ * return "result"
2746
+ * })
2747
+ * ```
2748
+ *
2749
+ * Can also be called in parallel and the steps will be executed
2750
+ * simulatenously:
2751
+ *
2752
+ * ```typescript
2753
+ * const [result1, result2] = await Promise.all([
2754
+ * context.run("step 1", () => {
2755
+ * return "result1"
2756
+ * }),
2757
+ * context.run("step 2", async () => {
2758
+ * return await fetchResults()
2759
+ * })
2760
+ * ])
2761
+ * ```
2762
+ *
2763
+ * @param stepName name of the step
2764
+ * @param stepFunction step function to be executed
2765
+ * @returns result of the step function
2766
+ */
2767
+ async run(stepName, stepFunction) {
2768
+ const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
2769
+ return await this.addStep(new LazyFunctionStep(this, stepName, wrappedStepFunction));
2770
+ }
2771
+ /**
2772
+ * Stops the execution for the duration provided.
2773
+ *
2774
+ * ```typescript
2775
+ * await context.sleep('sleep1', 3) // wait for three seconds
2776
+ * ```
2777
+ *
2778
+ * @param stepName
2779
+ * @param duration sleep duration in seconds
2780
+ * @returns undefined
2781
+ */
2782
+ async sleep(stepName, duration) {
2783
+ await this.addStep(new LazySleepStep(this, stepName, duration));
2784
+ }
2785
+ /**
2786
+ * Stops the execution until the date time provided.
2787
+ *
2788
+ * ```typescript
2789
+ * await context.sleepUntil('sleep1', Date.now() / 1000 + 3) // wait for three seconds
2790
+ * ```
2791
+ *
2792
+ * @param stepName
2793
+ * @param datetime time to sleep until. Can be provided as a number (in unix seconds),
2794
+ * as a Date object or a string (passed to `new Date(datetimeString)`)
2795
+ * @returns undefined
2796
+ */
2797
+ async sleepUntil(stepName, datetime) {
2798
+ let time;
2799
+ if (typeof datetime === "number") {
2800
+ time = datetime;
2801
+ } else {
2802
+ datetime = typeof datetime === "string" ? new Date(datetime) : datetime;
2803
+ time = Math.round(datetime.getTime() / 1e3);
2804
+ }
2805
+ await this.addStep(new LazySleepUntilStep(this, stepName, time));
2806
+ }
2807
+ async call(stepName, settings) {
2808
+ let callStep;
2809
+ if ("workflow" in settings) {
2810
+ const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
2811
+ const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
2812
+ callStep = new LazyCallStep({
2813
+ context: this,
2814
+ stepName,
2815
+ url,
2816
+ method: "POST",
2817
+ body: stringBody,
2818
+ headers: settings.headers || {},
2819
+ retries: settings.retries || 0,
2820
+ retryDelay: settings.retryDelay,
2821
+ timeout: settings.timeout,
2822
+ flowControl: settings.flowControl
2823
+ });
2824
+ } else {
2825
+ callStep = new LazyCallStep({
2826
+ context: this,
2827
+ stepName,
2828
+ url: settings.url,
2829
+ method: settings.method ?? "GET",
2830
+ body: settings.body,
2831
+ headers: settings.headers ?? {},
2832
+ retries: settings.retries ?? 0,
2833
+ retryDelay: settings.retryDelay,
2834
+ timeout: settings.timeout,
2835
+ flowControl: settings.flowControl
2836
+ });
2837
+ }
2838
+ return await this.addStep(callStep);
2839
+ }
2840
+ /**
2841
+ * Pauses workflow execution until a specific event occurs or a timeout is reached.
2842
+ *
2843
+ *```ts
2844
+ * const result = await workflow.waitForEvent("payment-confirmed", "payment.confirmed", {
2845
+ * timeout: "5m"
2846
+ * });
2847
+ *```
2848
+ *
2849
+ * To notify a waiting workflow:
2850
+ *
2851
+ * ```ts
2852
+ * import { Client } from "@upstash/workflow";
2853
+ *
2854
+ * const client = new Client({ token: "<QSTASH_TOKEN>" });
2855
+ *
2856
+ * await client.notify({
2857
+ * eventId: "payment.confirmed",
2858
+ * data: {
2859
+ * amount: 99.99,
2860
+ * currency: "USD"
2861
+ * }
2862
+ * })
2863
+ * ```
2864
+ *
2865
+ * Alternatively, you can use the `context.notify` method.
2866
+ *
2867
+ * @param stepName
2868
+ * @param eventId - Unique identifier for the event to wait for
2869
+ * @param options - Configuration options.
2870
+ * @returns `{ timeout: boolean, eventData: TEventData }`.
2871
+ * The `timeout` property specifies if the workflow has timed out. The `eventData`
2872
+ * is the data passed when notifying this workflow of an event.
2873
+ */
2874
+ async waitForEvent(stepName, eventId, options = {}) {
2875
+ const { timeout = "7d" } = options;
2876
+ const timeoutStr = typeof timeout === "string" ? timeout : `${timeout}s`;
2877
+ return await this.addStep(
2878
+ new LazyWaitForEventStep(this, stepName, eventId, timeoutStr)
2879
+ );
2880
+ }
2881
+ /**
2882
+ * Notify workflow runs waiting for an event
2883
+ *
2884
+ * ```ts
2885
+ * const { eventId, eventData, notifyResponse } = await context.notify(
2886
+ * "notify step", "event-id", "event-data"
2887
+ * );
2888
+ * ```
2889
+ *
2890
+ * Upon `context.notify`, the workflow runs waiting for the given eventId (context.waitForEvent)
2891
+ * will receive the given event data and resume execution.
2892
+ *
2893
+ * The response includes the same eventId and eventData. Additionally, there is
2894
+ * a notifyResponse field which contains a list of `Waiter` objects, each corresponding
2895
+ * to a notified workflow run.
2896
+ *
2897
+ * @param stepName
2898
+ * @param eventId event id to notify
2899
+ * @param eventData event data to notify with
2900
+ * @returns notify response which has event id, event data and list of waiters which were notified
2901
+ */
2902
+ async notify(stepName, eventId, eventData) {
2903
+ return await this.addStep(
2904
+ new LazyNotifyStep(this, stepName, eventId, eventData, this.qstashClient.http)
2905
+ );
2906
+ }
2907
+ async invoke(stepName, settings) {
2908
+ return await this.addStep(
2909
+ new LazyInvokeStep(this, stepName, settings)
2910
+ );
2911
+ }
2912
+ async createWebhook(stepName) {
2913
+ return await this.addStep(new LazyCreateWebhookStep(this, stepName));
2914
+ }
2915
+ async waitForWebhook(stepName, webhook, timeout) {
2916
+ return await this.addStep(new LazyWaitForWebhookStep(this, stepName, webhook, timeout));
2917
+ }
2918
+ /**
2919
+ * Cancel the current workflow run
2920
+ *
2921
+ * Will throw WorkflowCancelAbort to stop workflow execution.
2922
+ * Shouldn't be inside try/catch.
2923
+ */
2924
+ async cancel() {
2925
+ throw new WorkflowCancelAbort();
2926
+ }
2927
+ /**
2928
+ * Adds steps to the executor. Needed so that it can be overwritten in
2929
+ * DisabledWorkflowContext.
2930
+ */
2931
+ async addStep(step) {
2932
+ return await this.executor.addStep(step);
2933
+ }
2934
+ get api() {
2935
+ return new WorkflowApi({
2936
+ context: this
2937
+ });
2938
+ }
2939
+ };
2940
+
2941
+ // src/serve/authorization.ts
2942
+ var import_qstash9 = require("@upstash/qstash");
2943
+ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
2944
+ static disabledMessage = "disabled-qstash-worklfow-run";
2945
+ disabled = true;
2946
+ /**
2947
+ * overwrite the WorkflowContext.addStep method to always raise WorkflowAuthError
2948
+ * error in order to stop the execution whenever we encounter a step.
2949
+ *
2950
+ * @param _step
2951
+ */
2952
+ async addStep(_step) {
2953
+ throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
2954
+ }
2955
+ /**
2956
+ * overwrite cancel method to throw WorkflowAuthError with the disabledMessage
2957
+ */
2958
+ async cancel() {
2959
+ throw new WorkflowAuthError(_DisabledWorkflowContext.disabledMessage);
2960
+ }
2961
+ /**
2962
+ * copies the passed context to create a DisabledWorkflowContext. Then, runs the
2963
+ * route function with the new context.
2964
+ *
2965
+ * - returns "run-ended" if there are no steps found or
2966
+ * if the auth failed and user called `return`
2967
+ * - returns "step-found" if DisabledWorkflowContext.addStep is called.
2968
+ * - if there is another error, returns the error.
2969
+ *
2970
+ * @param routeFunction
2971
+ */
2972
+ static async tryAuthentication(routeFunction, context) {
2973
+ const disabledContext = new _DisabledWorkflowContext({
2974
+ qstashClient: new import_qstash9.Client({
2975
+ baseUrl: "disabled-client",
2976
+ token: "disabled-client"
2977
+ }),
2978
+ workflowRunId: context.workflowRunId,
2979
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
2980
+ headers: context.headers,
2981
+ steps: [],
2982
+ url: context.url,
2983
+ initialPayload: context.requestPayload,
2984
+ env: context.env,
2985
+ label: context.label
2986
+ });
2987
+ try {
2988
+ await routeFunction(disabledContext);
2989
+ } catch (error) {
2990
+ if (isInstanceOf(error, WorkflowAuthError) && error.stepName === this.disabledMessage || isInstanceOf(error, WorkflowNonRetryableError) || isInstanceOf(error, WorkflowRetryAfterError)) {
2991
+ return ok("step-found");
2992
+ }
2993
+ console.warn(
2994
+ "Upstash Workflow: Received an error while authorizing request. Please avoid throwing errors before the first step of your workflow."
2995
+ );
2996
+ return err(error);
2997
+ }
2998
+ return ok("run-ended");
2999
+ }
3000
+ };
3001
+
3002
+ // src/workflow-parser.ts
3003
+ var getPayload = async (request) => {
3004
+ try {
3005
+ return await request.text();
3006
+ } catch {
3007
+ return;
3008
+ }
3009
+ };
3010
+ var processRawSteps = (rawSteps) => {
3011
+ const [encodedInitialPayload, ...encodedSteps] = rawSteps;
3012
+ const rawInitialPayload = decodeBase64(encodedInitialPayload.body);
3013
+ const initialStep = {
3014
+ stepId: 0,
3015
+ stepName: "init",
3016
+ stepType: "Initial",
3017
+ out: rawInitialPayload,
3018
+ concurrent: NO_CONCURRENCY
3019
+ };
3020
+ const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
3021
+ const otherSteps = stepsToDecode.map((rawStep) => {
3022
+ const step = JSON.parse(decodeBase64(rawStep.body));
3023
+ return step;
3024
+ });
3025
+ const steps = [initialStep, ...otherSteps];
3026
+ return {
3027
+ rawInitialPayload,
3028
+ steps
3029
+ };
3030
+ };
3031
+ var deduplicateSteps = (steps) => {
3032
+ const targetStepIds = [];
3033
+ const stepIds = [];
3034
+ const deduplicatedSteps = [];
3035
+ for (const step of steps) {
3036
+ if (step.stepId === 0) {
3037
+ if (!targetStepIds.includes(step.targetStep ?? 0)) {
3038
+ deduplicatedSteps.push(step);
3039
+ targetStepIds.push(step.targetStep ?? 0);
3040
+ }
3041
+ } else {
3042
+ if (!stepIds.includes(step.stepId)) {
3043
+ deduplicatedSteps.push(step);
3044
+ stepIds.push(step.stepId);
3045
+ }
3046
+ }
3047
+ }
3048
+ return deduplicatedSteps;
3049
+ };
3050
+ var checkIfLastOneIsDuplicate = async (steps, dispatchDebug) => {
3051
+ if (steps.length < 2) {
3052
+ return false;
3053
+ }
3054
+ const lastStep = steps.at(-1);
3055
+ const lastStepId = lastStep.stepId;
3056
+ const lastTargetStepId = lastStep.targetStep;
3057
+ for (let index = 0; index < steps.length - 1; index++) {
3058
+ const step = steps[index];
3059
+ if (step.stepId === lastStepId && step.targetStep === lastTargetStepId) {
3060
+ const message = `Upstash Workflow: The step '${step.stepName}' with id '${step.stepId}' has run twice during workflow execution. Rest of the workflow will continue running as usual.`;
3061
+ await dispatchDebug?.("onWarning", {
3062
+ warning: message
3063
+ });
3064
+ return true;
3065
+ }
3066
+ }
3067
+ return false;
3068
+ };
3069
+ var validateRequest = (request) => {
3070
+ if (request.headers.get(WORKFLOW_UNKOWN_SDK_VERSION_HEADER)) {
3071
+ const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
3072
+ if (!workflowRunId2) {
3073
+ throw new WorkflowError(
3074
+ "Couldn't get workflow id from header when handling unknown sdk request"
3075
+ );
3076
+ }
3077
+ return {
3078
+ unknownSdk: true,
3079
+ isFirstInvocation: true,
3080
+ workflowRunId: workflowRunId2
3081
+ };
3082
+ }
3083
+ if (request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
3084
+ const workflowRunId2 = request.headers.get(WORKFLOW_ID_HEADER);
3085
+ if (!workflowRunId2) {
3086
+ throw new WorkflowError(
3087
+ "Couldn't get workflow id from header when handling failure callback request"
3088
+ );
3089
+ }
3090
+ return {
3091
+ unknownSdk: false,
3092
+ isFirstInvocation: true,
3093
+ workflowRunId: workflowRunId2
3094
+ };
3095
+ }
3096
+ const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
3097
+ const isFirstInvocation = !versionHeader;
3098
+ if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
3099
+ throw new WorkflowError(
3100
+ `Incompatible workflow sdk protocol version. Expected ${WORKFLOW_PROTOCOL_VERSION}, got ${versionHeader} from the request.`
3101
+ );
3102
+ }
3103
+ const workflowRunId = isFirstInvocation ? getWorkflowRunId() : request.headers.get(WORKFLOW_ID_HEADER) ?? "";
3104
+ if (workflowRunId.length === 0) {
3105
+ throw new WorkflowError("Couldn't get workflow id from header");
3106
+ }
3107
+ return {
3108
+ isFirstInvocation,
3109
+ workflowRunId,
3110
+ unknownSdk: false
3111
+ };
3112
+ };
3113
+ var parseRequest = async ({
3114
+ requestPayload,
3115
+ isFirstInvocation,
3116
+ unknownSdk,
3117
+ workflowRunId,
3118
+ requester,
3119
+ messageId,
3120
+ dispatchDebug
3121
+ }) => {
3122
+ if (isFirstInvocation && !unknownSdk) {
3123
+ return {
3124
+ rawInitialPayload: requestPayload ?? "",
3125
+ steps: [],
3126
+ isLastDuplicate: false,
3127
+ workflowRunEnded: false
3128
+ };
3129
+ } else {
3130
+ let rawSteps;
3131
+ if (!requestPayload) {
3132
+ await dispatchDebug?.("onInfo", {
3133
+ info: "request payload is empty, steps will be fetched from QStash."
3134
+ });
3135
+ const { steps: fetchedSteps, workflowRunEnded } = await getSteps(
3136
+ requester,
3137
+ workflowRunId,
3138
+ messageId,
3139
+ dispatchDebug
3140
+ );
3141
+ if (workflowRunEnded) {
3142
+ return {
3143
+ rawInitialPayload: void 0,
3144
+ steps: void 0,
3145
+ isLastDuplicate: void 0,
3146
+ workflowRunEnded: true
3147
+ };
3148
+ }
3149
+ rawSteps = fetchedSteps;
3150
+ } else {
3151
+ rawSteps = JSON.parse(requestPayload);
3152
+ }
3153
+ const { rawInitialPayload, steps } = processRawSteps(rawSteps);
3154
+ const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, dispatchDebug);
3155
+ const deduplicatedSteps = deduplicateSteps(steps);
3156
+ return {
3157
+ rawInitialPayload,
3158
+ steps: deduplicatedSteps,
3159
+ isLastDuplicate,
3160
+ workflowRunEnded: false
3161
+ };
3162
+ }
3163
+ };
3164
+ var handleFailure = async ({
3165
+ request,
3166
+ requestPayload,
3167
+ qstashClient,
3168
+ initialPayloadParser,
3169
+ routeFunction,
3170
+ failureFunction,
3171
+ env,
3172
+ dispatchDebug
3173
+ }) => {
3174
+ if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true" && !request.headers.get(WORKFLOW_FAILURE_CALLBACK_HEADER)) {
3175
+ return ok({ result: "not-failure-callback" });
3176
+ }
3177
+ if (!failureFunction) {
3178
+ return ok({ result: "failure-function-undefined" });
3179
+ }
3180
+ try {
3181
+ const { status, header, body, url, sourceBody, workflowRunId, workflowCreatedAt } = JSON.parse(
3182
+ requestPayload
3183
+ );
3184
+ const decodedBody = body ? decodeBase64(body) : "{}";
3185
+ let errorMessage = "";
3186
+ let failStack = "";
3187
+ try {
3188
+ const errorPayload = JSON.parse(decodedBody);
3189
+ if (errorPayload.message) {
3190
+ errorMessage = errorPayload.message;
3191
+ }
3192
+ if (errorPayload.stack) {
3193
+ failStack = errorPayload.stack;
3194
+ }
3195
+ } catch {
3196
+ }
3197
+ if (!errorMessage) {
3198
+ errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
3199
+ }
3200
+ const userHeaders = recreateUserHeaders(request.headers);
3201
+ const workflowContext = new WorkflowContext({
3202
+ qstashClient,
3203
+ workflowRunId,
3204
+ initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
3205
+ headers: userHeaders,
3206
+ steps: [],
3207
+ url,
3208
+ env,
3209
+ telemetry: void 0,
3210
+ // not going to make requests in authentication check
3211
+ label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
3212
+ workflowRunCreatedAt: workflowCreatedAt,
3213
+ middlewareManager: void 0
3214
+ });
3215
+ const authCheck = await DisabledWorkflowContext.tryAuthentication(
3216
+ routeFunction,
3217
+ workflowContext
3218
+ );
3219
+ if (authCheck.isErr()) {
3220
+ await dispatchDebug?.("onError", {
3221
+ error: authCheck.error
3222
+ });
3223
+ return err(authCheck.error);
3224
+ } else if (authCheck.value === "run-ended") {
3225
+ return err(new WorkflowError("Not authorized to run the failure function."));
3226
+ }
3227
+ const failureResponse = await failureFunction({
3228
+ context: workflowContext,
3229
+ failStatus: status,
3230
+ failResponse: errorMessage,
3231
+ failHeaders: header,
3232
+ failStack
3233
+ });
3234
+ return ok({ result: "failure-function-executed", response: failureResponse });
3235
+ } catch (error) {
3236
+ return err(error);
3237
+ }
3238
+ };
3239
+
3240
+ // src/serve/multi-region/handlers.ts
3241
+ var import_qstash10 = require("@upstash/qstash");
3242
+
3243
+ // src/serve/multi-region/utils.ts
3244
+ var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
3245
+ var getRegionFromEnvironment = (environment) => {
3246
+ const region = environment.QSTASH_REGION;
3247
+ return normalizeRegionHeader(region);
3248
+ };
3249
+ function readEnvironmentVariables(environmentVariables, environment, region) {
3250
+ const result = {};
3251
+ for (const variable of environmentVariables) {
3252
+ const key = region ? `${region}_${variable}` : variable;
3253
+ result[variable] = environment[key];
3254
+ }
3255
+ return result;
3256
+ }
3257
+ function readClientEnvironmentVariables(environment, region) {
3258
+ return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
3259
+ }
3260
+ function readReceiverEnvironmentVariables(environment, region) {
3261
+ return readEnvironmentVariables(
3262
+ ["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
3263
+ environment,
3264
+ region
3265
+ );
3266
+ }
3267
+ function normalizeRegionHeader(region) {
3268
+ if (!region) {
3269
+ return void 0;
3270
+ }
3271
+ region = region.replaceAll("-", "_").toUpperCase();
3272
+ if (VALID_REGIONS.includes(region)) {
3273
+ return region;
3274
+ }
3275
+ console.warn(
3276
+ `[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
3277
+ ", "
3278
+ )}.`
3279
+ );
3280
+ return void 0;
3281
+ }
3282
+
3283
+ // src/serve/multi-region/handlers.ts
3284
+ var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
3285
+ if (qstashHandlers.mode === "single-region") {
3286
+ return qstashHandlers.handlers;
3287
+ }
3288
+ let targetRegion;
3289
+ if (isFirstInvocation && !regionHeader) {
3290
+ targetRegion = qstashHandlers.defaultRegion;
3291
+ } else {
3292
+ const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
3293
+ targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
3294
+ }
3295
+ const handler = qstashHandlers.handlers[targetRegion];
3296
+ if (!handler) {
3297
+ console.warn(
3298
+ `[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
3299
+ );
3300
+ return qstashHandlers.handlers[qstashHandlers.defaultRegion];
3301
+ }
3302
+ return handler;
3303
+ };
3304
+ var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
3305
+ const clientEnv = readClientEnvironmentVariables(environment, region);
3306
+ const client = new import_qstash10.Client({
3307
+ ...clientOptions,
3308
+ baseUrl: clientEnv.QSTASH_URL,
3309
+ token: clientEnv.QSTASH_TOKEN
3310
+ });
3311
+ const receiver = getReceiver(environment, receiverConfig, region);
3312
+ return { client, receiver };
3313
+ };
3314
+ var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
3315
+ const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
3316
+ if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
3317
+ return {
3318
+ isMultiRegion: true,
3319
+ defaultRegion: getRegionFromEnvironment(environment),
3320
+ clientOptions: qstashClientOption
3321
+ };
3322
+ } else {
3323
+ return { isMultiRegion: false };
3324
+ }
3325
+ };
3326
+ var getQStashHandlers = ({
3327
+ environment,
3328
+ qstashClientOption,
3329
+ receiverConfig
3330
+ }) => {
3331
+ const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
3332
+ if (multiRegion.isMultiRegion) {
3333
+ const regions = ["US_EAST_1", "EU_CENTRAL_1"];
3334
+ const handlers = {};
3335
+ for (const region of regions) {
3336
+ try {
3337
+ handlers[region] = createRegionalHandler(
3338
+ environment,
3339
+ receiverConfig,
3340
+ region,
3341
+ multiRegion.clientOptions
3342
+ );
3343
+ } catch (error) {
3344
+ console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
3345
+ }
3346
+ }
3347
+ return {
3348
+ mode: "multi-region",
3349
+ handlers,
3350
+ defaultRegion: multiRegion.defaultRegion
3351
+ };
3352
+ } else {
3353
+ return {
3354
+ mode: "single-region",
3355
+ handlers: {
3356
+ client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash10.Client({
3357
+ ...qstashClientOption,
3358
+ baseUrl: environment.QSTASH_URL,
3359
+ token: environment.QSTASH_TOKEN
3360
+ }),
3361
+ receiver: getReceiver(environment, receiverConfig)
3362
+ }
3363
+ };
3364
+ }
3365
+ };
3366
+ var getReceiver = (environment, receiverConfig, region) => {
3367
+ if (typeof receiverConfig === "string") {
3368
+ if (receiverConfig === "set-to-undefined") {
3369
+ return void 0;
3370
+ }
3371
+ const receiverEnv = readReceiverEnvironmentVariables(environment, region);
3372
+ return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash10.Receiver({
3373
+ currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
3374
+ nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
3375
+ }) : void 0;
3376
+ } else {
3377
+ return receiverConfig;
3378
+ }
3379
+ };
3380
+ var getQStashHandlerOptions = (...params) => {
3381
+ const handlers = getQStashHandlers(...params);
3382
+ return {
3383
+ qstashHandlers: handlers,
3384
+ defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
3385
+ defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
3386
+ };
3387
+ };
3388
+
3389
+ // src/middleware/middleware.ts
3390
+ var WorkflowMiddleware = class {
3391
+ name;
3392
+ initCallbacks;
3393
+ /**
3394
+ * Callback functions
3395
+ *
3396
+ * Initially set to undefined, will be populated after init is called
3397
+ */
3398
+ middlewareCallbacks = void 0;
3399
+ constructor(parameters) {
3400
+ this.name = parameters.name;
3401
+ if ("init" in parameters) {
3402
+ this.initCallbacks = parameters.init;
3403
+ } else {
3404
+ this.middlewareCallbacks = parameters.callbacks;
3405
+ }
3406
+ }
3407
+ async ensureInit() {
3408
+ if (!this.middlewareCallbacks) {
3409
+ if (!this.initCallbacks) {
3410
+ throw new WorkflowError(`Middleware "${this.name}" has no callbacks or init defined.`);
3411
+ }
3412
+ this.middlewareCallbacks = await this.initCallbacks();
3413
+ }
3414
+ }
3415
+ /**
3416
+ * Gets a callback function by name.
3417
+ *
3418
+ * @param callback name of the callback to retrieve
3419
+ */
3420
+ getCallback(callback) {
3421
+ return this.middlewareCallbacks?.[callback];
3422
+ }
3423
+ };
3424
+
3425
+ // src/middleware/logging.ts
3426
+ var loggingMiddleware = new WorkflowMiddleware({
3427
+ name: "logging",
3428
+ callbacks: {
3429
+ afterExecution(params) {
3430
+ const { context, ...rest } = params;
3431
+ console.log(" [Upstash Workflow]: Step executed:", {
3432
+ workflowRunId: context.workflowRunId,
3433
+ ...rest
3434
+ });
3435
+ },
3436
+ beforeExecution(params) {
3437
+ const { context, ...rest } = params;
3438
+ console.log(" [Upstash Workflow]: Step execution started:", {
3439
+ workflowRunId: context.workflowRunId,
3440
+ ...rest
3441
+ });
3442
+ },
3443
+ runStarted(params) {
3444
+ const { context, ...rest } = params;
3445
+ console.log(" [Upstash Workflow]: Workflow run started:", {
3446
+ workflowRunId: context.workflowRunId,
3447
+ ...rest
3448
+ });
3449
+ },
3450
+ runCompleted(params) {
3451
+ const { context, ...rest } = params;
3452
+ console.log(" [Upstash Workflow]: Workflow run completed:", {
3453
+ workflowRunId: context.workflowRunId,
3454
+ ...rest
3455
+ });
3456
+ },
3457
+ onError: onErrorWithConsole,
3458
+ onWarning: onWarningWithConsole,
3459
+ onInfo: onInfoWithConsole
3460
+ }
3461
+ });
3462
+
3463
+ // src/serve/options.ts
3464
+ var createResponseData = (workflowRunId, detailedFinishCondition) => {
3465
+ const baseHeaders = {
3466
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
3467
+ "Upstash-workflow-sdk": VERSION
3468
+ };
3469
+ if (detailedFinishCondition?.condition === "auth-fail") {
3470
+ return {
3471
+ text: JSON.stringify({
3472
+ message: AUTH_FAIL_MESSAGE,
3473
+ workflowRunId
3474
+ }),
3475
+ status: 400,
3476
+ headers: baseHeaders
3477
+ };
3478
+ } else if (detailedFinishCondition?.condition === "non-retryable-error") {
3479
+ return {
3480
+ text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
3481
+ status: 489,
3482
+ headers: {
3483
+ ...baseHeaders,
3484
+ "Upstash-NonRetryable-Error": "true"
3485
+ }
3486
+ };
3487
+ } else if (detailedFinishCondition?.condition === "retry-after-error") {
3488
+ return {
3489
+ text: JSON.stringify(formatWorkflowError(detailedFinishCondition.result)),
3490
+ status: 429,
3491
+ headers: {
3492
+ ...baseHeaders,
3493
+ "Retry-After": detailedFinishCondition.result.retryAfter.toString()
3494
+ }
3495
+ };
3496
+ } else if (detailedFinishCondition?.condition === "failure-callback-executed") {
3497
+ return {
3498
+ text: JSON.stringify({ result: detailedFinishCondition.result ?? void 0 }),
3499
+ status: 200,
3500
+ headers: baseHeaders
3501
+ };
3502
+ } else if (detailedFinishCondition?.condition === "failure-callback-undefined") {
3503
+ return {
3504
+ text: JSON.stringify({
3505
+ workflowRunId,
3506
+ finishCondition: detailedFinishCondition.condition
3507
+ }),
3508
+ status: 200,
3509
+ headers: {
3510
+ ...baseHeaders,
3511
+ "Upstash-Workflow-Failure-Callback-Notfound": "true"
3512
+ }
3513
+ };
3514
+ }
3515
+ return {
3516
+ text: JSON.stringify({
3517
+ workflowRunId,
3518
+ finishCondition: detailedFinishCondition.condition
3519
+ }),
3520
+ status: 200,
3521
+ headers: baseHeaders
3522
+ };
3523
+ };
3524
+ var processOptions = (options, internalOptions) => {
3525
+ const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
3526
+ const {
3527
+ qstashHandlers,
3528
+ defaultClient: qstashClient,
3529
+ defaultReceiver: receiver
3530
+ } = getQStashHandlerOptions({
3531
+ environment,
3532
+ qstashClientOption: options?.qstashClient,
3533
+ receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
3534
+ });
3535
+ return {
3536
+ qstashClient,
3537
+ initialPayloadParser: (initialRequest) => {
3538
+ if (!initialRequest) {
3539
+ return void 0;
3540
+ }
3541
+ try {
3542
+ const parsed = JSON.parse(initialRequest);
3543
+ return options?.schema ? options.schema.parse(parsed) : parsed;
3544
+ } catch (error) {
3545
+ if (error instanceof SyntaxError) {
3546
+ return initialRequest;
3547
+ }
3548
+ throw error;
3549
+ }
3550
+ },
3551
+ receiver,
3552
+ baseUrl: environment.UPSTASH_WORKFLOW_URL,
3553
+ env: environment,
3554
+ disableTelemetry: false,
3555
+ ...options,
3556
+ // merge middlewares
3557
+ middlewares: [options?.middlewares ?? [], options?.verbose ? [loggingMiddleware] : []].flat(),
3558
+ internal: {
3559
+ generateResponse: internalOptions?.generateResponse ?? ((responseData) => {
3560
+ return new Response(responseData.text, {
3561
+ status: responseData.status,
3562
+ headers: responseData.headers
3563
+ });
3564
+ }),
3565
+ useJSONContent: internalOptions?.useJSONContent ?? false,
3566
+ qstashHandlers
3567
+ }
3568
+ };
3569
+ };
3570
+ var determineUrls = async (request, url, baseUrl, dispatchDebug) => {
3571
+ const initialWorkflowUrl = url ?? request.url;
3572
+ const workflowUrl = baseUrl ? initialWorkflowUrl.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, (_, matchedBaseUrl, path) => {
3573
+ return baseUrl + (path || "");
3574
+ }) : initialWorkflowUrl;
3575
+ if (workflowUrl !== initialWorkflowUrl) {
3576
+ await dispatchDebug("onInfo", {
3577
+ info: `The workflow URL's base URL has been replaced with the provided baseUrl. Original URL: ${initialWorkflowUrl}, New URL: ${workflowUrl}`
3578
+ });
3579
+ }
3580
+ if (workflowUrl.includes("localhost")) {
3581
+ await dispatchDebug("onInfo", {
3582
+ info: `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}`
3583
+ });
3584
+ }
3585
+ if (!(workflowUrl.startsWith("http://") || workflowUrl.startsWith("https://"))) {
3586
+ throw new WorkflowError(
3587
+ `Workflow URL should start with 'http://' or 'https://'. Recevied is '${workflowUrl}'`
3588
+ );
3589
+ }
3590
+ return {
3591
+ workflowUrl
3592
+ };
3593
+ };
3594
+ 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`;
3595
+
3596
+ // src/serve/index.ts
3597
+ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3598
+ const {
3599
+ initialPayloadParser,
3600
+ url,
3601
+ failureFunction,
3602
+ baseUrl,
3603
+ env,
3604
+ disableTelemetry,
3605
+ middlewares,
3606
+ internal
3607
+ } = processOptions(options, internalOptions);
3608
+ telemetry2 = disableTelemetry ? void 0 : telemetry2;
3609
+ const { generateResponse: responseGenerator, useJSONContent } = internal;
3610
+ const handler = async (request, middlewareManager) => {
3611
+ await middlewareManager.dispatchDebug("onInfo", {
3612
+ info: `Received request for workflow execution.`
3613
+ });
3614
+ const { workflowUrl } = await determineUrls(
3615
+ request,
3616
+ url,
3617
+ baseUrl,
3618
+ middlewareManager.dispatchDebug.bind(middlewareManager)
3619
+ );
3620
+ const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
3621
+ const regionHeader = request.headers.get("upstash-region");
3622
+ const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
3623
+ internal.qstashHandlers,
3624
+ regionHeader,
3625
+ isFirstInvocation
3626
+ );
3627
+ const requestPayload = await getPayload(request) ?? "";
3628
+ await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
3629
+ middlewareManager.assignWorkflowRunId(workflowRunId);
3630
+ await middlewareManager.dispatchDebug("onInfo", {
3631
+ info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
3632
+ });
3633
+ const { rawInitialPayload, steps, isLastDuplicate, workflowRunEnded } = await parseRequest({
3634
+ requestPayload,
3635
+ isFirstInvocation,
3636
+ unknownSdk,
3637
+ workflowRunId,
3638
+ requester: regionalClient.http,
3639
+ messageId: request.headers.get("upstash-message-id"),
3640
+ dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
3641
+ });
3642
+ if (workflowRunEnded) {
3643
+ return responseGenerator(
3644
+ createResponseData(workflowRunId, {
3645
+ condition: "workflow-already-ended"
3646
+ })
3647
+ );
3648
+ }
3649
+ if (isLastDuplicate) {
3650
+ return responseGenerator(
3651
+ createResponseData(workflowRunId, {
3652
+ condition: "duplicate-step"
3653
+ })
3654
+ );
3655
+ }
3656
+ const failureCheck = await handleFailure({
3657
+ request,
3658
+ requestPayload,
3659
+ qstashClient: regionalClient,
3660
+ initialPayloadParser,
3661
+ routeFunction,
3662
+ failureFunction,
3663
+ env,
3664
+ dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
3665
+ });
3666
+ if (failureCheck.isErr()) {
3667
+ throw failureCheck.error;
3668
+ } else if (failureCheck.value.result === "failure-function-executed") {
3669
+ await middlewareManager.dispatchDebug("onInfo", {
3670
+ info: `Handled failure callback.`
3671
+ });
3672
+ return responseGenerator(
3673
+ createResponseData(workflowRunId, {
3674
+ condition: "failure-callback-executed",
3675
+ result: failureCheck.value.response
3676
+ })
3677
+ );
3678
+ } else if (failureCheck.value.result === "failure-function-undefined") {
3679
+ await middlewareManager.dispatchDebug("onInfo", {
3680
+ info: `Failure callback invoked but no failure function defined.`
3681
+ });
3682
+ return responseGenerator(
3683
+ createResponseData(workflowRunId, {
3684
+ condition: "failure-callback-undefined"
3685
+ })
3686
+ );
3687
+ }
3688
+ const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3689
+ const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3690
+ const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3691
+ const workflowContext = new WorkflowContext({
3692
+ qstashClient: regionalClient,
3693
+ workflowRunId,
3694
+ initialPayload: initialPayloadParser(rawInitialPayload),
3695
+ headers: recreateUserHeaders(request.headers),
3696
+ steps,
3697
+ url: workflowUrl,
3698
+ env,
3699
+ telemetry: telemetry2,
3700
+ invokeCount,
3701
+ label,
3702
+ workflowRunCreatedAt: Number(workflowRunCreatedAt),
3703
+ middlewareManager
3704
+ });
3705
+ const authCheck = await DisabledWorkflowContext.tryAuthentication(
3706
+ routeFunction,
3707
+ workflowContext
3708
+ );
3709
+ if (authCheck.isErr()) {
3710
+ throw authCheck.error;
3711
+ } else if (authCheck.value === "run-ended") {
3712
+ await middlewareManager.dispatchDebug("onError", {
3713
+ error: new Error(AUTH_FAIL_MESSAGE)
3714
+ });
3715
+ return responseGenerator(
3716
+ createResponseData(isFirstInvocation ? "no-workflow-id" : workflowContext.workflowRunId, {
3717
+ condition: "auth-fail"
3718
+ })
3719
+ );
3720
+ }
3721
+ const callReturnCheck = await handleThirdPartyCallResult({
3722
+ request,
3723
+ requestPayload: rawInitialPayload,
3724
+ client: regionalClient,
3725
+ workflowUrl,
3726
+ telemetry: telemetry2,
3727
+ middlewareManager
3728
+ });
3729
+ if (callReturnCheck.isErr()) {
3730
+ throw callReturnCheck.error;
3731
+ } else if (callReturnCheck.value === "continue-workflow") {
3732
+ const result = isFirstInvocation ? await triggerFirstInvocation({
3733
+ workflowContext,
3734
+ useJSONContent,
3735
+ telemetry: telemetry2,
3736
+ invokeCount,
3737
+ middlewareManager,
3738
+ unknownSdk
3739
+ }) : await triggerRouteFunction({
3740
+ onStep: async () => {
3741
+ if (steps.length === 1) {
3742
+ await middlewareManager.dispatchLifecycle("runStarted", {});
3743
+ }
3744
+ return await routeFunction(workflowContext);
3745
+ },
3746
+ onCleanup: async (result2) => {
3747
+ await middlewareManager.dispatchLifecycle("runCompleted", {
3748
+ result: result2
3749
+ });
3750
+ await triggerWorkflowDelete(
3751
+ workflowContext,
3752
+ result2,
3753
+ false,
3754
+ middlewareManager.dispatchDebug.bind(middlewareManager)
3755
+ );
3756
+ },
3757
+ onCancel: async () => {
3758
+ await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
3759
+ },
3760
+ middlewareManager
3761
+ });
3762
+ if (result.isOk() && isInstanceOf(result.value, WorkflowNonRetryableError)) {
3763
+ return responseGenerator(
3764
+ createResponseData(workflowRunId, {
3765
+ condition: "non-retryable-error",
3766
+ result: result.value
3767
+ })
3768
+ );
3769
+ }
3770
+ if (result.isOk() && isInstanceOf(result.value, WorkflowRetryAfterError)) {
3771
+ return responseGenerator(
3772
+ createResponseData(workflowRunId, {
3773
+ condition: "retry-after-error",
3774
+ result: result.value
3775
+ })
3776
+ );
3777
+ }
3778
+ if (result.isErr()) {
3779
+ throw result.error;
3780
+ }
3781
+ await middlewareManager.dispatchDebug("onInfo", {
3782
+ info: `Workflow endpoint execution completed successfully.`
3783
+ });
3784
+ return responseGenerator(
3785
+ createResponseData(workflowContext.workflowRunId, {
3786
+ condition: "success"
3787
+ })
3788
+ );
3789
+ } else if (callReturnCheck.value === "workflow-ended") {
3790
+ return responseGenerator(
3791
+ createResponseData(workflowContext.workflowRunId, {
3792
+ condition: "workflow-already-ended"
3793
+ })
3794
+ );
3795
+ }
3796
+ return responseGenerator(
3797
+ createResponseData(workflowContext.workflowRunId, {
3798
+ condition: "fromCallback"
3799
+ })
3800
+ );
3801
+ };
3802
+ const safeHandler = async (request) => {
3803
+ const middlewareManager = new MiddlewareManager(middlewares);
3804
+ try {
3805
+ return await handler(request, middlewareManager);
3806
+ } catch (error) {
3807
+ const formattedError = formatWorkflowError(error);
3808
+ await middlewareManager.dispatchDebug("onError", {
3809
+ error: isInstanceOf(error, Error) ? error : new Error(formattedError.message)
3810
+ });
3811
+ return new Response(JSON.stringify(formattedError), {
3812
+ status: 500,
3813
+ headers: {
3814
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
3815
+ }
3816
+ });
3817
+ }
3818
+ };
3819
+ return { handler: safeHandler };
3820
+ };
3821
+
3822
+ // platforms/react-router.ts
3823
+ var telemetry = {
3824
+ sdk: SDK_TELEMETRY,
3825
+ framework: "react-router",
3826
+ runtime: process.versions.bun ? `bun@${process.versions.bun}/node@${process.version}` : `node@${process.version}`
3827
+ };
3828
+ var serve = (routeFunction, options) => {
3829
+ const { handler: serveHandler } = serveBase(
3830
+ routeFunction,
3831
+ telemetry,
3832
+ options
3833
+ );
3834
+ return async ({ request }) => {
3835
+ return await serveHandler(request);
3836
+ };
3837
+ };
3838
+ var createWorkflow = (...params) => {
3839
+ const [routeFunction, options = {}] = params;
3840
+ return {
3841
+ routeFunction,
3842
+ options,
3843
+ workflowId: void 0
3844
+ };
3845
+ };
3846
+ var serveMany = (workflows, options) => {
3847
+ return serveManyBase({
3848
+ workflows,
3849
+ getUrl(params) {
3850
+ return params.request.url;
3851
+ },
3852
+ serveMethod: (...params) => serve(...params),
3853
+ options
3854
+ }).handler;
3855
+ };
3856
+ // Annotate the CommonJS export names for ESM import in node:
3857
+ 0 && (module.exports = {
3858
+ createWorkflow,
3859
+ serve,
3860
+ serveMany
3861
+ });