@mastra/inngest 0.0.0-fix-fetching-workflow-snapshots-20250625000954 → 0.0.0-fix-traces-pagination-plus-share-for-cloud-20250717083008

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/src/index.ts CHANGED
@@ -33,13 +33,17 @@ export type InngestEngineType = {
33
33
 
34
34
  export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest }): ReturnType<typeof inngestServe> {
35
35
  const wfs = mastra.getWorkflows();
36
- const functions = Object.values(wfs).flatMap(wf => {
37
- if (wf instanceof InngestWorkflow) {
38
- wf.__registerMastra(mastra);
39
- return wf.getFunctions();
40
- }
41
- return [];
42
- });
36
+ const functions = Array.from(
37
+ new Set(
38
+ Object.values(wfs).flatMap(wf => {
39
+ if (wf instanceof InngestWorkflow) {
40
+ wf.__registerMastra(mastra);
41
+ return wf.getFunctions();
42
+ }
43
+ return [];
44
+ }),
45
+ ),
46
+ );
43
47
  return inngestServe({
44
48
  client: inngest,
45
49
  functions,
@@ -94,8 +98,15 @@ export class InngestRun<
94
98
  while (runs?.[0]?.status !== 'Completed' || runs?.[0]?.event_id !== eventId) {
95
99
  await new Promise(resolve => setTimeout(resolve, 1000));
96
100
  runs = await this.getRuns(eventId);
97
- if (runs?.[0]?.status === 'Failed' || runs?.[0]?.status === 'Cancelled') {
101
+ if (runs?.[0]?.status === 'Failed') {
102
+ console.log('run', runs?.[0]);
98
103
  throw new Error(`Function run ${runs?.[0]?.status}`);
104
+ } else if (runs?.[0]?.status === 'Cancelled') {
105
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
106
+ workflowName: this.workflowId,
107
+ runId: this.runId,
108
+ });
109
+ return { output: { result: { steps: snapshot?.context, status: 'canceled' } } };
99
110
  }
100
111
  }
101
112
  return runs?.[0];
@@ -108,6 +119,30 @@ export class InngestRun<
108
119
  });
109
120
  }
110
121
 
122
+ async cancel() {
123
+ await this.inngest.send({
124
+ name: `cancel.workflow.${this.workflowId}`,
125
+ data: {
126
+ runId: this.runId,
127
+ },
128
+ });
129
+
130
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
131
+ workflowName: this.workflowId,
132
+ runId: this.runId,
133
+ });
134
+ if (snapshot) {
135
+ await this.#mastra?.storage?.persistWorkflowSnapshot({
136
+ workflowName: this.workflowId,
137
+ runId: this.runId,
138
+ snapshot: {
139
+ ...snapshot,
140
+ status: 'canceled' as any,
141
+ },
142
+ });
143
+ }
144
+ }
145
+
111
146
  async start({
112
147
  inputData,
113
148
  }: {
@@ -147,7 +182,9 @@ export class InngestRun<
147
182
  result.error = new Error(result.error);
148
183
  }
149
184
 
150
- this.cleanup?.();
185
+ if (result.status !== 'suspended') {
186
+ this.cleanup?.();
187
+ }
151
188
  return result;
152
189
  }
153
190
 
@@ -432,23 +469,27 @@ export class InngestWorkflow<
432
469
 
433
470
  this.runs.set(runIdToUse, run);
434
471
 
435
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
436
- workflowName: this.id,
437
- runId: runIdToUse,
438
- snapshot: {
472
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
473
+
474
+ if (!workflowSnapshotInStorage) {
475
+ await this.mastra?.getStorage()?.persistWorkflowSnapshot({
476
+ workflowName: this.id,
439
477
  runId: runIdToUse,
440
- status: 'pending',
441
- value: {},
442
- context: {},
443
- activePaths: [],
444
- serializedStepGraph: this.serializedStepGraph,
445
- suspendedPaths: {},
446
- result: undefined,
447
- error: undefined,
448
- // @ts-ignore
449
- timestamp: Date.now(),
450
- },
451
- });
478
+ snapshot: {
479
+ runId: runIdToUse,
480
+ status: 'pending',
481
+ value: {},
482
+ context: {},
483
+ activePaths: [],
484
+ serializedStepGraph: this.serializedStepGraph,
485
+ suspendedPaths: {},
486
+ result: undefined,
487
+ error: undefined,
488
+ // @ts-ignore
489
+ timestamp: Date.now(),
490
+ },
491
+ });
492
+ }
452
493
 
453
494
  return run;
454
495
  }
