@upstash/qstash 2.6.2 → 2.6.4-workflow-alpha.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.
@@ -0,0 +1,1540 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7;
2
+
3
+
4
+
5
+
6
+ var _chunkZ3TALRVSjs = require('./chunk-Z3TALRVS.js');
7
+
8
+ // node_modules/neverthrow/dist/index.es.js
9
+ var defaultErrorConfig = {
10
+ withStackTrace: false
11
+ };
12
+ var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
13
+ const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
14
+ const maybeStack = config.withStackTrace ? new Error().stack : void 0;
15
+ return {
16
+ data,
17
+ message,
18
+ stack: maybeStack
19
+ };
20
+ };
21
+ function __awaiter(thisArg, _arguments, P, generator) {
22
+ function adopt(value) {
23
+ return value instanceof P ? value : new P(function(resolve) {
24
+ resolve(value);
25
+ });
26
+ }
27
+ return new (P || (P = Promise))(function(resolve, reject) {
28
+ function fulfilled(value) {
29
+ try {
30
+ step(generator.next(value));
31
+ } catch (e) {
32
+ reject(e);
33
+ }
34
+ }
35
+ function rejected(value) {
36
+ try {
37
+ step(generator["throw"](value));
38
+ } catch (e) {
39
+ reject(e);
40
+ }
41
+ }
42
+ function step(result) {
43
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
44
+ }
45
+ step((generator = generator.apply(thisArg, [])).next());
46
+ });
47
+ }
48
+ function __values(o) {
49
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
50
+ if (m)
51
+ return m.call(o);
52
+ if (o && typeof o.length === "number")
53
+ return {
54
+ next: function() {
55
+ if (o && i >= o.length)
56
+ o = void 0;
57
+ return { value: o && o[i++], done: !o };
58
+ }
59
+ };
60
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
61
+ }
62
+ function __await(v) {
63
+ return this instanceof __await ? (this.v = v, this) : new __await(v);
64
+ }
65
+ function __asyncGenerator(thisArg, _arguments, generator) {
66
+ if (!Symbol.asyncIterator)
67
+ throw new TypeError("Symbol.asyncIterator is not defined.");
68
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
69
+ return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
70
+ return this;
71
+ }, i;
72
+ function verb(n) {
73
+ if (g[n])
74
+ i[n] = function(v) {
75
+ return new Promise(function(a, b) {
76
+ q.push([n, v, a, b]) > 1 || resume(n, v);
77
+ });
78
+ };
79
+ }
80
+ function resume(n, v) {
81
+ try {
82
+ step(g[n](v));
83
+ } catch (e) {
84
+ settle(q[0][3], e);
85
+ }
86
+ }
87
+ function step(r) {
88
+ r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
89
+ }
90
+ function fulfill(value) {
91
+ resume("next", value);
92
+ }
93
+ function reject(value) {
94
+ resume("throw", value);
95
+ }
96
+ function settle(f, v) {
97
+ if (f(v), q.shift(), q.length)
98
+ resume(q[0][0], q[0][1]);
99
+ }
100
+ }
101
+ function __asyncDelegator(o) {
102
+ var i, p;
103
+ return i = {}, verb("next"), verb("throw", function(e) {
104
+ throw e;
105
+ }), verb("return"), i[Symbol.iterator] = function() {
106
+ return this;
107
+ }, i;
108
+ function verb(n, f) {
109
+ i[n] = o[n] ? function(v) {
110
+ return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v;
111
+ } : f;
112
+ }
113
+ }
114
+ function __asyncValues(o) {
115
+ if (!Symbol.asyncIterator)
116
+ throw new TypeError("Symbol.asyncIterator is not defined.");
117
+ var m = o[Symbol.asyncIterator], i;
118
+ 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() {
119
+ return this;
120
+ }, i);
121
+ function verb(n) {
122
+ i[n] = o[n] && function(v) {
123
+ return new Promise(function(resolve, reject) {
124
+ v = o[n](v), settle(resolve, reject, v.done, v.value);
125
+ });
126
+ };
127
+ }
128
+ function settle(resolve, reject, d, v) {
129
+ Promise.resolve(v).then(function(v2) {
130
+ resolve({ value: v2, done: d });
131
+ }, reject);
132
+ }
133
+ }
134
+ var ResultAsync = class _ResultAsync {
135
+ constructor(res) {
136
+ this._promise = res;
137
+ }
138
+ static fromSafePromise(promise) {
139
+ const newPromise = promise.then((value) => new Ok(value));
140
+ return new _ResultAsync(newPromise);
141
+ }
142
+ static fromPromise(promise, errorFn) {
143
+ const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
144
+ return new _ResultAsync(newPromise);
145
+ }
146
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
+ static fromThrowable(fn, errorFn) {
148
+ return (...args) => {
149
+ return new _ResultAsync((() => __awaiter(this, void 0, void 0, function* () {
150
+ try {
151
+ return new Ok(yield fn(...args));
152
+ } catch (error) {
153
+ return new Err(errorFn ? errorFn(error) : error);
154
+ }
155
+ }))());
156
+ };
157
+ }
158
+ static combine(asyncResultList) {
159
+ return combineResultAsyncList(asyncResultList);
160
+ }
161
+ static combineWithAllErrors(asyncResultList) {
162
+ return combineResultAsyncListWithAllErrors(asyncResultList);
163
+ }
164
+ map(f) {
165
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
166
+ if (res.isErr()) {
167
+ return new Err(res.error);
168
+ }
169
+ return new Ok(yield f(res.value));
170
+ })));
171
+ }
172
+ mapErr(f) {
173
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
174
+ if (res.isOk()) {
175
+ return new Ok(res.value);
176
+ }
177
+ return new Err(yield f(res.error));
178
+ })));
179
+ }
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
181
+ andThen(f) {
182
+ return new _ResultAsync(this._promise.then((res) => {
183
+ if (res.isErr()) {
184
+ return new Err(res.error);
185
+ }
186
+ const newValue = f(res.value);
187
+ return newValue instanceof _ResultAsync ? newValue._promise : newValue;
188
+ }));
189
+ }
190
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
191
+ orElse(f) {
192
+ return new _ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
193
+ if (res.isErr()) {
194
+ return f(res.error);
195
+ }
196
+ return new Ok(res.value);
197
+ })));
198
+ }
199
+ match(ok2, _err) {
200
+ return this._promise.then((res) => res.match(ok2, _err));
201
+ }
202
+ unwrapOr(t) {
203
+ return this._promise.then((res) => res.unwrapOr(t));
204
+ }
205
+ /**
206
+ * Emulates Rust's `?` operator in `safeTry`'s body. See also `safeTry`.
207
+ */
208
+ safeUnwrap() {
209
+ return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
210
+ return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
211
+ });
212
+ }
213
+ // Makes ResultAsync implement PromiseLike<Result>
214
+ then(successCallback, failureCallback) {
215
+ return this._promise.then(successCallback, failureCallback);
216
+ }
217
+ };
218
+ var errAsync = (err2) => new ResultAsync(Promise.resolve(new Err(err2)));
219
+ var fromPromise = ResultAsync.fromPromise;
220
+ var fromSafePromise = ResultAsync.fromSafePromise;
221
+ var fromAsyncThrowable = ResultAsync.fromThrowable;
222
+ var combineResultList = (resultList) => {
223
+ let acc = ok([]);
224
+ for (const result of resultList) {
225
+ if (result.isErr()) {
226
+ acc = err(result.error);
227
+ break;
228
+ } else {
229
+ acc.map((list) => list.push(result.value));
230
+ }
231
+ }
232
+ return acc;
233
+ };
234
+ var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
235
+ var combineResultListWithAllErrors = (resultList) => {
236
+ let acc = ok([]);
237
+ for (const result of resultList) {
238
+ if (result.isErr() && acc.isErr()) {
239
+ acc.error.push(result.error);
240
+ } else if (result.isErr() && acc.isOk()) {
241
+ acc = err([result.error]);
242
+ } else if (result.isOk() && acc.isOk()) {
243
+ acc.value.push(result.value);
244
+ }
245
+ }
246
+ return acc;
247
+ };
248
+ var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
249
+ var Result;
250
+ (function(Result2) {
251
+ function fromThrowable2(fn, errorFn) {
252
+ return (...args) => {
253
+ try {
254
+ const result = fn(...args);
255
+ return ok(result);
256
+ } catch (e) {
257
+ return err(errorFn ? errorFn(e) : e);
258
+ }
259
+ };
260
+ }
261
+ Result2.fromThrowable = fromThrowable2;
262
+ function combine(resultList) {
263
+ return combineResultList(resultList);
264
+ }
265
+ Result2.combine = combine;
266
+ function combineWithAllErrors(resultList) {
267
+ return combineResultListWithAllErrors(resultList);
268
+ }
269
+ Result2.combineWithAllErrors = combineWithAllErrors;
270
+ })(Result || (Result = {}));
271
+ var ok = (value) => new Ok(value);
272
+ var err = (err2) => new Err(err2);
273
+ var Ok = class {
274
+ constructor(value) {
275
+ this.value = value;
276
+ }
277
+ isOk() {
278
+ return true;
279
+ }
280
+ isErr() {
281
+ return !this.isOk();
282
+ }
283
+ map(f) {
284
+ return ok(f(this.value));
285
+ }
286
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
287
+ mapErr(_f) {
288
+ return ok(this.value);
289
+ }
290
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
291
+ andThen(f) {
292
+ return f(this.value);
293
+ }
294
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
295
+ orElse(_f) {
296
+ return ok(this.value);
297
+ }
298
+ asyncAndThen(f) {
299
+ return f(this.value);
300
+ }
301
+ asyncMap(f) {
302
+ return ResultAsync.fromSafePromise(f(this.value));
303
+ }
304
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
305
+ unwrapOr(_v) {
306
+ return this.value;
307
+ }
308
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
309
+ match(ok2, _err) {
310
+ return ok2(this.value);
311
+ }
312
+ safeUnwrap() {
313
+ const value = this.value;
314
+ return function* () {
315
+ return value;
316
+ }();
317
+ }
318
+ _unsafeUnwrap(_) {
319
+ return this.value;
320
+ }
321
+ _unsafeUnwrapErr(config) {
322
+ throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
323
+ }
324
+ };
325
+ var Err = class {
326
+ constructor(error) {
327
+ this.error = error;
328
+ }
329
+ isOk() {
330
+ return false;
331
+ }
332
+ isErr() {
333
+ return !this.isOk();
334
+ }
335
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
336
+ map(_f) {
337
+ return err(this.error);
338
+ }
339
+ mapErr(f) {
340
+ return err(f(this.error));
341
+ }
342
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
343
+ andThen(_f) {
344
+ return err(this.error);
345
+ }
346
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
347
+ orElse(f) {
348
+ return f(this.error);
349
+ }
350
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
351
+ asyncAndThen(_f) {
352
+ return errAsync(this.error);
353
+ }
354
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
355
+ asyncMap(_f) {
356
+ return errAsync(this.error);
357
+ }
358
+ unwrapOr(v) {
359
+ return v;
360
+ }
361
+ match(_ok, err2) {
362
+ return err2(this.error);
363
+ }
364
+ safeUnwrap() {
365
+ const error = this.error;
366
+ return function* () {
367
+ yield err(error);
368
+ throw new Error("Do not use this generator out of `safeTry`");
369
+ }();
370
+ }
371
+ _unsafeUnwrap(config) {
372
+ throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
373
+ }
374
+ _unsafeUnwrapErr(_) {
375
+ return this.error;
376
+ }
377
+ };
378
+ var fromThrowable = Result.fromThrowable;
379
+
380
+ // src/client/workflow/constants.ts
381
+ var WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId";
382
+ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
383
+ var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
384
+ var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
385
+ var WORKFLOW_PROTOCOL_VERSION = "1";
386
+ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
387
+ var DEFAULT_CONTENT_TYPE = "application/json";
388
+
389
+ // src/client/workflow/types.ts
390
+ var StepTypes = ["Initial", "Run", "SleepFor", "SleepUntil", "Call"];
391
+
392
+ // src/client/workflow/workflow-requests.ts
393
+ var triggerFirstInvocation = async (workflowContext, debug) => {
394
+ const headers = getHeaders(
395
+ "true",
396
+ workflowContext.workflowRunId,
397
+ workflowContext.url,
398
+ workflowContext.headers,
399
+ void 0,
400
+ workflowContext.failureUrl
401
+ );
402
+ await _optionalChain([debug, 'optionalAccess', _2 => _2.log, 'call', _3 => _3("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
403
+ headers,
404
+ requestPayload: workflowContext.requestPayload,
405
+ url: workflowContext.url
406
+ })]);
407
+ return fromSafePromise(
408
+ workflowContext.client.publishJSON({
409
+ headers,
410
+ method: "POST",
411
+ body: workflowContext.requestPayload,
412
+ url: workflowContext.url
413
+ })
414
+ );
415
+ };
416
+ var triggerRouteFunction = async ({
417
+ onCleanup,
418
+ onStep
419
+ }) => {
420
+ try {
421
+ await onStep();
422
+ await onCleanup();
423
+ return ok("workflow-finished");
424
+ } catch (error) {
425
+ const error_ = error;
426
+ return error_ instanceof _chunkZ3TALRVSjs.QstashWorkflowAbort ? ok("step-finished") : err(error_);
427
+ }
428
+ };
429
+ var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
430
+ await _optionalChain([debug, 'optionalAccess', _4 => _4.log, 'call', _5 => _5("SUBMIT", "SUBMIT_CLEANUP", {
431
+ deletedWorkflowRunId: workflowContext.workflowRunId
432
+ })]);
433
+ const result = await workflowContext.client.http.request({
434
+ path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
435
+ method: "DELETE",
436
+ parseResponseAsJson: false
437
+ });
438
+ await _optionalChain([debug, 'optionalAccess', _6 => _6.log, 'call', _7 => _7("SUBMIT", "SUBMIT_CLEANUP", result)]);
439
+ };
440
+ var recreateUserHeaders = (headers) => {
441
+ const filteredHeaders = new Headers();
442
+ const pairs = headers.entries();
443
+ for (const [header, value] of pairs) {
444
+ const headerLowerCase = header.toLowerCase();
445
+ if (!headerLowerCase.startsWith("upstash-workflow-") && !headerLowerCase.startsWith("x-vercel-")) {
446
+ filteredHeaders.append(header, value);
447
+ }
448
+ }
449
+ return filteredHeaders;
450
+ };
451
+ var handleThirdPartyCallResult = async (request, requestPayload, client, failureUrl, debug) => {
452
+ try {
453
+ if (request.headers.get("Upstash-Workflow-Callback")) {
454
+ const callbackMessage = JSON.parse(requestPayload);
455
+ if (!(callbackMessage.status >= 200 && callbackMessage.status < 300)) {
456
+ await _optionalChain([debug, 'optionalAccess', _8 => _8.log, 'call', _9 => _9("WARN", "SUBMIT_THIRD_PARTY_RESULT", callbackMessage)]);
457
+ return ok("call-will-retry");
458
+ }
459
+ const workflowRunId = request.headers.get(WORKFLOW_ID_HEADER);
460
+ const stepIdString = request.headers.get("Upstash-Workflow-StepId");
461
+ const stepName = request.headers.get("Upstash-Workflow-StepName");
462
+ const stepType = request.headers.get("Upstash-Workflow-StepType");
463
+ const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
464
+ const contentType = request.headers.get("Upstash-Workflow-ContentType");
465
+ if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
466
+ throw new Error(
467
+ `Missing info in callback message source header: ${JSON.stringify({
468
+ workflowRunId,
469
+ stepIdString,
470
+ stepName,
471
+ stepType,
472
+ concurrentString,
473
+ contentType
474
+ })}`
475
+ );
476
+ }
477
+ const userHeaders = recreateUserHeaders(request.headers);
478
+ const requestHeaders = getHeaders(
479
+ "false",
480
+ workflowRunId,
481
+ request.url,
482
+ userHeaders,
483
+ void 0,
484
+ failureUrl
485
+ );
486
+ const callResultStep = {
487
+ stepId: Number(stepIdString),
488
+ stepName,
489
+ stepType,
490
+ out: Buffer.from(callbackMessage.body, "base64").toString(),
491
+ concurrent: Number(concurrentString)
492
+ };
493
+ await _optionalChain([debug, 'optionalAccess', _10 => _10.log, 'call', _11 => _11("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
494
+ step: callResultStep,
495
+ headers: requestHeaders,
496
+ url: request.url
497
+ })]);
498
+ const result = await client.publishJSON({
499
+ headers: requestHeaders,
500
+ method: "POST",
501
+ body: callResultStep,
502
+ url: request.url
503
+ });
504
+ await _optionalChain([debug, 'optionalAccess', _12 => _12.log, 'call', _13 => _13("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
505
+ messageId: result.messageId
506
+ })]);
507
+ return ok("is-call-return");
508
+ } else {
509
+ return ok("continue-workflow");
510
+ }
511
+ } catch (error) {
512
+ const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
513
+ return err(
514
+ new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
515
+ `Error when handling call return (isCallReturn=${isCallReturn}): ${error}`
516
+ )
517
+ );
518
+ }
519
+ };
520
+ var getHeaders = (initHeaderValue, workflowRunId, workflowUrl, userHeaders, step, failureUrl) => {
521
+ const baseHeaders = {
522
+ [WORKFLOW_INIT_HEADER]: initHeaderValue,
523
+ [WORKFLOW_ID_HEADER]: workflowRunId,
524
+ [WORKFLOW_URL_HEADER]: workflowUrl,
525
+ [`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`]: WORKFLOW_PROTOCOL_VERSION,
526
+ ...failureUrl ? {
527
+ [`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`]: "true",
528
+ "Upstash-Failure-Callback": failureUrl
529
+ } : {}
530
+ };
531
+ if (userHeaders) {
532
+ for (const header of userHeaders.keys()) {
533
+ baseHeaders[`Upstash-Forward-${header}`] = userHeaders.get(header);
534
+ if (_optionalChain([step, 'optionalAccess', _14 => _14.callHeaders])) {
535
+ baseHeaders[`Upstash-Callback-Forward-${header}`] = userHeaders.get(header);
536
+ }
537
+ }
538
+ }
539
+ if (_optionalChain([step, 'optionalAccess', _15 => _15.callHeaders])) {
540
+ const forwardedHeaders = Object.fromEntries(
541
+ Object.entries(step.callHeaders).map(([header, value]) => [
542
+ `Upstash-Forward-${header}`,
543
+ value
544
+ ])
545
+ );
546
+ const contentType = step.callHeaders["Content-Type"];
547
+ return {
548
+ ...baseHeaders,
549
+ ...forwardedHeaders,
550
+ "Upstash-Callback": workflowUrl,
551
+ "Upstash-Callback-Workflow-RunId": workflowRunId,
552
+ "Upstash-Callback-Workflow-CallType": "fromCallback",
553
+ "Upstash-Callback-Workflow-Init": "false",
554
+ "Upstash-Callback-Workflow-Url": workflowUrl,
555
+ "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
556
+ "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
557
+ "Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
558
+ "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
559
+ "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
560
+ "Upstash-Callback-Forward-Upstash-Workflow-ContentType": _nullishCoalesce(contentType, () => ( DEFAULT_CONTENT_TYPE)),
561
+ "Upstash-Workflow-CallType": "toCallback"
562
+ };
563
+ }
564
+ return baseHeaders;
565
+ };
566
+ var verifyRequest = async (body, signature, verifier) => {
567
+ if (!verifier) {
568
+ return;
569
+ }
570
+ if (!signature) {
571
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)("`Upstash-Signature` header is not a string");
572
+ }
573
+ const isValid = await verifier.verify({
574
+ body,
575
+ signature
576
+ });
577
+ if (!isValid) {
578
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)("Invalid signature");
579
+ }
580
+ };
581
+
582
+ // src/client/workflow/auto-executor.ts
583
+ var AutoExecutor = (_class = class _AutoExecutor {
584
+
585
+ __init() {this.promises = /* @__PURE__ */ new WeakMap()}
586
+
587
+
588
+
589
+ __init2() {this.indexInCurrentList = 0}
590
+ __init3() {this.stepCount = 0}
591
+ __init4() {this.planStepCount = 0}
592
+ __init5() {this.executingStep = false}
593
+ constructor(context, debug) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);
594
+ this.context = context;
595
+ this.debug = debug;
596
+ this.nonPlanStepCount = this.context.steps.filter((step) => !step.targetStep).length;
597
+ }
598
+ /**
599
+ * Adds the step function to the list of step functions to run in
600
+ * parallel. After adding the function, defers the execution, so
601
+ * that if there is another step function to be added, it's also
602
+ * added.
603
+ *
604
+ * After all functions are added, list of functions are executed.
605
+ * If there is a single function, it's executed by itself. If there
606
+ * are multiple, they are run in parallel.
607
+ *
608
+ * If a function is already executing (this.executingStep), this
609
+ * means that there is a nested step which is not allowed. In this
610
+ * case, addStep throws QstashWorkflowError.
611
+ *
612
+ * @param stepInfo step plan to add
613
+ * @returns result of the step function
614
+ */
615
+ async addStep(stepInfo) {
616
+ if (this.executingStep) {
617
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
618
+ `A step can not be run inside another step. Tried to run '${stepInfo.stepName}' inside '${this.executingStep}'`
619
+ );
620
+ }
621
+ this.stepCount += 1;
622
+ const lazyStepList = _nullishCoalesce(this.activeLazyStepList, () => ( []));
623
+ if (!this.activeLazyStepList) {
624
+ this.activeLazyStepList = lazyStepList;
625
+ this.indexInCurrentList = 0;
626
+ }
627
+ lazyStepList.push(stepInfo);
628
+ const index = this.indexInCurrentList++;
629
+ const requestComplete = this.deferExecution().then(async () => {
630
+ if (!this.promises.has(lazyStepList)) {
631
+ const promise2 = this.getExecutionPromise(lazyStepList);
632
+ this.promises.set(lazyStepList, promise2);
633
+ this.activeLazyStepList = void 0;
634
+ this.planStepCount += lazyStepList.length > 1 ? lazyStepList.length : 0;
635
+ }
636
+ const promise = this.promises.get(lazyStepList);
637
+ return promise;
638
+ });
639
+ const result = await requestComplete;
640
+ return _AutoExecutor.getResult(lazyStepList, result, index);
641
+ }
642
+ /**
643
+ * Wraps a step function to set this.executingStep to step name
644
+ * before running and set this.executingStep to False after execution
645
+ * ends.
646
+ *
647
+ * this.executingStep allows us to detect nested steps which are not
648
+ * allowed.
649
+ *
650
+ * @param stepName name of the step being wrapped
651
+ * @param stepFunction step function to wrap
652
+ * @returns wrapped step function
653
+ */
654
+ async wrapStep(stepName, stepFunction) {
655
+ this.executingStep = stepName;
656
+ const result = await stepFunction();
657
+ this.executingStep = false;
658
+ return result;
659
+ }
660
+ /**
661
+ * Executes a step:
662
+ * - If the step result is available in the steps, returns the result
663
+ * - If the result is not avaiable, runs the function
664
+ * - Sends the result to QStash
665
+ *
666
+ * @param lazyStep lazy step to execute
667
+ * @returns step result
668
+ */
669
+ async runSingle(lazyStep) {
670
+ if (this.stepCount < this.nonPlanStepCount) {
671
+ const step = this.context.steps[this.stepCount + this.planStepCount];
672
+ validateStep(lazyStep, step);
673
+ await _optionalChain([this, 'access', _16 => _16.debug, 'optionalAccess', _17 => _17.log, 'call', _18 => _18("INFO", "RUN_SINGLE", {
674
+ fromRequest: true,
675
+ step,
676
+ stepCount: this.stepCount
677
+ })]);
678
+ return step.out;
679
+ }
680
+ const resultStep = await lazyStep.getResultStep(1, this.stepCount);
681
+ await _optionalChain([this, 'access', _19 => _19.debug, 'optionalAccess', _20 => _20.log, 'call', _21 => _21("INFO", "RUN_SINGLE", {
682
+ fromRequest: false,
683
+ step: resultStep,
684
+ stepCount: this.stepCount
685
+ })]);
686
+ await this.submitStepsToQstash([resultStep]);
687
+ return resultStep.out;
688
+ }
689
+ /**
690
+ * Runs steps in parallel.
691
+ *
692
+ * @param stepName parallel step name
693
+ * @param stepFunctions list of async functions to run in parallel
694
+ * @returns results of the functions run in parallel
695
+ */
696
+ async runParallel(parallelSteps) {
697
+ const initialStepCount = this.stepCount - (parallelSteps.length - 1);
698
+ const parallelCallState = this.getParallelCallState(parallelSteps.length, initialStepCount);
699
+ const sortedSteps = sortSteps(this.context.steps);
700
+ const plannedParallelStepCount = _optionalChain([sortedSteps, 'access', _22 => _22[initialStepCount + this.planStepCount], 'optionalAccess', _23 => _23.concurrent]);
701
+ if (parallelCallState !== "first" && plannedParallelStepCount !== parallelSteps.length) {
702
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
703
+ `Incompatible number of parallel steps when call state was '${parallelCallState}'. Expected ${parallelSteps.length}, got ${plannedParallelStepCount} from the request.`
704
+ );
705
+ }
706
+ await _optionalChain([this, 'access', _24 => _24.debug, 'optionalAccess', _25 => _25.log, 'call', _26 => _26("INFO", "RUN_PARALLEL", {
707
+ parallelCallState,
708
+ initialStepCount,
709
+ plannedParallelStepCount,
710
+ stepCount: this.stepCount,
711
+ planStepCount: this.planStepCount
712
+ })]);
713
+ switch (parallelCallState) {
714
+ case "first": {
715
+ const planSteps = parallelSteps.map(
716
+ (parallelStep, index) => parallelStep.getPlanStep(parallelSteps.length, initialStepCount + index)
717
+ );
718
+ await this.submitStepsToQstash(planSteps);
719
+ break;
720
+ }
721
+ case "partial": {
722
+ const planStep = this.context.steps.at(-1);
723
+ if (!planStep || planStep.targetStep === void 0) {
724
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
725
+ `There must be a last step and it should have targetStep larger than 0.Received: ${JSON.stringify(planStep)}`
726
+ );
727
+ }
728
+ const stepIndex = planStep.targetStep - initialStepCount;
729
+ validateStep(parallelSteps[stepIndex], planStep);
730
+ try {
731
+ const resultStep = await parallelSteps[stepIndex].getResultStep(
732
+ parallelSteps.length,
733
+ planStep.targetStep
734
+ );
735
+ await this.submitStepsToQstash([resultStep]);
736
+ } catch (error) {
737
+ if (error instanceof _chunkZ3TALRVSjs.QstashWorkflowAbort) {
738
+ throw error;
739
+ }
740
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
741
+ `Error submitting steps to qstash in partial parallel step execution: ${error}`
742
+ );
743
+ }
744
+ break;
745
+ }
746
+ case "discard": {
747
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowAbort)("discarded parallel");
748
+ }
749
+ case "last": {
750
+ const parallelResultSteps = sortedSteps.filter((step) => step.stepId >= initialStepCount).slice(0, parallelSteps.length);
751
+ validateParallelSteps(parallelSteps, parallelResultSteps);
752
+ return parallelResultSteps.map((step) => step.out);
753
+ }
754
+ }
755
+ const fillValue = void 0;
756
+ return Array.from({ length: parallelSteps.length }).fill(fillValue);
757
+ }
758
+ /**
759
+ * Determines the parallel call state
760
+ *
761
+ * First filters the steps to get the steps which are after `initialStepCount` parameter.
762
+ *
763
+ * Depending on the remaining steps, decides the parallel state:
764
+ * - "first": If there are no steps
765
+ * - "last" If there are equal to or more than `2 * parallelStepCount`. We multiply by two
766
+ * because each step in a parallel execution will have 2 steps: a plan step and a result
767
+ * step.
768
+ * - "partial": If the last step is a plan step
769
+ * - "discard": If the last step is not a plan step. This means that the parallel execution
770
+ * is in progress (there are still steps to run) and one step has finished and submitted
771
+ * its result to QStash
772
+ *
773
+ * @param parallelStepCount number of steps to run in parallel
774
+ * @param initialStepCount steps after the parallel invocation
775
+ * @returns parallel call state
776
+ */
777
+ getParallelCallState(parallelStepCount, initialStepCount) {
778
+ const remainingSteps = this.context.steps.filter(
779
+ (step) => (_nullishCoalesce(step.targetStep, () => ( step.stepId))) >= initialStepCount
780
+ );
781
+ if (remainingSteps.length === 0) {
782
+ return "first";
783
+ } else if (remainingSteps.length >= 2 * parallelStepCount) {
784
+ return "last";
785
+ } else if (_optionalChain([remainingSteps, 'access', _27 => _27.at, 'call', _28 => _28(-1), 'optionalAccess', _29 => _29.targetStep])) {
786
+ return "partial";
787
+ } else {
788
+ return "discard";
789
+ }
790
+ }
791
+ /**
792
+ * sends the steps to QStash as batch
793
+ *
794
+ * @param steps steps to send
795
+ */
796
+ async submitStepsToQstash(steps) {
797
+ if (steps.length === 0) {
798
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
799
+ `Unable to submit steps to Qstash. Provided list is empty. Current step: ${this.stepCount}`
800
+ );
801
+ }
802
+ await _optionalChain([this, 'access', _30 => _30.debug, 'optionalAccess', _31 => _31.log, 'call', _32 => _32("SUBMIT", "SUBMIT_STEP", { length: steps.length, steps })]);
803
+ const result = await this.context.client.batchJSON(
804
+ steps.map((singleStep) => {
805
+ const headers = getHeaders(
806
+ "false",
807
+ this.context.workflowRunId,
808
+ this.context.url,
809
+ this.context.headers,
810
+ singleStep,
811
+ this.context.failureUrl
812
+ );
813
+ const willWait = singleStep.concurrent === 1 || singleStep.stepId === 0;
814
+ return singleStep.callUrl ? (
815
+ // if the step is a third party call, we call the third party
816
+ // url (singleStep.callUrl) and pass information about the workflow
817
+ // in the headers (handled in getHeaders). QStash makes the request
818
+ // to callUrl and returns the result to Workflow endpoint.
819
+ // handleThirdPartyCallResult method sends the result of the third
820
+ // party call to QStash.
821
+ {
822
+ headers,
823
+ method: singleStep.callMethod,
824
+ body: singleStep.callBody,
825
+ url: singleStep.callUrl
826
+ }
827
+ ) : (
828
+ // if the step is not a third party call, we use workflow
829
+ // endpoint (context.url) as URL when calling QStash. QStash
830
+ // calls us back with the updated steps list.
831
+ {
832
+ headers,
833
+ method: "POST",
834
+ body: singleStep,
835
+ url: this.context.url,
836
+ notBefore: willWait ? singleStep.sleepUntil : void 0,
837
+ delay: willWait ? singleStep.sleepFor : void 0
838
+ }
839
+ );
840
+ })
841
+ );
842
+ await _optionalChain([this, 'access', _33 => _33.debug, 'optionalAccess', _34 => _34.log, 'call', _35 => _35("INFO", "SUBMIT_STEP", {
843
+ messageIds: result.map((message) => {
844
+ return {
845
+ message: message.messageId
846
+ };
847
+ })
848
+ })]);
849
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowAbort)(steps[0].stepName, steps[0]);
850
+ }
851
+ /**
852
+ * Get the promise by executing the lazt steps list. If there is a single
853
+ * step, we call `runSingle`. Otherwise `runParallel` is called.
854
+ *
855
+ * @param lazyStepList steps list to execute
856
+ * @returns promise corresponding to the execution
857
+ */
858
+ getExecutionPromise(lazyStepList) {
859
+ return lazyStepList.length === 1 ? this.runSingle(lazyStepList[0]) : this.runParallel(lazyStepList);
860
+ }
861
+ /**
862
+ * @param lazyStepList steps we executed
863
+ * @param result result of the promise from `getExecutionPromise`
864
+ * @param index index of the current step
865
+ * @returns result[index] if lazyStepList > 1, otherwise result
866
+ */
867
+ static getResult(lazyStepList, result, index) {
868
+ if (lazyStepList.length === 1) {
869
+ return result;
870
+ } else if (Array.isArray(result) && lazyStepList.length === result.length && index < lazyStepList.length) {
871
+ return result[index];
872
+ } else {
873
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
874
+ `Unexpected parallel call result while executing step ${index}: '${result}'. Expected ${lazyStepList.length} many items`
875
+ );
876
+ }
877
+ }
878
+ async deferExecution() {
879
+ await Promise.resolve();
880
+ await Promise.resolve();
881
+ }
882
+ }, _class);
883
+ var validateStep = (lazyStep, stepFromRequest) => {
884
+ if (lazyStep.stepName !== stepFromRequest.stepName) {
885
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
886
+ `Incompatible step name. Expected '${lazyStep.stepName}', got '${stepFromRequest.stepName}' from the request`
887
+ );
888
+ }
889
+ if (lazyStep.stepType !== stepFromRequest.stepType) {
890
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
891
+ `Incompatible step type. Expected '${lazyStep.stepType}', got '${stepFromRequest.stepType}' from the request`
892
+ );
893
+ }
894
+ };
895
+ var validateParallelSteps = (lazySteps, stepsFromRequest) => {
896
+ try {
897
+ for (const [index, stepFromRequest] of stepsFromRequest.entries()) {
898
+ validateStep(lazySteps[index], stepFromRequest);
899
+ }
900
+ } catch (error) {
901
+ if (error instanceof _chunkZ3TALRVSjs.QstashWorkflowError) {
902
+ const lazyStepNames = lazySteps.map((lazyStep) => lazyStep.stepName);
903
+ const lazyStepTypes = lazySteps.map((lazyStep) => lazyStep.stepType);
904
+ const requestStepNames = stepsFromRequest.map((step) => step.stepName);
905
+ const requestStepTypes = stepsFromRequest.map((step) => step.stepType);
906
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
907
+ `Incompatible steps detected in parallel execution: ${error.message}
908
+ > Step Names from the request: ${JSON.stringify(requestStepNames)}
909
+ Step Types from the request: ${JSON.stringify(requestStepTypes)}
910
+ > Step Names expected: ${JSON.stringify(lazyStepNames)}
911
+ Step Types expected: ${JSON.stringify(lazyStepTypes)}`
912
+ );
913
+ }
914
+ throw error;
915
+ }
916
+ };
917
+ var sortSteps = (steps) => {
918
+ const getStepId = (step) => _nullishCoalesce(step.targetStep, () => ( step.stepId));
919
+ return steps.toSorted((step, stepOther) => getStepId(step) - getStepId(stepOther));
920
+ };
921
+
922
+ // src/client/workflow/steps.ts
923
+ var BaseLazyStep = class {
924
+
925
+ // will be set in the subclasses
926
+ constructor(stepName) {
927
+ this.stepName = stepName;
928
+ }
929
+ };
930
+ var LazyFunctionStep = (_class2 = class extends BaseLazyStep {
931
+
932
+ __init6() {this.stepType = "Run"}
933
+ constructor(stepName, stepFunction) {
934
+ super(stepName);_class2.prototype.__init6.call(this);;
935
+ this.stepFunction = stepFunction;
936
+ }
937
+ getPlanStep(concurrent, targetStep) {
938
+ {
939
+ return {
940
+ stepId: 0,
941
+ stepName: this.stepName,
942
+ stepType: this.stepType,
943
+ concurrent,
944
+ targetStep
945
+ };
946
+ }
947
+ }
948
+ async getResultStep(concurrent, stepId) {
949
+ const result = await this.stepFunction();
950
+ return {
951
+ stepId,
952
+ stepName: this.stepName,
953
+ stepType: this.stepType,
954
+ out: result,
955
+ concurrent
956
+ };
957
+ }
958
+ }, _class2);
959
+ var LazySleepStep = (_class3 = class extends BaseLazyStep {
960
+
961
+ __init7() {this.stepType = "SleepFor"}
962
+ constructor(stepName, sleep) {
963
+ super(stepName);_class3.prototype.__init7.call(this);;
964
+ this.sleep = sleep;
965
+ }
966
+ getPlanStep(concurrent, targetStep) {
967
+ {
968
+ return {
969
+ stepId: 0,
970
+ stepName: this.stepName,
971
+ stepType: this.stepType,
972
+ sleepFor: this.sleep,
973
+ concurrent,
974
+ targetStep
975
+ };
976
+ }
977
+ }
978
+ async getResultStep(concurrent, stepId) {
979
+ return await Promise.resolve({
980
+ stepId,
981
+ stepName: this.stepName,
982
+ stepType: this.stepType,
983
+ sleepFor: this.sleep,
984
+ concurrent
985
+ });
986
+ }
987
+ }, _class3);
988
+ var LazySleepUntilStep = (_class4 = class extends BaseLazyStep {
989
+
990
+ __init8() {this.stepType = "SleepUntil"}
991
+ constructor(stepName, sleepUntil) {
992
+ super(stepName);_class4.prototype.__init8.call(this);;
993
+ this.sleepUntil = sleepUntil;
994
+ }
995
+ getPlanStep(concurrent, targetStep) {
996
+ {
997
+ return {
998
+ stepId: 0,
999
+ stepName: this.stepName,
1000
+ stepType: this.stepType,
1001
+ sleepUntil: this.sleepUntil,
1002
+ concurrent,
1003
+ targetStep
1004
+ };
1005
+ }
1006
+ }
1007
+ async getResultStep(concurrent, stepId) {
1008
+ return await Promise.resolve({
1009
+ stepId,
1010
+ stepName: this.stepName,
1011
+ stepType: this.stepType,
1012
+ sleepUntil: this.sleepUntil,
1013
+ concurrent
1014
+ });
1015
+ }
1016
+ }, _class4);
1017
+ var LazyCallStep = (_class5 = class extends BaseLazyStep {
1018
+
1019
+
1020
+
1021
+
1022
+ __init9() {this.stepType = "Call"}
1023
+ constructor(stepName, url, method, body, headers) {
1024
+ super(stepName);_class5.prototype.__init9.call(this);;
1025
+ this.url = url;
1026
+ this.method = method;
1027
+ this.body = body;
1028
+ this.headers = headers;
1029
+ }
1030
+ getPlanStep(concurrent, targetStep) {
1031
+ {
1032
+ return {
1033
+ stepId: 0,
1034
+ stepName: this.stepName,
1035
+ stepType: this.stepType,
1036
+ concurrent,
1037
+ targetStep
1038
+ };
1039
+ }
1040
+ }
1041
+ async getResultStep(concurrent, stepId) {
1042
+ return await Promise.resolve({
1043
+ stepId,
1044
+ stepName: this.stepName,
1045
+ stepType: this.stepType,
1046
+ concurrent,
1047
+ callUrl: this.url,
1048
+ callMethod: this.method,
1049
+ callBody: this.body,
1050
+ callHeaders: this.headers
1051
+ });
1052
+ }
1053
+ }, _class5);
1054
+
1055
+ // src/client/workflow/context.ts
1056
+ var WorkflowContext = class {
1057
+
1058
+
1059
+
1060
+
1061
+
1062
+
1063
+
1064
+
1065
+
1066
+ constructor({
1067
+ client,
1068
+ workflowRunId,
1069
+ headers,
1070
+ steps,
1071
+ url,
1072
+ failureUrl = false,
1073
+ debug,
1074
+ initialPayload,
1075
+ rawInitialPayload
1076
+ }) {
1077
+ this.client = client;
1078
+ this.workflowRunId = workflowRunId;
1079
+ this.steps = steps;
1080
+ this.url = url;
1081
+ this.failureUrl = failureUrl;
1082
+ this.headers = headers;
1083
+ this.requestPayload = initialPayload;
1084
+ this.rawInitialPayload = _nullishCoalesce(rawInitialPayload, () => ( JSON.stringify(this.requestPayload)));
1085
+ this.executor = new AutoExecutor(this, debug);
1086
+ }
1087
+ /**
1088
+ * Executes a workflow step
1089
+ *
1090
+ * ```typescript
1091
+ * const result = await context.run("step 1", async () => {
1092
+ * return await Promise.resolve("result")
1093
+ * })
1094
+ * ```
1095
+ *
1096
+ * Can also be called in parallel and the steps will be executed
1097
+ * simulatenously:
1098
+ *
1099
+ * ```typescript
1100
+ * const [result1, result2] = await Promise.all([
1101
+ * context.run("step 1", async () => {
1102
+ * return await Promise.resolve("result1")
1103
+ * })
1104
+ * context.run("step 2", async () => {
1105
+ * return await Promise.resolve("result2")
1106
+ * })
1107
+ * ])
1108
+ * ```
1109
+ *
1110
+ * @param stepName name of the step
1111
+ * @param stepFunction step function to be executed
1112
+ * @returns result of the step function
1113
+ */
1114
+ async run(stepName, stepFunction) {
1115
+ const wrappedStepFunction = async () => this.executor.wrapStep(stepName, stepFunction);
1116
+ return this.addStep(new LazyFunctionStep(stepName, wrappedStepFunction));
1117
+ }
1118
+ /**
1119
+ * Stops the execution for the duration provided.
1120
+ *
1121
+ * @param stepName
1122
+ * @param duration sleep duration in seconds
1123
+ * @returns
1124
+ */
1125
+ async sleep(stepName, duration) {
1126
+ await this.addStep(new LazySleepStep(stepName, duration));
1127
+ }
1128
+ /**
1129
+ * Stops the execution until the date time provided.
1130
+ *
1131
+ * @param stepName
1132
+ * @param datetime time to sleep until. Can be provided as a number (in unix seconds),
1133
+ * as a Date object or a string (passed to `new Date(datetimeString)`)
1134
+ * @returns
1135
+ */
1136
+ async sleepUntil(stepName, datetime) {
1137
+ let time;
1138
+ if (typeof datetime === "number") {
1139
+ time = datetime;
1140
+ } else {
1141
+ datetime = typeof datetime === "string" ? new Date(datetime) : datetime;
1142
+ time = Math.round(datetime.getTime() / 1e3);
1143
+ }
1144
+ await this.addStep(new LazySleepUntilStep(stepName, time));
1145
+ }
1146
+ async call(stepName, url, method, body, headers) {
1147
+ return await this.addStep(
1148
+ new LazyCallStep(stepName, url, method, body, _nullishCoalesce(headers, () => ( {})))
1149
+ );
1150
+ }
1151
+ /**
1152
+ * Adds steps to the executor. Needed so that it can be overwritten in
1153
+ * DisabledWorkflowContext.
1154
+ */
1155
+ async addStep(step) {
1156
+ return await this.executor.addStep(step);
1157
+ }
1158
+ };
1159
+ var DisabledWorkflowContext = (_class6 = class _DisabledWorkflowContext extends WorkflowContext {
1160
+ static __initStatic() {this.disabledMessage = "disabled-qstash-worklfow-run"}
1161
+ /**
1162
+ * overwrite the WorkflowContext.addStep method to always raise QstashWorkflowAbort
1163
+ * error in order to stop the execution whenever we encounter a step.
1164
+ *
1165
+ * @param _step
1166
+ */
1167
+ // eslint-disable-next-line @typescript-eslint/require-await
1168
+ async addStep(_step) {
1169
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowAbort)(_DisabledWorkflowContext.disabledMessage);
1170
+ }
1171
+ /**
1172
+ * copies the passed context to create a DisabledWorkflowContext. Then, runs the
1173
+ * route function with the new context.
1174
+ *
1175
+ * - returns "run-ended" if there are no steps found or
1176
+ * if the auth failed and user called `return`
1177
+ * - returns "step-found" if DisabledWorkflowContext.addStep is called.
1178
+ * - if there is another error, returns the error.
1179
+ *
1180
+ * @param routeFunction
1181
+ */
1182
+ static async tryAuthentication(routeFunction, context) {
1183
+ const disabledContext = new _DisabledWorkflowContext({
1184
+ client: new (0, _chunkZ3TALRVSjs.Client)({ baseUrl: "disabled-client", token: "disabled-client" }),
1185
+ workflowRunId: context.workflowRunId,
1186
+ headers: context.headers,
1187
+ steps: context.steps,
1188
+ url: context.url,
1189
+ failureUrl: context.failureUrl,
1190
+ initialPayload: context.requestPayload,
1191
+ rawInitialPayload: context.rawInitialPayload
1192
+ });
1193
+ try {
1194
+ await routeFunction(disabledContext);
1195
+ } catch (error) {
1196
+ if (error instanceof _chunkZ3TALRVSjs.QstashWorkflowAbort && error.stepName === this.disabledMessage) {
1197
+ return ok("step-found");
1198
+ }
1199
+ return err(error);
1200
+ }
1201
+ return ok("run-ended");
1202
+ }
1203
+ }, _class6.__initStatic(), _class6);
1204
+
1205
+ // src/client/workflow/logger.ts
1206
+ var LOG_LEVELS = ["DEBUG", "INFO", "SUBMIT", "WARN", "ERROR"];
1207
+ var WorkflowLogger = (_class7 = class _WorkflowLogger {
1208
+ __init10() {this.logs = []}
1209
+
1210
+ constructor(options) {;_class7.prototype.__init10.call(this);
1211
+ this.options = options;
1212
+ }
1213
+ async log(level, eventType, details) {
1214
+ if (this.shouldLog(level)) {
1215
+ const timestamp = Date.now();
1216
+ const logEntry = {
1217
+ timestamp,
1218
+ logLevel: level,
1219
+ eventType,
1220
+ details
1221
+ };
1222
+ this.logs.push(logEntry);
1223
+ if (this.options.logOutput === "console") {
1224
+ this.writeToConsole(logEntry);
1225
+ }
1226
+ await new Promise((resolve) => setTimeout(resolve, 100));
1227
+ }
1228
+ }
1229
+ writeToConsole(logEntry) {
1230
+ const JSON_SPACING = 2;
1231
+ console.log(JSON.stringify(logEntry, void 0, JSON_SPACING));
1232
+ }
1233
+ shouldLog(level) {
1234
+ return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.options.logLevel);
1235
+ }
1236
+ static getLogger(verbose) {
1237
+ if (typeof verbose === "object") {
1238
+ return verbose;
1239
+ } else {
1240
+ return verbose ? new _WorkflowLogger({
1241
+ logLevel: "INFO",
1242
+ logOutput: "console"
1243
+ }) : void 0;
1244
+ }
1245
+ }
1246
+ }, _class7);
1247
+
1248
+ // node_modules/nanoid/index.js
1249
+ var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto);
1250
+
1251
+ // node_modules/nanoid/url-alphabet/index.js
1252
+ var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
1253
+
1254
+ // node_modules/nanoid/index.js
1255
+ var POOL_SIZE_MULTIPLIER = 128;
1256
+ var pool;
1257
+ var poolOffset;
1258
+ var fillPool = (bytes) => {
1259
+ if (!pool || pool.length < bytes) {
1260
+ pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
1261
+ _crypto2.default.randomFillSync(pool);
1262
+ poolOffset = 0;
1263
+ } else if (poolOffset + bytes > pool.length) {
1264
+ _crypto2.default.randomFillSync(pool);
1265
+ poolOffset = 0;
1266
+ }
1267
+ poolOffset += bytes;
1268
+ };
1269
+ var nanoid = (size = 21) => {
1270
+ fillPool(size -= 0);
1271
+ let id = "";
1272
+ for (let i = poolOffset - size; i < poolOffset; i++) {
1273
+ id += urlAlphabet[pool[i] & 63];
1274
+ }
1275
+ return id;
1276
+ };
1277
+
1278
+ // src/client/workflow/workflow-parser.ts
1279
+ var getPayload = async (request) => {
1280
+ try {
1281
+ return await request.text();
1282
+ } catch (e2) {
1283
+ return;
1284
+ }
1285
+ };
1286
+ var decodeBase64 = (encodedString) => {
1287
+ return Buffer.from(encodedString, "base64").toString();
1288
+ };
1289
+ var parsePayload = (rawPayload) => {
1290
+ const [encodedInitialPayload, ...encodedSteps] = JSON.parse(rawPayload);
1291
+ const rawInitialPayload = decodeBase64(encodedInitialPayload.body);
1292
+ const initialStep = {
1293
+ stepId: 0,
1294
+ stepName: "init",
1295
+ stepType: "Initial",
1296
+ out: rawInitialPayload,
1297
+ concurrent: 1
1298
+ };
1299
+ const stepsToDecode = encodedSteps.filter((step) => step.callType === "step");
1300
+ const otherSteps = stepsToDecode.map((rawStep) => {
1301
+ return JSON.parse(decodeBase64(rawStep.body));
1302
+ });
1303
+ const steps = [initialStep, ...otherSteps];
1304
+ return {
1305
+ rawInitialPayload,
1306
+ steps
1307
+ };
1308
+ };
1309
+ var deduplicateSteps = (steps) => {
1310
+ const targetStepIds = [];
1311
+ const stepIds = [];
1312
+ const deduplicatedSteps = [];
1313
+ for (const step of steps) {
1314
+ if (step.stepId === 0) {
1315
+ if (!targetStepIds.includes(_nullishCoalesce(step.targetStep, () => ( 0)))) {
1316
+ deduplicatedSteps.push(step);
1317
+ targetStepIds.push(_nullishCoalesce(step.targetStep, () => ( 0)));
1318
+ }
1319
+ } else {
1320
+ if (!stepIds.includes(step.stepId)) {
1321
+ deduplicatedSteps.push(step);
1322
+ stepIds.push(step.stepId);
1323
+ }
1324
+ }
1325
+ }
1326
+ return deduplicatedSteps;
1327
+ };
1328
+ var checkIfLastOneIsDuplicate = async (steps, debug) => {
1329
+ if (steps.length < 2) {
1330
+ return false;
1331
+ }
1332
+ const lastStep = steps.at(-1);
1333
+ const lastStepId = lastStep.stepId;
1334
+ const lastTargetStepId = lastStep.targetStep;
1335
+ for (let index = 0; index < steps.length - 1; index++) {
1336
+ const step = steps[index];
1337
+ if (step.stepId === lastStepId && step.targetStep === lastTargetStepId) {
1338
+ const message = `Qstash Workflow: The step '${step.stepName}' with id '${step.stepId}' has run twice during workflow execution. Rest of the workflow will continue running as usual.`;
1339
+ await _optionalChain([debug, 'optionalAccess', _36 => _36.log, 'call', _37 => _37("WARN", "RESPONSE_DEFAULT", message)]);
1340
+ console.warn(message);
1341
+ return true;
1342
+ }
1343
+ }
1344
+ return false;
1345
+ };
1346
+ var validateRequest = (request) => {
1347
+ const versionHeader = request.headers.get(WORKFLOW_PROTOCOL_VERSION_HEADER);
1348
+ const isFirstInvocation = !versionHeader;
1349
+ if (!isFirstInvocation && versionHeader !== WORKFLOW_PROTOCOL_VERSION) {
1350
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
1351
+ `Incompatible workflow sdk protocol version. Expected ${WORKFLOW_PROTOCOL_VERSION}, got ${versionHeader} from the request.`
1352
+ );
1353
+ }
1354
+ const workflowRunId = isFirstInvocation ? `wfr_${nanoid()}` : _nullishCoalesce(request.headers.get(WORKFLOW_ID_HEADER), () => ( ""));
1355
+ if (workflowRunId.length === 0) {
1356
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)("Couldn't get workflow id from header");
1357
+ }
1358
+ return {
1359
+ isFirstInvocation,
1360
+ workflowRunId
1361
+ };
1362
+ };
1363
+ var parseRequest = async (request, isFirstInvocation, verifier, debug) => {
1364
+ const payload = await getPayload(request);
1365
+ await verifyRequest(_nullishCoalesce(payload, () => ( "")), request.headers.get("upstash-signature"), verifier);
1366
+ if (isFirstInvocation) {
1367
+ return {
1368
+ rawInitialPayload: _nullishCoalesce(payload, () => ( "")),
1369
+ steps: [],
1370
+ isLastDuplicate: false
1371
+ };
1372
+ } else {
1373
+ if (!payload) {
1374
+ throw new (0, _chunkZ3TALRVSjs.QstashWorkflowError)("Only first call can have an empty body");
1375
+ }
1376
+ const { rawInitialPayload, steps } = parsePayload(payload);
1377
+ const isLastDuplicate = await checkIfLastOneIsDuplicate(steps, debug);
1378
+ const deduplicatedSteps = deduplicateSteps(steps);
1379
+ return {
1380
+ rawInitialPayload,
1381
+ steps: deduplicatedSteps,
1382
+ isLastDuplicate
1383
+ };
1384
+ }
1385
+ };
1386
+ var handleFailure = async (request, failureFunction) => {
1387
+ if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
1388
+ return ok("not-failure-callback");
1389
+ }
1390
+ if (!failureFunction) {
1391
+ return err(
1392
+ new (0, _chunkZ3TALRVSjs.QstashWorkflowError)(
1393
+ "Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."
1394
+ )
1395
+ );
1396
+ }
1397
+ try {
1398
+ const { status, header, body } = await request.json();
1399
+ const decodedBody = atob(body);
1400
+ await failureFunction(status, header, decodedBody);
1401
+ } catch (error) {
1402
+ return err(error);
1403
+ }
1404
+ return ok("is-failure-callback");
1405
+ };
1406
+
1407
+ // src/client/workflow/serve.ts
1408
+ var processOptions = (options) => {
1409
+ const receiverEnvironmentVariablesSet = Boolean(
1410
+ process.env.QSTASH_CURRENT_SIGNING_KEY && process.env.QSTASH_NEXT_SIGNING_KEY
1411
+ );
1412
+ return {
1413
+ client: new (0, _chunkZ3TALRVSjs.Client)({
1414
+ baseUrl: process.env.QSTASH_URL,
1415
+ token: process.env.QSTASH_TOKEN
1416
+ }),
1417
+ onStepFinish: (workflowRunId, finishCondition) => new Response(JSON.stringify({ workflowRunId, finishCondition }), {
1418
+ status: 200
1419
+ }),
1420
+ initialPayloadParser: (initialRequest) => {
1421
+ if (!initialRequest) {
1422
+ return void 0;
1423
+ }
1424
+ try {
1425
+ return JSON.parse(initialRequest);
1426
+ } catch (error) {
1427
+ if (error instanceof SyntaxError) {
1428
+ return initialRequest;
1429
+ }
1430
+ throw error;
1431
+ }
1432
+ },
1433
+ url: "",
1434
+ // will be overwritten with request.url
1435
+ verbose: false,
1436
+ // initialize a receiver if the env variables are set:
1437
+ receiver: receiverEnvironmentVariablesSet && new (0, _chunkZ3TALRVSjs.Receiver)({
1438
+ currentSigningKey: process.env.QSTASH_CURRENT_SIGNING_KEY,
1439
+ nextSigningKey: process.env.QSTASH_NEXT_SIGNING_KEY
1440
+ }),
1441
+ failureUrl: false,
1442
+ failureFunction: false,
1443
+ ...options
1444
+ };
1445
+ };
1446
+ var serve = ({
1447
+ routeFunction,
1448
+ options
1449
+ }) => {
1450
+ const {
1451
+ client,
1452
+ onStepFinish,
1453
+ initialPayloadParser,
1454
+ url,
1455
+ verbose,
1456
+ receiver,
1457
+ failureUrl,
1458
+ failureFunction
1459
+ } = processOptions(options);
1460
+ const debug = WorkflowLogger.getLogger(verbose);
1461
+ const verifier = receiver || void 0;
1462
+ return async (request) => {
1463
+ const workflowUrl = url || request.url;
1464
+ const workflowFailureUrl = failureFunction ? workflowUrl : failureUrl;
1465
+ await _optionalChain([debug, 'optionalAccess', _38 => _38.log, 'call', _39 => _39("INFO", "ENDPOINT_START")]);
1466
+ const failureCheck = await handleFailure(request, failureFunction);
1467
+ if (failureCheck.isErr()) {
1468
+ throw failureCheck.error;
1469
+ } else if (failureCheck.value === "is-failure-callback") {
1470
+ return onStepFinish("no-workflow-id", "failure-callback");
1471
+ }
1472
+ const { isFirstInvocation, workflowRunId } = validateRequest(request);
1473
+ const { rawInitialPayload, steps, isLastDuplicate } = await parseRequest(
1474
+ request,
1475
+ isFirstInvocation,
1476
+ verifier,
1477
+ debug
1478
+ );
1479
+ if (isLastDuplicate) {
1480
+ return onStepFinish("no-workflow-id", "duplicate-step");
1481
+ }
1482
+ const workflowContext = new WorkflowContext({
1483
+ client,
1484
+ workflowRunId,
1485
+ initialPayload: initialPayloadParser(rawInitialPayload),
1486
+ rawInitialPayload,
1487
+ headers: recreateUserHeaders(request.headers),
1488
+ steps,
1489
+ url: workflowUrl,
1490
+ failureUrl: workflowFailureUrl,
1491
+ debug
1492
+ });
1493
+ const authCheck = await DisabledWorkflowContext.tryAuthentication(
1494
+ routeFunction,
1495
+ workflowContext
1496
+ );
1497
+ if (authCheck.isErr()) {
1498
+ await _optionalChain([debug, 'optionalAccess', _40 => _40.log, 'call', _41 => _41("ERROR", "ERROR", { error: authCheck.error })]);
1499
+ throw authCheck.error;
1500
+ } else if (authCheck.value === "run-ended") {
1501
+ return onStepFinish("no-workflow-id", "auth-fail");
1502
+ }
1503
+ const callReturnCheck = await handleThirdPartyCallResult(
1504
+ request,
1505
+ rawInitialPayload,
1506
+ client,
1507
+ workflowFailureUrl,
1508
+ debug
1509
+ );
1510
+ if (callReturnCheck.isErr()) {
1511
+ await _optionalChain([debug, 'optionalAccess', _42 => _42.log, 'call', _43 => _43("ERROR", "SUBMIT_THIRD_PARTY_RESULT", { error: callReturnCheck.error })]);
1512
+ throw callReturnCheck.error;
1513
+ } else if (callReturnCheck.value === "continue-workflow") {
1514
+ const result = isFirstInvocation ? await triggerFirstInvocation(workflowContext, debug) : await triggerRouteFunction({
1515
+ onStep: async () => routeFunction(workflowContext),
1516
+ onCleanup: async () => {
1517
+ await triggerWorkflowDelete(workflowContext, debug);
1518
+ }
1519
+ });
1520
+ if (result.isErr()) {
1521
+ await _optionalChain([debug, 'optionalAccess', _44 => _44.log, 'call', _45 => _45("ERROR", "ERROR", { error: result.error })]);
1522
+ throw result.error;
1523
+ }
1524
+ await _optionalChain([debug, 'optionalAccess', _46 => _46.log, 'call', _47 => _47("INFO", "RESPONSE_WORKFLOW", {
1525
+ workflowRunId: workflowContext.workflowRunId
1526
+ })]);
1527
+ return onStepFinish(workflowContext.workflowRunId, "success");
1528
+ }
1529
+ await _optionalChain([debug, 'optionalAccess', _48 => _48.log, 'call', _49 => _49("INFO", "RESPONSE_DEFAULT")]);
1530
+ return onStepFinish("no-workflow-id", "fromCallback");
1531
+ };
1532
+ };
1533
+
1534
+
1535
+
1536
+
1537
+
1538
+
1539
+
1540
+ exports.StepTypes = StepTypes; exports.WorkflowContext = WorkflowContext; exports.DisabledWorkflowContext = DisabledWorkflowContext; exports.WorkflowLogger = WorkflowLogger; exports.serve = serve;