@upstash/workflow 0.2.8 → 0.2.9

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.
@@ -36,12 +36,13 @@ var WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init";
36
36
  var WORKFLOW_URL_HEADER = "Upstash-Workflow-Url";
37
37
  var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
38
38
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
39
+ var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
39
40
  var WORKFLOW_PROTOCOL_VERSION = "1";
40
41
  var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
41
42
  var DEFAULT_CONTENT_TYPE = "application/json";
42
43
  var NO_CONCURRENCY = 1;
43
44
  var DEFAULT_RETRIES = 3;
44
- var VERSION = "v0.2.3";
45
+ var VERSION = "v0.2.7";
45
46
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
46
47
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
47
48
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -96,410 +97,71 @@ var StepTypes = [
96
97
  "SleepUntil",
97
98
  "Call",
98
99
  "Wait",
99
- "Notify"
100
+ "Notify",
101
+ "Invoke"
100
102
  ];
101
103
 
102
- // src/agents/adapters.ts
103
- import { createOpenAI } from "@ai-sdk/openai";
104
- import { tool } from "ai";
105
-
106
- // src/agents/constants.ts
107
- var AGENT_NAME_HEADER = "upstash-agent-name";
108
- var MANAGER_AGENT_PROMPT = `You are an agent orchestrating other AI Agents.
109
-
110
- These other agents have tools available to them.
111
-
112
- Given a prompt, utilize these agents to address requests.
113
-
114
- Don't always call all the agents provided to you at the same time. You can call one and use it's response to call another.
115
-
116
- Avoid calling the same agent twice in one turn. Instead, prefer to call it once but provide everything
117
- you need from that agent.
118
- `;
119
-
120
- // src/agents/adapters.ts
121
- var createWorkflowOpenAI = (context, config) => {
122
- const { baseURL, apiKey } = config ?? {};
123
- return createOpenAI({
124
- baseURL,
125
- apiKey,
126
- compatibility: "strict",
127
- fetch: async (input, init) => {
128
- try {
129
- const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
130
- const body = init?.body ? JSON.parse(init.body) : void 0;
131
- const agentName = headers[AGENT_NAME_HEADER];
132
- const stepName = agentName ? `Call Agent ${agentName}` : "Call Agent";
133
- const responseInfo = await context.call(stepName, {
134
- url: input.toString(),
135
- method: init?.method,
136
- headers,
137
- body
138
- });
139
- const responseHeaders = new Headers(
140
- Object.entries(responseInfo.header).reduce(
141
- (acc, [key, values]) => {
142
- acc[key] = values.join(", ");
143
- return acc;
144
- },
145
- {}
146
- )
147
- );
148
- return new Response(JSON.stringify(responseInfo.body), {
149
- status: responseInfo.status,
150
- headers: responseHeaders
151
- });
152
- } catch (error) {
153
- if (error instanceof Error && error.name === "WorkflowAbort") {
154
- throw error;
155
- } else {
156
- console.error("Error in fetch implementation:", error);
157
- throw error;
158
- }
159
- }
160
- }
161
- });
162
- };
163
- var wrapTools = ({
164
- context,
165
- tools
166
- }) => {
167
- return Object.fromEntries(
168
- Object.entries(tools).map((toolInfo) => {
169
- const [toolName, tool3] = toolInfo;
170
- const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
171
- const aiSDKTool = convertToAISDKTool(tool3);
172
- const execute = aiSDKTool.execute;
173
- if (execute && executeAsStep) {
174
- const wrappedExecute = (...params) => {
175
- return context.run(`Run tool ${toolName}`, () => execute(...params));
176
- };
177
- aiSDKTool.execute = wrappedExecute;
178
- }
179
- return [toolName, aiSDKTool];
180
- })
181
- );
182
- };
183
- var convertToAISDKTool = (tool3) => {
184
- const isLangchainTool = "invoke" in tool3;
185
- return isLangchainTool ? convertLangchainTool(tool3) : tool3;
186
- };
187
- var convertLangchainTool = (langchainTool) => {
188
- return tool({
189
- description: langchainTool.description,
190
- parameters: langchainTool.schema,
191
- execute: async (...param) => langchainTool.invoke(...param)
192
- });
193
- };
194
- var WorkflowTool = class {
195
- /**
196
- * description of the tool
197
- */
198
- description;
199
- /**
200
- * schema of the tool
201
- */
202
- schema;
203
- /**
204
- * function to invoke the tool
205
- */
206
- invoke;
207
- /**
208
- * whether the invoke method of the tool is to be wrapped with `context.run`
209
- */
210
- executeAsStep;
211
- /**
212
- *
213
- * @param description description of the tool
214
- * @param schema schema of the tool
215
- * @param invoke function to invoke the tool
216
- * @param executeAsStep whether the invoke method of the tool is to be wrapped with `context.run`
217
- */
218
- constructor(params) {
219
- this.description = params.description;
220
- this.schema = params.schema;
221
- this.invoke = params.invoke;
222
- this.executeAsStep = params.executeAsStep ?? true;
104
+ // src/utils.ts
105
+ var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
106
+ var NANOID_LENGTH = 21;
107
+ function getRandomInt() {
108
+ return Math.floor(Math.random() * NANOID_CHARS.length);
109
+ }
110
+ function nanoid() {
111
+ return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
112
+ }
113
+ function getWorkflowRunId(id) {
114
+ return `wfr_${id ?? nanoid()}`;
115
+ }
116
+ function decodeBase64(base64) {
117
+ try {
118
+ const binString = atob(base64);
119
+ const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
120
+ return new TextDecoder().decode(intArray);
121
+ } catch (error) {
122
+ console.warn(
123
+ `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
124
+ );
125
+ return atob(base64);
223
126
  }
224
- };
127
+ }
225
128
 
226
- // src/client/utils.ts
227
- import { QstashError as QstashError2 } from "@upstash/qstash";
228
- var makeNotifyRequest = async (requester, eventId, eventData) => {
229
- const result = await requester.request({
230
- path: ["v2", "notify", eventId],
231
- method: "POST",
232
- body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
233
- });
234
- return result;
235
- };
236
- var makeGetWaitersRequest = async (requester, eventId) => {
237
- const result = await requester.request({
238
- path: ["v2", "waiters", eventId],
239
- method: "GET"
240
- });
241
- return result;
129
+ // node_modules/neverthrow/dist/index.es.js
130
+ var defaultErrorConfig = {
131
+ withStackTrace: false
242
132
  };
243
- var makeCancelRequest = async (requester, workflowRunId) => {
244
- await requester.request({
245
- path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
246
- method: "DELETE",
247
- parseResponseAsJson: false
248
- });
249
- return true;
133
+ var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
134
+ const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
135
+ const maybeStack = config.withStackTrace ? new Error().stack : void 0;
136
+ return {
137
+ data,
138
+ message,
139
+ stack: maybeStack
140
+ };
250
141
  };
251
- var getSteps = async (requester, workflowRunId, messageId, debug) => {
252
- try {
253
- const steps = await requester.request({
254
- path: ["v2", "workflows", "runs", workflowRunId],
255
- parseResponseAsJson: true
142
+ function __awaiter(thisArg, _arguments, P, generator) {
143
+ function adopt(value) {
144
+ return value instanceof P ? value : new P(function(resolve) {
145
+ resolve(value);
256
146
  });
257
- if (!messageId) {
258
- await debug?.log("INFO", "ENDPOINT_START", {
259
- message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
260
- });
261
- return { steps, workflowRunEnded: false };
262
- } else {
263
- const index = steps.findIndex((item) => item.messageId === messageId);
264
- if (index === -1) {
265
- return { steps: [], workflowRunEnded: false };
147
+ }
148
+ return new (P || (P = Promise))(function(resolve, reject) {
149
+ function fulfilled(value) {
150
+ try {
151
+ step(generator.next(value));
152
+ } catch (e) {
153
+ reject(e);
266
154
  }
267
- const filteredSteps = steps.slice(0, index + 1);
268
- await debug?.log("INFO", "ENDPOINT_START", {
269
- message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
270
- });
271
- return { steps: filteredSteps, workflowRunEnded: false };
272
155
  }
273
- } catch (error) {
274
- if (error instanceof QstashError2 && error.status === 404) {
275
- await debug?.log("WARN", "ENDPOINT_START", {
276
- message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
277
- error
278
- });
279
- return { steps: void 0, workflowRunEnded: true };
280
- } else {
281
- throw error;
156
+ function rejected(value) {
157
+ try {
158
+ step(generator["throw"](value));
159
+ } catch (e) {
160
+ reject(e);
161
+ }
282
162
  }
283
- }
284
- };
285
-
286
- // src/context/steps.ts
287
- var BaseLazyStep = class {
288
- stepName;
289
- // will be set in the subclasses
290
- constructor(stepName) {
291
- if (!stepName) {
292
- throw new WorkflowError(
293
- "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
294
- );
295
- }
296
- this.stepName = stepName;
297
- }
298
- };
299
- var LazyFunctionStep = class extends BaseLazyStep {
300
- stepFunction;
301
- stepType = "Run";
302
- constructor(stepName, stepFunction) {
303
- super(stepName);
304
- this.stepFunction = stepFunction;
305
- }
306
- getPlanStep(concurrent, targetStep) {
307
- return {
308
- stepId: 0,
309
- stepName: this.stepName,
310
- stepType: this.stepType,
311
- concurrent,
312
- targetStep
313
- };
314
- }
315
- async getResultStep(concurrent, stepId) {
316
- let result = this.stepFunction();
317
- if (result instanceof Promise) {
318
- result = await result;
319
- }
320
- return {
321
- stepId,
322
- stepName: this.stepName,
323
- stepType: this.stepType,
324
- out: result,
325
- concurrent
326
- };
327
- }
328
- };
329
- var LazySleepStep = class extends BaseLazyStep {
330
- sleep;
331
- stepType = "SleepFor";
332
- constructor(stepName, sleep) {
333
- super(stepName);
334
- this.sleep = sleep;
335
- }
336
- getPlanStep(concurrent, targetStep) {
337
- return {
338
- stepId: 0,
339
- stepName: this.stepName,
340
- stepType: this.stepType,
341
- sleepFor: this.sleep,
342
- concurrent,
343
- targetStep
344
- };
345
- }
346
- async getResultStep(concurrent, stepId) {
347
- return await Promise.resolve({
348
- stepId,
349
- stepName: this.stepName,
350
- stepType: this.stepType,
351
- sleepFor: this.sleep,
352
- concurrent
353
- });
354
- }
355
- };
356
- var LazySleepUntilStep = class extends BaseLazyStep {
357
- sleepUntil;
358
- stepType = "SleepUntil";
359
- constructor(stepName, sleepUntil) {
360
- super(stepName);
361
- this.sleepUntil = sleepUntil;
362
- }
363
- getPlanStep(concurrent, targetStep) {
364
- return {
365
- stepId: 0,
366
- stepName: this.stepName,
367
- stepType: this.stepType,
368
- sleepUntil: this.sleepUntil,
369
- concurrent,
370
- targetStep
371
- };
372
- }
373
- async getResultStep(concurrent, stepId) {
374
- return await Promise.resolve({
375
- stepId,
376
- stepName: this.stepName,
377
- stepType: this.stepType,
378
- sleepUntil: this.sleepUntil,
379
- concurrent
380
- });
381
- }
382
- };
383
- var LazyCallStep = class extends BaseLazyStep {
384
- url;
385
- method;
386
- body;
387
- headers;
388
- retries;
389
- timeout;
390
- stepType = "Call";
391
- constructor(stepName, url, method, body, headers, retries, timeout) {
392
- super(stepName);
393
- this.url = url;
394
- this.method = method;
395
- this.body = body;
396
- this.headers = headers;
397
- this.retries = retries;
398
- this.timeout = timeout;
399
- }
400
- getPlanStep(concurrent, targetStep) {
401
- return {
402
- stepId: 0,
403
- stepName: this.stepName,
404
- stepType: this.stepType,
405
- concurrent,
406
- targetStep
407
- };
408
- }
409
- async getResultStep(concurrent, stepId) {
410
- return await Promise.resolve({
411
- stepId,
412
- stepName: this.stepName,
413
- stepType: this.stepType,
414
- concurrent,
415
- callUrl: this.url,
416
- callMethod: this.method,
417
- callBody: this.body,
418
- callHeaders: this.headers
419
- });
420
- }
421
- };
422
- var LazyWaitForEventStep = class extends BaseLazyStep {
423
- eventId;
424
- timeout;
425
- stepType = "Wait";
426
- constructor(stepName, eventId, timeout) {
427
- super(stepName);
428
- this.eventId = eventId;
429
- this.timeout = timeout;
430
- }
431
- getPlanStep(concurrent, targetStep) {
432
- return {
433
- stepId: 0,
434
- stepName: this.stepName,
435
- stepType: this.stepType,
436
- waitEventId: this.eventId,
437
- timeout: this.timeout,
438
- concurrent,
439
- targetStep
440
- };
441
- }
442
- async getResultStep(concurrent, stepId) {
443
- return await Promise.resolve({
444
- stepId,
445
- stepName: this.stepName,
446
- stepType: this.stepType,
447
- waitEventId: this.eventId,
448
- timeout: this.timeout,
449
- concurrent
450
- });
451
- }
452
- };
453
- var LazyNotifyStep = class extends LazyFunctionStep {
454
- stepType = "Notify";
455
- constructor(stepName, eventId, eventData, requester) {
456
- super(stepName, async () => {
457
- const notifyResponse = await makeNotifyRequest(requester, eventId, eventData);
458
- return {
459
- eventId,
460
- eventData,
461
- notifyResponse
462
- };
463
- });
464
- }
465
- };
466
-
467
- // node_modules/neverthrow/dist/index.es.js
468
- var defaultErrorConfig = {
469
- withStackTrace: false
470
- };
471
- var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
472
- const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
473
- const maybeStack = config.withStackTrace ? new Error().stack : void 0;
474
- return {
475
- data,
476
- message,
477
- stack: maybeStack
478
- };
479
- };
480
- function __awaiter(thisArg, _arguments, P, generator) {
481
- function adopt(value) {
482
- return value instanceof P ? value : new P(function(resolve) {
483
- resolve(value);
484
- });
485
- }
486
- return new (P || (P = Promise))(function(resolve, reject) {
487
- function fulfilled(value) {
488
- try {
489
- step(generator.next(value));
490
- } catch (e) {
491
- reject(e);
492
- }
493
- }
494
- function rejected(value) {
495
- try {
496
- step(generator["throw"](value));
497
- } catch (e) {
498
- reject(e);
499
- }
500
- }
501
- function step(result) {
502
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
163
+ function step(result) {
164
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
503
165
  }
504
166
  step((generator = generator.apply(thisArg, [])).next());
505
167
  });
@@ -702,556 +364,1154 @@ var combineResultList = (resultList) => {
702
364
  acc = err(result.error);
703
365
  break;
704
366
  } else {
705
- acc.map((list) => list.push(result.value));
367
+ acc.map((list) => list.push(result.value));
368
+ }
369
+ }
370
+ return acc;
371
+ };
372
+ var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
373
+ var combineResultListWithAllErrors = (resultList) => {
374
+ let acc = ok([]);
375
+ for (const result of resultList) {
376
+ if (result.isErr() && acc.isErr()) {
377
+ acc.error.push(result.error);
378
+ } else if (result.isErr() && acc.isOk()) {
379
+ acc = err([result.error]);
380
+ } else if (result.isOk() && acc.isOk()) {
381
+ acc.value.push(result.value);
382
+ }
383
+ }
384
+ return acc;
385
+ };
386
+ var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
387
+ var Result;
388
+ (function(Result2) {
389
+ function fromThrowable2(fn, errorFn) {
390
+ return (...args) => {
391
+ try {
392
+ const result = fn(...args);
393
+ return ok(result);
394
+ } catch (e) {
395
+ return err(errorFn ? errorFn(e) : e);
396
+ }
397
+ };
398
+ }
399
+ Result2.fromThrowable = fromThrowable2;
400
+ function combine(resultList) {
401
+ return combineResultList(resultList);
402
+ }
403
+ Result2.combine = combine;
404
+ function combineWithAllErrors(resultList) {
405
+ return combineResultListWithAllErrors(resultList);
406
+ }
407
+ Result2.combineWithAllErrors = combineWithAllErrors;
408
+ })(Result || (Result = {}));
409
+ var ok = (value) => new Ok(value);
410
+ function err(err2) {
411
+ return new Err(err2);
412
+ }
413
+ var Ok = class {
414
+ constructor(value) {
415
+ this.value = value;
416
+ }
417
+ isOk() {
418
+ return true;
419
+ }
420
+ isErr() {
421
+ return !this.isOk();
422
+ }
423
+ map(f) {
424
+ return ok(f(this.value));
425
+ }
426
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
427
+ mapErr(_f) {
428
+ return ok(this.value);
429
+ }
430
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
431
+ andThen(f) {
432
+ return f(this.value);
433
+ }
434
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
435
+ andThrough(f) {
436
+ return f(this.value).map((_value) => this.value);
437
+ }
438
+ andTee(f) {
439
+ try {
440
+ f(this.value);
441
+ } catch (e) {
442
+ }
443
+ return ok(this.value);
444
+ }
445
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
446
+ orElse(_f) {
447
+ return ok(this.value);
448
+ }
449
+ asyncAndThen(f) {
450
+ return f(this.value);
451
+ }
452
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
453
+ asyncAndThrough(f) {
454
+ return f(this.value).map(() => this.value);
455
+ }
456
+ asyncMap(f) {
457
+ return ResultAsync.fromSafePromise(f(this.value));
458
+ }
459
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
460
+ unwrapOr(_v) {
461
+ return this.value;
462
+ }
463
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
464
+ match(ok2, _err) {
465
+ return ok2(this.value);
466
+ }
467
+ safeUnwrap() {
468
+ const value = this.value;
469
+ return function* () {
470
+ return value;
471
+ }();
472
+ }
473
+ _unsafeUnwrap(_) {
474
+ return this.value;
475
+ }
476
+ _unsafeUnwrapErr(config) {
477
+ throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
478
+ }
479
+ };
480
+ var Err = class {
481
+ constructor(error) {
482
+ this.error = error;
483
+ }
484
+ isOk() {
485
+ return false;
486
+ }
487
+ isErr() {
488
+ return !this.isOk();
489
+ }
490
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
491
+ map(_f) {
492
+ return err(this.error);
493
+ }
494
+ mapErr(f) {
495
+ return err(f(this.error));
496
+ }
497
+ andThrough(_f) {
498
+ return err(this.error);
499
+ }
500
+ andTee(_f) {
501
+ return err(this.error);
502
+ }
503
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
504
+ andThen(_f) {
505
+ return err(this.error);
506
+ }
507
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
508
+ orElse(f) {
509
+ return f(this.error);
510
+ }
511
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
512
+ asyncAndThen(_f) {
513
+ return errAsync(this.error);
514
+ }
515
+ asyncAndThrough(_f) {
516
+ return errAsync(this.error);
517
+ }
518
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
519
+ asyncMap(_f) {
520
+ return errAsync(this.error);
521
+ }
522
+ unwrapOr(v) {
523
+ return v;
524
+ }
525
+ match(_ok, err2) {
526
+ return err2(this.error);
527
+ }
528
+ safeUnwrap() {
529
+ const error = this.error;
530
+ return function* () {
531
+ yield err(error);
532
+ throw new Error("Do not use this generator out of `safeTry`");
533
+ }();
534
+ }
535
+ _unsafeUnwrap(config) {
536
+ throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
537
+ }
538
+ _unsafeUnwrapErr(_) {
539
+ return this.error;
540
+ }
541
+ };
542
+ var fromThrowable = Result.fromThrowable;
543
+
544
+ // src/workflow-requests.ts
545
+ import { QstashError as QstashError3 } from "@upstash/qstash";
546
+
547
+ // src/client/utils.ts
548
+ import { QstashError as QstashError2 } from "@upstash/qstash";
549
+ var makeNotifyRequest = async (requester, eventId, eventData) => {
550
+ const result = await requester.request({
551
+ path: ["v2", "notify", eventId],
552
+ method: "POST",
553
+ body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
554
+ });
555
+ return result;
556
+ };
557
+ var makeGetWaitersRequest = async (requester, eventId) => {
558
+ const result = await requester.request({
559
+ path: ["v2", "waiters", eventId],
560
+ method: "GET"
561
+ });
562
+ return result;
563
+ };
564
+ var makeCancelRequest = async (requester, workflowRunId) => {
565
+ await requester.request({
566
+ path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
567
+ method: "DELETE",
568
+ parseResponseAsJson: false
569
+ });
570
+ return true;
571
+ };
572
+ var getSteps = async (requester, workflowRunId, messageId, debug) => {
573
+ try {
574
+ const steps = await requester.request({
575
+ path: ["v2", "workflows", "runs", workflowRunId],
576
+ parseResponseAsJson: true
577
+ });
578
+ if (!messageId) {
579
+ await debug?.log("INFO", "ENDPOINT_START", {
580
+ message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
581
+ });
582
+ return { steps, workflowRunEnded: false };
583
+ } else {
584
+ const index = steps.findIndex((item) => item.messageId === messageId);
585
+ if (index === -1) {
586
+ return { steps: [], workflowRunEnded: false };
587
+ }
588
+ const filteredSteps = steps.slice(0, index + 1);
589
+ await debug?.log("INFO", "ENDPOINT_START", {
590
+ message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
591
+ });
592
+ return { steps: filteredSteps, workflowRunEnded: false };
593
+ }
594
+ } catch (error) {
595
+ if (error instanceof QstashError2 && error.status === 404) {
596
+ await debug?.log("WARN", "ENDPOINT_START", {
597
+ message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
598
+ error
599
+ });
600
+ return { steps: void 0, workflowRunEnded: true };
601
+ } else {
602
+ throw error;
603
+ }
604
+ }
605
+ };
606
+
607
+ // src/workflow-requests.ts
608
+ var triggerFirstInvocation = async ({
609
+ workflowContext,
610
+ useJSONContent,
611
+ telemetry,
612
+ debug,
613
+ invokeCount
614
+ }) => {
615
+ const { headers } = getHeaders({
616
+ initHeaderValue: "true",
617
+ workflowRunId: workflowContext.workflowRunId,
618
+ workflowUrl: workflowContext.url,
619
+ userHeaders: workflowContext.headers,
620
+ failureUrl: workflowContext.failureUrl,
621
+ retries: workflowContext.retries,
622
+ telemetry,
623
+ invokeCount,
624
+ flowControl: workflowContext.flowControl
625
+ });
626
+ if (workflowContext.headers.get("content-type")) {
627
+ headers["content-type"] = workflowContext.headers.get("content-type");
628
+ }
629
+ if (useJSONContent) {
630
+ headers["content-type"] = "application/json";
631
+ }
632
+ try {
633
+ const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
634
+ const result = await workflowContext.qstashClient.publish({
635
+ headers,
636
+ method: "POST",
637
+ body,
638
+ url: workflowContext.url
639
+ });
640
+ if (result.deduplicated) {
641
+ await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
642
+ message: `Workflow run ${workflowContext.workflowRunId} already exists. A new one isn't created.`,
643
+ headers,
644
+ requestPayload: workflowContext.requestPayload,
645
+ url: workflowContext.url,
646
+ messageId: result.messageId
647
+ });
648
+ return ok("workflow-run-already-exists");
649
+ } else {
650
+ await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
651
+ headers,
652
+ requestPayload: workflowContext.requestPayload,
653
+ url: workflowContext.url,
654
+ messageId: result.messageId
655
+ });
656
+ return ok("success");
657
+ }
658
+ } catch (error) {
659
+ const error_ = error;
660
+ return err(error_);
661
+ }
662
+ };
663
+ var triggerRouteFunction = async ({
664
+ onCleanup,
665
+ onStep,
666
+ onCancel,
667
+ debug
668
+ }) => {
669
+ try {
670
+ const result = await onStep();
671
+ await onCleanup(result);
672
+ return ok("workflow-finished");
673
+ } catch (error) {
674
+ const error_ = error;
675
+ if (error instanceof QstashError3 && error.status === 400) {
676
+ await debug?.log("WARN", "RESPONSE_WORKFLOW", {
677
+ message: `tried to append to a cancelled workflow. exiting without publishing.`,
678
+ name: error.name,
679
+ errorMessage: error.message
680
+ });
681
+ return ok("workflow-was-finished");
682
+ } else if (!(error_ instanceof WorkflowAbort)) {
683
+ return err(error_);
684
+ } else if (error_.cancelWorkflow) {
685
+ await onCancel();
686
+ return ok("workflow-finished");
687
+ } else {
688
+ return ok("step-finished");
689
+ }
690
+ }
691
+ };
692
+ var triggerWorkflowDelete = async (workflowContext, result, debug, cancel = false) => {
693
+ await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
694
+ deletedWorkflowRunId: workflowContext.workflowRunId
695
+ });
696
+ await workflowContext.qstashClient.http.request({
697
+ path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
698
+ method: "DELETE",
699
+ parseResponseAsJson: false,
700
+ body: JSON.stringify(result)
701
+ });
702
+ await debug?.log(
703
+ "SUBMIT",
704
+ "SUBMIT_CLEANUP",
705
+ `workflow run ${workflowContext.workflowRunId} deleted.`
706
+ );
707
+ };
708
+ var recreateUserHeaders = (headers) => {
709
+ const filteredHeaders = new Headers();
710
+ const pairs = headers.entries();
711
+ for (const [header, value] of pairs) {
712
+ const headerLowerCase = header.toLowerCase();
713
+ if (!headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
714
+ !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
715
+ headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
716
+ headerLowerCase !== "render-proxy-ttl") {
717
+ filteredHeaders.append(header, value);
718
+ }
719
+ }
720
+ return filteredHeaders;
721
+ };
722
+ var handleThirdPartyCallResult = async ({
723
+ request,
724
+ requestPayload,
725
+ client,
726
+ workflowUrl,
727
+ failureUrl,
728
+ retries,
729
+ telemetry,
730
+ flowControl,
731
+ debug
732
+ }) => {
733
+ try {
734
+ if (request.headers.get("Upstash-Workflow-Callback")) {
735
+ let callbackPayload;
736
+ if (requestPayload) {
737
+ callbackPayload = requestPayload;
738
+ } else {
739
+ const workflowRunId2 = request.headers.get("upstash-workflow-runid");
740
+ const messageId = request.headers.get("upstash-message-id");
741
+ if (!workflowRunId2)
742
+ throw new WorkflowError("workflow run id missing in context.call lazy fetch.");
743
+ if (!messageId) throw new WorkflowError("message id missing in context.call lazy fetch.");
744
+ const { steps, workflowRunEnded } = await getSteps(
745
+ client.http,
746
+ workflowRunId2,
747
+ messageId,
748
+ debug
749
+ );
750
+ if (workflowRunEnded) {
751
+ return ok("workflow-ended");
752
+ }
753
+ const failingStep = steps.find((step) => step.messageId === messageId);
754
+ if (!failingStep)
755
+ throw new WorkflowError(
756
+ "Failed to submit the context.call. " + (steps.length === 0 ? "No steps found." : `No step was found with matching messageId ${messageId} out of ${steps.length} steps.`)
757
+ );
758
+ callbackPayload = atob(failingStep.body);
759
+ }
760
+ const callbackMessage = JSON.parse(callbackPayload);
761
+ if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
762
+ await debug?.log("WARN", "SUBMIT_THIRD_PARTY_RESULT", {
763
+ status: callbackMessage.status,
764
+ body: atob(callbackMessage.body ?? "")
765
+ });
766
+ console.warn(
767
+ `Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
768
+ ${atob(callbackMessage.body ?? "")}`
769
+ );
770
+ return ok("call-will-retry");
771
+ }
772
+ const workflowRunId = request.headers.get(WORKFLOW_ID_HEADER);
773
+ const stepIdString = request.headers.get("Upstash-Workflow-StepId");
774
+ const stepName = request.headers.get("Upstash-Workflow-StepName");
775
+ const stepType = request.headers.get("Upstash-Workflow-StepType");
776
+ const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
777
+ const contentType = request.headers.get("Upstash-Workflow-ContentType");
778
+ const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
779
+ if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
780
+ throw new Error(
781
+ `Missing info in callback message source header: ${JSON.stringify({
782
+ workflowRunId,
783
+ stepIdString,
784
+ stepName,
785
+ stepType,
786
+ concurrentString,
787
+ contentType
788
+ })}`
789
+ );
790
+ }
791
+ const userHeaders = recreateUserHeaders(request.headers);
792
+ const { headers: requestHeaders } = getHeaders({
793
+ initHeaderValue: "false",
794
+ workflowRunId,
795
+ workflowUrl,
796
+ userHeaders,
797
+ failureUrl,
798
+ retries,
799
+ telemetry,
800
+ invokeCount: Number(invokeCount),
801
+ flowControl
802
+ });
803
+ const callResponse = {
804
+ status: callbackMessage.status,
805
+ body: atob(callbackMessage.body ?? ""),
806
+ header: callbackMessage.header
807
+ };
808
+ const callResultStep = {
809
+ stepId: Number(stepIdString),
810
+ stepName,
811
+ stepType,
812
+ out: JSON.stringify(callResponse),
813
+ concurrent: Number(concurrentString)
814
+ };
815
+ await debug?.log("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
816
+ step: callResultStep,
817
+ headers: requestHeaders,
818
+ url: workflowUrl
819
+ });
820
+ const result = await client.publishJSON({
821
+ headers: requestHeaders,
822
+ method: "POST",
823
+ body: callResultStep,
824
+ url: workflowUrl
825
+ });
826
+ await debug?.log("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
827
+ messageId: result.messageId
828
+ });
829
+ return ok("is-call-return");
830
+ } else {
831
+ return ok("continue-workflow");
706
832
  }
