@auto-engineer/pipeline 1.68.0 → 1.70.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 (71) 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 +63 -0
  5. package/dist/src/engine/workflow-processor.d.ts +3 -0
  6. package/dist/src/engine/workflow-processor.d.ts.map +1 -1
  7. package/dist/src/engine/workflow-processor.js +50 -7
  8. package/dist/src/engine/workflow-processor.js.map +1 -1
  9. package/dist/src/index.d.ts +0 -2
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/index.js +0 -2
  12. package/dist/src/index.js.map +1 -1
  13. package/dist/src/server/phased-bridge.d.ts +13 -0
  14. package/dist/src/server/phased-bridge.d.ts.map +1 -0
  15. package/dist/src/server/phased-bridge.js +103 -0
  16. package/dist/src/server/phased-bridge.js.map +1 -0
  17. package/dist/src/server/pipeline-server.d.ts +2 -2
  18. package/dist/src/server/pipeline-server.d.ts.map +1 -1
  19. package/dist/src/server/pipeline-server.js +19 -39
  20. package/dist/src/server/pipeline-server.js.map +1 -1
  21. package/dist/src/server/v2-runtime-bridge.d.ts +21 -0
  22. package/dist/src/server/v2-runtime-bridge.d.ts.map +1 -0
  23. package/dist/src/server/v2-runtime-bridge.js +184 -0
  24. package/dist/src/server/v2-runtime-bridge.js.map +1 -0
  25. package/dist/src/store/pipeline-event-store.d.ts.map +1 -1
  26. package/dist/src/store/pipeline-event-store.js +0 -30
  27. package/dist/src/store/pipeline-event-store.js.map +1 -1
  28. package/dist/src/store/pipeline-read-model.d.ts +0 -15
  29. package/dist/src/store/pipeline-read-model.d.ts.map +1 -1
  30. package/dist/src/store/pipeline-read-model.js +0 -49
  31. package/dist/src/store/pipeline-read-model.js.map +1 -1
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/ketchup-plan.md +10 -12
  34. package/package.json +3 -3
  35. package/src/engine/workflow-processor.specs.ts +101 -0
  36. package/src/engine/workflow-processor.ts +54 -8
  37. package/src/index.ts +0 -2
  38. package/src/server/phased-bridge.specs.ts +272 -0
  39. package/src/server/phased-bridge.ts +130 -0
  40. package/src/server/pipeline-server.specs.ts +94 -0
  41. package/src/server/pipeline-server.ts +21 -42
  42. package/src/server/v2-runtime-bridge.specs.ts +347 -0
  43. package/src/server/v2-runtime-bridge.ts +255 -0
  44. package/src/store/pipeline-event-store.specs.ts +0 -137
  45. package/src/store/pipeline-event-store.ts +0 -35
  46. package/src/store/pipeline-read-model.specs.ts +0 -567
  47. package/src/store/pipeline-read-model.ts +0 -71
  48. package/dist/src/projections/phased-execution-projection.d.ts +0 -77
  49. package/dist/src/projections/phased-execution-projection.d.ts.map +0 -1
  50. package/dist/src/projections/phased-execution-projection.js +0 -54
  51. package/dist/src/projections/phased-execution-projection.js.map +0 -1
  52. package/dist/src/projections/settled-instance-projection.d.ts +0 -67
  53. package/dist/src/projections/settled-instance-projection.d.ts.map +0 -1
  54. package/dist/src/projections/settled-instance-projection.js +0 -66
  55. package/dist/src/projections/settled-instance-projection.js.map +0 -1
  56. package/dist/src/runtime/phased-executor.d.ts +0 -34
  57. package/dist/src/runtime/phased-executor.d.ts.map +0 -1
  58. package/dist/src/runtime/phased-executor.js +0 -172
  59. package/dist/src/runtime/phased-executor.js.map +0 -1
  60. package/dist/src/runtime/settled-tracker.d.ts +0 -44
  61. package/dist/src/runtime/settled-tracker.d.ts.map +0 -1
  62. package/dist/src/runtime/settled-tracker.js +0 -170
  63. package/dist/src/runtime/settled-tracker.js.map +0 -1
  64. package/src/projections/phased-execution-projection.specs.ts +0 -202
  65. package/src/projections/phased-execution-projection.ts +0 -146
  66. package/src/projections/settled-instance-projection.specs.ts +0 -296
  67. package/src/projections/settled-instance-projection.ts +0 -160
  68. package/src/runtime/phased-executor.specs.ts +0 -680
  69. package/src/runtime/phased-executor.ts +0 -230
  70. package/src/runtime/settled-tracker.specs.ts +0 -1044
  71. package/src/runtime/settled-tracker.ts +0 -235
