@upstash/workflow 0.2.20 → 0.2.21

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