@@ -458,8 +499,12 @@ export class InngestWorkflow<
458
499
  return this.function;
459
500
  }
460
501
  this.function = this.inngest.createFunction(
461
- // @ts-ignore
462
- { id: `workflow.${this.id}`, retries: this.retryConfig?.attempts ?? 0 },
502
+ {
503
+ id: `workflow.${this.id}`,
504
+ // @ts-ignore
505
+ retries: this.retryConfig?.attempts ?? 0,
506
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
507
+ },
463
508
  { event: `workflow.${this.id}` },
464
509
  async ({ event, step, attempt, publish }) => {
465
510
  let { inputData, runId, resume } = event.data;
@@ -508,6 +553,7 @@ export class InngestWorkflow<
508
553
  retryConfig: this.retryConfig,
509
554
  runtimeContext: new RuntimeContext(), // TODO
510
555
  resume,
556
+ abortController: new AbortController(),
511
557
  });
512
558
 
513
559
  return { result, runId };
@@ -630,7 +676,7 @@ export function createStep<
630
676
  outputSchema: z.object({
631
677
  text: z.string(),
632
678
  }),
633
- execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext }) => {
679
+ execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
634
680
  let streamPromise = {} as {
635
681
  promise: Promise<string>;
636
682
  resolve: (value: string) => void;
@@ -656,8 +702,13 @@ export function createStep<
656
702
  onFinish: result => {
657
703
  streamPromise.resolve(result.text);
658
704
  },
705
+ abortSignal,
659
706
  });
660
707
 
708
+ if (abortSignal.aborted) {
709
+ return abort();
710
+ }
711
+
661
712
  for await (const chunk of fullStream) {
662
713
  switch (chunk.type) {
663
714
  case 'text-delta':
@@ -815,6 +866,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
815
866
  delay?: number;
816
867
  };
817
868
  runtimeContext: RuntimeContext;
869
+ abortController: AbortController;
818
870
  }): Promise<TOutput> {
819
871
  await params.emitter.emit('watch-v2', {
820
872
  type: 'start',
@@ -914,6 +966,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
914
966
  resume,
915
967
  prevOutput,
916
968
  emitter,
969
+ abortController,
917
970
  runtimeContext,
918
971
  }: {
919
972
  workflowId: string;
@@ -927,6 +980,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
927
980
  };
928
981
  prevOutput: any;
929
982
  emitter: Emitter;
983
+ abortController: AbortController;
930
984
  runtimeContext: RuntimeContext;
931
985
  }): Promise<StepResult<any, any, any, any>> {
932
986
  return super.executeStep({
@@ -938,12 +992,163 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
938
992
  resume,
939
993
  prevOutput,
940
994
  emitter,
995
+ abortController,
941
996
  runtimeContext,
942
997
  });
943
998
  }
944
999
 