@@ -1,170 +0,0 @@
1
- export class SettledTracker {
2
- constructor(options) {
3
- this.handlerTemplates = new Map();
4
- this.commandToTemplateIds = new Map();
5
- this.readModel = options.readModel;
6
- this.onDispatch = options.onDispatch;
7
- this.onError = options.onError;
8
- this.onEventEmit = options.onEventEmit;
9
- }
10
- registerHandler(registration) {
11
- const templateId = this.generateTemplateId(registration);
12
- this.handlerTemplates.set(templateId, {
13
- id: templateId,
14
- registration,
15
- });
16
- for (const commandType of registration.commandTypes) {
17
- const existing = this.commandToTemplateIds.get(commandType) ?? new Set();
18
- existing.add(templateId);
19
- this.commandToTemplateIds.set(commandType, existing);
20
- }
21
- }
22
- async onCommandStarted(command) {
23
- const { type: commandType, correlationId, requestId } = command;
24
- if (!this.isValidId(correlationId) || !this.isValidId(requestId)) {
25
- return;
26
- }
27
- const templateIds = this.commandToTemplateIds.get(commandType);
28
- if (!templateIds) {
29
- return;
30
- }
31
- for (const templateId of templateIds) {
32
- const template = this.handlerTemplates.get(templateId);
33
- if (template) {
34
- await this.ensureInstanceExists(template, correlationId);
35
- await this.markCommandStarted(template.id, correlationId, commandType);
36
- }
37
- }
38
- }
39
- async isWaitingForAsync(correlationId, commandType) {
40
- const instances = await this.readModel.getActiveSettledInstances(correlationId);
41
- for (const instance of instances) {
42
- const tracker = instance.commandTrackers.find((t) => t.commandType === commandType);
43
- if (tracker?.hasStarted === true && tracker.hasCompleted === false) {
44
- return true;
45
- }
46
- }
47
- return false;
48
- }
49
- async onEventReceived(event, sourceCommandType) {
50
- const correlationId = event.correlationId;
51
- if (!this.isValidId(correlationId)) {
52
- return;
53
- }
54
- const instances = await this.readModel.getActiveSettledInstances(correlationId);
55
- for (const instanceDoc of instances) {
56
- const tracker = instanceDoc.commandTrackers.find((t) => t.commandType === sourceCommandType);
57
- if (tracker && !tracker.hasCompleted) {
58
- await this.emitEvent({
59
- type: 'SettledEventReceived',
60
- data: {
61
- templateId: instanceDoc.templateId,
62
- correlationId,
63
- commandType: sourceCommandType,
64
- event,
65
- },
66
- });
67
- const updatedInstance = await this.readModel.getSettledInstance(instanceDoc.templateId, correlationId);
68
- if (updatedInstance) {
69
- await this.checkAndFireHandler(updatedInstance);
70
- }
71
- }
72
- }
73
- }
74
- isValidId(id) {
75
- return id !== undefined && id !== null && id !== '';
76
- }
77
- generateTemplateId(registration) {
78
- return `template-${registration.commandTypes.join(',')}`;
79
- }
80
- async ensureInstanceExists(template, correlationId) {
81
- const existing = await this.readModel.getSettledInstance(template.id, correlationId);
82
- const isActiveInstance = existing !== null && existing.status === 'active';
83
- if (isActiveInstance) {
84
- return false;
85
- }
86
- await this.emitEvent({
87
- type: 'SettledInstanceCreated',
88
- data: {
89
- templateId: template.id,
90
- correlationId,
91
- commandTypes: template.registration.commandTypes,
92
- },
93
- });
94
- return true;
95
- }
96
- async markCommandStarted(templateId, correlationId, commandType) {
97
- await this.emitEvent({
98
- type: 'SettledCommandStarted',
99
- data: {
100
- templateId,
101
- correlationId,
102
- commandType,
103
- },
104
- });
105
- }
106
- async emitEvent(event) {
107
- await this.onEventEmit?.(event);
108
- }
109
- async checkAndFireHandler(instanceDoc) {
110
- const allComplete = instanceDoc.commandTrackers.every((tracker) => tracker.hasStarted && tracker.hasCompleted);
111
- if (!allComplete) {
112
- return;
113
- }
114
- const template = this.handlerTemplates.get(instanceDoc.templateId);
115
- const eventsByCommandType = this.collectEventsFromDoc(instanceDoc);
116
- try {
117
- const send = (commandType, data) => {
118
- if (this.onDispatch) {
119
- this.onDispatch(commandType, data, instanceDoc.correlationId);
120
- }
121
- };
122
- const result = template.registration.handler(eventsByCommandType, send);
123
- const persist = this.shouldPersist(result);
124
- await this.emitEvent({
125
- type: 'SettledHandlerFired',
126
- data: {
127
- templateId: instanceDoc.templateId,
128
- correlationId: instanceDoc.correlationId,
129
- persist,
130
- },
131
- });
132
- await this.emitEvent({
133
- type: 'SettledInstanceReset',
134
- data: {
135
- templateId: instanceDoc.templateId,
136
- correlationId: instanceDoc.correlationId,
137
- },
138
- });
139
- }
140
- catch (error) {
141
- const commandTypes = instanceDoc.commandTrackers.map((t) => t.commandType);
142
- this.onError?.(error, {
143
- commandTypes,
144
- correlationId: instanceDoc.correlationId,
145
- });
146
- await this.emitEvent({
147
- type: 'SettledInstanceCleaned',
148
- data: {
149
- templateId: instanceDoc.templateId,
150
- correlationId: instanceDoc.correlationId,
151
- },
152
- });
153
- }
154
- }
155
- collectEventsFromDoc(instanceDoc) {
156
- const events = {};
157
- for (const tracker of instanceDoc.commandTrackers) {
158
- events[tracker.commandType] = [...tracker.events];
159
- }
160
- return events;
161
- }
162
- shouldPersist(result) {
163
- return (result !== null &&
164
- result !== undefined &&
165
- typeof result === 'object' &&
166
- 'persist' in result &&
167
- result.persist === true);
168
- }
169
- }
170
- //# sourceMappingURL=settled-tracker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"settled-tracker.js","sourceRoot":"","sources":["../../../src/runtime/settled-tracker.ts"],"names":[],"mappings":"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"}
@@ -1,202 +0,0 @@
1
- import type { Event } from '@auto-engineer/message-bus';
2
- import { describe, expect, it } from 'vitest';
3
- import type { PhasedExecutionDocument, PhasedExecutionEvent } from './phased-execution-projection';
4
- import { evolve } from './phased-execution-projection';
5
-
6
- describe('PhasedExecutionProjection', () => {
7
- const triggerEvent: Event = { type: 'TestEvent', correlationId: 'c1', data: { items: ['a', 'b'] } };
8
-
9
- describe('PhasedExecutionStarted', () => {
10
- it('should create initial document with items and phases', () => {
11
- const event: PhasedExecutionEvent = {
12
- type: 'PhasedExecutionStarted',
13
- data: {
14
- executionId: 'exec-1',
15
- correlationId: 'c1',
16
- handlerId: 'handler-1',
17
- triggerEvent,
18
- items: [
19
- { key: 'a', phase: 'prepare', dispatched: false, completed: false },
20
- { key: 'b', phase: 'execute', dispatched: false, completed: false },
21
- ],
22
- phases: ['prepare', 'execute'],
23
- },
24
- };
25
-
26
- const result = evolve(null, event);
27
-
28
- expect(result.executionId).toBe('exec-1');
29
- expect(result.correlationId).toBe('c1');
30
- expect(result.handlerId).toBe('handler-1');
31
- expect(result.status).toBe('active');
32
- expect(result.currentPhaseIndex).toBe(0);
33
- expect(result.items).toHaveLength(2);
34
- expect(result.phases).toEqual(['prepare', 'execute']);
35
- expect(result.failedItems).toEqual([]);
36
- });
37
- });
38
-
39
- describe('PhasedItemDispatched', () => {
40
- it('should mark item as dispatched', () => {
41
- const doc: PhasedExecutionDocument = {
42
- executionId: 'exec-1',
43
- correlationId: 'c1',
44
- handlerId: 'handler-1',
45
- triggerEvent,
46
- items: [
47
- { key: 'a', phase: 'prepare', dispatched: false, completed: false },
48
- { key: 'b', phase: 'execute', dispatched: false, completed: false },
49
- ],
50
- phases: ['prepare', 'execute'],
51
- currentPhaseIndex: 0,
52
- status: 'active',
53
- failedItems: [],
54
- };
55
-
56
- const event: PhasedExecutionEvent = {
57
- type: 'PhasedItemDispatched',
58
- data: { executionId: 'exec-1', itemKey: 'a', phase: 'prepare' },
59
- };
60
-
61
- const result = evolve(doc, event);
62
-
63
- expect(result.items.find((i) => i.key === 'a')?.dispatched).toBe(true);
64
- expect(result.items.find((i) => i.key === 'b')?.dispatched).toBe(false);
65
- });
66
-
67
- it('should throw when document is null', () => {
68
- const event: PhasedExecutionEvent = {
69
- type: 'PhasedItemDispatched',
70
- data: { executionId: 'exec-1', itemKey: 'a', phase: 'prepare' },
71
- };
72
-
73
- expect(() => evolve(null, event)).toThrow('Cannot apply PhasedItemDispatched to null document');
74
- });
75
- });
76
-
77
- describe('PhasedItemCompleted', () => {
78
- it('should mark item as completed', () => {
79
- const doc: PhasedExecutionDocument = {
80
- executionId: 'exec-1',
81
- correlationId: 'c1',
82
- handlerId: 'handler-1',
83
- triggerEvent,
84
- items: [{ key: 'a', phase: 'prepare', dispatched: true, completed: false }],
85
- phases: ['prepare'],
86
- currentPhaseIndex: 0,
87
- status: 'active',
88
- failedItems: [],
89
- };
90
-
91
- const resultEvent: Event = { type: 'ItemDone', correlationId: 'c1', data: {} };
92
- const event: PhasedExecutionEvent = {
93
- type: 'PhasedItemCompleted',
94
- data: { executionId: 'exec-1', itemKey: 'a', resultEvent },
95
- };
96
-
97
- const result = evolve(doc, event);
98
-
99
- expect(result.items.find((i) => i.key === 'a')?.completed).toBe(true);
100
- });
101
- });
102
-
103
- describe('PhasedItemFailed', () => {
104
- it('should add item to failedItems', () => {
105
- const doc: PhasedExecutionDocument = {
106
- executionId: 'exec-1',
107
- correlationId: 'c1',
108
- handlerId: 'handler-1',
109
- triggerEvent,
110
- items: [{ key: 'a', phase: 'prepare', dispatched: true, completed: false }],
111
- phases: ['prepare'],
112
- currentPhaseIndex: 0,
113
- status: 'active',
114
- failedItems: [],
115
- };
116
-
117
- const event: PhasedExecutionEvent = {
118
- type: 'PhasedItemFailed',
119
- data: { executionId: 'exec-1', itemKey: 'a', error: { message: 'Failed' } },
120
- };
121
-
122
- const result = evolve(doc, event);
123
-
124
- expect(result.failedItems).toHaveLength(1);
125
- expect(result.failedItems[0].key).toBe('a');
126
- expect(result.failedItems[0].error).toEqual({ message: 'Failed' });
127
- });
128
- });
129
-
130
- describe('PhasedPhaseAdvanced', () => {
131
- it('should update currentPhaseIndex', () => {
132
- const doc: PhasedExecutionDocument = {
133
- executionId: 'exec-1',
134
- correlationId: 'c1',
135
- handlerId: 'handler-1',
136
- triggerEvent,
137
- items: [{ key: 'a', phase: 'prepare', dispatched: true, completed: true }],
138
- phases: ['prepare', 'execute'],
139
- currentPhaseIndex: 0,
140
- status: 'active',
141
- failedItems: [],
142
- };
143
-
144
- const event: PhasedExecutionEvent = {
145
- type: 'PhasedPhaseAdvanced',
146
- data: { executionId: 'exec-1', fromPhase: 0, toPhase: 1 },
147
- };
148
-
149
- const result = evolve(doc, event);
150
-
151
- expect(result.currentPhaseIndex).toBe(1);
152
- });
153
- });
154
-
155
- describe('PhasedExecutionCompleted', () => {
156
- it('should set status to completed on success', () => {
157
- const doc: PhasedExecutionDocument = {
158
- executionId: 'exec-1',
159
- correlationId: 'c1',
160
- handlerId: 'handler-1',
161
- triggerEvent,
162
- items: [{ key: 'a', phase: 'prepare', dispatched: true, completed: true }],
163
- phases: ['prepare'],
164
- currentPhaseIndex: 1,
165
- status: 'active',
166
- failedItems: [],
167
- };
168
-
169
- const event: PhasedExecutionEvent = {
170
- type: 'PhasedExecutionCompleted',
171
- data: { executionId: 'exec-1', success: true, results: ['a'] },
172
- };
173
-
174
- const result = evolve(doc, event);
175
-
176
- expect(result.status).toBe('completed');
177
- });
178
-
179
- it('should set status to failed on failure', () => {
180
- const doc: PhasedExecutionDocument = {
181
- executionId: 'exec-1',
182
- correlationId: 'c1',
183
- handlerId: 'handler-1',
184
- triggerEvent,
185
- items: [{ key: 'a', phase: 'prepare', dispatched: true, completed: false }],
186
- phases: ['prepare'],
187
- currentPhaseIndex: 0,
188
- status: 'active',
189
- failedItems: [{ key: 'a', error: { message: 'Failed' } }],
190
- };
191
-
192
- const event: PhasedExecutionEvent = {
193
- type: 'PhasedExecutionCompleted',
194
- data: { executionId: 'exec-1', success: false, results: [] },
195
- };
196
-
197
- const result = evolve(doc, event);
198
-
199
- expect(result.status).toBe('failed');
200
- });
201
- });
202
- });
@@ -1,146 +0,0 @@
1
- import type { Event } from '@auto-engineer/message-bus';
2
-
3
- interface ItemTracker {
4
- key: string;
5
- phase: string;
6
- dispatched: boolean;
7
- completed: boolean;
8
- }
9
-
10
- export interface PhasedExecutionDocument {
11
- [key: string]: unknown;
12
- executionId: string;
13
- correlationId: string;
14
- handlerId: string;
15
- triggerEvent: Event;
16
- items: ItemTracker[];
17
- phases: readonly string[];
18
- currentPhaseIndex: number;
19
- status: 'active' | 'completed' | 'failed';
20
- failedItems: Array<{ key: string; error: unknown }>;
21
- }
22
-
23
- export interface PhasedExecutionStartedEvent {
24
- type: 'PhasedExecutionStarted';
25
- data: {
26
- executionId: string;
27
- correlationId: string;
28
- handlerId: string;
29
- triggerEvent: Event;
30
- items: ItemTracker[];
31
- phases: readonly string[];
32
- };
33
- }
34
-
35
- export interface PhasedItemDispatchedEvent {
36
- type: 'PhasedItemDispatched';
37
- data: {
38
- executionId: string;
39
- itemKey: string;
40
- phase: string;
41
- };
42
- }
43
-
44
- export interface PhasedItemCompletedEvent {
45
- type: 'PhasedItemCompleted';
46
- data: {
47
- executionId: string;
48
- itemKey: string;
49
- resultEvent: Event;
50
- };
51
- }
52
-
53
- export interface PhasedItemFailedEvent {
54
- type: 'PhasedItemFailed';
55
- data: {
56
- executionId: string;
57
- itemKey: string;
58
- error: unknown;
59
- };
60
- }
61
-
62
- export interface PhasedPhaseAdvancedEvent {
63
- type: 'PhasedPhaseAdvanced';
64
- data: {
65
- executionId: string;
66
- fromPhase: number;
67
- toPhase: number;
68
- };
69
- }
70
-
71
- export interface PhasedExecutionCompletedEvent {
72
- type: 'PhasedExecutionCompleted';
73
- data: {
74
- executionId: string;
75
- success: boolean;
76
- results: string[];
77
- };
78
- }
79
-
80
- export type PhasedExecutionEvent =
81
- | PhasedExecutionStartedEvent
82
- | PhasedItemDispatchedEvent
83
- | PhasedItemCompletedEvent
84
- | PhasedItemFailedEvent
85
- | PhasedPhaseAdvancedEvent
86
- | PhasedExecutionCompletedEvent;
87
-
88
- function assertDocument(
89
- document: PhasedExecutionDocument | null,
90
- eventType: string,
91
- ): asserts document is PhasedExecutionDocument {
92
- if (document === null) {
93
- throw new Error(`Cannot apply ${eventType} to null document`);
94
- }
95
- }
96
-
97
- function evolveItemDispatched(document: PhasedExecutionDocument, itemKey: string): PhasedExecutionDocument {
98
- return {
99
- ...document,
100
- items: document.items.map((item) => (item.key === itemKey ? { ...item, dispatched: true } : item)),
101
- };
102
- }
103
-
104
- function evolveItemCompleted(document: PhasedExecutionDocument, itemKey: string): PhasedExecutionDocument {
105
- return {
106
- ...document,
107
- items: document.items.map((item) => (item.key === itemKey ? { ...item, completed: true } : item)),
108
- };
109
- }
110
-
111
- export function evolve(document: PhasedExecutionDocument | null, event: PhasedExecutionEvent): PhasedExecutionDocument {
112
- switch (event.type) {
113
- case 'PhasedExecutionStarted': {
114
- const { executionId, correlationId, handlerId, triggerEvent, items, phases } = event.data;
115
- return {
116
- executionId,
117
- correlationId,
118
- handlerId,
119
- triggerEvent,
120
- items: items.map((item) => ({ ...item })),
121
- phases,
122
- currentPhaseIndex: 0,
123
- status: 'active',
124
- failedItems: [],
125
- };
126
- }
127
- case 'PhasedItemDispatched':
128
- assertDocument(document, event.type);
129
- return evolveItemDispatched(document, event.data.itemKey);
130
- case 'PhasedItemCompleted':
131
- assertDocument(document, event.type);
132
- return evolveItemCompleted(document, event.data.itemKey);
133
- case 'PhasedItemFailed':
134
- assertDocument(document, event.type);
135
- return {
136
- ...document,
137
- failedItems: [...document.failedItems, { key: event.data.itemKey, error: event.data.error }],
138
- };
139
- case 'PhasedPhaseAdvanced':
140
- assertDocument(document, event.type);
141
- return { ...document, currentPhaseIndex: event.data.toPhase };
142
- case 'PhasedExecutionCompleted':
143
- assertDocument(document, event.type);
144
- return { ...document, status: event.data.success ? 'completed' : 'failed' };
145
- }
146
- }