@auto-engineer/pipeline 1.65.0 → 1.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +6 -6
  3. package/.turbo/turbo-type-check.log +1 -1
  4. package/CHANGELOG.md +135 -0
  5. package/dist/src/builder/define-v2.d.ts +101 -0
  6. package/dist/src/builder/define-v2.d.ts.map +1 -0
  7. package/dist/src/builder/define-v2.js +209 -0
  8. package/dist/src/builder/define-v2.js.map +1 -0
  9. package/dist/src/engine/command-dispatcher.d.ts +31 -0
  10. package/dist/src/engine/command-dispatcher.d.ts.map +1 -0
  11. package/dist/src/engine/command-dispatcher.js +26 -0
  12. package/dist/src/engine/command-dispatcher.js.map +1 -0
  13. package/dist/src/engine/event-router.d.ts +21 -0
  14. package/dist/src/engine/event-router.d.ts.map +1 -0
  15. package/dist/src/engine/event-router.js +22 -0
  16. package/dist/src/engine/event-router.js.map +1 -0
  17. package/dist/src/engine/index.d.ts +15 -0
  18. package/dist/src/engine/index.d.ts.map +1 -0
  19. package/dist/src/engine/index.js +15 -0
  20. package/dist/src/engine/index.js.map +1 -0
  21. package/dist/src/engine/pipeline-engine.d.ts +37 -0
  22. package/dist/src/engine/pipeline-engine.d.ts.map +1 -0
  23. package/dist/src/engine/pipeline-engine.js +53 -0
  24. package/dist/src/engine/pipeline-engine.js.map +1 -0
  25. package/dist/src/engine/projections/item-status.d.ts +9 -0
  26. package/dist/src/engine/projections/item-status.d.ts.map +1 -0
  27. package/dist/src/engine/projections/item-status.js +9 -0
  28. package/dist/src/engine/projections/item-status.js.map +1 -0
  29. package/dist/src/engine/projections/latest-run.d.ts +9 -0
  30. package/dist/src/engine/projections/latest-run.d.ts.map +1 -0
  31. package/dist/src/engine/projections/latest-run.js +9 -0
  32. package/dist/src/engine/projections/latest-run.js.map +1 -0
  33. package/dist/src/engine/projections/message-log.d.ts +9 -0
  34. package/dist/src/engine/projections/message-log.d.ts.map +1 -0
  35. package/dist/src/engine/projections/message-log.js +10 -0
  36. package/dist/src/engine/projections/message-log.js.map +1 -0
  37. package/dist/src/engine/projections/node-status.d.ts +9 -0
  38. package/dist/src/engine/projections/node-status.d.ts.map +1 -0
  39. package/dist/src/engine/projections/node-status.js +9 -0
  40. package/dist/src/engine/projections/node-status.js.map +1 -0
  41. package/dist/src/engine/projections/stats.d.ts +9 -0
  42. package/dist/src/engine/projections/stats.d.ts.map +1 -0
  43. package/dist/src/engine/projections/stats.js +9 -0
  44. package/dist/src/engine/projections/stats.js.map +1 -0
  45. package/dist/src/engine/sqlite-consumer.d.ts +11 -0
  46. package/dist/src/engine/sqlite-consumer.d.ts.map +1 -0
  47. package/dist/src/engine/sqlite-consumer.js +27 -0
  48. package/dist/src/engine/sqlite-consumer.js.map +1 -0
  49. package/dist/src/engine/sqlite-store.d.ts +10 -0
  50. package/dist/src/engine/sqlite-store.d.ts.map +1 -0
  51. package/dist/src/engine/sqlite-store.js +14 -0
  52. package/dist/src/engine/sqlite-store.js.map +1 -0
  53. package/dist/src/engine/workflow-processor.d.ts +20 -0
  54. package/dist/src/engine/workflow-processor.d.ts.map +1 -0
  55. package/dist/src/engine/workflow-processor.js +36 -0
  56. package/dist/src/engine/workflow-processor.js.map +1 -0
  57. package/dist/src/engine/workflows/await-workflow.d.ts +33 -0
  58. package/dist/src/engine/workflows/await-workflow.d.ts.map +1 -0
  59. package/dist/src/engine/workflows/await-workflow.js +45 -0
  60. package/dist/src/engine/workflows/await-workflow.js.map +1 -0
  61. package/dist/src/engine/workflows/phased-workflow.d.ts +64 -0
  62. package/dist/src/engine/workflows/phased-workflow.d.ts.map +1 -0
  63. package/dist/src/engine/workflows/phased-workflow.js +103 -0
  64. package/dist/src/engine/workflows/phased-workflow.js.map +1 -0
  65. package/dist/src/engine/workflows/settled-workflow.d.ts +62 -0
  66. package/dist/src/engine/workflows/settled-workflow.d.ts.map +1 -0
  67. package/dist/src/engine/workflows/settled-workflow.js +92 -0
  68. package/dist/src/engine/workflows/settled-workflow.js.map +1 -0
  69. package/dist/src/graph/types.d.ts +1 -1
  70. package/dist/src/graph/types.d.ts.map +1 -1
  71. package/dist/src/index.d.ts +2 -0
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +2 -0
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/server/pipeline-server-v2.d.ts +48 -0
  76. package/dist/src/server/pipeline-server-v2.d.ts.map +1 -0
  77. package/dist/src/server/pipeline-server-v2.js +61 -0
  78. package/dist/src/server/pipeline-server-v2.js.map +1 -0
  79. package/dist/src/server/pipeline-server.d.ts +5 -1
  80. package/dist/src/server/pipeline-server.d.ts.map +1 -1
  81. package/dist/src/server/pipeline-server.js +71 -10
  82. package/dist/src/server/pipeline-server.js.map +1 -1
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/ketchup-plan.md +13 -0
  85. package/package.json +3 -3
  86. package/src/builder/define-v2.specs.ts +236 -0
  87. package/src/builder/define-v2.ts +351 -0
  88. package/src/engine/command-dispatcher.specs.ts +62 -0
  89. package/src/engine/command-dispatcher.ts +46 -0
  90. package/src/engine/event-router.specs.ts +75 -0
  91. package/src/engine/event-router.ts +36 -0
  92. package/src/engine/index.ts +39 -0
  93. package/src/engine/pipeline-engine-e2e.specs.ts +776 -0
  94. package/src/engine/pipeline-engine.integration.specs.ts +126 -0
  95. package/src/engine/pipeline-engine.specs.ts +70 -0
  96. package/src/engine/pipeline-engine.ts +82 -0
  97. package/src/engine/projections/item-status.ts +11 -0
  98. package/src/engine/projections/latest-run.ts +10 -0
  99. package/src/engine/projections/message-log.ts +11 -0
  100. package/src/engine/projections/node-status.ts +10 -0
  101. package/src/engine/projections/projections.specs.ts +176 -0
  102. package/src/engine/projections/stats.ts +10 -0
  103. package/src/engine/sqlite-consumer.specs.ts +42 -0
  104. package/src/engine/sqlite-consumer.ts +34 -0
  105. package/src/engine/sqlite-store.specs.ts +46 -0
  106. package/src/engine/sqlite-store.ts +21 -0
  107. package/src/engine/workflow-processor.specs.ts +37 -0
  108. package/src/engine/workflow-processor.ts +57 -0
  109. package/src/engine/workflows/await-workflow.specs.ts +104 -0
  110. package/src/engine/workflows/await-workflow.ts +66 -0
  111. package/src/engine/workflows/phased-workflow.specs.ts +383 -0
  112. package/src/engine/workflows/phased-workflow.ts +153 -0
  113. package/src/engine/workflows/settled-workflow.specs.ts +364 -0
  114. package/src/engine/workflows/settled-workflow.ts +139 -0
  115. package/src/graph/types.ts +1 -1
  116. package/src/index.ts +2 -0
  117. package/src/server/pipeline-server-v2.specs.ts +91 -0
  118. package/src/server/pipeline-server-v2.ts +70 -0
  119. package/src/server/pipeline-server.specs.ts +327 -134
  120. package/src/server/pipeline-server.ts +77 -11