945
- async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
946
- await this.inngestStep.sleep(id, duration);
1000
+ // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
1001
+ // await this.inngestStep.sleep(id, duration);
1002
+ // }
1003
+
1004
+ async executeSleep({
1005
+ workflowId,
1006
+ runId,
1007
+ entry,
1008
+ prevOutput,
1009
+ stepResults,
1010
+ emitter,
1011
+ abortController,
1012
+ runtimeContext,
1013
+ }: {
1014
+ workflowId: string;
1015
+ runId: string;
1016
+ serializedStepGraph: SerializedStepFlowEntry[];
1017
+ entry: {
1018
+ type: 'sleep';
1019
+ id: string;
1020
+ duration?: number;
1021
+ fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
1022
+ };
1023
+ prevStep: StepFlowEntry;
1024
+ prevOutput: any;
1025
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1026
+ resume?: {
1027
+ steps: string[];
1028
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1029
+ resumePayload: any;
1030
+ resumePath: number[];
1031
+ };
1032
+ executionContext: ExecutionContext;
1033
+ emitter: Emitter;
1034
+ abortController: AbortController;
1035
+ runtimeContext: RuntimeContext;
1036
+ }): Promise<void> {
1037
+ let { duration, fn } = entry;
1038
+
1039
+ if (fn) {
1040
+ duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
1041
+ return await fn({
1042
+ runId,
1043
+ mastra: this.mastra!,
1044
+ runtimeContext,
1045
+ inputData: prevOutput,
1046
+ runCount: -1,
1047
+ getInitData: () => stepResults?.input as any,
1048
+ getStepResult: (step: any) => {
1049
+ if (!step?.id) {
1050
+ return null;
1051
+ }
1052
+
1053
+ const result = stepResults[step.id];
1054
+ if (result?.status === 'success') {
1055
+ return result.output;
1056
+ }
1057
+
1058
+ return null;
1059
+ },
1060
+
1061
+ // TODO: this function shouldn't have suspend probably?
1062
+ suspend: async (_suspendPayload: any): Promise<any> => {},
1063
+ bail: () => {},
1064
+ abort: () => {
1065
+ abortController?.abort();
1066
+ },
1067
+ [EMITTER_SYMBOL]: emitter,
1068
+ engine: { step: this.inngestStep },
1069
+ abortSignal: abortController?.signal,
1070
+ });
1071
+ });
1072
+ }
1073
+
1074
+ await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
1075
+ }
1076
+
1077
+ async executeSleepUntil({
1078
+ workflowId,
1079
+ runId,
1080
+ entry,
1081
+ prevOutput,
1082
+ stepResults,
1083
+ emitter,
1084
+ abortController,
1085
+ runtimeContext,
1086
+ }: {
1087
+ workflowId: string;
1088
+ runId: string;
1089
+ serializedStepGraph: SerializedStepFlowEntry[];
1090
+ entry: {
1091
+ type: 'sleepUntil';
1092
+ id: string;
1093
+ date?: Date;
1094
+ fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
1095
+ };
1096
+ prevStep: StepFlowEntry;
1097
+ prevOutput: any;
1098
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1099
+ resume?: {
1100
+ steps: string[];
1101
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1102
+ resumePayload: any;
1103
+ resumePath: number[];
1104
+ };
1105
+ executionContext: ExecutionContext;
1106
+ emitter: Emitter;
1107
+ abortController: AbortController;
1108
+ runtimeContext: RuntimeContext;
1109
+ }): Promise<void> {
1110
+ let { date, fn } = entry;
1111
+
1112
+ if (fn) {
1113
+ date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
1114
+ return await fn({
1115
+ runId,
1116
+ mastra: this.mastra!,
1117
+ runtimeContext,
1118
+ inputData: prevOutput,
1119
+ runCount: -1,
1120
+ getInitData: () => stepResults?.input as any,
1121
+ getStepResult: (step: any) => {
1122
+ if (!step?.id) {
1123
+ return null;
1124
+ }
1125
+
1126
+ const result = stepResults[step.id];
1127
+ if (result?.status === 'success') {
1128
+ return result.output;
1129
+ }
1130
+
1131
+ return null;
1132
+ },
1133
+
1134
+ // TODO: this function shouldn't have suspend probably?
1135
+ suspend: async (_suspendPayload: any): Promise<any> => {},
1136
+ bail: () => {},
1137
+ abort: () => {
1138
+ abortController?.abort();
1139
+ },
1140
+ [EMITTER_SYMBOL]: emitter,
1141
+ engine: { step: this.inngestStep },
1142
+ abortSignal: abortController?.signal,
1143
+ });
1144
+ });
1145
+ }
1146
+
1147
+ if (!(date instanceof Date)) {
1148
+ return;
1149
+ }
1150
+
1151
+ await this.inngestStep.sleepUntil(entry.id, date);
947
1152
  }
948
1153
 
949
1154
  async executeWaitForEvent({ event, timeout }: { event: string; timeout?: number }): Promise<any> {
@@ -966,17 +1171,12 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
966
1171
  resume,
967
1172
  prevOutput,
968
1173
  emitter,
1174
+ abortController,
969
1175
  runtimeContext,
970
1176
  }: {
971
1177
  step: Step<string, any, any>;
972
1178
  stepResults: Record<string, StepResult<any, any, any, any>>;
973
- executionContext: {
974
- workflowId: string;
975
- runId: string;
976
- executionPath: number[];
977
- suspendedPaths: Record<string, number[]>;
978
- retryConfig: { attempts: number; delay: number };
979
- };
1179
+ executionContext: ExecutionContext;
980
1180
  resume?: {
981
1181
  steps: string[];
982
1182
  resumePayload: any;
@@ -984,6 +1184,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
984
1184
  };
985
1185
  prevOutput: any;
986
1186
  emitter: Emitter;
1187
+ abortController: AbortController;
987
1188
  runtimeContext: RuntimeContext;
988
1189
  }): Promise<StepResult<any, any, any, any>> {
989
1190
  const startedAt = await this.inngestStep.run(
@@ -1016,6 +1217,9 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1016
1217
  type: 'step-start',
1017
1218
  payload: {
1018
1219
  id: step.id,
1220
+ status: 'running',
1221
+ payload: prevOutput,
1222
+ startedAt,
1019
1223
  },
1020
1224
  });
