@mastra/inngest 0.0.0-vnext-inngest-20250506123700

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,693 @@
1
+ 'use strict';
2
+
3
+ var vNext = require('@mastra/core/workflows/vNext');
4
+ var realtime = require('@inngest/realtime');
5
+ var hono = require('inngest/hono');
6
+ var di = require('@mastra/core/di');
7
+ var crypto = require('crypto');
8
+
9
+ // src/index.ts
10
+ function serve({ mastra, ingest }) {
11
+ const wfs = mastra.vnext_getWorkflows();
12
+ const functions = Object.values(wfs).flatMap((wf) => {
13
+ if (wf instanceof InngestWorkflow) {
14
+ return wf.getFunctions();
15
+ }
16
+ return [];
17
+ });
18
+ return hono.serve({
19
+ client: ingest,
20
+ functions
21
+ });
22
+ }
23
+ var InngestRun = class extends vNext.Run {
24
+ inngest;
25
+ #mastra;
26
+ constructor(params, inngest) {
27
+ super(params);
28
+ this.inngest = inngest;
29
+ this.#mastra = params.mastra;
30
+ }
31
+ async getRuns(eventId) {
32
+ const response = await fetch(`${this.inngest.apiBaseUrl}/v1/events/${eventId}/runs`, {
33
+ headers: {
34
+ Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
35
+ }
36
+ });
37
+ const json = await response.json();
38
+ return json.data;
39
+ }
40
+ async getRunOutput(eventId) {
41
+ let runs = await this.getRuns(eventId);
42
+ while (runs?.[0]?.status !== "Completed") {
43
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
44
+ runs = await this.getRuns(eventId);
45
+ if (runs?.[0]?.status === "Failed" || runs?.[0]?.status === "Cancelled") {
46
+ throw new Error(`Function run ${runs?.[0]?.status}`);
47
+ }
48
+ }
49
+ return runs?.[0];
50
+ }
51
+ async start({
52
+ inputData,
53
+ runtimeContext
54
+ }) {
55
+ const eventOutput = await this.inngest.send({
56
+ name: `workflow.${this.workflowId}`,
57
+ data: {
58
+ inputData,
59
+ runId: this.runId
60
+ }
61
+ });
62
+ const eventId = eventOutput.ids[0];
63
+ if (!eventId) {
64
+ throw new Error("Event ID is not set");
65
+ }
66
+ const runOutput = await this.getRunOutput(eventId);
67
+ const result = runOutput?.output?.result;
68
+ if (result.status === "failed") {
69
+ result.error = new Error(result.error);
70
+ }
71
+ return result;
72
+ }
73
+ async resume(params) {
74
+ const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
75
+ (step) => typeof step === "string" ? step : step?.id
76
+ );
77
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
78
+ workflowName: this.workflowId,
79
+ runId: this.runId
80
+ });
81
+ const eventOutput = await this.inngest.send({
82
+ name: `workflow.${this.workflowId}`,
83
+ data: {
84
+ inputData: params.resumeData,
85
+ runId: this.runId,
86
+ stepResults: snapshot?.context,
87
+ resume: {
88
+ steps,
89
+ stepResults: snapshot?.context,
90
+ resumePayload: params.resumeData,
91
+ // @ts-ignore
92
+ resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
93
+ }
94
+ }
95
+ });
96
+ const eventId = eventOutput.ids[0];
97
+ if (!eventId) {
98
+ throw new Error("Event ID is not set");
99
+ }
100
+ const runOutput = await this.getRunOutput(eventId);
101
+ const result = runOutput?.output?.result;
102
+ if (result.status === "failed") {
103
+ result.error = new Error(result.error);
104
+ }
105
+ return result;
106
+ }
107
+ watch(cb) {
108
+ const streamPromise = realtime.subscribe(
109
+ {
110
+ channel: `workflow:${this.workflowId}:${this.runId}`,
111
+ topics: ["watch"],
112
+ app: this.inngest
113
+ },
114
+ (message) => {
115
+ cb(message.data);
116
+ }
117
+ );
118
+ return () => {
119
+ streamPromise.then((stream) => {
120
+ stream.cancel();
121
+ });
122
+ };
123
+ }
124
+ };
125
+ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
126
+ #mastra;
127
+ inngest;
128
+ function;
129
+ constructor(params, inngest) {
130
+ super(params);
131
+ this.#mastra = params.mastra;
132
+ this.inngest = inngest;
133
+ }
134
+ __registerMastra(mastra) {
135
+ this.#mastra = mastra;
136
+ this.executionEngine.__registerMastra(mastra);
137
+ if (this.executionGraph.steps.length) {
138
+ for (const step of this.executionGraph.steps) {
139
+ if (step.type === "step" && step.step instanceof _InngestWorkflow) {
140
+ step.step.__registerMastra(mastra);
141
+ }
142
+ }
143
+ }
144
+ }
145
+ createRun(options) {
146
+ const runIdToUse = options?.runId || crypto.randomUUID();
147
+ return new InngestRun(
148
+ {
149
+ workflowId: this.id,
150
+ runId: runIdToUse,
151
+ executionEngine: this.executionEngine,
152
+ executionGraph: this.executionGraph,
153
+ mastra: this.#mastra,
154
+ retryConfig: this.retryConfig
155
+ },
156
+ this.inngest
157
+ );
158
+ }
159
+ getFunction() {
160
+ if (this.function) {
161
+ return this.function;
162
+ }
163
+ this.function = this.inngest.createFunction(
164
+ // @ts-ignore
165
+ { id: `workflow.${this.id}`, retries: this.retryConfig?.attempts ?? 0 },
166
+ { event: `workflow.${this.id}` },
167
+ async ({ event, step, attempt, publish }) => {
168
+ let { inputData, runId, resume } = event.data;
169
+ if (!runId) {
170
+ runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
171
+ return crypto.randomUUID();
172
+ });
173
+ }
174
+ const emitter = {
175
+ emit: async (event2, data) => {
176
+ if (!publish) {
177
+ return;
178
+ }
179
+ try {
180
+ await publish({
181
+ channel: `workflow:${this.id}:${runId}`,
182
+ topic: "watch",
183
+ data
184
+ });
185
+ } catch (err) {
186
+ this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
187
+ }
188
+ }
189
+ };
190
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
191
+ const result = await engine.execute({
192
+ workflowId: this.id,
193
+ runId,
194
+ graph: this.executionGraph,
195
+ input: inputData,
196
+ emitter,
197
+ retryConfig: this.retryConfig,
198
+ runtimeContext: new di.RuntimeContext(),
199
+ // TODO
200
+ resume
201
+ });
202
+ return { result, runId };
203
+ }
204
+ );
205
+ return this.function;
206
+ }
207
+ getNestedFunctions(steps) {
208
+ return steps.flatMap((step) => {
209
+ if (step.type === "step" || step.type === "loop" || step.type === "foreach") {
210
+ if (step.step instanceof _InngestWorkflow) {
211
+ return [step.step.getFunction(), ...step.step.getNestedFunctions(step.step.executionGraph.steps)];
212
+ }
213
+ return [];
214
+ } else if (step.type === "parallel" || step.type === "conditional") {
215
+ return this.getNestedFunctions(step.steps);
216
+ }
217
+ return [];
218
+ });
219
+ }
220
+ getFunctions() {
221
+ return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
222
+ }
223
+ };
224
+ function cloneWorkflow(workflow, opts) {
225
+ const wf = new InngestWorkflow(
226
+ {
227
+ id: opts.id,
228
+ inputSchema: workflow.inputSchema,
229
+ outputSchema: workflow.outputSchema,
230
+ steps: workflow.stepDefs,
231
+ mastra: workflow.mastra
232
+ },
233
+ workflow.inngest
234
+ );
235
+ wf.setStepFlow(workflow.stepGraph);
236
+ wf.commit();
237
+ return wf;
238
+ }
239
+ function init(inngest) {
240
+ return {
241
+ createWorkflow(params) {
242
+ return new InngestWorkflow(params, inngest);
243
+ },
244
+ createStep: vNext.createStep,
245
+ cloneStep: vNext.cloneStep,
246
+ cloneWorkflow
247
+ };
248
+ }
249
+ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
250
+ inngestStep;
251
+ inngestAttempts;
252
+ constructor(mastra, inngestStep, inngestAttempts = 0) {
253
+ super({ mastra });
254
+ this.inngestStep = inngestStep;
255
+ this.inngestAttempts = inngestAttempts;
256
+ }
257
+ async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
258
+ const base = {
259
+ status: lastOutput.status,
260
+ steps: stepResults
261
+ };
262
+ if (lastOutput.status === "success") {
263
+ await emitter.emit("watch", {
264
+ type: "watch",
265
+ payload: {
266
+ workflowState: {
267
+ status: lastOutput.status,
268
+ steps: stepResults,
269
+ result: lastOutput.output
270
+ }
271
+ },
272
+ eventTimestamp: Date.now()
273
+ });
274
+ base.result = lastOutput.output;
275
+ } else if (lastOutput.status === "failed") {
276
+ base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
277
+ await emitter.emit("watch", {
278
+ type: "watch",
279
+ payload: {
280
+ workflowState: {
281
+ status: lastOutput.status,
282
+ steps: stepResults,
283
+ result: null,
284
+ error: base.error
285
+ }
286
+ },
287
+ eventTimestamp: Date.now()
288
+ });
289
+ } else if (lastOutput.status === "suspended") {
290
+ await emitter.emit("watch", {
291
+ type: "watch",
292
+ payload: {
293
+ workflowState: {
294
+ status: lastOutput.status,
295
+ steps: stepResults,
296
+ result: null,
297
+ error: null
298
+ }
299
+ },
300
+ eventTimestamp: Date.now()
301
+ });
302
+ const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
303
+ if (stepResult?.status === "suspended") {
304
+ const nestedPath = stepResult?.payload?.__workflow_meta?.path;
305
+ return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
306
+ }
307
+ return [];
308
+ });
309
+ base.suspended = suspendedStepIds;
310
+ }
311
+ executionSpan?.end();
312
+ return base;
313
+ }
314
+ async superExecuteStep({
315
+ workflowId,
316
+ runId,
317
+ step,
318
+ stepResults,
319
+ executionContext,
320
+ resume,
321
+ prevOutput,
322
+ emitter,
323
+ runtimeContext
324
+ }) {
325
+ return super.executeStep({
326
+ workflowId,
327
+ runId,
328
+ step,
329
+ stepResults,
330
+ executionContext,
331
+ resume,
332
+ prevOutput,
333
+ emitter,
334
+ runtimeContext
335
+ });
336
+ }
337
+ async executeStep({
338
+ step,
339
+ stepResults,
340
+ executionContext,
341
+ resume,
342
+ prevOutput,
343
+ emitter,
344
+ runtimeContext
345
+ }) {
346
+ await this.inngestStep.run(
347
+ `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
348
+ async () => {
349
+ await emitter.emit("watch", {
350
+ type: "watch",
351
+ payload: {
352
+ currentStep: {
353
+ id: step.id,
354
+ status: "running"
355
+ },
356
+ workflowState: {
357
+ status: "running",
358
+ steps: {
359
+ ...stepResults,
360
+ [step.id]: {
361
+ status: "running"
362
+ }
363
+ },
364
+ result: null,
365
+ error: null
366
+ }
367
+ },
368
+ eventTimestamp: Date.now()
369
+ });
370
+ }
371
+ );
372
+ if (step instanceof InngestWorkflow) {
373
+ const isResume = !!resume?.steps?.length;
374
+ let result;
375
+ let runId;
376
+ if (isResume) {
377
+ runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? crypto.randomUUID();
378
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
379
+ workflowName: step.id,
380
+ runId
381
+ });
382
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
383
+ function: step.getFunction(),
384
+ data: {
385
+ inputData: prevOutput,
386
+ runId,
387
+ resume: {
388
+ runId,
389
+ steps: resume.steps.slice(1),
390
+ stepResults: snapshot?.context,
391
+ resumePayload: resume.resumePayload,
392
+ // @ts-ignore
393
+ resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
394
+ }
395
+ }
396
+ });
397
+ result = invokeResp.result;
398
+ runId = invokeResp.runId;
399
+ } else {
400
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
401
+ function: step.getFunction(),
402
+ data: {
403
+ inputData: prevOutput
404
+ }
405
+ });
406
+ result = invokeResp.result;
407
+ runId = invokeResp.runId;
408
+ }
409
+ const res = await this.inngestStep.run(
410
+ `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
411
+ async () => {
412
+ if (result.status === "failed") {
413
+ await emitter.emit("watch", {
414
+ type: "watch",
415
+ payload: {
416
+ currentStep: {
417
+ id: step.id,
418
+ status: "failed",
419
+ error: result?.error
420
+ },
421
+ workflowState: {
422
+ status: "running",
423
+ steps: stepResults,
424
+ result: null,
425
+ error: null
426
+ }
427
+ },
428
+ eventTimestamp: Date.now()
429
+ });
430
+ return { executionContext, result: { status: "failed", error: result?.error } };
431
+ } else if (result.status === "suspended") {
432
+ const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
433
+ const stepRes2 = stepResult;
434
+ return stepRes2?.status === "suspended";
435
+ });
436
+ for (const [stepName, stepResult] of suspendedSteps) {
437
+ const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
438
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
439
+ await emitter.emit("watch", {
440
+ type: "watch",
441
+ payload: {
442
+ currentStep: {
443
+ id: step.id,
444
+ status: "suspended",
445
+ payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
446
+ },
447
+ workflowState: {
448
+ status: "running",
449
+ steps: stepResults,
450
+ result: null,
451
+ error: null
452
+ }
453
+ },
454
+ eventTimestamp: Date.now()
455
+ });
456
+ return {
457
+ executionContext,
458
+ result: {
459
+ status: "suspended",
460
+ payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
461
+ }
462
+ };
463
+ }
464
+ await emitter.emit("watch", {
465
+ type: "watch",
466
+ payload: {
467
+ currentStep: {
468
+ id: step.id,
469
+ status: "suspended",
470
+ payload: {}
471
+ },
472
+ workflowState: {
473
+ status: "running",
474
+ steps: stepResults,
475
+ result: null,
476
+ error: null
477
+ }
478
+ },
479
+ eventTimestamp: Date.now()
480
+ });
481
+ return {
482
+ executionContext,
483
+ result: {
484
+ status: "suspended",
485
+ payload: {}
486
+ }
487
+ };
488
+ }
489
+ await emitter.emit("watch", {
490
+ type: "watch",
491
+ payload: {
492
+ currentStep: {
493
+ id: step.id,
494
+ status: "success",
495
+ output: result?.result
496
+ },
497
+ workflowState: {
498
+ status: "running",
499
+ steps: stepResults,
500
+ result: null,
501
+ error: null
502
+ }
503
+ },
504
+ eventTimestamp: Date.now()
505
+ });
506
+ return { executionContext, result: { status: "success", output: result?.result } };
507
+ }
508
+ );
509
+ Object.assign(executionContext, res.executionContext);
510
+ return res.result;
511
+ }
512
+ const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
513
+ let execResults;
514
+ let suspended;
515
+ try {
516
+ const result = await step.execute({
517
+ mastra: this.mastra,
518
+ runtimeContext,
519
+ inputData: prevOutput,
520
+ resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
521
+ getInitData: () => stepResults?.input,
522
+ getStepResult: (step2) => {
523
+ const result2 = stepResults[step2.id];
524
+ if (result2?.status === "success") {
525
+ return result2.output;
526
+ }
527
+ return null;
528
+ },
529
+ suspend: async (suspendPayload) => {
530
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
531
+ suspended = { payload: suspendPayload };
532
+ },
533
+ resume: {
534
+ steps: resume?.steps?.slice(1) || [],
535
+ resumePayload: resume?.resumePayload,
536
+ // @ts-ignore
537
+ runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
538
+ },
539
+ emitter
540
+ });
541
+ execResults = { status: "success", output: result };
542
+ } catch (e) {
543
+ execResults = { status: "failed", error: e instanceof Error ? e.message : String(e) };
544
+ }
545
+ if (suspended) {
546
+ execResults = { status: "suspended", payload: suspended.payload };
547
+ }
548
+ if (execResults.status === "failed") {
549
+ if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
550
+ throw execResults.error;
551
+ }
552
+ }
553
+ await emitter.emit("watch", {
554
+ type: "watch",
555
+ payload: {
556
+ currentStep: {
557
+ id: step.id,
558
+ status: execResults.status,
559
+ output: execResults.output
560
+ },
561
+ workflowState: {
562
+ status: "running",
563
+ steps: stepResults,
564
+ result: null,
565
+ error: null
566
+ }
567
+ },
568
+ eventTimestamp: Date.now()
569
+ });
570
+ return { result: execResults, executionContext, stepResults };
571
+ });
572
+ Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
573
+ Object.assign(stepResults, stepRes.stepResults);
574
+ return stepRes.result;
575
+ }
576
+ async persistStepUpdate({
577
+ workflowId,
578
+ runId,
579
+ stepResults,
580
+ executionContext
581
+ }) {
582
+ await this.inngestStep.run(
583
+ `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
584
+ async () => {
585
+ await this.mastra?.getStorage()?.persistWorkflowSnapshot({
586
+ workflowName: workflowId,
587
+ runId,
588
+ snapshot: {
589
+ runId,
590
+ value: {},
591
+ context: stepResults,
592
+ activePaths: [],
593
+ suspendedPaths: executionContext.suspendedPaths,
594
+ // @ts-ignore
595
+ timestamp: Date.now()
596
+ }
597
+ });
598
+ }
599
+ );
600
+ }
601
+ async executeConditional({
602
+ workflowId,
603
+ runId,
604
+ entry,
605
+ prevOutput,
606
+ prevStep,
607
+ stepResults,
608
+ resume,
609
+ executionContext,
610
+ emitter,
611
+ runtimeContext
612
+ }) {
613
+ let execResults;
614
+ const truthyIndexes = (await Promise.all(
615
+ entry.conditions.map(
616
+ (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
617
+ try {
618
+ const result = await cond({
619
+ mastra: this.mastra,
620
+ runtimeContext,
621
+ inputData: prevOutput,
622
+ getInitData: () => stepResults?.input,
623
+ getStepResult: (step) => {
624
+ if (!step?.id) {
625
+ return null;
626
+ }
627
+ const result2 = stepResults[step.id];
628
+ if (result2?.status === "success") {
629
+ return result2.output;
630
+ }
631
+ return null;
632
+ },
633
+ // TODO: this function shouldn't have suspend probably?
634
+ suspend: async (_suspendPayload) => {
635
+ },
636
+ emitter
637
+ });
638
+ return result ? index : null;
639
+ } catch (e) {
640
+ return null;
641
+ }
642
+ })
643
+ )
644
+ )).filter((index) => index !== null);
645
+ const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
646
+ const results = await Promise.all(
647
+ stepsToRun.map(
648
+ (step, index) => this.executeEntry({
649
+ workflowId,
650
+ runId,
651
+ entry: step,
652
+ prevStep,
653
+ stepResults,
654
+ resume,
655
+ executionContext: {
656
+ workflowId,
657
+ runId,
658
+ executionPath: [...executionContext.executionPath, index],
659
+ suspendedPaths: executionContext.suspendedPaths,
660
+ retryConfig: executionContext.retryConfig,
661
+ executionSpan: executionContext.executionSpan
662
+ },
663
+ emitter,
664
+ runtimeContext
665
+ })
666
+ )
667
+ );
668
+ const hasFailed = results.find((result) => result.status === "failed");
669
+ const hasSuspended = results.find((result) => result.status === "suspended");
670
+ if (hasFailed) {
671
+ execResults = { status: "failed", error: hasFailed.error };
672
+ } else if (hasSuspended) {
673
+ execResults = { status: "suspended", payload: hasSuspended.payload };
674
+ } else {
675
+ execResults = {
676
+ status: "success",
677
+ output: results.reduce((acc, result, index) => {
678
+ if (result.status === "success") {
679
+ acc[stepsToRun[index].step.id] = result.output;
680
+ }
681
+ return acc;
682
+ }, {})
683
+ };
684
+ }
685
+ return execResults;
686
+ }
687
+ };
688
+
689
+ exports.InngestExecutionEngine = InngestExecutionEngine;
690
+ exports.InngestRun = InngestRun;
691
+ exports.InngestWorkflow = InngestWorkflow;
692
+ exports.init = init;
693
+ exports.serve = serve;
@@ -0,0 +1,5 @@
1
+ export { serve } from './_tsup-dts-rollup.cjs';
2
+ export { init } from './_tsup-dts-rollup.cjs';
3
+ export { InngestRun } from './_tsup-dts-rollup.cjs';
4
+ export { InngestWorkflow } from './_tsup-dts-rollup.cjs';
5
+ export { InngestExecutionEngine } from './_tsup-dts-rollup.cjs';
@@ -0,0 +1,5 @@
1
+ export { serve } from './_tsup-dts-rollup.js';
2
+ export { init } from './_tsup-dts-rollup.js';
3
+ export { InngestRun } from './_tsup-dts-rollup.js';
4
+ export { InngestWorkflow } from './_tsup-dts-rollup.js';
5
+ export { InngestExecutionEngine } from './_tsup-dts-rollup.js';