@@ -6,6 +6,8 @@ import {
6
6
  type Event,
7
7
  type MessageBus,
8
8
  } from '@auto-engineer/message-bus';
9
+ import type { EventStore } from '@event-driven-io/emmett';
10
+ import { getSQLiteEventStore, readMessagesBatch, sqliteConnection } from '@event-driven-io/emmett-sqlite';
9
11
  import cors from 'cors';
10
12
  import express from 'express';
11
13
  import getPort from 'get-port';
@@ -36,6 +38,7 @@ export interface CommandHandlerWithMetadata extends CommandHandler {
36
38
 
37
39
  export interface PipelineServerConfig {
38
40
  port: number;
41
+ storeFileName?: string;
39
42
  }
40
43
 
41
44
  interface EventWithCorrelation extends Event {
@@ -58,9 +61,12 @@ export class PipelineServer {
58
61
  private readonly eventStoreContext: PipelineEventStoreContext;
59
62
  private readonly itemKeyExtractors = new Map<string, (data: unknown) => string | undefined>();
60
63
  private readonly middleware: express.RequestHandler[] = [];
61
- private readonly seenCorrelations = new Set<string>();
64
+ private readonly storeFileName?: string;
65
+ private sqliteEventStore?: EventStore;
66
+ private currentSessionId = '';
62
67
 
63
68
  constructor(config: PipelineServerConfig) {
69
+ this.storeFileName = config.storeFileName;
64
70
  this.requestedPort = config.port;
65
71
  this.actualPort = config.port;
66
72
  this.app = express();
@@ -173,9 +179,61 @@ export class PipelineServer {
173
179
  });
174
180
  });
175
181
 
182
+ if (this.storeFileName) {
183
+ this.sqliteEventStore = getSQLiteEventStore({
184
+ fileName: this.storeFileName,
185
+ schema: { autoMigration: 'CreateOrUpdate' },
186
+ });
187
+
188
+ await this.replayEventsFromSQLite();
189
+
190
+ const originalAppend = this.eventStoreContext.eventStore.appendToStream.bind(this.eventStoreContext.eventStore);
191
+ this.eventStoreContext.eventStore.appendToStream = async (streamName, events, options) => {
192
+ const result = await originalAppend(streamName, events, options);
193
+ await this.sqliteEventStore!.appendToStream(streamName, events);
194
+ return result;
195
+ };
196
+ }
197
+
198
+ const restoredSessionId = await this.eventStoreContext.readModel.getLatestCorrelationId();
199
+ if (restoredSessionId) {
200
+ this.currentSessionId = restoredSessionId;
201
+ } else {
202
+ this.currentSessionId = `session-${nanoid()}`;
203
+ await this.broadcastPipelineRunStarted(this.currentSessionId, 'PipelineStarted');
204
+ }
176
205
  await this.emitPipelineStartedEvent();
177
206
  }