833
+ } catch (error) {
834
+ const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
835
+ return err(
836
+ new WorkflowError(`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`)
837
+ );
707
838
  }
708
- return acc;
709
839
  };
710
- var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
711
- var combineResultListWithAllErrors = (resultList) => {
712
- let acc = ok([]);
713
- for (const result of resultList) {
714
- if (result.isErr() && acc.isErr()) {
715
- acc.error.push(result.error);
716
- } else if (result.isErr() && acc.isOk()) {
717
- acc = err([result.error]);
718
- } else if (result.isOk() && acc.isOk()) {
719
- acc.value.push(result.value);
720
- }
721
- }
722
- return acc;
840
+ var getTelemetryHeaders = (telemetry) => {
841
+ return {
842
+ [TELEMETRY_HEADER_SDK]: telemetry.sdk,
843
+ [TELEMETRY_HEADER_FRAMEWORK]: telemetry.framework,
844
+ [TELEMETRY_HEADER_RUNTIME]: telemetry.runtime ?? "unknown"
845
+ };
723
846
  };
724
- var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
725
- var Result;
726
- (function(Result2) {
727
- function fromThrowable2(fn, errorFn) {
728
- return (...args) => {
729
- try {
730
- const result = fn(...args);
731
- return ok(result);
732
- } catch (e) {
733
- return err(errorFn ? errorFn(e) : e);
734
- }
735
- };
736
- }
737
- Result2.fromThrowable = fromThrowable2;
738
- function combine(resultList) {
739
- return combineResultList(resultList);
740
- }
741
- Result2.combine = combine;
742
- function combineWithAllErrors(resultList) {
743
- return combineResultListWithAllErrors(resultList);
744
- }
745
- Result2.combineWithAllErrors = combineWithAllErrors;
746
- })(Result || (Result = {}));
747
- var ok = (value) => new Ok(value);
748
- function err(err2) {
749
- return new Err(err2);
750
- }
751
- var Ok = class {
752
- constructor(value) {
753
- this.value = value;
754
- }
755
- isOk() {
756
- return true;
757
- }
758
- isErr() {
759
- return !this.isOk();
760
- }
761
- map(f) {
762
- return ok(f(this.value));
763
- }
764
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
765
- mapErr(_f) {
766
- return ok(this.value);
847
+ var getHeaders = ({
848
+ initHeaderValue,
849
+ workflowRunId,
850
+ workflowUrl,
851
+ userHeaders,
852
+ failureUrl,
853
+ retries,
854
+ step,
855
+ callRetries,
856
+ callTimeout,
857
+ telemetry,
858
+ invokeCount,
859
+ flowControl,
860
+ callFlowControl
861
+ }) => {
862
+ const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
863
+ const baseHeaders = {
864
+ [WORKFLOW_INIT_HEADER]: initHeaderValue,
865
+ [WORKFLOW_ID_HEADER]: workflowRunId,
866
+ [WORKFLOW_URL_HEADER]: workflowUrl,
867
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
868
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
869
+ "content-type": contentType,
870
+ ...telemetry ? getTelemetryHeaders(telemetry) : {}
871
+ };
872
+ if (invokeCount !== void 0 && !step?.callUrl) {
873
+ baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
767
874
  }
768
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
769
- andThen(f) {
770
- return f(this.value);
875
+ if (!step?.callUrl) {
876
+ baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
771
877
  }
772
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
773
- andThrough(f) {
774
- return f(this.value).map((_value) => this.value);
878
+ if (callTimeout) {
879
+ baseHeaders[`Upstash-Timeout`] = callTimeout.toString();
775
880
  }
776
- andTee(f) {
777
- try {
778
- f(this.value);
779
- } catch (e) {
881
+ if (failureUrl) {
882
+ baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
883
+ baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
884
+ baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
885
+ baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
886
+ baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
887
+ baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
888
+ if (retries !== void 0) {
889
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
890
+ }
891
+ if (flowControl) {
892
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
893
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
894
+ baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
895
+ }
896
+ if (!step?.callUrl) {
897
+ baseHeaders["Upstash-Failure-Callback"] = failureUrl;
780
898
  }
781
- return ok(this.value);
782
- }
783
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
784
- orElse(_f) {
785
- return ok(this.value);
786
- }
787
- asyncAndThen(f) {
788
- return f(this.value);
789
- }
790
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
791
- asyncAndThrough(f) {
792
- return f(this.value).map(() => this.value);
793
- }
794
- asyncMap(f) {
795
- return ResultAsync.fromSafePromise(f(this.value));
796
- }
797
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
798
- unwrapOr(_v) {
799
- return this.value;
800
- }
801
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
802
- match(ok2, _err) {
803
- return ok2(this.value);
804
- }
805
- safeUnwrap() {
806
- const value = this.value;
807
- return function* () {
808
- return value;
809
- }();
810
- }
811
- _unsafeUnwrap(_) {
812
- return this.value;
813
- }
814
- _unsafeUnwrapErr(config) {
815
- throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
816
- }
817
- };
818
- var Err = class {
819
- constructor(error) {
820
- this.error = error;
821
- }
822
- isOk() {
823
- return false;
824
- }
825
- isErr() {
826
- return !this.isOk();
827
- }
828
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
829
- map(_f) {
830
- return err(this.error);
831
- }
832
- mapErr(f) {
833
- return err(f(this.error));
834
- }
835
- andThrough(_f) {
836
- return err(this.error);
837
- }
838
- andTee(_f) {
839
- return err(this.error);
840
- }
841
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
842
- andThen(_f) {
843
- return err(this.error);
844
- }
845
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
846
- orElse(f) {
847
- return f(this.error);
848
- }
849
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
850
- asyncAndThen(_f) {
851
- return errAsync(this.error);
852
899
  }
853
- asyncAndThrough(_f) {
854
- return errAsync(this.error);
900
+ if (step?.callUrl) {
901
+ baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
902
+ baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
903
+ if (retries !== void 0) {
904
+ baseHeaders["Upstash-Callback-Retries"] = retries.toString();
905
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
906
+ }
907
+ if (callFlowControl) {
908
+ const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
909
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
910
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
911
+ }
912
+ if (flowControl) {
913
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
914
+ baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
915
+ baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
916
+ }
917
+ } else {
918
+ if (flowControl) {
919
+ const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
920
+ baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
921
+ baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
922
+ }
923
+ if (retries !== void 0) {
924
+ baseHeaders["Upstash-Retries"] = retries.toString();
925
+ baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
926
+ }
855
927
  }
856
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
857
- asyncMap(_f) {
858
- return errAsync(this.error);
928
+ if (userHeaders) {
929
+ for (const header of userHeaders.keys()) {
930
+ if (step?.callHeaders) {
931
+ baseHeaders[`Upstash-Callback-Forward-${header}`] = userHeaders.get(header);
932
+ } else {
933
+ baseHeaders[`Upstash-Forward-${header}`] = userHeaders.get(header);
934
+ }
935
+ baseHeaders[`Upstash-Failure-Callback-Forward-${header}`] = userHeaders.get(header);
936
+ }
859
937
  }
860
- unwrapOr(v) {
861
- return v;
938
+ if (step?.callHeaders) {
939
+ const forwardedHeaders = Object.fromEntries(
940
+ Object.entries(step.callHeaders).map(([header, value]) => [
941
+ `Upstash-Forward-${header}`,
942
+ value
943
+ ])
944
+ );
945
+ return {
946
+ headers: {
947
+ ...baseHeaders,
948
+ ...forwardedHeaders,
949
+ "Upstash-Callback": workflowUrl,
950
+ "Upstash-Callback-Workflow-RunId": workflowRunId,
951
+ "Upstash-Callback-Workflow-CallType": "fromCallback",
952
+ "Upstash-Callback-Workflow-Init": "false",
953
+ "Upstash-Callback-Workflow-Url": workflowUrl,
954
+ "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
955
+ "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
956
+ "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
957
+ "Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
958
+ "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
959
+ "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
960
+ "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
961
+ [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
962
+ "Upstash-Workflow-CallType": "toCallback"
963
+ }
964
+ };
862
965
  }
863
- match(_ok, err2) {
864
- return err2(this.error);
966
+ if (step?.waitEventId) {
967
+ return {
968
+ headers: {
969
+ ...baseHeaders,
970
+ "Upstash-Workflow-CallType": "step"
971
+ },
972
+ timeoutHeaders: {
973
+ // to include user headers:
974
+ ...Object.fromEntries(
975
+ Object.entries(baseHeaders).map(([header, value]) => [header, [value]])
976
+ ),
977
+ // to include telemetry headers:
978
+ ...telemetry ? Object.fromEntries(
979
+ Object.entries(getTelemetryHeaders(telemetry)).map(([header, value]) => [
980
+ header,
981
+ [value]
982
+ ])
983
+ ) : {},
984
+ // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
985
+ "Upstash-Workflow-Runid": [workflowRunId],
986
+ [WORKFLOW_INIT_HEADER]: ["false"],
987
+ [WORKFLOW_URL_HEADER]: [workflowUrl],
988
+ "Upstash-Workflow-CallType": ["step"]
989
+ }
990
+ };
865
991
  }
866
- safeUnwrap() {
867
- const error = this.error;
868
- return function* () {
869
- yield err(error);
870
- throw new Error("Do not use this generator out of `safeTry`");
871
- }();
992
+ return { headers: baseHeaders };
993
+ };
994
+ var verifyRequest = async (body, signature, verifier) => {
995
+ if (!verifier) {
996
+ return;
872
997
  }
873
- _unsafeUnwrap(config) {
874
- throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
998
+ try {
999
+ if (!signature) {
1000
+ throw new Error("`Upstash-Signature` header is not passed.");
1001
+ }
1002
+ const isValid = await verifier.verify({
1003
+ body,
1004
+ signature
1005
+ });
1006
+ if (!isValid) {
1007
+ throw new Error("Signature in `Upstash-Signature` header is not valid");
1008
+ }
1009
+ } catch (error) {
1010
+ throw new WorkflowError(
1011
+ `Failed to verify that the Workflow request comes from QStash: ${error}
1012
+
1013
+ If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
1014
+
1015
+ If you want to disable QStash Verification, you should clear env variables QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY`
1016
+ );
875
1017
  }
876
- _unsafeUnwrapErr(_) {
877
- return this.error;
1018
+ };
1019
+ var prepareFlowControl = (flowControl) => {
1020
+ const parallelism = flowControl.parallelism?.toString();
1021
+ const rate = flowControl.ratePerSecond?.toString();
1022
+ const controlValue = [
1023
+ parallelism ? `parallelism=${parallelism}` : void 0,
1024
+ rate ? `rate=${rate}` : void 0
1025
+ ].filter(Boolean);
1026
+ if (controlValue.length === 0) {
1027
+ throw new QstashError3("Provide at least one of parallelism or ratePerSecond for flowControl");
878
1028
  }
1029
+ return {
1030
+ flowControlKey: flowControl.key,
1031
+ flowControlValue: controlValue.join(", ")
1032
+ };
879
1033
  };
880
- var fromThrowable = Result.fromThrowable;
881
1034
 
882
- // src/workflow-requests.ts
883
- import { QstashError as QstashError3 } from "@upstash/qstash";
884
- var triggerFirstInvocation = async ({
885
- workflowContext,
886
- useJSONContent,
887
- telemetry,
888
- debug
1035
+ // src/serve/serve-many.ts
1036
+ var getWorkflowId = (url) => {
1037
+ const components = url.split("/");
1038
+ const lastComponent = components[components.length - 1];
1039
+ return lastComponent.split("?")[0];
1040
+ };
1041
+ var serveManyBase = ({
1042
+ workflows,
1043
+ getUrl,
1044
+ serveMethod,
1045
+ options
889
1046
  }) => {
890
- const { headers } = getHeaders({
1047
+ const workflowIds = [];
1048
+ const workflowMap = Object.fromEntries(
1049
+ Object.entries(workflows).map((workflow) => {
1050
+ const workflowId = workflow[0];
1051
+ if (workflowIds.includes(workflowId)) {
1052
+ throw new WorkflowError(
1053
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
1054
+ );
1055
+ }
1056
+ if (workflowId.includes("/")) {
1057
+ throw new WorkflowError(
1058
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
1059
+ );
1060
+ }
1061
+ workflowIds.push(workflowId);
1062
+ workflow[1].workflowId = workflowId;
1063
+ workflow[1].options = {
1064
+ ...options,
1065
+ ...workflow[1].options
1066
+ };
1067
+ const params = [workflow[1].routeFunction, workflow[1].options];
1068
+ const handler = serveMethod(...params);
1069
+ return [workflowId, handler];
1070
+ })
1071
+ );
1072
+ return {
1073
+ handler: async (...params) => {
1074
+ const url = getUrl(...params);
1075
+ const pickedWorkflowId = getWorkflowId(url);
1076
+ if (!pickedWorkflowId) {
1077
+ return new Response(
1078
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
1079
+ {
1080
+ status: 404
1081
+ }
1082
+ );
1083
+ }
1084
+ const workflow = workflowMap[pickedWorkflowId];
1085
+ if (!workflow) {
1086
+ return new Response(
1087
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
1088
+ {
1089
+ status: 404
1090
+ }
1091
+ );
1092
+ }
1093
+ return await workflow(...params);
1094
+ }
1095
+ };
1096
+ };
1097
+ var invokeWorkflow = async ({
1098
+ settings,
1099
+ invokeStep,
1100
+ context,
1101
+ invokeCount,
1102
+ telemetry
1103
+ }) => {
1104
+ const {
1105
+ body,
1106
+ workflow,
1107
+ headers = {},
1108
+ workflowRunId = getWorkflowRunId(),
1109
+ retries,
1110
+ flowControl
1111
+ } = settings;
1112
+ const { workflowId } = workflow;
1113
+ const {
1114
+ retries: workflowRetries,
1115
+ failureFunction,
1116
+ failureUrl,
1117
+ useJSONContent,
1118
+ flowControl: workflowFlowControl
1119
+ } = workflow.options;
1120
+ if (!workflowId) {
1121
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1122
+ }
1123
+ const { headers: invokerHeaders } = getHeaders({
1124
+ initHeaderValue: "false",
1125
+ workflowRunId: context.workflowRunId,
1126
+ workflowUrl: context.url,
1127
+ userHeaders: context.headers,
1128
+ failureUrl: context.failureUrl,
1129
+ retries: context.retries,
1130
+ telemetry,
1131
+ invokeCount,
1132
+ flowControl: context.flowControl
1133
+ });
1134
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1135
+ const newUrl = context.url.replace(/[^/]+$/, workflowId);
1136
+ const { headers: triggerHeaders } = getHeaders({
891
1137
  initHeaderValue: "true",
892
- workflowRunId: workflowContext.workflowRunId,
893
- workflowUrl: workflowContext.url,
894
- userHeaders: workflowContext.headers,
895
- failureUrl: workflowContext.failureUrl,
896
- retries: workflowContext.retries,
897
- telemetry
1138
+ workflowRunId,
1139
+ workflowUrl: newUrl,
1140
+ userHeaders: new Headers(headers),
1141
+ retries: retries ?? workflowRetries,
1142
+ telemetry,
1143
+ failureUrl: failureFunction ? newUrl : failureUrl,
1144
+ invokeCount: invokeCount + 1,
1145
+ flowControl: flowControl ?? workflowFlowControl
898
1146
  });
899
- if (workflowContext.headers.get("content-type")) {
900
- headers["content-type"] = workflowContext.headers.get("content-type");
901
- }
1147
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
902
1148
  if (useJSONContent) {
903
- headers["content-type"] = "application/json";
1149
+ triggerHeaders["content-type"] = "application/json";
904
1150
  }
905
- try {
906
- const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
907
- const result = await workflowContext.qstashClient.publish({
908
- headers,
909
- method: "POST",
910
- body,
911
- url: workflowContext.url
912
- });
913
- if (result.deduplicated) {
914
- await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
915
- message: `Workflow run ${workflowContext.workflowRunId} already exists. A new one isn't created.`,
916
- headers,
917
- requestPayload: workflowContext.requestPayload,
918
- url: workflowContext.url,
919
- messageId: result.messageId
920
- });
921
- return ok("workflow-run-already-exists");
922
- } else {
923
- await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
924
- headers,
925
- requestPayload: workflowContext.requestPayload,
926
- url: workflowContext.url,
927
- messageId: result.messageId
928
- });
929
- return ok("success");
1151
+ const request = {
1152
+ body: JSON.stringify(body),
1153
+ headers: Object.fromEntries(
1154
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1155
+ ),
1156
+ workflowRunId,
1157
+ workflowUrl: context.url,
1158
+ step: invokeStep
1159
+ };
1160
+ await context.qstashClient.publish({
1161
+ headers: triggerHeaders,
1162
+ method: "POST",
1163
+ body: JSON.stringify(request),
1164
+ url: newUrl
1165
+ });
1166
+ };
1167
+
1168
+ // src/agents/adapters.ts
1169
+ import { createOpenAI } from "@ai-sdk/openai";
1170
+ import { tool } from "ai";
1171
+
1172
+ // src/agents/constants.ts
1173
+ var AGENT_NAME_HEADER = "upstash-agent-name";
1174
+ var MANAGER_AGENT_PROMPT = `You are an agent orchestrating other AI Agents.
1175
+
1176
+ These other agents have tools available to them.
1177
+
1178
+ Given a prompt, utilize these agents to address requests.
1179
+
1180
+ Don't always call all the agents provided to you at the same time. You can call one and use it's response to call another.
1181
+
1182
+ Avoid calling the same agent twice in one turn. Instead, prefer to call it once but provide everything
1183
+ you need from that agent.
1184
+ `;
1185
+
1186
+ // src/agents/adapters.ts
1187
+ var createWorkflowOpenAI = (context, config) => {
1188
+ const { baseURL, apiKey } = config ?? {};
1189
+ return createOpenAI({
1190
+ baseURL,
1191
+ apiKey,
1192
+ compatibility: "strict",
1193
+ fetch: async (input, init) => {
1194
+ try {
1195
+ const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
1196
+ const body = init?.body ? JSON.parse(init.body) : void 0;
1197
+ const agentName = headers[AGENT_NAME_HEADER];
1198
+ const stepName = agentName ? `Call Agent ${agentName}` : "Call Agent";
1199
+ const responseInfo = await context.call(stepName, {
1200
+ url: input.toString(),
1201
+ method: init?.method,
1202
+ headers,
1203
+ body
1204
+ });
1205
+ const responseHeaders = new Headers(
1206
+ Object.entries(responseInfo.header).reduce(
1207
+ (acc, [key, values]) => {
1208
+ acc[key] = values.join(", ");
1209
+ return acc;
1210
+ },
1211
+ {}
1212
+ )
1213
+ );
1214
+ return new Response(JSON.stringify(responseInfo.body), {
1215
+ status: responseInfo.status,
1216
+ headers: responseHeaders
1217
+ });
1218
+ } catch (error) {
1219
+ if (error instanceof Error && error.name === "WorkflowAbort") {
1220
+ throw error;
1221
+ } else {
1222
+ console.error("Error in fetch implementation:", error);
1223
+ throw error;
1224
+ }
1225
+ }
930
1226
  }
931
- } catch (error) {
932
- const error_ = error;
933
- return err(error_);
1227
+ });
1228
+ };
1229
+ var wrapTools = ({
1230
+ context,
1231
+ tools
1232
+ }) => {
1233
+ return Object.fromEntries(
1234
+ Object.entries(tools).map((toolInfo) => {
1235
+ const [toolName, tool3] = toolInfo;
1236
+ const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
1237
+ const aiSDKTool = convertToAISDKTool(tool3);
1238
+ const execute = aiSDKTool.execute;
1239
+ if (execute && executeAsStep) {
1240
+ const wrappedExecute = (...params) => {
1241
+ return context.run(`Run tool ${toolName}`, () => execute(...params));
1242
+ };
1243
+ aiSDKTool.execute = wrappedExecute;
1244
+ }
1245
+ return [toolName, aiSDKTool];
1246
+ })
1247
+ );
1248
+ };
1249
+ var convertToAISDKTool = (tool3) => {
1250
+ const isLangchainTool = "invoke" in tool3;
1251
+ return isLangchainTool ? convertLangchainTool(tool3) : tool3;
1252
+ };
1253
+ var convertLangchainTool = (langchainTool) => {
1254
+ return tool({
1255
+ description: langchainTool.description,
1256
+ parameters: langchainTool.schema,
1257
+ execute: async (...param) => langchainTool.invoke(...param)
1258
+ });
1259
+ };
1260
+ var WorkflowTool = class {
1261
+ /**
1262
+ * description of the tool
1263
+ */
1264
+ description;
1265
+ /**
1266
+ * schema of the tool
1267
+ */
1268
+ schema;
1269
+ /**
1270
+ * function to invoke the tool
1271
+ */
1272
+ invoke;
1273
+ /**
1274
+ * whether the invoke method of the tool is to be wrapped with `context.run`
1275
+ */
1276
+ executeAsStep;
1277
+ /**
1278
+ *
1279
+ * @param description description of the tool
1280
+ * @param schema schema of the tool
1281
+ * @param invoke function to invoke the tool
1282
+ * @param executeAsStep whether the invoke method of the tool is to be wrapped with `context.run`
1283
+ */
1284
+ constructor(params) {
1285
+ this.description = params.description;
1286
+ this.schema = params.schema;
1287
+ this.invoke = params.invoke;
1288
+ this.executeAsStep = params.executeAsStep ?? true;
934
1289
  }
935
1290
  };
936
- var triggerRouteFunction = async ({
937
- onCleanup,
938
- onStep,
939
- onCancel,
940
- debug
941
- }) => {
942
- try {
943
- await onStep();
944
- await onCleanup();
945
- return ok("workflow-finished");
946
- } catch (error) {
947
- const error_ = error;
948
- if (error instanceof QstashError3 && error.status === 400) {
949
- await debug?.log("WARN", "RESPONSE_WORKFLOW", {
950
- message: `tried to append to a cancelled workflow. exiting without publishing.`,
951
- name: error.name,
952
- errorMessage: error.message
953
- });
954
- return ok("workflow-was-finished");
955
- } else if (!(error_ instanceof WorkflowAbort)) {
956
- return err(error_);
957
- } else if (error_.cancelWorkflow) {
958
- await onCancel();
959
- return ok("workflow-finished");
960
- } else {
961
- return ok("step-finished");
1291
+
1292
+ // src/context/steps.ts
1293
+ var BaseLazyStep = class {
1294
+ stepName;
1295
+ // will be set in the subclasses
1296
+ constructor(stepName) {
1297
+ if (!stepName) {
1298
+ throw new WorkflowError(
1299
+ "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
1300
+ );
962
1301
  }
1302
+ this.stepName = stepName;
963
1303
  }
964
1304
  };
965
- var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
966
- await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
967
- deletedWorkflowRunId: workflowContext.workflowRunId
968
- });
969
- await workflowContext.qstashClient.http.request({
970
- path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
971
- method: "DELETE",
972
- parseResponseAsJson: false
973
- });
974
- await debug?.log(
975
- "SUBMIT",
976
- "SUBMIT_CLEANUP",
977
- `workflow run ${workflowContext.workflowRunId} deleted.`
978
- );
979
- };
980
- var recreateUserHeaders = (headers) => {
981
- const filteredHeaders = new Headers();
982
- const pairs = headers.entries();
983
- for (const [header, value] of pairs) {
984
- const headerLowerCase = header.toLowerCase();
985
- if (!headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
986
- !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
987
- headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
988
- headerLowerCase !== "render-proxy-ttl") {
989
- filteredHeaders.append(header, value);
990
- }
1305
+ var LazyFunctionStep = class extends BaseLazyStep {
1306
+ stepFunction;
1307
+ stepType = "Run";
1308
+ constructor(stepName, stepFunction) {
1309
+ super(stepName);
1310
+ this.stepFunction = stepFunction;
991
1311
  }
992
- return filteredHeaders;
993
- };
994
- var handleThirdPartyCallResult = async ({
995
- request,
996
- requestPayload,
997
- client,
998
- workflowUrl,
999
- failureUrl,
1000
- retries,
1001
- telemetry,
1002
- debug
1003
- }) => {
1004
- try {
1005
- if (request.headers.get("Upstash-Workflow-Callback")) {
1006
- let callbackPayload;
1007
- if (requestPayload) {
1008
- callbackPayload = requestPayload;
1009
- } else {
1010
- const workflowRunId2 = request.headers.get("upstash-workflow-runid");
1011
- const messageId = request.headers.get("upstash-message-id");
1012
- if (!workflowRunId2)
1013
- throw new WorkflowError("workflow run id missing in context.call lazy fetch.");
1014
- if (!messageId) throw new WorkflowError("message id missing in context.call lazy fetch.");
1015
- const { steps, workflowRunEnded } = await getSteps(
1016
- client.http,
1017
- workflowRunId2,
1018
- messageId,
1019
- debug
1020
- );
1021
- if (workflowRunEnded) {
1022
- return ok("workflow-ended");
1023
- }
1024
- const failingStep = steps.find((step) => step.messageId === messageId);
1025
- if (!failingStep)
1026
- throw new WorkflowError(
1027
- "Failed to submit the context.call. " + (steps.length === 0 ? "No steps found." : `No step was found with matching messageId ${messageId} out of ${steps.length} steps.`)
1028
- );
1029
- callbackPayload = atob(failingStep.body);
1030
- }
1031
- const callbackMessage = JSON.parse(callbackPayload);
1032
- if (!(callbackMessage.status >= 200 && callbackMessage.status < 300) && callbackMessage.maxRetries && callbackMessage.retried !== callbackMessage.maxRetries) {
1033
- await debug?.log("WARN", "SUBMIT_THIRD_PARTY_RESULT", {
1034
- status: callbackMessage.status,
1035
- body: atob(callbackMessage.body ?? "")
1036
- });
1037
- console.warn(
1038
- `Workflow Warning: "context.call" failed with status ${callbackMessage.status} and will retry (retried ${callbackMessage.retried ?? 0} out of ${callbackMessage.maxRetries} times). Error Message:
1039
- ${atob(callbackMessage.body ?? "")}`
1040
- );
1041
- return ok("call-will-retry");
1042
- }
1043
- const workflowRunId = request.headers.get(WORKFLOW_ID_HEADER);
1044
- const stepIdString = request.headers.get("Upstash-Workflow-StepId");
1045
- const stepName = request.headers.get("Upstash-Workflow-StepName");
1046
- const stepType = request.headers.get("Upstash-Workflow-StepType");
1047
- const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
1048
- const contentType = request.headers.get("Upstash-Workflow-ContentType");
1049
- if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
1050
- throw new Error(
1051
- `Missing info in callback message source header: ${JSON.stringify({
1052
- workflowRunId,
1053
- stepIdString,
1054
- stepName,
1055
- stepType,
1056
- concurrentString,
1057
- contentType
1058
- })}`
1059
- );
1060
- }
1061
- const userHeaders = recreateUserHeaders(request.headers);
1062
- const { headers: requestHeaders } = getHeaders({
1063
- initHeaderValue: "false",
1064
- workflowRunId,
1065
- workflowUrl,
1066
- userHeaders,
1067
- failureUrl,
1068
- retries,
1069
- telemetry
1070
- });
1071
- const callResponse = {
1072
- status: callbackMessage.status,
1073
- body: atob(callbackMessage.body ?? ""),
1074
- header: callbackMessage.header
1075
- };
1076
- const callResultStep = {
1077
- stepId: Number(stepIdString),
1078
- stepName,
1079
- stepType,
1080
- out: JSON.stringify(callResponse),
1081
- concurrent: Number(concurrentString)
1082
- };
1083
- await debug?.log("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
1084
- step: callResultStep,
1085
- headers: requestHeaders,
1086
- url: workflowUrl
1087
- });
1088
- const result = await client.publishJSON({
1089
- headers: requestHeaders,
1090
- method: "POST",
1091
- body: callResultStep,
1092
- url: workflowUrl
1093
- });
1094
- await debug?.log("SUBMIT", "SUBMIT_THIRD_PARTY_RESULT", {
1095
- messageId: result.messageId
1096
- });
1097
- return ok("is-call-return");
1098
- } else {
1099
- return ok("continue-workflow");
1312
+ getPlanStep(concurrent, targetStep) {
1313
+ return {
1314
+ stepId: 0,
1315
+ stepName: this.stepName,
1316
+ stepType: this.stepType,
1317
+ concurrent,
1318
+ targetStep
1319
+ };
1320
+ }
1321
+ async getResultStep(concurrent, stepId) {
1322
+ let result = this.stepFunction();
1323
+ if (result instanceof Promise) {
1324
+ result = await result;
1100
1325
  }
1101
- } catch (error) {
1102
- const isCallReturn = request.headers.get("Upstash-Workflow-Callback");
1103
- return err(
1104
- new WorkflowError(`Error when handling call return (isCallReturn=${isCallReturn}): ${error}`)
1105
- );
1326
+ return {
1327
+ stepId,
1328
+ stepName: this.stepName,
1329
+ stepType: this.stepType,
1330
+ out: result,
1331
+ concurrent
1332
+ };
1106
1333
  }
1107
1334
  };
1108
- var getTelemetryHeaders = (telemetry) => {
1109
- return {
1110
- [TELEMETRY_HEADER_SDK]: telemetry.sdk,
1111
- [TELEMETRY_HEADER_FRAMEWORK]: telemetry.framework,
1112
- [TELEMETRY_HEADER_RUNTIME]: telemetry.runtime ?? "unknown"
1113
- };
1114
- };
1115
- var getHeaders = ({
1116
- initHeaderValue,
1117
- workflowRunId,
1118
- workflowUrl,
1119
- userHeaders,
1120
- failureUrl,
1121
- retries,
1122
- step,
1123
- callRetries,
1124
- callTimeout,
1125
- telemetry
1126
- }) => {
1127
- const baseHeaders = {
1128
- [WORKFLOW_INIT_HEADER]: initHeaderValue,
1129
- [WORKFLOW_ID_HEADER]: workflowRunId,
1130
- [WORKFLOW_URL_HEADER]: workflowUrl,
1131
- [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1132
- ...telemetry ? getTelemetryHeaders(telemetry) : {}
1133
- };
1134
- if (!step?.callUrl) {
1135
- baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1335
+ var LazySleepStep = class extends BaseLazyStep {
1336
+ sleep;
1337
+ stepType = "SleepFor";
1338
+ constructor(stepName, sleep) {
1339
+ super(stepName);
1340
+ this.sleep = sleep;
1136
1341
  }
1137
- if (callTimeout) {
1138
- baseHeaders[`Upstash-Timeout`] = callTimeout.toString();
1342
+ getPlanStep(concurrent, targetStep) {
1343
+ return {
1344
+ stepId: 0,
1345
+ stepName: this.stepName,
1346
+ stepType: this.stepType,
1347
+ sleepFor: this.sleep,
1348
+ concurrent,
1349
+ targetStep
1350
+ };
1351
+ }
1352
+ async getResultStep(concurrent, stepId) {
1353
+ return await Promise.resolve({
1354
+ stepId,
1355
+ stepName: this.stepName,
1356
+ stepType: this.stepType,
1357
+ sleepFor: this.sleep,
1358
+ concurrent
1359
+ });
1360
+ }
1361
+ };
1362
+ var LazySleepUntilStep = class extends BaseLazyStep {
1363
+ sleepUntil;
1364
+ stepType = "SleepUntil";
1365
+ constructor(stepName, sleepUntil) {
1366
+ super(stepName);
1367
+ this.sleepUntil = sleepUntil;
1139
1368
  }
1140
- if (failureUrl) {
1141
- baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
1142
- baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
1143
- baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
1144
- baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
1145
- baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
1146
- baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
1147
- if (retries !== void 0) {
1148
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1149
- }
1150
- if (!step?.callUrl) {
1151
- baseHeaders["Upstash-Failure-Callback"] = failureUrl;
1152
- }
1369
+ getPlanStep(concurrent, targetStep) {
1370
+ return {
1371
+ stepId: 0,
1372
+ stepName: this.stepName,
1373
+ stepType: this.stepType,
1374
+ sleepUntil: this.sleepUntil,
1375
+ concurrent,
1376
+ targetStep
1377
+ };
1153
1378
  }
1154
- if (step?.callUrl) {
1155
- baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
1156
- baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
1157
- if (retries !== void 0) {
1158
- baseHeaders["Upstash-Callback-Retries"] = retries.toString();
1159
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1160
- }
1161
- } else if (retries !== void 0) {
1162
- baseHeaders["Upstash-Retries"] = retries.toString();
1163
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
1379
+ async getResultStep(concurrent, stepId) {
1380
+ return await Promise.resolve({
1381
+ stepId,
1382
+ stepName: this.stepName,
1383
+ stepType: this.stepType,
1384
+ sleepUntil: this.sleepUntil,
1385
+ concurrent
1386
+ });
1164
1387
  }
1165
- if (userHeaders) {
1166
- for (const header of userHeaders.keys()) {
1167
- if (step?.callHeaders) {
1168
- baseHeaders[`Upstash-Callback-Forward-${header}`] = userHeaders.get(header);
1169
- } else {
1170
- baseHeaders[`Upstash-Forward-${header}`] = userHeaders.get(header);
1171
- }
1172
- baseHeaders[`Upstash-Failure-Callback-Forward-${header}`] = userHeaders.get(header);
1173
- }
1388
+ };
1389
+ var LazyCallStep = class extends BaseLazyStep {
1390
+ url;
1391
+ method;
1392
+ body;
1393
+ headers;
1394
+ retries;
1395
+ timeout;
1396
+ flowControl;
1397
+ stepType = "Call";
1398
+ constructor(stepName, url, method, body, headers, retries, timeout, flowControl) {
1399
+ super(stepName);
1400
+ this.url = url;
1401
+ this.method = method;
1402
+ this.body = body;
1403
+ this.headers = headers;
1404
+ this.retries = retries;
1405
+ this.timeout = timeout;
1406
+ this.flowControl = flowControl;
1174
1407
  }
1175
- const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1176
- if (step?.callHeaders) {
1177
- const forwardedHeaders = Object.fromEntries(
1178
- Object.entries(step.callHeaders).map(([header, value]) => [
1179
- `Upstash-Forward-${header}`,
1180
- value
1181
- ])
1182
- );
1408
+ getPlanStep(concurrent, targetStep) {
1183
1409
  return {
1184
- headers: {
1185
- ...baseHeaders,
1186
- ...forwardedHeaders,
1187
- "Upstash-Callback": workflowUrl,
1188
- "Upstash-Callback-Workflow-RunId": workflowRunId,
1189
- "Upstash-Callback-Workflow-CallType": "fromCallback",
1190
- "Upstash-Callback-Workflow-Init": "false",
1191
- "Upstash-Callback-Workflow-Url": workflowUrl,
1192
- "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
1193
- "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
1194
- "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
1195
- "Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
1196
- "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
1197
- "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1198
- "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1199
- "Upstash-Workflow-CallType": "toCallback"
1200
- }
1410
+ stepId: 0,
1411
+ stepName: this.stepName,
1412
+ stepType: this.stepType,
1413
+ concurrent,
1414
+ targetStep
1201
1415
  };
1202
1416
  }
1203
- if (step?.waitEventId) {
1417
+ async getResultStep(concurrent, stepId) {
1418
+ return await Promise.resolve({
1419
+ stepId,
1420
+ stepName: this.stepName,
1421
+ stepType: this.stepType,
1422
+ concurrent,
1423
+ callUrl: this.url,
1424
+ callMethod: this.method,
1425
+ callBody: this.body,
1426
+ callHeaders: this.headers
1427
+ });
1428
+ }
1429
+ };
1430
+ var LazyWaitForEventStep = class extends BaseLazyStep {
1431
+ eventId;
1432
+ timeout;
1433
+ stepType = "Wait";
1434
+ constructor(stepName, eventId, timeout) {
1435
+ super(stepName);
1436
+ this.eventId = eventId;
1437
+ this.timeout = timeout;
1438
+ }
1439
+ getPlanStep(concurrent, targetStep) {
1204
1440
  return {
1205
- headers: {
1206
- ...baseHeaders,
1207
- "Upstash-Workflow-CallType": "step"
1208
- },
1209
- timeoutHeaders: {
1210
- // to include user headers:
1211
- ...Object.fromEntries(
1212
- Object.entries(baseHeaders).map(([header, value]) => [header, [value]])
1213
- ),
1214
- // to include telemetry headers:
1215
- ...telemetry ? Object.fromEntries(
1216
- Object.entries(getTelemetryHeaders(telemetry)).map(([header, value]) => [
1217
- header,
1218
- [value]
1219
- ])
1220
- ) : {},
1221
- // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1222
- "Upstash-Workflow-Runid": [workflowRunId],
1223
- [WORKFLOW_INIT_HEADER]: ["false"],
1224
- [WORKFLOW_URL_HEADER]: [workflowUrl],
1225
- "Upstash-Workflow-CallType": ["step"],
1226
- "Content-Type": [contentType]
1227
- }
1441
+ stepId: 0,
1442
+ stepName: this.stepName,
1443
+ stepType: this.stepType,
1444
+ waitEventId: this.eventId,
1445
+ timeout: this.timeout,
1446
+ concurrent,
1447
+ targetStep
1228
1448
  };
1229
1449
  }
1230
- return { headers: baseHeaders };
1450
+ async getResultStep(concurrent, stepId) {
1451
+ return await Promise.resolve({
1452
+ stepId,
1453
+ stepName: this.stepName,
1454
+ stepType: this.stepType,
1455
+ waitEventId: this.eventId,
1456
+ timeout: this.timeout,
1457
+ concurrent
1458
+ });
1459
+ }
1231
1460
  };
1232
- var verifyRequest = async (body, signature, verifier) => {
1233
- if (!verifier) {
1234
- return;
1461
+ var LazyNotifyStep = class extends LazyFunctionStep {
1462
+ stepType = "Notify";
1463
+ constructor(stepName, eventId, eventData, requester) {
1464
+ super(stepName, async () => {
1465
+ const notifyResponse = await makeNotifyRequest(requester, eventId, eventData);
1466
+ return {
1467
+ eventId,
1468
+ eventData,
1469
+ notifyResponse
1470
+ };
1471
+ });
1235
1472
  }
1236
- try {
1237
- if (!signature) {
1238
- throw new Error("`Upstash-Signature` header is not passed.");
1239
- }
1240
- const isValid = await verifier.verify({
1473
+ };
1474
+ var LazyInvokeStep = class extends BaseLazyStep {
1475
+ stepType = "Invoke";
1476
+ params;
1477
+ constructor(stepName, {
1478
+ workflow,
1479
+ body,
1480
+ headers = {},
1481
+ workflowRunId,
1482
+ retries,
1483
+ flowControl
1484
+ }) {
1485
+ super(stepName);
1486
+ this.params = {
1487
+ workflow,
1241
1488
  body,
1242
- signature
1489
+ headers,
1490
+ workflowRunId: getWorkflowRunId(workflowRunId),
1491
+ retries,
1492
+ flowControl
1493
+ };
1494
+ }
1495
+ getPlanStep(concurrent, targetStep) {
1496
+ return {
1497
+ stepId: 0,
1498
+ stepName: this.stepName,
1499
+ stepType: this.stepType,
1500
+ concurrent,
1501
+ targetStep
1502
+ };
1503
+ }
1504
+ /**
1505
+ * won't be used as it's the server who will add the result step
1506
+ * in Invoke step.
1507
+ */
1508
+ getResultStep(concurrent, stepId) {
1509
+ return Promise.resolve({
1510
+ stepId,
1511
+ stepName: this.stepName,
1512
+ stepType: this.stepType,
1513
+ concurrent
1243
1514
  });
1244
- if (!isValid) {
1245
- throw new Error("Signature in `Upstash-Signature` header is not valid");
1246
- }
1247
- } catch (error) {
1248
- throw new WorkflowError(
1249
- `Failed to verify that the Workflow request comes from QStash: ${error}
1250
-
1251
- If signature is missing, trigger the workflow endpoint by publishing your request to QStash instead of calling it directly.
1252
-
1253
- If you want to disable QStash Verification, you should clear env variables QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY`
1254
- );
1255
1515
  }
1256
1516
  };
1257
1517
 
@@ -1265,14 +1525,16 @@ var AutoExecutor = class _AutoExecutor {
1265
1525
  nonPlanStepCount;
1266
1526
  steps;
1267
1527
  indexInCurrentList = 0;
1528
+ invokeCount;
1268
1529
  telemetry;
1269
1530
  stepCount = 0;
1270
1531
  planStepCount = 0;
1271
1532
  executingStep = false;
1272
- constructor(context, steps, telemetry, debug) {
1533
+ constructor(context, steps, telemetry, invokeCount, debug) {
1273
1534
  this.context = context;
1274
1535
  this.steps = steps;
1275
1536
  this.telemetry = telemetry;
1537
+ this.invokeCount = invokeCount ?? 0;
1276
1538
  this.debug = debug;
1277
1539
  this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
1278
1540
  }
@@ -1495,7 +1757,9 @@ var AutoExecutor = class _AutoExecutor {
1495
1757
  step: waitStep,
1496
1758
  failureUrl: this.context.failureUrl,
1497
1759
  retries: this.context.retries,
1498
- telemetry: this.telemetry
1760
+ telemetry: this.telemetry,
1761
+ invokeCount: this.invokeCount,
1762
+ flowControl: this.context.flowControl
1499
1763
  });
1500
1764
  const waitBody = {
1501
1765
  url: this.context.url,
@@ -1518,7 +1782,19 @@ var AutoExecutor = class _AutoExecutor {
1518
1782
  method: "POST",
1519
1783
  parseResponseAsJson: false
1520
1784
  });
1521
- throw new WorkflowAbort(steps[0].stepName, steps[0]);
1785
+ throw new WorkflowAbort(waitStep.stepName, waitStep);
1786
+ }
1787
+ if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1788
+ const invokeStep = steps[0];
1789
+ const lazyInvokeStep = lazySteps[0];
1790
+ await invokeWorkflow({
1791
+ settings: lazyInvokeStep.params,
1792
+ invokeStep,
1793
+ context: this.context,
1794
+ invokeCount: this.invokeCount,
1795
+ telemetry: this.telemetry
1796
+ });
1797
+ throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1522
1798
  }
1523
1799
  const result = await this.context.qstashClient.batchJSON(
1524
1800
  steps.map((singleStep, index) => {
@@ -1533,11 +1809,14 @@ var AutoExecutor = class _AutoExecutor {
1533
1809
  retries: this.context.retries,
1534
1810
  callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
1535
1811
  callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
1536
- telemetry: this.telemetry
1812
+ telemetry: this.telemetry,
1813
+ invokeCount: this.invokeCount,
1814
+ flowControl: this.context.flowControl,
1815
+ callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
1537
1816
  });
1538
1817
  const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1539
1818
  singleStep.out = JSON.stringify(singleStep.out);
1540
- return singleStep.callUrl ? (
1819
+ return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
1541
1820
  // if the step is a third party call, we call the third party
1542
1821
  // url (singleStep.callUrl) and pass information about the workflow
1543
1822
  // in the headers (handled in getHeaders). QStash makes the request
@@ -2101,6 +2380,11 @@ var WorkflowContext = class {
2101
2380
  * Number of retries
2102
2381
  */
2103
2382
  retries;
2383
+ /**
2384
+ * Settings for controlling the number of active requests
2385
+ * and number of requests per second with the same key.
2386
+ */
2387
+ flowControl;
2104
2388
  constructor({
2105
2389
  qstashClient,
2106
2390
  workflowRunId,
@@ -2112,7 +2396,9 @@ var WorkflowContext = class {
2112
2396
  initialPayload,
2113
2397
  env,
2114
2398
  retries,
2115
- telemetry
2399
+ telemetry,
2400
+ invokeCount,
2401
+ flowControl
2116
2402
  }) {
2117
2403
  this.qstashClient = qstashClient;
2118
2404
  this.workflowRunId = workflowRunId;
@@ -2123,7 +2409,8 @@ var WorkflowContext = class {
2123
2409
  this.requestPayload = initialPayload;
2124
2410
  this.env = env ?? {};
2125
2411
  this.retries = retries ?? DEFAULT_RETRIES;
2126
- this.executor = new AutoExecutor(this, this.steps, telemetry, debug);
2412
+ this.flowControl = flowControl;
2413
+ this.executor = new AutoExecutor(this, this.steps, telemetry, invokeCount, debug);
2127
2414
  }
2128
2415
  /**
2129
2416
  * Executes a workflow step
@@ -2225,7 +2512,7 @@ var WorkflowContext = class {
2225
2512
  * }
2226
2513
  */
2227
2514
  async call(stepName, settings) {
2228
- const { url, method = "GET", body, headers = {}, retries = 0, timeout } = settings;
2515
+ const { url, method = "GET", body, headers = {}, retries = 0, timeout, flowControl } = settings;
2229
2516
  const result = await this.addStep(
2230
2517
  new LazyCallStep(
2231
2518
  stepName,
@@ -2234,7 +2521,8 @@ var WorkflowContext = class {
2234
2521
  body,
2235
2522
  headers,
2236
2523
  retries,
2237
- timeout
2524
+ timeout,
2525
+ flowControl
2238
2526
  )
2239
2527
  );
2240
2528
  if (typeof result === "string") {
@@ -2343,6 +2631,13 @@ var WorkflowContext = class {
2343
2631
  return result;
2344
2632
  }
2345
2633
  }
2634
+ async invoke(stepName, settings) {
2635
+ const result = await this.addStep(new LazyInvokeStep(stepName, settings));
2636
+ return {
2637
+ ...result,
2638
+ body: result.body ? JSON.parse(result.body) : void 0
2639
+ };
2640
+ }
2346
2641
  /**
2347
2642
  * Cancel the current workflow run
2348
2643
  *
@@ -2420,31 +2715,6 @@ var WorkflowLogger = class _WorkflowLogger {
2420
2715
  }
2421
2716
  };
2422
2717
 
2423
- // src/utils.ts
2424
- var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
2425
- var NANOID_LENGTH = 21;
2426
- function getRandomInt() {
2427
- return Math.floor(Math.random() * NANOID_CHARS.length);
2428
- }
2429
- function nanoid() {
2430
- return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
2431
- }
2432
- function getWorkflowRunId(id) {
2433
- return `wfr_${id ?? nanoid()}`;
2434
- }
2435
- function decodeBase64(base64) {
2436
- try {
2437
- const binString = atob(base64);
2438
- const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
2439
- return new TextDecoder().decode(intArray);
2440
- } catch (error) {
2441
- console.warn(
2442
- `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
2443
- );
2444
- return atob(base64);
2445
- }
2446
- }
2447
-
2448
2718
  // src/serve/authorization.ts
2449
2719
  import { Client as Client2 } from "@upstash/qstash";
2450
2720
  var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
@@ -2489,7 +2759,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
2489
2759
  failureUrl: context.failureUrl,
2490
2760
  initialPayload: context.requestPayload,
2491
2761
  env: context.env,
2492
- retries: context.retries
2762
+ retries: context.retries,
2763
+ flowControl: context.flowControl
2493
2764
  });
2494
2765
  try {
2495
2766
  await routeFunction(disabledContext);
@@ -2642,7 +2913,7 @@ var parseRequest = async (requestPayload, isFirstInvocation, workflowRunId, requ
2642
2913
  };
2643
2914
  }
2644
2915
  };
2645
- var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, debug) => {
2916
+ var handleFailure = async (request, requestPayload, qstashClient, initialPayloadParser, routeFunction, failureFunction, env, retries, flowControl, debug) => {
2646
2917
  if (request.headers.get(WORKFLOW_FAILURE_HEADER) !== "true") {
2647
2918
  return ok("not-failure-callback");
2648
2919
  }
@@ -2654,22 +2925,21 @@ var handleFailure = async (request, requestPayload, qstashClient, initialPayload
2654
2925
  );
2655
2926
  }
2656
2927
  try {
2657
- const { status, header, body, url, sourceHeader, sourceBody, workflowRunId } = JSON.parse(
2658
- requestPayload
2659
- );
2928
+ const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload);
2660
2929
  const decodedBody = body ? decodeBase64(body) : "{}";
2661
2930
  const errorPayload = JSON.parse(decodedBody);
2662
2931
  const workflowContext = new WorkflowContext({
2663
2932
  qstashClient,
2664
2933
  workflowRunId,
2665
2934
  initialPayload: sourceBody ? initialPayloadParser(decodeBase64(sourceBody)) : void 0,
2666
- headers: recreateUserHeaders(new Headers(sourceHeader)),
2935
+ headers: recreateUserHeaders(request.headers),
2667
2936
  steps: [],
2668
2937
  url,
2669
2938
  failureUrl: url,
2670
2939
  debug,
2671
2940
  env,
2672
2941
  retries,
2942
+ flowControl,
2673
2943
  telemetry: void 0
2674
2944
  // not going to make requests in authentication check
2675
2945
  });
@@ -2796,7 +3066,8 @@ var serveBase = (routeFunction, telemetry, options) => {
2796
3066
  env,
2797
3067
  retries,
2798
3068
  useJSONContent,
2799
- disableTelemetry
3069
+ disableTelemetry,
3070
+ flowControl
2800
3071
  } = processOptions(options);
2801
3072
  telemetry = disableTelemetry ? void 0 : telemetry;
2802
3073
  const debug = WorkflowLogger.getLogger(verbose);
@@ -2837,6 +3108,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2837
3108
  failureFunction,
2838
3109
  env,
2839
3110
  retries,
3111
+ flowControl,
2840
3112
  debug
2841
3113
  );
2842
3114
  if (failureCheck.isErr()) {
@@ -2845,6 +3117,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2845
3117
  await debug?.log("WARN", "RESPONSE_DEFAULT", "failureFunction executed");
2846
3118
  return onStepFinish(workflowRunId, "failure-callback");
2847
3119
  }
3120
+ const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
2848
3121
  const workflowContext = new WorkflowContext({
2849
3122
  qstashClient,
2850
3123
  workflowRunId,
@@ -2856,7 +3129,9 @@ var serveBase = (routeFunction, telemetry, options) => {
2856
3129
  debug,
2857
3130
  env,
2858
3131
  retries,
2859
- telemetry
3132
+ telemetry,
3133
+ invokeCount,
3134
+ flowControl
2860
3135
  });
2861
3136
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
2862
3137
  routeFunction,
@@ -2879,6 +3154,7 @@ var serveBase = (routeFunction, telemetry, options) => {
2879
3154
  workflowUrl,
2880
3155
  failureUrl: workflowFailureUrl,
2881
3156
  retries,
3157
+ flowControl,
2882
3158
  telemetry,
2883
3159
  debug
2884
3160
  });
@@ -2888,10 +3164,16 @@ var serveBase = (routeFunction, telemetry, options) => {
2888
3164
  });
2889
3165
  throw callReturnCheck.error;
2890
3166
  } else if (callReturnCheck.value === "continue-workflow") {
2891
- const result = isFirstInvocation ? await triggerFirstInvocation({ workflowContext, useJSONContent, telemetry, debug }) : await triggerRouteFunction({
3167
+ const result = isFirstInvocation ? await triggerFirstInvocation({
3168
+ workflowContext,
3169
+ useJSONContent,
3170
+ telemetry,
3171
+ debug,
3172
+ invokeCount
3173
+ }) : await triggerRouteFunction({
2892
3174
  onStep: async () => routeFunction(workflowContext),
2893
- onCleanup: async () => {
2894
- await triggerWorkflowDelete(workflowContext, debug);
3175
+ onCleanup: async (result2) => {
3176
+ await triggerWorkflowDelete(workflowContext, result2, debug);
2895
3177
  },
2896
3178
  onCancel: async () => {
2897
3179
  await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
@@ -2942,12 +3224,13 @@ export {
2942
3224
  SDK_TELEMETRY,
2943
3225
  WorkflowError,
2944
3226
  WorkflowAbort,
3227
+ getWorkflowRunId,
2945
3228
  StepTypes,
2946
3229
  triggerFirstInvocation,
3230
+ serveManyBase,
2947
3231
  WorkflowTool,
2948
3232
  WorkflowContext,
2949
3233
  WorkflowLogger,
2950
- getWorkflowRunId,
2951
3234
  serveBase,
2952
3235
  serve
2953
3236
  };