1021
1225
 
@@ -1091,6 +1295,8 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1091
1295
  payload: {
1092
1296
  id: step.id,
1093
1297
  status: 'failed',
1298
+ error: result?.error,
1299
+ payload: prevOutput,
1094
1300
  },
1095
1301
  });
1096
1302
 
@@ -1128,6 +1334,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1128
1334
  type: 'step-suspended',
1129
1335
  payload: {
1130
1336
  id: step.id,
1337
+ status: 'suspended',
1131
1338
  },
1132
1339
  });
1133
1340
 
@@ -1187,6 +1394,15 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1187
1394
  eventTimestamp: Date.now(),
1188
1395
  });
1189
1396
 
1397
+ await emitter.emit('watch-v2', {
1398
+ type: 'step-result',
1399
+ payload: {
1400
+ id: step.id,
1401
+ status: 'success',
1402
+ output: result?.result,
1403
+ },
1404
+ });
1405
+
1190
1406
  await emitter.emit('watch-v2', {
1191
1407
  type: 'step-finish',
1192
1408
  payload: {
@@ -1241,6 +1457,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1241
1457
  engine: {
1242
1458
  step: this.inngestStep,
1243
1459
  },
1460
+ abortSignal: abortController.signal,
1244
1461
  });
1245
1462
  const endedAt = Date.now();
1246
1463
 
@@ -1307,8 +1524,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1307
1524
  type: 'step-suspended',
1308
1525
  payload: {
1309
1526
  id: step.id,
1310
- status: execResults.status,
1311
- output: execResults.status === 'success' ? execResults?.output : undefined,
1527
+ ...execResults,
1312
1528
  },
1313
1529
  });
1314
1530
  } else {
@@ -1316,8 +1532,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1316
1532
  type: 'step-result',
1317
1533
  payload: {
1318
1534
  id: step.id,
1319
- status: execResults.status,
1320
- output: execResults.status === 'success' ? execResults?.output : undefined,
1535
+ ...execResults,
1321
1536
  },
1322
1537
  });
1323
1538
 
@@ -1360,6 +1575,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1360
1575
  workflowStatus: 'success' | 'failed' | 'suspended' | 'running';
1361
1576
  result?: Record<string, any>;
1362
1577
  error?: string | Error;
1578
+ runtimeContext: RuntimeContext;
1363
1579
  }) {
1364
1580
  await this.inngestStep.run(
1365
1581
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
@@ -1396,6 +1612,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1396
1612
  resume,
1397
1613
  executionContext,
1398
1614
  emitter,
1615
+ abortController,
1399
1616
  runtimeContext,
1400
1617
  }: {
1401
1618
  workflowId: string;
@@ -1417,6 +1634,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1417
1634
  };
1418
1635
  executionContext: ExecutionContext;
1419
1636
  emitter: Emitter;
1637
+ abortController: AbortController;
1420
1638
  runtimeContext: RuntimeContext;
1421
1639
  }): Promise<StepResult<any, any, any, any>> {
1422
1640
  let execResults: any;
@@ -1429,6 +1647,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1429
1647
  runId,
1430
1648
  mastra: this.mastra!,
1431
1649
  runtimeContext,
1650
+ runCount: -1,
1432
1651
  inputData: prevOutput,
1433
1652
  getInitData: () => stepResults?.input as any,
1434
1653
  getStepResult: (step: any) => {
@@ -1447,10 +1666,14 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1447
1666
  // TODO: this function shouldn't have suspend probably?
1448
1667
  suspend: async (_suspendPayload: any) => {},
1449
1668
  bail: () => {},
1669
+ abort: () => {
1670
+ abortController.abort();
1671
+ },
1450
1672
  [EMITTER_SYMBOL]: emitter,
1451
1673
  engine: {
1452
1674
  step: this.inngestStep,
1453
1675
  },
1676
+ abortSignal: abortController.signal,
1454
1677
  });
1455
1678
  return result ? index : null;
1456
1679
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1482,6 +1705,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
1482
1705
  executionSpan: executionContext.executionSpan,
1483
1706
  },
1484
1707
  emitter,
1708
+ abortController,
1485
1709
  runtimeContext,
1486
1710
  }),
1487
1711
  ),
package/vitest.config.ts CHANGED
@@ -4,5 +4,11 @@ export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
6
  include: ['src/**/*.test.ts'],
7
+ pool: 'threads',
8
+ poolOptions: {
9
+ threads: {
10
+ singleThread: true,
11
+ },
12
+ },
7
13
  },
8
14
  });