178
207
 
208
+ private async replayEventsFromSQLite(): Promise<void> {
209
+ const connection = sqliteConnection({ fileName: this.storeFileName! });
210
+ let lastPosition = 0n;
211
+
212
+ try {
213
+ while (true) {
214
+ const { messages, currentGlobalPosition } = await readMessagesBatch(connection, {
215
+ after: lastPosition,
216
+ batchSize: 1000,
217
+ });
218
+ if (messages.length === 0) break;
219
+
220
+ for (const message of messages) {
221
+ const metadata: { streamName?: string } = message.metadata ?? {};
222
+ const streamName = metadata.streamName ?? 'pipeline-replay';
223
+ await this.eventStoreContext.eventStore.appendToStream(streamName, [
224
+ { type: message.type, data: message.data },
225
+ ]);
226
+ }
227
+ lastPosition = currentGlobalPosition;
228
+ }
229
+ } catch (error: unknown) {
230
+ const isMissingTable = error instanceof Error && error.message.includes('no such table');
231
+ if (!isMissingTable) throw error;
232
+ } finally {
233
+ connection.close();
234
+ }
235
+ }
236
+
179
237
  async stop(): Promise<void> {
180
238
  this.sseManager.closeAll();
181
239
  await new Promise<void>((resolve) => {
@@ -241,7 +299,7 @@ export class PipelineServer {
241
299
  const completeGraph = this.markBackLinks(graphWithEnrichedEvents);
242
300
  const filterOptions = this.parseFilterOptions(req.query);
243
301
  const filteredGraph = filterGraph(completeGraph, filterOptions);
244
- const correlationId = req.query.correlationId as string | undefined;
302
+ const correlationId = (req.query.correlationId as string | undefined) ?? this.currentSessionId;
245
303
  const graphWithStatus = await this.addStatusToCommandNodes(filteredGraph, correlationId);
246
304
 
247
305
  const latestRun = await this.eventStoreContext.readModel.getLatestCorrelationId();
@@ -270,6 +328,19 @@ export class PipelineServer {
270
328
  void (async () => {
271
329
  const command = req.body as Command;
272
330
 
331
+ if (command.type === 'RestartPipeline') {
332
+ const previousSessionId = this.currentSessionId;
333
+ this.currentSessionId = `session-${nanoid()}`;
334
+ const requestId = `req-${nanoid()}`;
335
+ await this.broadcastPipelineRunStarted(this.currentSessionId, 'RestartPipeline');
336
+ await this.emitDomainEventEmitted(this.currentSessionId, requestId, 'PipelineRestarted', {
337
+ correlationId: this.currentSessionId,
338
+ previousSessionId,
339
+ });
340
+ res.json({ status: 'ack', sessionId: this.currentSessionId });
341
+ return;
342
+ }
343
+
273
344
  if (!this.commandHandlers.has(command.type)) {
274
345
  res.status(404).json({
275
346
  status: 'nack',
@@ -922,15 +993,10 @@ export class PipelineServer {
922
993
  const handler = this.commandHandlers.get(command.type);
923
994
  if (!handler) return;
924
995
 
925
- if (!this.seenCorrelations.has(command.correlationId)) {
926
- this.seenCorrelations.add(command.correlationId);
927
- await this.broadcastPipelineRunStarted(command.correlationId, command.type);
928
- }
929
-
930
996
  const itemKey = this.extractItemKey(command.type, command.data, command.requestId);
931
- await this.getOrCreateItemStatus(command.correlationId, command.type, itemKey, command.requestId);
997
+ await this.getOrCreateItemStatus(this.currentSessionId, command.type, itemKey, command.requestId);
932
998
 
933
- await this.updateNodeStatus(command.correlationId, command.type, 'running');
999
+ await this.updateNodeStatus(this.currentSessionId, command.type, 'running');
934
1000
  await this.settledTracker.onCommandStarted(command);
935
1001
 
936
1002
  const ctx = this.createContext(command.correlationId);
@@ -956,8 +1022,8 @@ export class PipelineServer {
956
1022
  }
957
1023
 
958
1024
  const finalStatus = this.getStatusFromEvents(events);
959
- await this.updateItemStatus(command.correlationId, command.type, itemKey, finalStatus);
960
- await this.updateNodeStatus(command.correlationId, command.type, finalStatus);
1025
+ await this.updateItemStatus(this.currentSessionId, command.type, itemKey, finalStatus);
1026
+ await this.updateNodeStatus(this.currentSessionId, command.type, finalStatus);
961
1027
 
962
1028
  const eventsWithIds: EventWithCorrelation[] = events.map((event) => ({
963
1029
  ...event,