@upstash/workflow 0.1.0

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/h3.js ADDED
@@ -0,0 +1,2328 @@
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/h3.ts
21
+ var h3_exports = {};
22
+ __export(h3_exports, {
23
+ serve: () => serve2
24
+ });
25
+ module.exports = __toCommonJS(h3_exports);
26
+
27
+ // node_modules/defu/dist/defu.mjs
28
+ function isPlainObject(value) {
29
+ if (value === null || typeof value !== "object") {
30
+ return false;
31
+ }
32
+ const prototype = Object.getPrototypeOf(value);
33
+ if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
34
+ return false;
35
+ }
36
+ if (Symbol.iterator in value) {
37
+ return false;
38
+ }
39
+ if (Symbol.toStringTag in value) {
40
+ return Object.prototype.toString.call(value) === "[object Module]";
41
+ }
42
+ return true;
43
+ }
44
+ function _defu(baseObject, defaults, namespace = ".", merger) {
45
+ if (!isPlainObject(defaults)) {
46
+ return _defu(baseObject, {}, namespace, merger);
47
+ }
48
+ const object = Object.assign({}, defaults);
49
+ for (const key in baseObject) {
50
+ if (key === "__proto__" || key === "constructor") {
51
+ continue;
52
+ }
53
+ const value = baseObject[key];
54
+ if (value === null || value === void 0) {
55
+ continue;
56
+ }
57
+ if (merger && merger(object, key, value, namespace)) {
58
+ continue;
59
+ }
60
+ if (Array.isArray(value) && Array.isArray(object[key])) {
61
+ object[key] = [...value, ...object[key]];
62
+ } else if (isPlainObject(value) && isPlainObject(object[key])) {
63
+ object[key] = _defu(
64
+ value,
65
+ object[key],
66
+ (namespace ? `${namespace}.` : "") + key.toString(),
67
+ merger
68
+ );
69
+ } else {
70
+ object[key] = value;
71
+ }
72
+ }
73
+ return object;
74
+ }
75
+ function createDefu(merger) {
76
+ return (...arguments_) => (
77
+ // eslint-disable-next-line unicorn/no-array-reduce
78
+ arguments_.reduce((p, c) => _defu(p, c, "", merger), {})
79
+ );
80
+ }
81
+ var defu = createDefu();
82
+ var defuFn = createDefu((object, key, currentValue) => {
83
+ if (object[key] !== void 0 && typeof currentValue === "function") {
84
+ object[key] = currentValue(object[key]);
85
+ return true;
86
+ }
87
+ });
88
+ var defuArrayFn = createDefu((object, key, currentValue) => {
89
+ if (Array.isArray(object[key]) && typeof currentValue === "function") {
90
+ object[key] = currentValue(object[key]);
91
+ return true;
92
+ }
93
+ });
94
+
95
+ // node_modules/h3/dist/index.mjs
96
+ function hasProp(obj, prop) {
97
+ try {
98
+ return prop in obj;
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+ var __defProp$2 = Object.defineProperty;
104
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
105
+ var __publicField$2 = (obj, key, value) => {
106
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
107
+ return value;
108
+ };
109
+ var H3Error = class extends Error {
110
+ constructor(message, opts = {}) {
111
+ super(message, opts);
112
+ __publicField$2(this, "statusCode", 500);
113
+ __publicField$2(this, "fatal", false);
114
+ __publicField$2(this, "unhandled", false);
115
+ __publicField$2(this, "statusMessage");
116
+ __publicField$2(this, "data");
117
+ __publicField$2(this, "cause");
118
+ if (opts.cause && !this.cause) {
119
+ this.cause = opts.cause;
120
+ }
121
+ }
122
+ toJSON() {
123
+ const obj = {
124
+ message: this.message,
125
+ statusCode: sanitizeStatusCode(this.statusCode, 500)
126
+ };
127
+ if (this.statusMessage) {
128
+ obj.statusMessage = sanitizeStatusMessage(this.statusMessage);
129
+ }
130
+ if (this.data !== void 0) {
131
+ obj.data = this.data;
132
+ }
133
+ return obj;
134
+ }
135
+ };
136
+ __publicField$2(H3Error, "__h3_error__", true);
137
+ function createError(input) {
138
+ if (typeof input === "string") {
139
+ return new H3Error(input);
140
+ }
141
+ if (isError(input)) {
142
+ return input;
143
+ }
144
+ const err2 = new H3Error(input.message ?? input.statusMessage ?? "", {
145
+ cause: input.cause || input
146
+ });
147
+ if (hasProp(input, "stack")) {
148
+ try {
149
+ Object.defineProperty(err2, "stack", {
150
+ get() {
151
+ return input.stack;
152
+ }
153
+ });
154
+ } catch {
155
+ try {
156
+ err2.stack = input.stack;
157
+ } catch {
158
+ }
159
+ }
160
+ }
161
+ if (input.data) {
162
+ err2.data = input.data;
163
+ }
164
+ if (input.statusCode) {
165
+ err2.statusCode = sanitizeStatusCode(input.statusCode, err2.statusCode);
166
+ } else if (input.status) {
167
+ err2.statusCode = sanitizeStatusCode(input.status, err2.statusCode);
168
+ }
169
+ if (input.statusMessage) {
170
+ err2.statusMessage = input.statusMessage;
171
+ } else if (input.statusText) {
172
+ err2.statusMessage = input.statusText;
173
+ }
174
+ if (err2.statusMessage) {
175
+ const originalMessage = err2.statusMessage;
176
+ const sanitizedMessage = sanitizeStatusMessage(err2.statusMessage);
177
+ if (sanitizedMessage !== originalMessage) {
178
+ console.warn(
179
+ "[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future, `statusMessage` will be sanitized by default."
180
+ );
181
+ }
182
+ }
183
+ if (input.fatal !== void 0) {
184
+ err2.fatal = input.fatal;
185
+ }
186
+ if (input.unhandled !== void 0) {
187
+ err2.unhandled = input.unhandled;
188
+ }
189
+ return err2;
190
+ }
191
+ function isError(input) {
192
+ return input?.constructor?.__h3_error__ === true;
193
+ }
194
+ function isMethod(event, expected, allowHead) {
195
+ if (allowHead && event.method === "HEAD") {
196
+ return true;
197
+ }
198
+ if (typeof expected === "string") {
199
+ if (event.method === expected) {
200
+ return true;
201
+ }
202
+ } else if (expected.includes(event.method)) {
203
+ return true;
204
+ }
205
+ return false;
206
+ }
207
+ function assertMethod(event, expected, allowHead) {
208
+ if (!isMethod(event, expected, allowHead)) {
209
+ throw createError({
210
+ statusCode: 405,
211
+ statusMessage: "HTTP method is not allowed."
212
+ });
213
+ }
214
+ }
215
+ var RawBodySymbol = Symbol.for("h3RawBody");
216
+ var ParsedBodySymbol = Symbol.for("h3ParsedBody");
217
+ var PayloadMethods$1 = ["PATCH", "POST", "PUT", "DELETE"];
218
+ function readRawBody(event, encoding = "utf8") {
219
+ assertMethod(event, PayloadMethods$1);
220
+ const _rawBody = event._requestBody || event.web?.request?.body || event.node.req[RawBodySymbol] || event.node.req.rawBody || event.node.req.body;
221
+ if (_rawBody) {
222
+ const promise2 = Promise.resolve(_rawBody).then((_resolved) => {
223
+ if (Buffer.isBuffer(_resolved)) {
224
+ return _resolved;
225
+ }
226
+ if (typeof _resolved.pipeTo === "function") {
227
+ return new Promise((resolve, reject) => {
228
+ const chunks = [];
229
+ _resolved.pipeTo(
230
+ new WritableStream({
231
+ write(chunk) {
232
+ chunks.push(chunk);
233
+ },
234
+ close() {
235
+ resolve(Buffer.concat(chunks));
236
+ },
237
+ abort(reason) {
238
+ reject(reason);
239
+ }
240
+ })
241
+ ).catch(reject);
242
+ });
243
+ } else if (typeof _resolved.pipe === "function") {
244
+ return new Promise((resolve, reject) => {
245
+ const chunks = [];
246
+ _resolved.on("data", (chunk) => {
247
+ chunks.push(chunk);
248
+ }).on("end", () => {
249
+ resolve(Buffer.concat(chunks));
250
+ }).on("error", reject);
251
+ });
252
+ }
253
+ if (_resolved.constructor === Object) {
254
+ return Buffer.from(JSON.stringify(_resolved));
255
+ }
256
+ return Buffer.from(_resolved);
257
+ });
258
+ return encoding ? promise2.then((buff) => buff.toString(encoding)) : promise2;
259
+ }
260
+ if (!Number.parseInt(event.node.req.headers["content-length"] || "") && !String(event.node.req.headers["transfer-encoding"] ?? "").split(",").map((e) => e.trim()).filter(Boolean).includes("chunked")) {
261
+ return Promise.resolve(void 0);
262
+ }
263
+ const promise = event.node.req[RawBodySymbol] = new Promise(
264
+ (resolve, reject) => {
265
+ const bodyData = [];
266
+ event.node.req.on("error", (err2) => {
267
+ reject(err2);
268
+ }).on("data", (chunk) => {
269
+ bodyData.push(chunk);
270
+ }).on("end", () => {
271
+ resolve(Buffer.concat(bodyData));
272
+ });
273
+ }
274
+ );
275
+ const result = encoding ? promise.then((buff) => buff.toString(encoding)) : promise;
276
+ return result;
277
+ }
278
+ var DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g;
279
+ function sanitizeStatusMessage(statusMessage = "") {
280
+ return statusMessage.replace(DISALLOWED_STATUS_CHARS, "");
281
+ }
282
+ function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
283
+ if (!statusCode) {
284
+ return defaultStatusCode;
285
+ }
286
+ if (typeof statusCode === "string") {
287
+ statusCode = Number.parseInt(statusCode, 10);
288
+ }
289
+ if (statusCode < 100 || statusCode > 999) {
290
+ return defaultStatusCode;
291
+ }
292
+ return statusCode;
293
+ }
294
+ var getSessionPromise = Symbol("getSession");
295
+ function defineEventHandler(handler) {
296
+ if (typeof handler === "function") {
297
+ handler.__is_handler__ = true;
298
+ return handler;
299
+ }
300
+ const _hooks = {
301
+ onRequest: _normalizeArray(handler.onRequest),
302
+ onBeforeResponse: _normalizeArray(handler.onBeforeResponse)
303
+ };
304
+ const _handler = (event) => {
305
+ return _callHandler(event, handler.handler, _hooks);
306
+ };
307
+ _handler.__is_handler__ = true;
308
+ _handler.__resolve__ = handler.handler.__resolve__;
309
+ _handler.__websocket__ = handler.websocket;
310
+ return _handler;
311
+ }
312
+ function _normalizeArray(input) {
313
+ return input ? Array.isArray(input) ? input : [input] : void 0;
314
+ }
315
+ async function _callHandler(event, handler, hooks) {
316
+ if (hooks.onRequest) {
317
+ for (const hook of hooks.onRequest) {
318
+ await hook(event);
319
+ if (event.handled) {
320
+ return;
321
+ }
322
+ }
323
+ }
324
+ const body = await handler(event);
325
+ const response = { body };
326
+ if (hooks.onBeforeResponse) {
327
+ for (const hook of hooks.onBeforeResponse) {
328
+ await hook(event, response);
329
+ }
330
+ }
331
+ return response.body;
332
+ }
333
+ var H3Headers = globalThis.Headers;
334
+ var H3Response = globalThis.Response;
335
+
336
+ // src/error.ts
337
+ var import_qstash = require("@upstash/qstash");
338
+ var QStashWorkflowError = class extends import_qstash.QstashError {
339
+ constructor(message) {
340
+ super(message);
341
+ this.name = "QStashWorkflowError";
342
+ }
343
+ };
344
+ var QStashWorkflowAbort = class extends Error {
345
+ stepInfo;
346
+ stepName;
347
+ constructor(stepName, stepInfo) {
348
+ super(
349
+ `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}'.`
350
+ );
351
+ this.name = "QStashWorkflowAbort";
352
+ this.stepName = stepName;
353
+ this.stepInfo = stepInfo;
354
+ }
355
+ };
356
+ var formatWorkflowError = (error) => {
357
+ return error instanceof Error ? {
358
+ error: error.name,
359
+ message: error.message
360
+ } : {
361
+ error: "Error",
362
+ message: "An error occured while executing workflow."
363
+ };
364
+ };
365
+
366
+ // node_modules/neverthrow/dist/index.es.js
367
+ var defaultErrorConfig = {
368
+ withStackTrace: false
369
+ };
370
+ var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
371
+ const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
372
+ const maybeStack = config.withStackTrace ? new Error().stack : void 0;
373
+ return {
374
+ data,
375
+ message,
376
+ stack: maybeStack
377
+ };
378
+ };
379
+ function __awaiter(thisArg, _arguments, P, generator) {
380
+ function adopt(value) {
381
+ return value instanceof P ? value : new P(function(resolve) {
382
+ resolve(value);
383
+ });
384
+ }
385
+ return new (P || (P = Promise))(function(resolve, reject) {
386
+ function fulfilled(value) {
387
+ try {
388
+ step(generator.next(value));
389
+ } catch (e) {
390
+ reject(e);
391
+ }
392
+ }
393
+ function rejected(value) {
394
+ try {
395
+ step(generator["throw"](value));
396
+ } catch (e) {
397
+ reject(e);
398
+ }
399
+ }
400
+ function step(result) {
401
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
402
+ }
403
+ step((generator = generator.apply(thisArg, [])).next());
404
+ });
405
+ }
406
+ function __values(o) {
407
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
408
+ if (m) return m.call(o);
409
+ if (o && typeof o.length === "number") return {
410
+ next: function() {
411
+ if (o && i >= o.length) o = void 0;
412
+ return { value: o && o[i++], done: !o };
413
+ }
414
+ };
415
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
416
+ }
417
+ function __await(v) {
418
+ return this instanceof __await ? (this.v = v, this) : new __await(v);
419
+ }
420
+ function __asyncGenerator(thisArg, _arguments, generator) {
421
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
422
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
423
+ return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
424
+ return this;
425
+ }, i;
426
+ function verb(n) {
427
+ if (g[n]) i[n] = function(v) {
428
+ return new Promise(function(a, b) {
429
+ q.push([n, v, a, b]) > 1 || resume(n, v);
430
+ });
431
+ };
432
+ }
433
+ function resume(n, v) {
434
+ try {
435
+ step(g[n](v));
436
+ } catch (e) {
437
+ settle(q[0][3], e);
438
+ }
439
+ }
440
+ function step(r) {
441
+ r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
442
+ }
443
+ function fulfill(value) {
444
+ resume("next", value);
445
+ }
446
+ function reject(value) {
447
+ resume("throw", value);
448
+ }
449
+ function settle(f, v) {
450
+ if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]);
451
+ }
452
+ }
453
+ function __asyncDelegator(o) {
454
+ var i, p;
455
+ return i = {}, verb("next"), verb("throw", function(e) {
456
+ throw e;
457
+ }), verb("return"), i[Symbol.iterator] = function() {
458
+ return this;
459
+ }, i;
460
+ function verb(n, f) {
461
+ i[n] = o[n] ? function(v) {
462
+ return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v;
463
+ } : f;
464
+ }
465
+ }
466
+ function __asyncValues(o) {
467
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
468
+ var m = o[Symbol.asyncIterator], i;
469
+ 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() {
470
+ return this;
471
+ }, i);
472
+ function verb(n) {
473
+ i[n] = o[n] && function(v) {
474
+ return new Promise(function(resolve, reject) {
475
+ v = o[n](v), settle(resolve, reject, v.done, v.value);
476
+ });
477
+ };
478
+ }
479
+ function settle(resolve, reject, d, v) {
480
+ Promise.resolve(v).then(function(v2) {
481
+ resolve({ value: v2, done: d });
482
+ }, reject);
483
+ }
484
+ }
485
+ var ResultAsync = class _ResultAsync {
486
+ constructor(res) {
487
+ this._promise = res;
488
+ }
489
+ static fromSafePromise(promise) {
490
+ const newPromise = promise.then((value) => new Ok(value));
491
+ return new _ResultAsync(newPromise);
492
+ }
493
+ static fromPromise(promise, errorFn) {
494
+ const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
495
+ return new _ResultAsync(newPromise);
496
+ }
497
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
498
+ static fromThrowable(fn, errorFn) {
499
+ return (...args) => {
500
+ return new _ResultAsync((() => __awaiter(this, void 0, void 0, function* () {
501
+ try {
502
+ return new Ok(yield fn(...args));
503
+ } catch (error) {
504
+ return new Err(errorFn ? errorFn(error) : error);
505
+ }
506
+ }))());
507
+ };
508
+ }
509
+ static combine(asyncResultList) {
510
+ return combineResultAsyncList(asyncResultList);
511
+ }
512
+ static combineWithAllErrors(asyncResultList) {
513
+ return combineResultAsyncListWithAllErrors(asyncResultList);
514
+ }
515
+ map(f) {
516
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
517
+ if (res.isErr()) {
518
+ return new Err(res.error);
519
+ }
520
+ return new Ok(yield f(res.value));
521
+ })));
522
+ }
523
+ andThrough(f) {
524
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
525
+ if (res.isErr()) {
526
+ return new Err(res.error);
527
+ }
528
+ const newRes = yield f(res.value);
529
+ if (newRes.isErr()) {
530
+ return new Err(newRes.error);
531
+ }
532
+ return new Ok(res.value);
533
+ })));
534
+ }
535
+ andTee(f) {
536
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
537
+ if (res.isErr()) {
538
+ return new Err(res.error);
539
+ }
540
+ try {
541
+ yield f(res.value);
542
+ } catch (e) {
543
+ }
544
+ return new Ok(res.value);
545
+ })));
546
+ }
547
+ mapErr(f) {
548
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
549
+ if (res.isOk()) {
550
+ return new Ok(res.value);
551
+ }
552
+ return new Err(yield f(res.error));
553
+ })));
554
+ }
555
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
556
+ andThen(f) {
557
+ return new _ResultAsync(this._promise.then((res) => {
558
+ if (res.isErr()) {
559
+ return new Err(res.error);
560
+ }
561
+ const newValue = f(res.value);
562
+ return newValue instanceof _ResultAsync ? newValue._promise : newValue;
563
+ }));
564
+ }
565
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
566
+ orElse(f) {
567
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
568
+ if (res.isErr()) {
569
+ return f(res.error);
570
+ }
571
+ return new Ok(res.value);
572
+ })));
573
+ }
574
+ match(ok2, _err) {
575
+ return this._promise.then((res) => res.match(ok2, _err));
576
+ }
577
+ unwrapOr(t) {
578
+ return this._promise.then((res) => res.unwrapOr(t));
579
+ }
580
+ /**
581
+ * Emulates Rust's `?` operator in `safeTry`'s body. See also `safeTry`.
582
+ */
583
+ safeUnwrap() {
584
+ return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
585
+ return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
586
+ });
587
+ }
588
+ // Makes ResultAsync implement PromiseLike<Result>
589
+ then(successCallback, failureCallback) {
590
+ return this._promise.then(successCallback, failureCallback);
591
+ }
592
+ };
593
+ var errAsync = (err2) => new ResultAsync(Promise.resolve(new Err(err2)));
594
+ var fromPromise = ResultAsync.fromPromise;
595
+ var fromSafePromise = ResultAsync.fromSafePromise;
596
+ var fromAsyncThrowable = ResultAsync.fromThrowable;
597
+ var combineResultList = (resultList) => {
598
+ let acc = ok([]);
599
+ for (const result of resultList) {
600
+ if (result.isErr()) {
601
+ acc = err(result.error);
602
+ break;
603
+ } else {
604
+ acc.map((list) => list.push(result.value));
605
+ }
606
+ }
607
+ return acc;
608
+ };
609
+ var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
610
+ var combineResultListWithAllErrors = (resultList) => {
611
+ let acc = ok([]);
612
+ for (const result of resultList) {
613
+ if (result.isErr() && acc.isErr()) {
614
+ acc.error.push(result.error);
615
+ } else if (result.isErr() && acc.isOk()) {
616
+ acc = err([result.error]);
617
+ } else if (result.isOk() && acc.isOk()) {
618
+ acc.value.push(result.value);
619
+ }
620
+ }
621
+ return acc;
622
+ };
623
+ var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
624
+ var Result;
625
+ (function(Result2) {
626
+ function fromThrowable2(fn, errorFn) {
627
+ return (...args) => {
628
+ try {
629
+ const result = fn(...args);
630
+ return ok(result);
631
+ } catch (e) {
632
+ return err(errorFn ? errorFn(e) : e);
633
+ }
634
+ };
635
+ }
636
+ Result2.fromThrowable = fromThrowable2;
637
+ function combine(resultList) {
638
+ return combineResultList(resultList);
639
+ }
640
+ Result2.combine = combine;
641
+ function combineWithAllErrors(resultList) {
642
+ return combineResultListWithAllErrors(resultList);
643
+ }
644
+ Result2.combineWithAllErrors = combineWithAllErrors;
645
+ })(Result || (Result = {}));
646
+ var ok = (value) => new Ok(value);
647
+ function err(err2) {
648
+ return new Err(err2);
649
+ }
650
+ var Ok = class {
651
+ constructor(value) {
652
+ this.value = value;
653
+ }
654
+ isOk() {
655
+ return true;
656
+ }
657
+ isErr() {
658
+ return !this.isOk();
659
+ }
660
+ map(f) {
661
+ return ok(f(this.value));
662
+ }
663
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
664
+ mapErr(_f) {
665
+ return ok(this.value);
666
+ }
667
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
668
+ andThen(f) {
669
+ return f(this.value);
670
+ }
671
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
672
+ andThrough(f) {
673
+ return f(this.value).map((_value) => this.value);
674
+ }
675
+ andTee(f) {
676
+ try {
677
+ f(this.value);
678
+ } catch (e) {
679
+ }
680
+ return ok(this.value);
681
+ }
682
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
683
+ orElse(_f) {
684
+ return ok(this.value);
685
+ }
686
+ asyncAndThen(f) {
687
+ return f(this.value);
688
+ }
689
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
690
+ asyncAndThrough(f) {
691
+ return f(this.value).map(() => this.value);
692
+ }
693
+ asyncMap(f) {
694
+ return ResultAsync.fromSafePromise(f(this.value));
695
+ }
696
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
697
+ unwrapOr(_v) {
698
+ return this.value;
699
+ }
700
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
701
+ match(ok2, _err) {
702
+ return ok2(this.value);
703
+ }
704
+ safeUnwrap() {
705
+ const value = this.value;
706
+ return function* () {
707
+ return value;
708
+ }();
709
+ }
710
+ _unsafeUnwrap(_) {
711
+ return this.value;
712
+ }
713
+ _unsafeUnwrapErr(config) {
714
+ throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
715
+ }
716
+ };
717
+ var Err = class {
718
+ constructor(error) {
719
+ this.error = error;
720
+ }
721
+ isOk() {
722
+ return false;
723
+ }
724
+ isErr() {
725
+ return !this.isOk();
726
+ }
727
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
728
+ map(_f) {
729
+ return err(this.error);
730
+ }
731
+ mapErr(f) {
732
+ return err(f(this.error));
733
+ }
734
+ andThrough(_f) {
735
+ return err(this.error);
736
+ }
737
+ andTee(_f) {
738
+ return err(this.error);
739
+ }
740
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
741
+ andThen(_f) {
742
+ return err(this.error);
743
+ }
744
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
745
+ orElse(f) {
746
+ return f(this.error);
747
+ }
748
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
749
+ asyncAndThen(_f) {
750
+ return errAsync(this.error);
751
+ }
752
+ asyncAndThrough(_f) {
753
+ return errAsync(this.error);
754
+ }
755
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
756
+ asyncMap(_f) {
757
+ return errAsync(this.error);
758
+ }
759
+ unwrapOr(v) {
760
+ return v;
761
+ }
762
+ match(_ok, err2) {
763
+ return err2(this.error);
764
+ }
765
+ safeUnwrap() {
766
+ const error = this.error;
767
+ return function* () {
768
+ yield err(error);
769
+ throw new Error("Do not use this generator out of `safeTry`");
770
+ }();
771
+ }
772
+ _unsafeUnwrap(config) {
773
+ throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
774
+ }
775
+ _unsafeUnwrapErr(_) {
776
+ return this.error;
777
+ }
778
+ };
779
+ var fromThrowable = Result.fromThrowable;
780
+
781
+ // src/constants.ts
782
+ var WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId";
783
+ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
784
+ var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
785
+ var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
786
+ var WORKFLOW_PROTOCOL_VERSION = "1";
787
+ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
788
+ var DEFAULT_CONTENT_TYPE = "application/json";
789
+ var NO_CONCURRENCY = 1;
790
+ var DEFAULT_RETRIES = 3;
791
+
792
+ // src/types.ts
793
+ var StepTypes = [
794
+ "Initial",
795
+ "Run",
796
+ "SleepFor",
797
+ "SleepUntil",
798
+ "Call",
799
+ "Wait",
800
+ "Notify"
801
+ ];
802
+
803
+ // src/workflow-requests.ts
804
+ var triggerFirstInvocation = async (workflowContext, retries, debug) => {
805
+ const { headers } = getHeaders(
806
+ "true",
807
+ workflowContext.workflowRunId,
808
+ workflowContext.url,
809
+ workflowContext.headers,
810
+ void 0,
811
+ workflowContext.failureUrl,
812
+ retries
813
+ );
814
+ await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
815
+ headers,
816
+ requestPayload: workflowContext.requestPayload,
817
+ url: workflowContext.url
818
+ });
819
+ try {
820
+ await workflowContext.qstashClient.publishJSON({
821
+ headers,
822
+ method: "POST",
823
+ body: workflowContext.requestPayload,
824
+ url: workflowContext.url
825
+ });
826
+ return ok("success");
827
+ } catch (error) {
828
+ const error_ = error;
829
+ return err(error_);
830
+ }
831
+ };
832
+ var triggerRouteFunction = async ({
833
+ onCleanup,
834
+ onStep
835
+ }) => {
836
+ try {
837
+ await onStep();
838
+ await onCleanup();
839
+ return ok("workflow-finished");
840
+ } catch (error) {
841
+ const error_ = error;
842
+ return error_ instanceof QStashWorkflowAbort ? ok("step-finished") : err(error_);
843
+ }
844
+ };
845
+ var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
846
+ await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
847
+ deletedWorkflowRunId: workflowContext.workflowRunId
848
+ });
849
+ const result = await workflowContext.qstashClient.http.request({
850
+ path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
851
+ method: "DELETE",
852
+ parseResponseAsJson: false
853
+ });
854
+ await debug?.log("SUBMIT", "SUBMIT_CLEANUP", result);
855
+ };
856
+ var recreateUserHeaders = (headers) => {
857
+ const filteredHeaders = new Headers();
858
+ const pairs = headers.entries();
859
+ for (const [header, value] of pairs) {
860
+ const headerLowerCase = header.toLowerCase();
861
+ if (!headerLowerCase.startsWith("upstash-workflow-") && !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && headerLowerCase !== "cf-connecting-ip") {
862
+ filteredHeaders.append(header, value);
863
+ }
864
+ }
865
+ return filteredHeaders;
866
+ };
867
+ var handleThirdPartyCallResult = async (request, requestPayload, client, workflowUrl, failureUrl, retries, debug) => {
868
+ try {
869
+ if (request.headers.get("Upstash-Workflow-Callback")) {
870
+ const callbackMessage = JSON.parse(requestPayload);
871
+ if (!(callbackMessage.status >= 200 && callbackMessage.status < 300)) {
872
+ await debug?.log("WARN", "SUBMIT_THIRD_PARTY_RESULT", {
873
+ status: callbackMessage.status,
874
+ body: atob(callbackMessage.body)
875
+ });
876
+ console.warn(
877
+ `Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (if there are retries remaining). Error Message:
878
+ ${atob(callbackMessage.body)}`
879
+ );
880
+ return ok("call-will-retry");
881
+ }
882
+ const workflowRunId = request.headers.get(WORKFLOW_ID_HEADER);
883
+ const stepIdString = request.headers.get("Upstash-Workflow-StepId");
884
+ const stepName = request.headers.get("Upstash-Workflow-StepName");
885
+ const stepType = request.headers.get("Upstash-Workflow-StepType");
886
+ const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
887
+ const contentType = request.headers.get("Upstash-Workflow-ContentType");
888
+ if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
889
+ throw new Error(
890
+ `Missing info in callback message source header: ${JSON.stringify({
891
+ workflowRunId,
892
+ stepIdString,
893
+ stepName,
894
+ stepType,
895
+ concurrentString,
896
+ contentType
897
+ })}`
898
+ );
899
+ }
900
+ const userHeaders = recreateUserHeaders(request.headers);
901
+ const { headers: requestHeaders } = getHeaders(
902
+ "false",
903
+ workflowRunId,
904
+ workflowUrl,
905
+ userHeaders,
906
+ void 0,
907
+ failureUrl,
908
+ retries
909
+ );
910
+ const callResultStep = {
911
+ stepId: Number(stepIdString),
912
+ stepName,
913
+ stepType,
914
+ out: atob(callbackMessage.body),
915
+ concurrent: Number(concurrentString)
916
+ };
917
+ await debug?.log("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
918
+ step: callResultStep,
919
+ headers: requestHeaders,
920
+ url: workflowUrl
921
+ });
922
+ const result = await client.publishJSON({
923
+ headers: requestHeaders,
924
+ method: "POST",
925
+ body: callResultStep,
926
+ url: workflowUrl
927
+ });
928
+ await debug?.log("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
929
+ messageId: result.messageId
930
+ });
931
+ return ok("is-call-return");
932
+ } else {
933
+ return ok("continue-workflow");
934
+ }
935
+ } catch (error) {
936
+ const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
937
+ return err(
938
+ new QStashWorkflowError(
939
+ `Error when handling call return (isCallReturn=${isCallReturn}): ${error}`
940
+ )
941
+ );
942
+ }
943
+ };
944
+ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step, failureUrl, retries) => {
945
+ const baseHeaders = {
946
+ [WORKFLOW_INIT_HEADER]: initHeaderValue,
947
+ [WORKFLOW_ID_HEADER]: workflowRunId,
948
+ [WORKFLOW_URL_HEADER]: workflowUrl,
949
+ [`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`]: WORKFLOW_PROTOCOL_VERSION,
950
+ ...failureUrl ? {
951
+ [`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`]: "true",
952
+ "Upstash-Failure-Callback": failureUrl
953
+ } : {},
954
+ ...retries === void 0 ? {} : {
955
+ "Upstash-Retries": retries.toString()
956
+ }
957
+ };
958
+ if (userHeaders) {
959
+ for (const header of userHeaders.keys()) {
960
+ if (step?.callHeaders) {
961
+ baseHeaders[`Upstash-Callback-Forward-${header}`] = userHeaders.get(header);
962
+ } else {
963
+ baseHeaders[`Upstash-Forward-${header}`] = userHeaders.get(header);
964
+ }
965
+ }
966
+ }
967
+ const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
968
+ if (step?.callHeaders) {
969
+ const forwardedHeaders = Object.fromEntries(
970
+ Object.entries(step.callHeaders).map(([header, value]) => [
971
+ `Upstash-Forward-${header}`,
972
+ value
973
+ ])
974
+ );
975
+ return {
976
+ headers: {
977
+ ...baseHeaders,
978
+ ...forwardedHeaders,
979
+ "Upstash-Callback": workflowUrl,
980
+ "Upstash-Callback-Workflow-RunId": workflowRunId,
981
+ "Upstash-Callback-Workflow-CallType": "fromCallback",
982
+ "Upstash-Callback-Workflow-Init": "false",
983
+ "Upstash-Callback-Workflow-Url": workflowUrl,
984
+ "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
985
+ "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
986
+ "Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
987
+ "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
988
+ "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
989
+ "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
990
+ "Upstash-Workflow-CallType": "toCallback"
991
+ }
992
+ };
993
+ }
994
+ if (step?.waitEventId) {
995
+ return {
996
+ headers: {
997
+ ...baseHeaders,
998
+ "Upstash-Workflow-CallType": "step"
999
+ },
1000
+ timeoutHeaders: {
1001
+ // to include user headers:
1002
+ ...Object.fromEntries(
1003
+ Object.entries(baseHeaders).map(([header, value]) => [header, [value]])
1004
+ ),
1005
+ // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1006
+ "Upstash-Workflow-Runid": [workflowRunId],
1007
+ [WORKFLOW_INIT_HEADER]: ["false"],
1008
+ [WORKFLOW_URL_HEADER]: [workflowUrl],
1009
+ "Upstash-Workflow-CallType": ["step"],
1010
+ "Content-Type": [contentType]
1011
+ }
1012
+ };
1013
+ }
1014
+ return { headers: baseHeaders };
1015
+ };
1016
+ var verifyRequest = async (body, signature, verifier) => {
1017
+ if (!verifier) {
1018
+ return;
1019
+ }
1020
+ try {
1021
+ if (!signature) {
1022
+ throw new Error("`Upstash-Signature` header is not passed.");
1023
+ }
1024
+ const isValid = await verifier.verify({
1025
+ body,
1026
+ signature
1027
+ });
1028
+ if (!isValid) {
1029
+ throw new Error("Signature in `Upstash-Signature` header is not valid");
1030
+ }
1031
+ } catch (error) {
1032
+ throw new QStashWorkflowError(
1033
+ `Failed to verify that the Workflow request comes from QStash: ${error}
1034
+
1035
+ If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
1036
+
1037
+ If you want to disable QStash Verification, you should clear env variables QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY`
1038
+ );
1039
+ }
1040
+ };
1041
+
1042
+ // src/context/auto-executor.ts
1043
+ var AutoExecutor = class _AutoExecutor {
1044
+ context;
1045
+ promises = /* @__PURE__ */ new WeakMap();
1046
+ activeLazyStepList;
1047
+ debug;
1048
+ nonPlanStepCount;
1049
+ steps;
1050
+ indexInCurrentList = 0;
1051
+ stepCount = 0;
1052
+ planStepCount = 0;
1053
+ executingStep = false;
1054
+ constructor(context, steps, debug) {
1055
+ this.context = context;
1056
+ this.debug = debug;
1057
+ this.steps = steps;
1058
+ this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
1059
+ }
1060
+ /**
1061
+ * Adds the step function to the list of step functions to run in
1062
+ * parallel. After adding the function, defers the execution, so
1063
+ * that if there is another step function to be added, it's also
1064
+ * added.
1065
+ *
1066
+ * After all functions are added, list of functions are executed.
1067
+ * If there is a single function, it's executed by itself. If there
1068
+ * are multiple, they are run in parallel.
1069
+ *
1070
+ * If a function is already executing (this.executingStep), this
1071
+ * means that there is a nested step which is not allowed. In this
1072
+ * case, addStep throws QStashWorkflowError.
1073
+ *
1074
+ * @param stepInfo step plan to add
1075
+ * @returns result of the step function
1076
+ */
1077
+ async addStep(stepInfo) {
1078
+ if (this.executingStep) {
1079
+ throw new QStashWorkflowError(
1080
+ `A step can not be run inside another step. Tried to run '${stepInfo.stepName}' inside '${this.executingStep}'`
1081
+ );
1082
+ }
1083
+ this.stepCount += 1;
1084
+ const lazyStepList = this.activeLazyStepList ?? [];
1085
+ if (!this.activeLazyStepList) {
1086
+ this.activeLazyStepList = lazyStepList;
1087
+ this.indexInCurrentList = 0;
1088
+ }
1089
+ lazyStepList.push(stepInfo);
1090
+ const index = this.indexInCurrentList++;
1091
+ const requestComplete = this.deferExecution().then(async () => {
1092
+ if (!this.promises.has(lazyStepList)) {
1093
+ const promise2 = this.getExecutionPromise(lazyStepList);
1094
+ this.promises.set(lazyStepList, promise2);
1095
+ this.activeLazyStepList = void 0;
1096
+ this.planStepCount += lazyStepList.length > 1 ? lazyStepList.length : 0;
1097
+ }
1098
+ const promise = this.promises.get(lazyStepList);
1099
+ return promise;
1100
+ });
1101
+ const result = await requestComplete;
1102
+ return _AutoExecutor.getResult(lazyStepList, result, index);
1103
+ }
1104
+ /**
1105
+ * Wraps a step function to set this.executingStep to step name
1106
+ * before running and set this.executingStep to False after execution
1107
+ * ends.
1108
+ *
1109
+ * this.executingStep allows us to detect nested steps which are not
1110
+ * allowed.
1111
+ *
1112
+ * @param stepName name of the step being wrapped
1113
+ * @param stepFunction step function to wrap
1114
+ * @returns wrapped step function
1115
+ */
1116
+ wrapStep(stepName, stepFunction) {
1117
+ this.executingStep = stepName;
1118
+ const result = stepFunction();
1119
+ this.executingStep = false;
1120
+ return result;
1121
+ }
1122
+ /**
1123
+ * Executes a step:
1124
+ * - If the step result is available in the steps, returns the result
1125
+ * - If the result is not avaiable, runs the function
1126
+ * - Sends the result to QStash
1127
+ *
1128
+ * @param lazyStep lazy step to execute
1129
+ * @returns step result
1130
+ */
1131
+ async runSingle(lazyStep) {
1132
+ if (this.stepCount < this.nonPlanStepCount) {
1133
+ const step = this.steps[this.stepCount + this.planStepCount];
1134
+ validateStep(lazyStep, step);
1135
+ await this.debug?.log("INFO", "RUN_SINGLE", {
1136
+ fromRequest: true,
1137
+ step,
1138
+ stepCount: this.stepCount
1139
+ });
1140
+ return step.out;
1141
+ }
1142
+ const resultStep = await lazyStep.getResultStep(NO_CONCURRENCY, this.stepCount);
1143
+ await this.debug?.log("INFO", "RUN_SINGLE", {
1144
+ fromRequest: false,
1145
+ step: resultStep,
1146
+ stepCount: this.stepCount
1147
+ });
1148
+ await this.submitStepsToQStash([resultStep]);
1149
+ return resultStep.out;
1150
+ }
1151
+ /**
1152
+ * Runs steps in parallel.
1153
+ *
1154
+ * @param stepName parallel step name
1155
+ * @param stepFunctions list of async functions to run in parallel
1156
+ * @returns results of the functions run in parallel
1157
+ */
1158
+ async runParallel(parallelSteps) {
1159
+ const initialStepCount = this.stepCount - (parallelSteps.length - 1);
1160
+ const parallelCallState = this.getParallelCallState(parallelSteps.length, initialStepCount);
1161
+ const sortedSteps = sortSteps(this.steps);
1162
+ const plannedParallelStepCount = sortedSteps[initialStepCount + this.planStepCount]?.concurrent;
1163
+ if (parallelCallState !== "first" && plannedParallelStepCount !== parallelSteps.length) {
1164
+ throw new QStashWorkflowError(
1165
+ `Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
1166
+ );
1167
+ }
1168
+ await this.debug?.log("INFO", "RUN_PARALLEL", {
1169
+ parallelCallState,
1170
+ initialStepCount,
1171
+ plannedParallelStepCount,
1172
+ stepCount: this.stepCount,
1173
+ planStepCount: this.planStepCount
1174
+ });
1175
+ switch (parallelCallState) {
1176
+ case "first": {
1177
+ const planSteps = parallelSteps.map(
1178
+ (parallelStep, index) => parallelStep.getPlanStep(parallelSteps.length, initialStepCount + index)
1179
+ );
1180
+ await this.submitStepsToQStash(planSteps);
1181
+ break;
1182
+ }
1183
+ case "partial": {
1184
+ const planStep = this.steps.at(-1);
1185
+ if (!planStep || planStep.targetStep === void 0) {
1186
+ throw new QStashWorkflowError(
1187
+ `There must be a last step and it should have targetStep larger than 0.Received: ${JSON.stringify(planStep)}`
1188
+ );
1189
+ }
1190
+ const stepIndex = planStep.targetStep - initialStepCount;
1191
+ validateStep(parallelSteps[stepIndex], planStep);
1192
+ try {
1193
+ const resultStep = await parallelSteps[stepIndex].getResultStep(
1194
+ parallelSteps.length,
1195
+ planStep.targetStep
1196
+ );
1197
+ await this.submitStepsToQStash([resultStep]);
1198
+ } catch (error) {
1199
+ if (error instanceof QStashWorkflowAbort) {
1200
+ throw error;
1201
+ }
1202
+ throw new QStashWorkflowError(
1203
+ `Error submitting steps to QStash in partial parallel step execution: ${error}`
1204
+ );
1205
+ }
1206
+ break;
1207
+ }
1208
+ case "discard": {
1209
+ throw new QStashWorkflowAbort("discarded parallel");
1210
+ }
1211
+ case "last": {
1212
+ const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
1213
+ validateParallelSteps(parallelSteps, parallelResultSteps);
1214
+ return parallelResultSteps.map((step) => step.out);
1215
+ }
1216
+ }
1217
+ const fillValue = void 0;
1218
+ return Array.from({ length: parallelSteps.length }).fill(fillValue);
1219
+ }
1220
+ /**
1221
+ * Determines the parallel call state
1222
+ *
1223
+ * First filters the steps to get the steps which are after `initialStepCount` parameter.
1224
+ *
1225
+ * Depending on the remaining steps, decides the parallel state:
1226
+ * - "first": If there are no steps
1227
+ * - "last" If there are equal to or more than `2 * parallelStepCount`. We multiply by two
1228
+ * because each step in a parallel execution will have 2 steps: a plan step and a result
1229
+ * step.
1230
+ * - "partial": If the last step is a plan step
1231
+ * - "discard": If the last step is not a plan step. This means that the parallel execution
1232
+ * is in progress (there are still steps to run) and one step has finished and submitted
1233
+ * its result to QStash
1234
+ *
1235
+ * @param parallelStepCount number of steps to run in parallel
1236
+ * @param initialStepCount steps after the parallel invocation
1237
+ * @returns parallel call state
1238
+ */
1239
+ getParallelCallState(parallelStepCount, initialStepCount) {
1240
+ const remainingSteps = this.steps.filter(
1241
+ (step) => (step.targetStep || step.stepId) >= initialStepCount
1242
+ );
1243
+ if (remainingSteps.length === 0) {
1244
+ return "first";
1245
+ } else if (remainingSteps.length >= 2 * parallelStepCount) {
1246
+ return "last";
1247
+ } else if (remainingSteps.at(-1)?.targetStep) {
1248
+ return "partial";
1249
+ } else {
1250
+ return "discard";
1251
+ }
1252
+ }
1253
+ /**
1254
+ * sends the steps to QStash as batch
1255
+ *
1256
+ * @param steps steps to send
1257
+ */
1258
+ async submitStepsToQStash(steps) {
1259
+ if (steps.length === 0) {
1260
+ throw new QStashWorkflowError(
1261
+ `Unable to submit steps to QStash. Provided list is empty. Current step: ${this.stepCount}`
1262
+ );
1263
+ }
1264
+ await this.debug?.log("SUBMIT", "SUBMIT_STEP", {
1265
+ length: steps.length,
1266
+ steps
1267
+ });
1268
+ if (steps[0].waitEventId && steps.length === 1) {
1269
+ const waitStep = steps[0];
1270
+ const { headers, timeoutHeaders } = getHeaders(
1271
+ "false",
1272
+ this.context.workflowRunId,
1273
+ this.context.url,
1274
+ this.context.headers,
1275
+ waitStep,
1276
+ this.context.failureUrl,
1277
+ this.context.retries
1278
+ );
1279
+ const waitBody = {
1280
+ url: this.context.url,
1281
+ timeout: waitStep.timeout,
1282
+ timeoutBody: void 0,
1283
+ timeoutUrl: this.context.url,
1284
+ timeoutHeaders,
1285
+ step: {
1286
+ stepId: waitStep.stepId,
1287
+ stepType: "Wait",
1288
+ stepName: waitStep.stepName,
1289
+ concurrent: waitStep.concurrent,
1290
+ targetStep: waitStep.targetStep
1291
+ }
1292
+ };
1293
+ await this.context.qstashClient.http.request({
1294
+ path: ["v2", "wait", waitStep.waitEventId],
1295
+ body: JSON.stringify(waitBody),
1296
+ headers,
1297
+ method: "POST"
1298
+ });
1299
+ throw new QStashWorkflowAbort(steps[0].stepName, steps[0]);
1300
+ }
1301
+ const result = await this.context.qstashClient.batchJSON(
1302
+ steps.map((singleStep) => {
1303
+ const { headers } = getHeaders(
1304
+ "false",
1305
+ this.context.workflowRunId,
1306
+ this.context.url,
1307
+ this.context.headers,
1308
+ singleStep,
1309
+ this.context.failureUrl,
1310
+ this.context.retries
1311
+ );
1312
+ const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1313
+ return singleStep.callUrl ? (
1314
+ // if the step is a third party call, we call the third party
1315
+ // url (singleStep.callUrl) and pass information about the workflow
1316
+ // in the headers (handled in getHeaders). QStash makes the request
1317
+ // to callUrl and returns the result to Workflow endpoint.
1318
+ // handleThirdPartyCallResult method sends the result of the third
1319
+ // party call to QStash.
1320
+ {
1321
+ headers,
1322
+ method: singleStep.callMethod,
1323
+ body: singleStep.callBody,
1324
+ url: singleStep.callUrl
1325
+ }
1326
+ ) : (
1327
+ // if the step is not a third party call, we use workflow
1328
+ // endpoint (context.url) as URL when calling QStash. QStash
1329
+ // calls us back with the updated steps list.
1330
+ {
1331
+ headers,
1332
+ method: "POST",
1333
+ body: singleStep,
1334
+ url: this.context.url,
1335
+ notBefore: willWait ? singleStep.sleepUntil : void 0,
1336
+ delay: willWait ? singleStep.sleepFor : void 0
1337
+ }
1338
+ );
1339
+ })
1340
+ );
1341
+ await this.debug?.log("INFO", "SUBMIT_STEP", {
1342
+ messageIds: result.map((message) => {
1343
+ return {
1344
+ message: message.messageId
1345
+ };
1346
+ })
1347
+ });
1348
+ throw new QStashWorkflowAbort(steps[0].stepName, steps[0]);
1349
+ }
1350
+ /**
1351
+ * Get the promise by executing the lazt steps list. If there is a single
1352
+ * step, we call `runSingle`. Otherwise `runParallel` is called.
1353
+ *
1354
+ * @param lazyStepList steps list to execute
1355
+ * @returns promise corresponding to the execution
1356
+ */
1357
+ getExecutionPromise(lazyStepList) {
1358
+ return lazyStepList.length === 1 ? this.runSingle(lazyStepList[0]) : this.runParallel(lazyStepList);
1359
+ }
1360
+ /**
1361
+ * @param lazyStepList steps we executed
1362
+ * @param result result of the promise from `getExecutionPromise`
1363
+ * @param index index of the current step
1364
+ * @returns result[index] if lazyStepList > 1, otherwise result
1365
+ */
1366
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
1367
+ static getResult(lazyStepList, result, index) {
1368
+ if (lazyStepList.length === 1) {
1369
+ return result;
1370
+ } else if (Array.isArray(result) && lazyStepList.length === result.length && index < lazyStepList.length) {
1371
+ return result[index];
1372
+ } else {
1373
+ throw new QStashWorkflowError(
1374
+ `Unexpected parallel call result while executing step ${index}: '${result}'. Expected ${lazyStepList.length} many items`
1375
+ );
1376
+ }
1377
+ }
1378
+ async deferExecution() {
1379
+ await Promise.resolve();
1380
+ await Promise.resolve();
1381
+ }
1382
+ };
1383
+ var validateStep = (lazyStep, stepFromRequest) => {
1384
+ if (lazyStep.stepName !== stepFromRequest.stepName) {
1385
+ throw new QStashWorkflowError(
1386
+ `Incompatible step name. Expected '${lazyStep.stepName}', got '${stepFromRequest.stepName}' from the request`
1387
+ );
1388
+ }
1389
+ if (lazyStep.stepType !== stepFromRequest.stepType) {
1390
+ throw new QStashWorkflowError(
1391
+ `Incompatible step type. Expected '${lazyStep.stepType}', got '${stepFromRequest.stepType}' from the request`
1392
+ );
1393
+ }
1394
+ };
1395
+ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
1396
+ try {
1397
+ for (const [index, stepFromRequest] of stepsFromRequest.entries()) {
1398
+ validateStep(lazySteps[index], stepFromRequest);
1399
+ }
1400
+ } catch (error) {
1401
+ if (error instanceof QStashWorkflowError) {
1402
+ const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
1403
+ const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
1404
+ const requestStepNames = stepsFromRequest.map((step) => step.stepName);
1405
+ const requestStepTypes = stepsFromRequest.map((step) => step.stepType);
1406
+ throw new QStashWorkflowError(
1407
+ `Incompatible steps detected in parallel execution: ${error.message}
1408
+ > Step Names from the request: ${JSON.stringify(requestStepNames)}
1409
+ Step Types from the request: ${JSON.stringify(requestStepTypes)}
1410
+ > Step Names expected: ${JSON.stringify(lazyStepNames)}
1411
+ Step Types expected: ${JSON.stringify(lazyStepTypes)}`
1412
+ );
1413
+ }
1414
+ throw error;
1415
+ }
1416
+ };
1417
+ var sortSteps = (steps) => {
1418
+ const getStepId = (step) => step.targetStep || step.stepId;
1419
+ return steps.toSorted((step, stepOther) => getStepId(step) - getStepId(stepOther));
1420
+ };
1421
+
1422
+ // src/context/steps.ts
1423
+ var BaseLazyStep = class {
1424
+ stepName;
1425
+ // will be set in the subclasses
1426
+ constructor(stepName) {
1427
+ this.stepName = stepName;
1428
+ }
1429
+ };
1430
+ var LazyFunctionStep = class extends BaseLazyStep {
1431
+ stepFunction;
1432
+ stepType = "Run";
1433
+ constructor(stepName, stepFunction) {
1434
+ super(stepName);
1435
+ this.stepFunction = stepFunction;
1436
+ }
1437
+ getPlanStep(concurrent, targetStep) {
1438
+ return {
1439
+ stepId: 0,
1440
+ stepName: this.stepName,
1441
+ stepType: this.stepType,
1442
+ concurrent,
1443
+ targetStep
1444
+ };
1445
+ }
1446
+ async getResultStep(concurrent, stepId) {
1447
+ let result = this.stepFunction();
1448
+ if (result instanceof Promise) {
1449
+ result = await result;
1450
+ }
1451
+ return {
1452
+ stepId,
1453
+ stepName: this.stepName,
1454
+ stepType: this.stepType,
1455
+ out: result,
1456
+ concurrent
1457
+ };
1458
+ }
1459
+ };
1460
+ var LazySleepStep = class extends BaseLazyStep {
1461
+ sleep;
1462
+ stepType = "SleepFor";
1463
+ constructor(stepName, sleep) {
1464
+ super(stepName);
1465
+ this.sleep = sleep;
1466
+ }
1467
+ getPlanStep(concurrent, targetStep) {
1468
+ return {
1469
+ stepId: 0,
1470
+ stepName: this.stepName,
1471
+ stepType: this.stepType,
1472
+ sleepFor: this.sleep,
1473
+ concurrent,
1474
+ targetStep
1475
+ };
1476
+ }
1477
+ async getResultStep(concurrent, stepId) {
1478
+ return await Promise.resolve({
1479
+ stepId,
1480
+ stepName: this.stepName,
1481
+ stepType: this.stepType,
1482
+ sleepFor: this.sleep,
1483
+ concurrent
1484
+ });
1485
+ }
1486
+ };
1487
+ var LazySleepUntilStep = class extends BaseLazyStep {
1488
+ sleepUntil;
1489
+ stepType = "SleepUntil";
1490
+ constructor(stepName, sleepUntil) {
1491
+ super(stepName);
1492
+ this.sleepUntil = sleepUntil;
1493
+ }
1494
+ getPlanStep(concurrent, targetStep) {
1495
+ return {
1496
+ stepId: 0,
1497
+ stepName: this.stepName,
1498
+ stepType: this.stepType,
1499
+ sleepUntil: this.sleepUntil,
1500
+ concurrent,
1501
+ targetStep
1502
+ };
1503
+ }
1504
+ async getResultStep(concurrent, stepId) {
1505
+ return await Promise.resolve({
1506
+ stepId,
1507
+ stepName: this.stepName,
1508
+ stepType: this.stepType,
1509
+ sleepUntil: this.sleepUntil,
1510
+ concurrent
1511
+ });
1512
+ }
1513
+ };
1514
+ var LazyCallStep = class extends BaseLazyStep {
1515
+ url;
1516
+ method;
1517
+ body;
1518
+ headers;
1519
+ stepType = "Call";
1520
+ constructor(stepName, url, method, body, headers) {
1521
+ super(stepName);
1522
+ this.url = url;
1523
+ this.method = method;
1524
+ this.body = body;
1525
+ this.headers = headers;
1526
+ }
1527
+ getPlanStep(concurrent, targetStep) {
1528
+ return {
1529
+ stepId: 0,
1530
+ stepName: this.stepName,
1531
+ stepType: this.stepType,
1532
+ concurrent,
1533
+ targetStep
1534
+ };
1535
+ }
1536
+ async getResultStep(concurrent, stepId) {
1537
+ return await Promise.resolve({
1538
+ stepId,
1539
+ stepName: this.stepName,
1540
+ stepType: this.stepType,
1541
+ concurrent,
1542
+ callUrl: this.url,
1543
+ callMethod: this.method,
1544
+ callBody: this.body,
1545
+ callHeaders: this.headers
1546
+ });
1547
+ }
1548
+ };
1549
+ var LazyWaitForEventStep = class extends BaseLazyStep {
1550
+ eventId;
1551
+ timeout;
1552
+ stepType = "Wait";
1553
+ constructor(stepName, eventId, timeout) {
1554
+ super(stepName);
1555
+ this.eventId = eventId;
1556
+ this.timeout = timeout;
1557
+ }
1558
+ getPlanStep(concurrent, targetStep) {
1559
+ return {
1560
+ stepId: 0,
1561
+ stepName: this.stepName,
1562
+ stepType: this.stepType,
1563
+ waitEventId: this.eventId,
1564
+ timeout: this.timeout,
1565
+ concurrent,
1566
+ targetStep
1567
+ };
1568
+ }
1569
+ async getResultStep(concurrent, stepId) {
1570
+ return await Promise.resolve({
1571
+ stepId,
1572
+ stepName: this.stepName,
1573
+ stepType: this.stepType,
1574
+ waitEventId: this.eventId,
1575
+ timeout: this.timeout,
1576
+ concurrent
1577
+ });
1578
+ }
1579
+ };
1580
+
1581
+ // src/context/context.ts
1582
+ var WorkflowContext = class {
1583
+ executor;
1584
+ steps;
1585
+ /**
1586
+ * QStash client of the workflow
1587
+ *
1588
+ * Can be overwritten by passing `qstashClient` parameter in `serve`:
1589
+ *
1590
+ * ```ts
1591
+ * import { Client } from "@upstash/qstash"
1592
+ *
1593
+ * export const POST = serve(
1594
+ * async (context) => {
1595
+ * ...
1596
+ * },
1597
+ * {
1598
+ * qstashClient: new Client({...})
1599
+ * }
1600
+ * )
1601
+ * ```
1602
+ */
1603
+ qstashClient;
1604
+ /**
1605
+ * Run id of the workflow
1606
+ */
1607
+ workflowRunId;
1608
+ /**
1609
+ * URL of the workflow
1610
+ *
1611
+ * Can be overwritten by passing a `url` parameter in `serve`:
1612
+ *
1613
+ * ```ts
1614
+ * export const POST = serve(
1615
+ * async (context) => {
1616
+ * ...
1617
+ * },
1618
+ * {
1619
+ * url: "new-url-value"
1620
+ * }
1621
+ * )
1622
+ * ```
1623
+ */
1624
+ url;
1625
+ /**
1626
+ * URL to call in case of workflow failure with QStash failure callback
1627
+ *
1628
+ * https://upstash.com/docs/qstash/features/callbacks#what-is-a-failure-callback
1629
+ *
1630
+ * Can be overwritten by passing a `failureUrl` parameter in `serve`:
1631
+ *
1632
+ * ```ts
1633
+ * export const POST = serve(
1634
+ * async (context) => {
1635
+ * ...
1636
+ * },
1637
+ * {
1638
+ * failureUrl: "new-url-value"
1639
+ * }
1640
+ * )
1641
+ * ```
1642
+ */
1643
+ failureUrl;
1644
+ /**
1645
+ * Payload of the request which started the workflow.
1646
+ *
1647
+ * To specify its type, you can define `serve` as follows:
1648
+ *
1649
+ * ```ts
1650
+ * // set requestPayload type to MyPayload:
1651
+ * export const POST = serve<MyPayload>(
1652
+ * async (context) => {
1653
+ * ...
1654
+ * }
1655
+ * )
1656
+ * ```
1657
+ *
1658
+ * By default, `serve` tries to apply `JSON.parse` to the request payload.
1659
+ * If your payload is encoded in a format other than JSON, you can utilize
1660
+ * the `initialPayloadParser` parameter:
1661
+ *
1662
+ * ```ts
1663
+ * export const POST = serve<MyPayload>(
1664
+ * async (context) => {
1665
+ * ...
1666
+ * },
1667
+ * {
1668
+ * initialPayloadParser: (initialPayload) => {return doSomething(initialPayload)}
1669
+ * }
1670
+ * )
1671
+ * ```
1672
+ */
1673
+ requestPayload;
1674
+ /**
1675
+ * headers of the initial request
1676
+ */
1677
+ headers;
1678
+ /**
1679
+ * initial payload as a raw string
1680
+ */
1681
+ rawInitialPayload;
1682
+ /**
1683
+ * Map of environment variables and their values.
1684
+ *
1685
+ * Can be set using the `env` option of serve:
1686
+ *
1687
+ * ```ts
1688
+ * export const POST = serve<MyPayload>(
1689
+ * async (context) => {
1690
+ * const key = context.env["API_KEY"];
1691
+ * },
1692
+ * {
1693
+ * env: {
1694
+ * "API_KEY": "*****";
1695
+ * }
1696
+ * }
1697
+ * )
1698
+ * ```
1699
+ *
1700
+ * Default value is set to `process.env`.
1701
+ */
1702
+ env;
1703
+ /**
1704
+ * Number of retries
1705
+ */
1706
+ retries;
1707
+ constructor({
1708
+ qstashClient,
1709
+ workflowRunId,
1710
+ headers,
1711
+ steps,
1712
+ url,
1713
+ failureUrl,
1714
+ debug,
1715
+ initialPayload,
1716
+ rawInitialPayload,
1717
+ env,
1718
+ retries
1719
+ }) {
1720
+ this.qstashClient = qstashClient;
1721
+ this.workflowRunId = workflowRunId;
1722
+ this.steps = steps;
1723
+ this.url = url;
1724
+ this.failureUrl = failureUrl;
1725
+ this.headers = headers;
1726
+ this.requestPayload = initialPayload;
1727
+ this.rawInitialPayload = rawInitialPayload ?? JSON.stringify(this.requestPayload);
1728
+ this.env = env ?? {};
1729
+ this.retries = retries ?? DEFAULT_RETRIES;
1730
+ this.executor = new AutoExecutor(this, this.steps, debug);
1731
+ }
1732
+ /**
1733
+ * Executes a workflow step
1734
+ *
1735
+ * ```typescript
1736
+ * const result = await context.run("step 1", () => {
1737
+ * return "result"
1738
+ * })
1739
+ * ```
1740
+ *
1741
+ * Can also be called in parallel and the steps will be executed
1742
+ * simulatenously:
1743
+ *
1744
+ * ```typescript
1745
+ * const [result1, result2] = await Promise.all([
1746
+ * context.run("step 1", () => {
1747
+ * return "result1"
1748
+ * })
1749
+ * context.run("step 2", async () => {
1750
+ * return await fetchResults()
1751
+ * })
1752
+ * ])
1753
+ * ```
1754
+ *
1755
+ * @param stepName name of the step
1756
+ * @param stepFunction step function to be executed
1757
+ * @returns result of the step function
1758
+ */
1759
+ async run(stepName, stepFunction) {
1760
+ const wrappedStepFunction = () => this.executor.wrapStep(stepName, stepFunction);
1761
+ return this.addStep(new LazyFunctionStep(stepName, wrappedStepFunction));
1762
+ }
1763
+ /**
1764
+ * Stops the execution for the duration provided.
1765
+ *
1766
+ * @param stepName
1767
+ * @param duration sleep duration in seconds
1768
+ * @returns undefined
1769
+ */
1770
+ async sleep(stepName, duration) {
1771
+ await this.addStep(new LazySleepStep(stepName, duration));
1772
+ }
1773
+ /**
1774
+ * Stops the execution until the date time provided.
1775
+ *
1776
+ * @param stepName
1777
+ * @param datetime time to sleep until. Can be provided as a number (in unix seconds),
1778
+ * as a Date object or a string (passed to `new Date(datetimeString)`)
1779
+ * @returns undefined
1780
+ */
1781
+ async sleepUntil(stepName, datetime) {
1782
+ let time;
1783
+ if (typeof datetime === "number") {
1784
+ time = datetime;
1785
+ } else {
1786
+ datetime = typeof datetime === "string" ? new Date(datetime) : datetime;
1787
+ time = Math.round(datetime.getTime() / 1e3);
1788
+ }
1789
+ await this.addStep(new LazySleepUntilStep(stepName, time));
1790
+ }
1791
+ /**
1792
+ * Makes a third party call through QStash in order to make a
1793
+ * network call without consuming any runtime.
1794
+ *
1795
+ * ```ts
1796
+ * const postResult = await context.call<string>(
1797
+ * "post call step",
1798
+ * `https://www.some-endpoint.com/api`,
1799
+ * "POST",
1800
+ * "my-payload"
1801
+ * );
1802
+ * ```
1803
+ *
1804
+ * tries to parse the result of the request as JSON. If it's
1805
+ * not a JSON which can be parsed, simply returns the response
1806
+ * body as it is.
1807
+ *
1808
+ * @param stepName
1809
+ * @param url url to call
1810
+ * @param method call method
1811
+ * @param body call body
1812
+ * @param headers call headers
1813
+ * @returns call result (parsed as JSON if possible)
1814
+ */
1815
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
1816
+ async call(stepName, url, method, body, headers) {
1817
+ const result = await this.addStep(
1818
+ new LazyCallStep(stepName, url, method, body, headers ?? {})
1819
+ );
1820
+ try {
1821
+ return JSON.parse(result);
1822
+ } catch {
1823
+ return result;
1824
+ }
1825
+ }
1826
+ async waitForEvent(stepName, eventId, timeout) {
1827
+ const result = await this.addStep(
1828
+ new LazyWaitForEventStep(
1829
+ stepName,
1830
+ eventId,
1831
+ typeof timeout === "string" ? timeout : `${timeout}s`
1832
+ )
1833
+ );
1834
+ return result;
1835
+ }
1836
+ /**
1837
+ * Adds steps to the executor. Needed so that it can be overwritten in
1838
+ * DisabledWorkflowContext.
1839
+ */
1840
+ async addStep(step) {
1841
+ return await this.executor.addStep(step);
1842
+ }
1843
+ };
1844
+
1845
+ // src/logger.ts
1846
+ var LOG_LEVELS = ["DEBUG", "INFO", "SUBMIT", "WARN", "ERROR"];
1847
+ var WorkflowLogger = class _WorkflowLogger {
1848
+ logs = [];
1849
+ options;
1850
+ workflowRunId = void 0;
1851
+ constructor(options) {
1852
+ this.options = options;
1853
+ }
1854
+ async log(level, eventType, details) {
1855
+ if (this.shouldLog(level)) {
1856
+ const timestamp = Date.now();
1857
+ const logEntry = {
1858
+ timestamp,
1859
+ workflowRunId: this.workflowRunId ?? "",
1860
+ logLevel: level,
1861
+ eventType,
1862
+ details
1863
+ };
1864
+ this.logs.push(logEntry);
1865
+ if (this.options.logOutput === "console") {
1866
+ this.writeToConsole(logEntry);
1867
+ }
1868
+ await new Promise((resolve) => setTimeout(resolve, 100));
1869
+ }
1870
+ }
1871
+ setWorkflowRunId(workflowRunId) {
1872
+ this.workflowRunId = workflowRunId;
1873
+ }
1874
+ writeToConsole(logEntry) {
1875
+ const JSON_SPACING = 2;
1876
+ console.log(JSON.stringify(logEntry, void 0, JSON_SPACING));
1877
+ }
1878
+ shouldLog(level) {
1879
+ return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
1880
+ }
1881
+ static getLogger(verbose) {
1882
+ if (typeof verbose === "object") {
1883
+ return verbose;
1884
+ } else {
1885
+ return verbose ? new _WorkflowLogger({
1886
+ logLevel: "INFO",
1887
+ logOutput: "console"
1888
+ }) : void 0;
1889
+ }
1890
+ }
1891
+ };
1892
+
1893
+ // src/utils.ts
1894
+ var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
1895
+ var NANOID_LENGTH = 21;
1896
+ function nanoid() {
1897
+ return [...crypto.getRandomValues(new Uint8Array(NANOID_LENGTH))].map((x) => NANOID_CHARS[x % NANOID_CHARS.length]).join("");
1898
+ }
1899
+ function decodeBase64(base64) {
1900
+ try {
1901
+ const binString = atob(base64);
1902
+ const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
1903
+ return new TextDecoder().decode(intArray);
1904
+ } catch (error) {
1905
+ console.warn(
1906
+ `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
1907
+ );
1908
+ return atob(base64);
1909
+ }
1910
+ }
1911
+
1912
+ // src/workflow-parser.ts
1913
+ var getPayload = async (request) => {
1914
+ try {
1915
+ return await request.text();
1916
+ } catch {
1917
+ return;
1918
+ }
1919
+ };
1920
+ var parsePayload = (rawPayload) => {
1921
+ const [encodedInitialPayload, ...encodedSteps] = JSON.parse(rawPayload);
1922
+ const rawInitialPayload = decodeBase64(encodedInitialPayload.body);
1923
+ const initialStep = {
1924
+ stepId: 0,
1925
+ stepName: "init",
1926
+ stepType: "Initial",
1927
+ out: rawInitialPayload,
1928
+ concurrent: NO_CONCURRENCY
1929
+ };
1930
+ const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
1931
+ const otherSteps = stepsToDecode.map((rawStep) => {
1932
+ const step = JSON.parse(decodeBase64(rawStep.body));
1933
+ if (step.waitEventId) {
1934
+ const newOut = {
1935
+ notifyBody: step.out,
1936
+ timeout: step.waitTimeout ?? false
1937
+ };
1938
+ step.out = newOut;
1939
+ }
1940
+ return step;
1941
+ });
1942
+ const steps = [initialStep, ...otherSteps];
1943
+ return {
1944
+ rawInitialPayload,
1945
+ steps
1946
+ };
1947
+ };
1948
+ var deduplicateSteps = (steps) => {
1949
+ const targetStepIds = [];
1950
+ const stepIds = [];
1951
+ const deduplicatedSteps = [];
1952
+ for (const step of steps) {
1953
+ if (step.stepId === 0) {
1954
+ if (!targetStepIds.includes(step.targetStep ?? 0)) {
1955
+ deduplicatedSteps.push(step);
1956
+ targetStepIds.push(step.targetStep ?? 0);
1957
+ }
1958
+ } else {
1959
+ if (!stepIds.includes(step.stepId)) {
1960
+ deduplicatedSteps.push(step);
1961
+ stepIds.push(step.stepId);
1962
+ }
1963
+ }
1964
+ }
1965
+ return deduplicatedSteps;
1966
+ };
1967
+ var checkIfLastOneIsDuplicate = async (steps, debug) => {
1968
+ if (steps.length < 2) {
1969
+ return false;
1970
+ }
1971
+ const lastStep = steps.at(-1);
1972
+ const lastStepId = lastStep.stepId;
1973
+ const lastTargetStepId = lastStep.targetStep;
1974
+ for (let index = 0; index < steps.length - 1; index++) {
1975
+ const step = steps[index];
1976
+ if (step.stepId === lastStepId && step.targetStep === lastTargetStepId) {
1977
+ 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.`;
1978
+ await debug?.log("WARN", "RESPONSE_DEFAULT", message);
1979
+ console.warn(message);
1980
+ return true;
1981
+ }
1982
+ }
1983
+ return false;
1984
+ };
1985
+ var validateRequest = (request) => {
1986
+ const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
1987
+ const isFirstInvocation = !versionHeader;
1988
+ if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
1989
+ throw new QStashWorkflowError(
1990
+ `Incompatible workflow sdk protocol version. Expected ${WORKFLOW_PROTOCOL_VERSION}, got ${versionHeader} from the request.`
1991
+ );
1992
+ }
1993
+ const workflowRunId = isFirstInvocation ? `wfr_${nanoid()}` : request.headers.get(WORKFLOW_ID_HEADER) ?? "";
1994
+ if (workflowRunId.length === 0) {
1995
+ throw new QStashWorkflowError("Couldn't get workflow id from header");
1996
+ }
1997
+ return {
1998
+ isFirstInvocation,
1999
+ workflowRunId
2000
+ };
2001
+ };
2002
+ var parseRequest = async (requestPayload, isFirstInvocation, debug) => {
2003
+ if (isFirstInvocation) {
2004
+ return {
2005
+ rawInitialPayload: requestPayload ?? "",
2006
+ steps: [],
2007
+ isLastDuplicate: false
2008
+ };
2009
+ } else {
2010
+ if (!requestPayload) {
2011
+ throw new QStashWorkflowError("Only first call can have an empty body");
2012
+ }
2013
+ const { rawInitialPayload, steps } = parsePayload(requestPayload);
2014
+ const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, debug);
2015
+ const deduplicatedSteps = deduplicateSteps(steps);
2016
+ return {
2017
+ rawInitialPayload,
2018
+ steps: deduplicatedSteps,
2019
+ isLastDuplicate
2020
+ };
2021
+ }
2022
+ };
2023
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, failureFunction, debug) => {
2024
+ if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
2025
+ return ok("not-failure-callback");
2026
+ }
2027
+ if (!failureFunction) {
2028
+ return err(
2029
+ new QStashWorkflowError(
2030
+ "Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
2031
+ )
2032
+ );
2033
+ }
2034
+ try {
2035
+ const { status, header, body, url, sourceHeader, sourceBody, workflowRunId } = JSON.parse(
2036
+ requestPayload
2037
+ );
2038
+ const decodedBody = body ? decodeBase64(body) : "{}";
2039
+ const errorPayload = JSON.parse(decodedBody);
2040
+ const {
2041
+ rawInitialPayload,
2042
+ steps,
2043
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2044
+ isLastDuplicate: _isLastDuplicate
2045
+ } = await parseRequest(decodeBase64(sourceBody), false, debug);
2046
+ const workflowContext = new WorkflowContext({
2047
+ qstashClient,
2048
+ workflowRunId,
2049
+ initialPayload: initialPayloadParser(rawInitialPayload),
2050
+ rawInitialPayload,
2051
+ headers: recreateUserHeaders(new Headers(sourceHeader)),
2052
+ steps,
2053
+ url,
2054
+ failureUrl: url,
2055
+ debug
2056
+ });
2057
+ await failureFunction(workflowContext, status, errorPayload.message, header);
2058
+ } catch (error) {
2059
+ return err(error);
2060
+ }
2061
+ return ok("is-failure-callback");
2062
+ };
2063
+
2064
+ // src/serve/authorization.ts
2065
+ var import_qstash2 = require("@upstash/qstash");
2066
+ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
2067
+ static disabledMessage = "disabled-qstash-worklfow-run";
2068
+ /**
2069
+ * overwrite the WorkflowContext.addStep method to always raise QStashWorkflowAbort
2070
+ * error in order to stop the execution whenever we encounter a step.
2071
+ *
2072
+ * @param _step
2073
+ */
2074
+ async addStep(_step) {
2075
+ throw new QStashWorkflowAbort(_DisabledWorkflowContext.disabledMessage);
2076
+ }
2077
+ /**
2078
+ * copies the passed context to create a DisabledWorkflowContext. Then, runs the
2079
+ * route function with the new context.
2080
+ *
2081
+ * - returns "run-ended" if there are no steps found or
2082
+ * if the auth failed and user called `return`
2083
+ * - returns "step-found" if DisabledWorkflowContext.addStep is called.
2084
+ * - if there is another error, returns the error.
2085
+ *
2086
+ * @param routeFunction
2087
+ */
2088
+ static async tryAuthentication(routeFunction, context) {
2089
+ const disabledContext = new _DisabledWorkflowContext({
2090
+ qstashClient: new import_qstash2.Client({
2091
+ baseUrl: "disabled-client",
2092
+ token: "disabled-client"
2093
+ }),
2094
+ workflowRunId: context.workflowRunId,
2095
+ headers: context.headers,
2096
+ steps: [],
2097
+ url: context.url,
2098
+ failureUrl: context.failureUrl,
2099
+ initialPayload: context.requestPayload,
2100
+ rawInitialPayload: context.rawInitialPayload,
2101
+ env: context.env,
2102
+ retries: context.retries
2103
+ });
2104
+ try {
2105
+ await routeFunction(disabledContext);
2106
+ } catch (error) {
2107
+ if (error instanceof QStashWorkflowAbort && error.stepName === this.disabledMessage) {
2108
+ return ok("step-found");
2109
+ }
2110
+ return err(error);
2111
+ }
2112
+ return ok("run-ended");
2113
+ }
2114
+ };
2115
+
2116
+ // src/serve/options.ts
2117
+ var import_qstash3 = require("@upstash/qstash");
2118
+ var import_qstash4 = require("@upstash/qstash");
2119
+ var processOptions = (options) => {
2120
+ const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
2121
+ const receiverEnvironmentVariablesSet = Boolean(
2122
+ environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
2123
+ );
2124
+ return {
2125
+ qstashClient: new import_qstash4.Client({
2126
+ baseUrl: environment.QSTASH_URL,
2127
+ token: environment.QSTASH_TOKEN
2128
+ }),
2129
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2130
+ onStepFinish: (workflowRunId, _finishCondition) => new Response(JSON.stringify({ workflowRunId }), {
2131
+ status: 200
2132
+ }),
2133
+ initialPayloadParser: (initialRequest) => {
2134
+ if (!initialRequest) {
2135
+ return void 0;
2136
+ }
2137
+ try {
2138
+ return JSON.parse(initialRequest);
2139
+ } catch (error) {
2140
+ if (error instanceof SyntaxError) {
2141
+ return initialRequest;
2142
+ }
2143
+ throw error;
2144
+ }
2145
+ },
2146
+ receiver: receiverEnvironmentVariablesSet ? new import_qstash3.Receiver({
2147
+ currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
2148
+ nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
2149
+ }) : void 0,
2150
+ baseUrl: environment.UPSTASH_WORKFLOW_URL,
2151
+ env: environment,
2152
+ retries: DEFAULT_RETRIES,
2153
+ ...options
2154
+ };
2155
+ };
2156
+ var determineUrls = async (request, url, baseUrl, failureFunction, failureUrl, debug) => {
2157
+ const initialWorkflowUrl = url ?? request.url;
2158
+ const workflowUrl = baseUrl ? initialWorkflowUrl.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, (_, matchedBaseUrl, path) => {
2159
+ return baseUrl + (path || "");
2160
+ }) : initialWorkflowUrl;
2161
+ if (workflowUrl !== initialWorkflowUrl) {
2162
+ await debug?.log("WARN", "ENDPOINT_START", {
2163
+ warning: `Upstash Workflow: replacing the base of the url with "${baseUrl}" and using it as workflow endpoint.`,
2164
+ originalURL: initialWorkflowUrl,
2165
+ updatedURL: workflowUrl
2166
+ });
2167
+ }
2168
+ const workflowFailureUrl = failureFunction ? workflowUrl : failureUrl;
2169
+ return {
2170
+ workflowUrl,
2171
+ workflowFailureUrl
2172
+ };
2173
+ };
2174
+
2175
+ // src/serve/index.ts
2176
+ var serve = (routeFunction, options) => {
2177
+ const {
2178
+ qstashClient,
2179
+ onStepFinish,
2180
+ initialPayloadParser,
2181
+ url,
2182
+ verbose,
2183
+ receiver,
2184
+ failureUrl,
2185
+ failureFunction,
2186
+ baseUrl,
2187
+ env,
2188
+ retries
2189
+ } = processOptions(options);
2190
+ const debug = WorkflowLogger.getLogger(verbose);
2191
+ const handler = async (request) => {
2192
+ const { workflowUrl, workflowFailureUrl } = await determineUrls(
2193
+ request,
2194
+ url,
2195
+ baseUrl,
2196
+ failureFunction,
2197
+ failureUrl,
2198
+ debug
2199
+ );
2200
+ const requestPayload = await getPayload(request) ?? "";
2201
+ await verifyRequest(requestPayload, request.headers.get("upstash-signature"), receiver);
2202
+ const { isFirstInvocation, workflowRunId } = validateRequest(request);
2203
+ debug?.setWorkflowRunId(workflowRunId);
2204
+ const { rawInitialPayload, steps, isLastDuplicate } = await parseRequest(
2205
+ requestPayload,
2206
+ isFirstInvocation,
2207
+ debug
2208
+ );
2209
+ if (isLastDuplicate) {
2210
+ return onStepFinish("no-workflow-id", "duplicate-step");
2211
+ }
2212
+ const failureCheck = await handleFailure(
2213
+ request,
2214
+ requestPayload,
2215
+ qstashClient,
2216
+ initialPayloadParser,
2217
+ failureFunction
2218
+ );
2219
+ if (failureCheck.isErr()) {
2220
+ throw failureCheck.error;
2221
+ } else if (failureCheck.value === "is-failure-callback") {
2222
+ await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
2223
+ return onStepFinish("no-workflow-id", "failure-callback");
2224
+ }
2225
+ const workflowContext = new WorkflowContext({
2226
+ qstashClient,
2227
+ workflowRunId,
2228
+ initialPayload: initialPayloadParser(rawInitialPayload),
2229
+ rawInitialPayload,
2230
+ headers: recreateUserHeaders(request.headers),
2231
+ steps,
2232
+ url: workflowUrl,
2233
+ failureUrl: workflowFailureUrl,
2234
+ debug,
2235
+ env
2236
+ });
2237
+ const authCheck = await DisabledWorkflowContext.tryAuthentication(
2238
+ routeFunction,
2239
+ workflowContext
2240
+ );
2241
+ if (authCheck.isErr()) {
2242
+ await debug?.log("ERROR", "ERROR", { error: authCheck.error.message });
2243
+ throw authCheck.error;
2244
+ } else if (authCheck.value === "run-ended") {
2245
+ return onStepFinish("no-workflow-id", "auth-fail");
2246
+ }
2247
+ const callReturnCheck = await handleThirdPartyCallResult(
2248
+ request,
2249
+ rawInitialPayload,
2250
+ qstashClient,
2251
+ workflowUrl,
2252
+ workflowFailureUrl,
2253
+ retries,
2254
+ debug
2255
+ );
2256
+ if (callReturnCheck.isErr()) {
2257
+ await debug?.log("ERROR", "SUBMIT_THIRD_PARTY_RESULT", {
2258
+ error: callReturnCheck.error.message
2259
+ });
2260
+ throw callReturnCheck.error;
2261
+ } else if (callReturnCheck.value === "continue-workflow") {
2262
+ const result = isFirstInvocation ? await triggerFirstInvocation(workflowContext, retries, debug) : await triggerRouteFunction({
2263
+ onStep: async () => routeFunction(workflowContext),
2264
+ onCleanup: async () => {
2265
+ await triggerWorkflowDelete(workflowContext, debug);
2266
+ }
2267
+ });
2268
+ if (result.isErr()) {
2269
+ await debug?.log("ERROR", "ERROR", { error: result.error.message });
2270
+ throw result.error;
2271
+ }
2272
+ await debug?.log("INFO", "RESPONSE_WORKFLOW");
2273
+ return onStepFinish(workflowContext.workflowRunId, "success");
2274
+ }
2275
+ await debug?.log("INFO", "RESPONSE_DEFAULT");
2276
+ return onStepFinish("no-workflow-id", "fromCallback");
2277
+ };
2278
+ return async (request) => {
2279
+ try {
2280
+ return await handler(request);
2281
+ } catch (error) {
2282
+ console.error(error);
2283
+ return new Response(JSON.stringify(formatWorkflowError(error)), {
2284
+ status: 500
2285
+ });
2286
+ }
2287
+ };
2288
+ };
2289
+
2290
+ // src/client/index.ts
2291
+ var import_qstash5 = require("@upstash/qstash");
2292
+
2293
+ // platforms/h3.ts
2294
+ function transformHeaders(headers) {
2295
+ const formattedHeaders = Object.entries(headers).map(([key, value]) => [
2296
+ key,
2297
+ Array.isArray(value) ? value.join(", ") : value ?? ""
2298
+ ]);
2299
+ return formattedHeaders;
2300
+ }
2301
+ var serve2 = (routeFunction, options) => {
2302
+ const handler = defineEventHandler(async (event) => {
2303
+ const method = event.node.req.method;
2304
+ if (method?.toUpperCase() !== "POST") {
2305
+ return {
2306
+ status: 405,
2307
+ body: "Only POST requests are allowed in worklfows"
2308
+ };
2309
+ }
2310
+ const request_ = event.node.req;
2311
+ const protocol = request_.headers["x-forwarded-proto"];
2312
+ const host = request_.headers.host;
2313
+ const url = `${protocol}://${host}${event.path}`;
2314
+ const headers = transformHeaders(request_.headers);
2315
+ const request = new Request(url, {
2316
+ headers,
2317
+ body: await readRawBody(event),
2318
+ method: "POST"
2319
+ });
2320
+ const serveHandler = serve(routeFunction, options);
2321
+ return await serveHandler(request);
2322
+ });
2323
+ return handler;
2324
+ };
2325
+ // Annotate the CommonJS export names for ESM import in node:
2326
+ 0 && (module.exports = {
2327
+ serve
2328
+ });