@auto-engineer/pipeline 0.0.1

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 (270) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/LICENSE +10 -0
  3. package/claude.md +160 -0
  4. package/dist/src/builder/define.d.ts +90 -0
  5. package/dist/src/builder/define.d.ts.map +1 -0
  6. package/dist/src/builder/define.js +425 -0
  7. package/dist/src/builder/define.js.map +1 -0
  8. package/dist/src/builder/define.specs.d.ts +2 -0
  9. package/dist/src/builder/define.specs.d.ts.map +1 -0
  10. package/dist/src/builder/define.specs.js +435 -0
  11. package/dist/src/builder/define.specs.js.map +1 -0
  12. package/dist/src/config/pipeline-config.d.ts +13 -0
  13. package/dist/src/config/pipeline-config.d.ts.map +1 -0
  14. package/dist/src/config/pipeline-config.js +15 -0
  15. package/dist/src/config/pipeline-config.js.map +1 -0
  16. package/dist/src/core/descriptors.d.ts +84 -0
  17. package/dist/src/core/descriptors.d.ts.map +1 -0
  18. package/dist/src/core/descriptors.js +2 -0
  19. package/dist/src/core/descriptors.js.map +1 -0
  20. package/dist/src/core/descriptors.specs.d.ts +2 -0
  21. package/dist/src/core/descriptors.specs.d.ts.map +1 -0
  22. package/dist/src/core/descriptors.specs.js +24 -0
  23. package/dist/src/core/descriptors.specs.js.map +1 -0
  24. package/dist/src/core/types.d.ts +10 -0
  25. package/dist/src/core/types.d.ts.map +1 -0
  26. package/dist/src/core/types.js +4 -0
  27. package/dist/src/core/types.js.map +1 -0
  28. package/dist/src/core/types.specs.d.ts +2 -0
  29. package/dist/src/core/types.specs.d.ts.map +1 -0
  30. package/dist/src/core/types.specs.js +40 -0
  31. package/dist/src/core/types.specs.js.map +1 -0
  32. package/dist/src/graph/types.d.ts +17 -0
  33. package/dist/src/graph/types.d.ts.map +1 -0
  34. package/dist/src/graph/types.js +2 -0
  35. package/dist/src/graph/types.js.map +1 -0
  36. package/dist/src/graph/types.specs.d.ts +2 -0
  37. package/dist/src/graph/types.specs.d.ts.map +1 -0
  38. package/dist/src/graph/types.specs.js +148 -0
  39. package/dist/src/graph/types.specs.js.map +1 -0
  40. package/dist/src/index.d.ts +20 -0
  41. package/dist/src/index.d.ts.map +1 -0
  42. package/dist/src/index.js +12 -0
  43. package/dist/src/index.js.map +1 -0
  44. package/dist/src/logging/event-logger.d.ts +21 -0
  45. package/dist/src/logging/event-logger.d.ts.map +1 -0
  46. package/dist/src/logging/event-logger.js +31 -0
  47. package/dist/src/logging/event-logger.js.map +1 -0
  48. package/dist/src/logging/event-logger.specs.d.ts +2 -0
  49. package/dist/src/logging/event-logger.specs.d.ts.map +1 -0
  50. package/dist/src/logging/event-logger.specs.js +81 -0
  51. package/dist/src/logging/event-logger.specs.js.map +1 -0
  52. package/dist/src/plugins/handler-adapter.d.ts +5 -0
  53. package/dist/src/plugins/handler-adapter.d.ts.map +1 -0
  54. package/dist/src/plugins/handler-adapter.js +17 -0
  55. package/dist/src/plugins/handler-adapter.js.map +1 -0
  56. package/dist/src/plugins/handler-adapter.specs.d.ts +2 -0
  57. package/dist/src/plugins/handler-adapter.specs.d.ts.map +1 -0
  58. package/dist/src/plugins/handler-adapter.specs.js +129 -0
  59. package/dist/src/plugins/handler-adapter.specs.js.map +1 -0
  60. package/dist/src/plugins/plugin-loader.d.ts +25 -0
  61. package/dist/src/plugins/plugin-loader.d.ts.map +1 -0
  62. package/dist/src/plugins/plugin-loader.js +150 -0
  63. package/dist/src/plugins/plugin-loader.js.map +1 -0
  64. package/dist/src/plugins/plugin-loader.specs.d.ts +2 -0
  65. package/dist/src/plugins/plugin-loader.specs.d.ts.map +1 -0
  66. package/dist/src/plugins/plugin-loader.specs.js +246 -0
  67. package/dist/src/plugins/plugin-loader.specs.js.map +1 -0
  68. package/dist/src/runtime/await-tracker.d.ts +10 -0
  69. package/dist/src/runtime/await-tracker.d.ts.map +1 -0
  70. package/dist/src/runtime/await-tracker.js +42 -0
  71. package/dist/src/runtime/await-tracker.js.map +1 -0
  72. package/dist/src/runtime/await-tracker.specs.d.ts +2 -0
  73. package/dist/src/runtime/await-tracker.specs.d.ts.map +1 -0
  74. package/dist/src/runtime/await-tracker.specs.js +46 -0
  75. package/dist/src/runtime/await-tracker.specs.js.map +1 -0
  76. package/dist/src/runtime/context.d.ts +12 -0
  77. package/dist/src/runtime/context.d.ts.map +1 -0
  78. package/dist/src/runtime/context.js +2 -0
  79. package/dist/src/runtime/context.js.map +1 -0
  80. package/dist/src/runtime/context.specs.d.ts +2 -0
  81. package/dist/src/runtime/context.specs.d.ts.map +1 -0
  82. package/dist/src/runtime/context.specs.js +26 -0
  83. package/dist/src/runtime/context.specs.js.map +1 -0
  84. package/dist/src/runtime/event-command-map.d.ts +15 -0
  85. package/dist/src/runtime/event-command-map.d.ts.map +1 -0
  86. package/dist/src/runtime/event-command-map.js +26 -0
  87. package/dist/src/runtime/event-command-map.js.map +1 -0
  88. package/dist/src/runtime/event-command-map.specs.d.ts +2 -0
  89. package/dist/src/runtime/event-command-map.specs.d.ts.map +1 -0
  90. package/dist/src/runtime/event-command-map.specs.js +108 -0
  91. package/dist/src/runtime/event-command-map.specs.js.map +1 -0
  92. package/dist/src/runtime/phased-executor.d.ts +29 -0
  93. package/dist/src/runtime/phased-executor.d.ts.map +1 -0
  94. package/dist/src/runtime/phased-executor.js +164 -0
  95. package/dist/src/runtime/phased-executor.js.map +1 -0
  96. package/dist/src/runtime/phased-executor.specs.d.ts +2 -0
  97. package/dist/src/runtime/phased-executor.specs.d.ts.map +1 -0
  98. package/dist/src/runtime/phased-executor.specs.js +256 -0
  99. package/dist/src/runtime/phased-executor.specs.js.map +1 -0
  100. package/dist/src/runtime/pipeline-runtime.d.ts +17 -0
  101. package/dist/src/runtime/pipeline-runtime.d.ts.map +1 -0
  102. package/dist/src/runtime/pipeline-runtime.js +87 -0
  103. package/dist/src/runtime/pipeline-runtime.js.map +1 -0
  104. package/dist/src/runtime/pipeline-runtime.specs.d.ts +2 -0
  105. package/dist/src/runtime/pipeline-runtime.specs.d.ts.map +1 -0
  106. package/dist/src/runtime/pipeline-runtime.specs.js +192 -0
  107. package/dist/src/runtime/pipeline-runtime.specs.js.map +1 -0
  108. package/dist/src/runtime/settled-tracker.d.ts +42 -0
  109. package/dist/src/runtime/settled-tracker.d.ts.map +1 -0
  110. package/dist/src/runtime/settled-tracker.js +161 -0
  111. package/dist/src/runtime/settled-tracker.js.map +1 -0
  112. package/dist/src/runtime/settled-tracker.specs.d.ts +2 -0
  113. package/dist/src/runtime/settled-tracker.specs.d.ts.map +1 -0
  114. package/dist/src/runtime/settled-tracker.specs.js +361 -0
  115. package/dist/src/runtime/settled-tracker.specs.js.map +1 -0
  116. package/dist/src/server/full-orchestration.e2e.specs.d.ts +2 -0
  117. package/dist/src/server/full-orchestration.e2e.specs.d.ts.map +1 -0
  118. package/dist/src/server/full-orchestration.e2e.specs.js +561 -0
  119. package/dist/src/server/full-orchestration.e2e.specs.js.map +1 -0
  120. package/dist/src/server/pipeline-server.d.ts +59 -0
  121. package/dist/src/server/pipeline-server.d.ts.map +1 -0
  122. package/dist/src/server/pipeline-server.e2e.specs.d.ts +2 -0
  123. package/dist/src/server/pipeline-server.e2e.specs.d.ts.map +1 -0
  124. package/dist/src/server/pipeline-server.e2e.specs.js +381 -0
  125. package/dist/src/server/pipeline-server.e2e.specs.js.map +1 -0
  126. package/dist/src/server/pipeline-server.js +527 -0
  127. package/dist/src/server/pipeline-server.js.map +1 -0
  128. package/dist/src/server/pipeline-server.specs.d.ts +2 -0
  129. package/dist/src/server/pipeline-server.specs.d.ts.map +1 -0
  130. package/dist/src/server/pipeline-server.specs.js +662 -0
  131. package/dist/src/server/pipeline-server.specs.js.map +1 -0
  132. package/dist/src/server/sse-manager.d.ts +12 -0
  133. package/dist/src/server/sse-manager.d.ts.map +1 -0
  134. package/dist/src/server/sse-manager.js +63 -0
  135. package/dist/src/server/sse-manager.js.map +1 -0
  136. package/dist/src/server/sse-manager.specs.d.ts +2 -0
  137. package/dist/src/server/sse-manager.specs.d.ts.map +1 -0
  138. package/dist/src/server/sse-manager.specs.js +158 -0
  139. package/dist/src/server/sse-manager.specs.js.map +1 -0
  140. package/dist/src/testing/event-capture.d.ts +14 -0
  141. package/dist/src/testing/event-capture.d.ts.map +1 -0
  142. package/dist/src/testing/event-capture.js +55 -0
  143. package/dist/src/testing/event-capture.js.map +1 -0
  144. package/dist/src/testing/event-capture.specs.d.ts +2 -0
  145. package/dist/src/testing/event-capture.specs.d.ts.map +1 -0
  146. package/dist/src/testing/event-capture.specs.js +114 -0
  147. package/dist/src/testing/event-capture.specs.js.map +1 -0
  148. package/dist/src/testing/fixtures/kanban-full.pipeline.d.ts +7 -0
  149. package/dist/src/testing/fixtures/kanban-full.pipeline.d.ts.map +1 -0
  150. package/dist/src/testing/fixtures/kanban-full.pipeline.js +168 -0
  151. package/dist/src/testing/fixtures/kanban-full.pipeline.js.map +1 -0
  152. package/dist/src/testing/fixtures/kanban-full.pipeline.specs.d.ts +2 -0
  153. package/dist/src/testing/fixtures/kanban-full.pipeline.specs.d.ts.map +1 -0
  154. package/dist/src/testing/fixtures/kanban-full.pipeline.specs.js +263 -0
  155. package/dist/src/testing/fixtures/kanban-full.pipeline.specs.js.map +1 -0
  156. package/dist/src/testing/fixtures/kanban-todo.config.d.ts +3 -0
  157. package/dist/src/testing/fixtures/kanban-todo.config.d.ts.map +1 -0
  158. package/dist/src/testing/fixtures/kanban-todo.config.js +19 -0
  159. package/dist/src/testing/fixtures/kanban-todo.config.js.map +1 -0
  160. package/dist/src/testing/fixtures/kanban.pipeline.d.ts +5 -0
  161. package/dist/src/testing/fixtures/kanban.pipeline.d.ts.map +1 -0
  162. package/dist/src/testing/fixtures/kanban.pipeline.js +76 -0
  163. package/dist/src/testing/fixtures/kanban.pipeline.js.map +1 -0
  164. package/dist/src/testing/fixtures/kanban.pipeline.specs.d.ts +2 -0
  165. package/dist/src/testing/fixtures/kanban.pipeline.specs.d.ts.map +1 -0
  166. package/dist/src/testing/fixtures/kanban.pipeline.specs.js +29 -0
  167. package/dist/src/testing/fixtures/kanban.pipeline.specs.js.map +1 -0
  168. package/dist/src/testing/kanban-todo.e2e.specs.d.ts +2 -0
  169. package/dist/src/testing/kanban-todo.e2e.specs.d.ts.map +1 -0
  170. package/dist/src/testing/kanban-todo.e2e.specs.js +160 -0
  171. package/dist/src/testing/kanban-todo.e2e.specs.js.map +1 -0
  172. package/dist/src/testing/mock-handlers.d.ts +21 -0
  173. package/dist/src/testing/mock-handlers.d.ts.map +1 -0
  174. package/dist/src/testing/mock-handlers.js +34 -0
  175. package/dist/src/testing/mock-handlers.js.map +1 -0
  176. package/dist/src/testing/mock-handlers.specs.d.ts +2 -0
  177. package/dist/src/testing/mock-handlers.specs.d.ts.map +1 -0
  178. package/dist/src/testing/mock-handlers.specs.js +193 -0
  179. package/dist/src/testing/mock-handlers.specs.js.map +1 -0
  180. package/dist/src/testing/real-execution.e2e.specs.d.ts +2 -0
  181. package/dist/src/testing/real-execution.e2e.specs.d.ts.map +1 -0
  182. package/dist/src/testing/real-execution.e2e.specs.js +140 -0
  183. package/dist/src/testing/real-execution.e2e.specs.js.map +1 -0
  184. package/dist/src/testing/real-plugin.e2e.specs.d.ts +2 -0
  185. package/dist/src/testing/real-plugin.e2e.specs.d.ts.map +1 -0
  186. package/dist/src/testing/real-plugin.e2e.specs.js +65 -0
  187. package/dist/src/testing/real-plugin.e2e.specs.js.map +1 -0
  188. package/dist/src/testing/server-startup.e2e.specs.d.ts +2 -0
  189. package/dist/src/testing/server-startup.e2e.specs.d.ts.map +1 -0
  190. package/dist/src/testing/server-startup.e2e.specs.js +104 -0
  191. package/dist/src/testing/server-startup.e2e.specs.js.map +1 -0
  192. package/dist/src/testing/snapshot-compare.d.ts +18 -0
  193. package/dist/src/testing/snapshot-compare.d.ts.map +1 -0
  194. package/dist/src/testing/snapshot-compare.js +86 -0
  195. package/dist/src/testing/snapshot-compare.js.map +1 -0
  196. package/dist/src/testing/snapshot-compare.specs.d.ts +2 -0
  197. package/dist/src/testing/snapshot-compare.specs.d.ts.map +1 -0
  198. package/dist/src/testing/snapshot-compare.specs.js +112 -0
  199. package/dist/src/testing/snapshot-compare.specs.js.map +1 -0
  200. package/dist/src/testing/snapshot-sanitize.d.ts +8 -0
  201. package/dist/src/testing/snapshot-sanitize.d.ts.map +1 -0
  202. package/dist/src/testing/snapshot-sanitize.js +10 -0
  203. package/dist/src/testing/snapshot-sanitize.js.map +1 -0
  204. package/dist/src/testing/snapshot-sanitize.specs.d.ts +2 -0
  205. package/dist/src/testing/snapshot-sanitize.specs.d.ts.map +1 -0
  206. package/dist/src/testing/snapshot-sanitize.specs.js +104 -0
  207. package/dist/src/testing/snapshot-sanitize.specs.js.map +1 -0
  208. package/dist/tsconfig.tsbuildinfo +1 -0
  209. package/docs/testing-analysis.md +395 -0
  210. package/package.json +31 -0
  211. package/pipeline-api-new.md +1078 -0
  212. package/pomodoro-plan.md +651 -0
  213. package/scripts/run-kanban-e2e.ts +219 -0
  214. package/scripts/start-server.ts +64 -0
  215. package/snapshots/e2e-run-2025-12-22T15-52-03.json +613 -0
  216. package/snapshots/e2e-run-2025-12-22T16-51-30.json +699 -0
  217. package/src/builder/define.specs.ts +531 -0
  218. package/src/builder/define.ts +700 -0
  219. package/src/config/pipeline-config.ts +32 -0
  220. package/src/core/descriptors.specs.ts +28 -0
  221. package/src/core/descriptors.ts +99 -0
  222. package/src/core/types.specs.ts +44 -0
  223. package/src/core/types.ts +16 -0
  224. package/src/graph/types.specs.ts +176 -0
  225. package/src/graph/types.ts +19 -0
  226. package/src/index.ts +54 -0
  227. package/src/logging/event-logger.specs.ts +100 -0
  228. package/src/logging/event-logger.ts +50 -0
  229. package/src/plugins/handler-adapter.specs.ts +164 -0
  230. package/src/plugins/handler-adapter.ts +21 -0
  231. package/src/plugins/plugin-loader.specs.ts +295 -0
  232. package/src/plugins/plugin-loader.ts +202 -0
  233. package/src/runtime/await-tracker.specs.ts +52 -0
  234. package/src/runtime/await-tracker.ts +50 -0
  235. package/src/runtime/context.specs.ts +28 -0
  236. package/src/runtime/context.ts +13 -0
  237. package/src/runtime/event-command-map.specs.ts +136 -0
  238. package/src/runtime/event-command-map.ts +38 -0
  239. package/src/runtime/phased-executor.specs.ts +358 -0
  240. package/src/runtime/phased-executor.ts +224 -0
  241. package/src/runtime/pipeline-runtime.specs.ts +214 -0
  242. package/src/runtime/pipeline-runtime.ts +119 -0
  243. package/src/runtime/settled-tracker.specs.ts +448 -0
  244. package/src/runtime/settled-tracker.ts +237 -0
  245. package/src/server/full-orchestration.e2e.specs.ts +672 -0
  246. package/src/server/pipeline-server.e2e.specs.ts +505 -0
  247. package/src/server/pipeline-server.specs.ts +761 -0
  248. package/src/server/pipeline-server.ts +656 -0
  249. package/src/server/sse-manager.specs.ts +208 -0
  250. package/src/server/sse-manager.ts +79 -0
  251. package/src/testing/event-capture.specs.ts +143 -0
  252. package/src/testing/event-capture.ts +65 -0
  253. package/src/testing/fixtures/kanban-full.pipeline.specs.ts +337 -0
  254. package/src/testing/fixtures/kanban-full.pipeline.ts +225 -0
  255. package/src/testing/fixtures/kanban-todo.config.ts +19 -0
  256. package/src/testing/fixtures/kanban.pipeline.specs.ts +33 -0
  257. package/src/testing/fixtures/kanban.pipeline.ts +124 -0
  258. package/src/testing/kanban-todo.e2e.specs.ts +209 -0
  259. package/src/testing/mock-handlers.specs.ts +229 -0
  260. package/src/testing/mock-handlers.ts +58 -0
  261. package/src/testing/real-execution.e2e.specs.ts +193 -0
  262. package/src/testing/real-plugin.e2e.specs.ts +94 -0
  263. package/src/testing/server-startup.e2e.specs.ts +162 -0
  264. package/src/testing/snapshot-compare.specs.ts +136 -0
  265. package/src/testing/snapshot-compare.ts +106 -0
  266. package/src/testing/snapshot-sanitize.specs.ts +131 -0
  267. package/src/testing/snapshot-sanitize.ts +17 -0
  268. package/tsconfig.json +11 -0
  269. package/tsconfig.test.json +9 -0
  270. package/vitest.config.ts +29 -0
@@ -0,0 +1,32 @@
1
+ import type { Pipeline } from '../builder/define';
2
+ import { adaptHandlers } from '../plugins/handler-adapter';
3
+ import { PluginLoader } from '../plugins/plugin-loader';
4
+ import type { CommandHandlerWithMetadata } from '../server/pipeline-server';
5
+
6
+ export interface PipelineConfig {
7
+ plugins: string[];
8
+ pipeline: Pipeline;
9
+ }
10
+
11
+ export interface LoadedPipelineConfig {
12
+ handlers: CommandHandlerWithMetadata[];
13
+ pipeline: Pipeline;
14
+ }
15
+
16
+ export function pipelineConfig(config: PipelineConfig): PipelineConfig {
17
+ return config;
18
+ }
19
+
20
+ export async function loadPipelineConfig(
21
+ config: PipelineConfig,
22
+ workspaceRoot?: string,
23
+ ): Promise<LoadedPipelineConfig> {
24
+ const loader = new PluginLoader(workspaceRoot);
25
+ const pluginHandlers = await loader.loadPlugins(config.plugins);
26
+ const handlers = adaptHandlers(pluginHandlers);
27
+
28
+ return {
29
+ handlers,
30
+ pipeline: config.pipeline,
31
+ };
32
+ }
@@ -0,0 +1,28 @@
1
+ import type { KeyExtractor, PipelineDescriptor } from './descriptors';
2
+ import type { Event } from './types';
3
+
4
+ describe('PipelineDescriptor', () => {
5
+ it('should create PipelineDescriptor', () => {
6
+ const descriptor: PipelineDescriptor = {
7
+ name: 'test-pipeline',
8
+ version: '1.0.0',
9
+ keys: new Map(),
10
+ handlers: [],
11
+ };
12
+ expect(descriptor.name).toBe('test-pipeline');
13
+ });
14
+
15
+ it('should store key extractors', () => {
16
+ type SliceEvent = Event & { data: { slicePath?: string } };
17
+ const extractor: KeyExtractor = (e) => (e as SliceEvent).data.slicePath ?? '';
18
+ const keys = new Map<string, KeyExtractor>();
19
+ keys.set('bySlice', extractor);
20
+
21
+ const descriptor: PipelineDescriptor = {
22
+ name: 'test',
23
+ keys,
24
+ handlers: [],
25
+ };
26
+ expect(descriptor.keys.get('bySlice')).toBe(extractor);
27
+ });
28
+ });
@@ -0,0 +1,99 @@
1
+ import type { Event } from '@auto-engineer/message-bus';
2
+ import type { PipelineContext } from '../runtime/context';
3
+ import type { CommandDispatch } from './types';
4
+
5
+ export type KeyExtractor = (event: Event) => string;
6
+
7
+ export type EventPredicate = (event: Event) => boolean;
8
+
9
+ export interface EmitHandlerDescriptor {
10
+ type: 'emit';
11
+ eventType: string;
12
+ predicate?: EventPredicate;
13
+ commands: CommandDispatch[];
14
+ }
15
+
16
+ export interface SuccessContext<T = unknown> {
17
+ results: T[];
18
+ duration: number;
19
+ triggerEvent: Event;
20
+ }
21
+
22
+ export interface FailureContext<T = unknown> {
23
+ failures: Array<{ key: string; error: unknown }>;
24
+ successes: T[];
25
+ triggerEvent: Event;
26
+ }
27
+
28
+ export interface GatherEventConfig<T = unknown> {
29
+ eventType: string;
30
+ dataFactory: (context: T) => Record<string, unknown>;
31
+ }
32
+
33
+ export interface RunAwaitHandlerDescriptor {
34
+ type: 'run-await';
35
+ eventType: string;
36
+ predicate?: EventPredicate;
37
+ commands: CommandDispatch[] | ((event: Event) => CommandDispatch[]);
38
+ awaitConfig: {
39
+ keyName: string;
40
+ key: KeyExtractor;
41
+ timeout?: number;
42
+ };
43
+ onSuccess?: GatherEventConfig<SuccessContext>;
44
+ onFailure?: GatherEventConfig<FailureContext>;
45
+ }
46
+
47
+ export interface ForEachPhasedDescriptor {
48
+ type: 'foreach-phased';
49
+ eventType: string;
50
+ predicate?: EventPredicate;
51
+ itemsSelector: (event: Event) => unknown[];
52
+ phases: readonly string[];
53
+ classifier: (item: unknown) => string;
54
+ stopOnFailure: boolean;
55
+ emitFactory: (item: unknown, phase: string, event: Event) => CommandDispatch;
56
+ completion: {
57
+ successEvent: string;
58
+ failureEvent: string;
59
+ itemKey: KeyExtractor;
60
+ };
61
+ }
62
+
63
+ export interface CustomHandlerDescriptor {
64
+ type: 'custom';
65
+ eventType: string;
66
+ predicate?: EventPredicate;
67
+ handler: (event: Event, ctx: PipelineContext) => void | Promise<void>;
68
+ declaredEmits?: string[];
69
+ }
70
+
71
+ type SettledSendFunction = (commandType: string, data: unknown) => void;
72
+
73
+ export type SettledHandler = (
74
+ events: Record<string, Event[]>,
75
+ send: SettledSendFunction,
76
+ ) => void | { persist: boolean };
77
+
78
+ export interface SettledHandlerDescriptor {
79
+ type: 'settled';
80
+ commandTypes: readonly string[];
81
+ handler: SettledHandler;
82
+ dispatches?: readonly string[];
83
+ }
84
+
85
+ export type EventHandlerDescriptor =
86
+ | EmitHandlerDescriptor
87
+ | RunAwaitHandlerDescriptor
88
+ | ForEachPhasedDescriptor
89
+ | CustomHandlerDescriptor;
90
+
91
+ export type HandlerDescriptor = EventHandlerDescriptor | SettledHandlerDescriptor;
92
+
93
+ export interface PipelineDescriptor {
94
+ name: string;
95
+ version?: string;
96
+ description?: string;
97
+ keys: Map<string, KeyExtractor>;
98
+ handlers: HandlerDescriptor[];
99
+ }
@@ -0,0 +1,44 @@
1
+ import { type Command, type CommandDispatch, dispatch, type Event } from './types';
2
+
3
+ describe('Core Types', () => {
4
+ it('should re-export Command and Event from message-bus', () => {
5
+ const cmd: Command = { type: 'Test', data: {} };
6
+ const evt: Event = { type: 'TestDone', data: {} };
7
+
8
+ expect(cmd.type).toBe('Test');
9
+ expect(evt.type).toBe('TestDone');
10
+ });
11
+ });
12
+
13
+ describe('CommandDispatch', () => {
14
+ it('should create CommandDispatch with static data', () => {
15
+ const cmd: CommandDispatch = {
16
+ commandType: 'CheckTests',
17
+ data: { targetDirectory: './src', scope: 'slice' },
18
+ };
19
+ expect(cmd).toEqual({
20
+ commandType: 'CheckTests',
21
+ data: { targetDirectory: './src', scope: 'slice' },
22
+ });
23
+ });
24
+
25
+ it('should create CommandDispatch with data factory', () => {
26
+ const cmd: CommandDispatch = {
27
+ commandType: 'ImplementSlice',
28
+ data: (e: Event) => ({ slicePath: e.data.path }),
29
+ };
30
+ const event: Event = { type: 'SliceGenerated', data: { path: './slice' } };
31
+ const resolved = typeof cmd.data === 'function' ? cmd.data(event) : cmd.data;
32
+ expect(resolved).toEqual({ slicePath: './slice' });
33
+ });
34
+ });
35
+
36
+ describe('dispatch()', () => {
37
+ it('should create CommandDispatch via dispatch()', () => {
38
+ const cmd = dispatch('CheckTests', { targetDirectory: './src' });
39
+ expect(cmd).toEqual({
40
+ commandType: 'CheckTests',
41
+ data: { targetDirectory: './src' },
42
+ });
43
+ });
44
+ });
@@ -0,0 +1,16 @@
1
+ import type { Event } from '@auto-engineer/message-bus';
2
+
3
+ export type { Command, Event } from '@auto-engineer/message-bus';
4
+
5
+ export type DataRecord = Record<string, unknown>;
6
+
7
+ export type DataOrFactory<D extends DataRecord = DataRecord> = D | ((event: Event) => D);
8
+
9
+ export interface CommandDispatch<D extends DataRecord = DataRecord> {
10
+ commandType: string;
11
+ data: DataOrFactory<D>;
12
+ }
13
+
14
+ export function dispatch<D extends DataRecord>(commandType: string, data: DataOrFactory<D>): CommandDispatch<D> {
15
+ return { commandType, data };
16
+ }
@@ -0,0 +1,176 @@
1
+ import { define } from '../builder/define';
2
+ import type { GraphIR } from './types';
3
+
4
+ describe('GraphIR type', () => {
5
+ it('should define GraphIR with nodes and edges', () => {
6
+ const graph: GraphIR = {
7
+ nodes: [
8
+ { id: 'evt:Start', type: 'event', label: 'Start' },
9
+ { id: 'cmd:Process', type: 'command', label: 'Process' },
10
+ ],
11
+ edges: [{ from: 'evt:Start', to: 'cmd:Process', label: 'triggers' }],
12
+ };
13
+ expect(graph.nodes).toHaveLength(2);
14
+ });
15
+ });
16
+
17
+ describe('Pipeline.toGraph()', () => {
18
+ it('should extract graph from emit handler', () => {
19
+ const pipeline = define('test').on('Start').emit('Process', {}).build();
20
+
21
+ const graph = pipeline.toGraph();
22
+ expect(graph.nodes.some((n) => n.id === 'evt:Start')).toBe(true);
23
+ expect(graph.nodes.some((n) => n.id === 'cmd:Process')).toBe(true);
24
+ });
25
+
26
+ it('should include edges from event to commands', () => {
27
+ const pipeline = define('test').on('Start').emit('Process', {}).build();
28
+
29
+ const graph = pipeline.toGraph();
30
+ expect(graph.edges.some((e) => e.from === 'evt:Start' && e.to === 'cmd:Process')).toBe(true);
31
+ });
32
+
33
+ it('should extract graph from run-await handler', () => {
34
+ const pipeline = define('test')
35
+ .on('BatchReady')
36
+ .run([{ commandType: 'ProcessItem', data: {} }])
37
+ .awaitAll('byItem', () => 'key')
38
+ .onSuccess('BatchComplete', () => ({}))
39
+ .onFailure('BatchFailed', () => ({}))
40
+ .build();
41
+
42
+ const graph = pipeline.toGraph();
43
+ expect(graph.nodes.some((n) => n.id === 'evt:BatchReady')).toBe(true);
44
+ expect(graph.nodes.some((n) => n.id === 'cmd:ProcessItem')).toBe(true);
45
+ expect(graph.nodes.some((n) => n.id === 'evt:BatchComplete')).toBe(true);
46
+ expect(graph.nodes.some((n) => n.id === 'evt:BatchFailed')).toBe(true);
47
+ });
48
+
49
+ it('should extract graph from run-await handler with command factory', () => {
50
+ type BatchEvent = { data: { items: Array<{ id: string }> } };
51
+ const pipeline = define('test')
52
+ .on('BatchReady')
53
+ .run((e: BatchEvent) => e.data.items.map((item) => ({ commandType: 'ProcessItem', data: { id: item.id } })))
54
+ .awaitAll('byItem', () => 'key')
55
+ .build();
56
+
57
+ const graph = pipeline.toGraph();
58
+ expect(graph.nodes.some((n) => n.id === 'evt:BatchReady')).toBe(true);
59
+ expect(graph.nodes).toHaveLength(1);
60
+ expect(graph.edges).toHaveLength(0);
61
+ });
62
+
63
+ it('should extract graph from foreach-phased handler', () => {
64
+ const pipeline = define('test')
65
+ .on('ItemsReady')
66
+ .forEach(() => [])
67
+ .groupInto(['phase1'], () => 'phase1')
68
+ .process('ProcessItem', () => ({}))
69
+ .onComplete({ success: 'AllDone', failure: 'SomeFailed', itemKey: () => '' })
70
+ .build();
71
+
72
+ const graph = pipeline.toGraph();
73
+ expect(graph.nodes.some((n) => n.id === 'evt:ItemsReady')).toBe(true);
74
+ expect(graph.nodes.some((n) => n.id === 'cmd:ProcessItem')).toBe(true);
75
+ expect(graph.nodes.some((n) => n.id === 'evt:AllDone')).toBe(true);
76
+ expect(graph.nodes.some((n) => n.id === 'evt:SomeFailed')).toBe(true);
77
+ });
78
+
79
+ it('should include edges from command to onComplete success/failure events', () => {
80
+ const pipeline = define('test')
81
+ .on('ItemsReady')
82
+ .forEach(() => [])
83
+ .groupInto(['phase1'], () => 'phase1')
84
+ .process('ProcessItem', () => ({}))
85
+ .onComplete({ success: 'AllDone', failure: 'SomeFailed', itemKey: () => '' })
86
+ .build();
87
+
88
+ const graph = pipeline.toGraph();
89
+ const hasSuccessEdge = graph.edges.some((e) => e.from === 'cmd:ProcessItem' && e.to === 'evt:AllDone');
90
+ const hasFailureEdge = graph.edges.some((e) => e.from === 'cmd:ProcessItem' && e.to === 'evt:SomeFailed');
91
+ expect(hasSuccessEdge).toBe(true);
92
+ expect(hasFailureEdge).toBe(true);
93
+ });
94
+
95
+ it('should extract graph from custom handler using declaredEmits', () => {
96
+ const pipeline = define('test')
97
+ .on('CustomEvent')
98
+ .handle(async () => {}, { emits: ['EventA', 'EventB'] })
99
+ .build();
100
+
101
+ const graph = pipeline.toGraph();
102
+ expect(graph.nodes.some((n) => n.id === 'evt:CustomEvent')).toBe(true);
103
+ expect(graph.nodes.some((n) => n.id === 'evt:EventA')).toBe(true);
104
+ expect(graph.nodes.some((n) => n.id === 'evt:EventB')).toBe(true);
105
+ });
106
+
107
+ it('should extract graph from custom handler without declaredEmits', () => {
108
+ const pipeline = define('test')
109
+ .on('CustomEvent')
110
+ .handle(async () => {})
111
+ .build();
112
+
113
+ const graph = pipeline.toGraph();
114
+ expect(graph.nodes.some((n) => n.id === 'evt:CustomEvent')).toBe(true);
115
+ expect(graph.nodes).toHaveLength(1);
116
+ });
117
+
118
+ it('should handle multiple handlers in pipeline', () => {
119
+ const pipeline = define('test').on('Start').emit('ProcessA', {}).on('ProcessADone').emit('ProcessB', {}).build();
120
+
121
+ const graph = pipeline.toGraph();
122
+ expect(graph.nodes).toHaveLength(4);
123
+ expect(graph.edges).toHaveLength(2);
124
+ });
125
+
126
+ it('should deduplicate nodes with same id', () => {
127
+ const pipeline = define('test').on('Start').emit('Process', {}).on('Start').emit('OtherProcess', {}).build();
128
+
129
+ const graph = pipeline.toGraph();
130
+ const startNodes = graph.nodes.filter((n) => n.id === 'evt:Start');
131
+ expect(startNodes).toHaveLength(1);
132
+ });
133
+
134
+ it('should create settled node with type settled', () => {
135
+ const pipeline = define('test')
136
+ .on('Start')
137
+ .emit('CheckA', {})
138
+ .settled(['CheckA'])
139
+ .dispatch({ dispatches: [] }, () => {})
140
+ .build();
141
+
142
+ const graph = pipeline.toGraph();
143
+ const settledNode = graph.nodes.find((n) => n.id.startsWith('settled:'));
144
+ expect(settledNode).toBeDefined();
145
+ expect(settledNode?.type).toBe('settled');
146
+ });
147
+
148
+ it('should mark edges from settled nodes to dispatched commands as backLink', () => {
149
+ const pipeline = define('test')
150
+ .on('Start')
151
+ .emit('CheckA', {})
152
+ .settled(['CheckA'])
153
+ .dispatch({ dispatches: ['RetryCommand'] }, () => {})
154
+ .build();
155
+
156
+ const graph = pipeline.toGraph();
157
+ const backEdge = graph.edges.find((e) => e.from.startsWith('settled:') && e.to === 'cmd:RetryCommand');
158
+ expect(backEdge).toBeDefined();
159
+ expect(backEdge?.backLink).toBe(true);
160
+ });
161
+
162
+ it('should not mark forward edges as backLink', () => {
163
+ const pipeline = define('test')
164
+ .on('Start')
165
+ .emit('CheckA', {})
166
+ .settled(['CheckA'])
167
+ .dispatch({ dispatches: ['RetryCommand'] }, () => {})
168
+ .build();
169
+
170
+ const graph = pipeline.toGraph();
171
+ const forwardEdges = graph.edges.filter((e) => !e.from.startsWith('settled:'));
172
+ for (const edge of forwardEdges) {
173
+ expect(edge.backLink).not.toBe(true);
174
+ }
175
+ });
176
+ });
@@ -0,0 +1,19 @@
1
+ export type NodeType = 'event' | 'command' | 'settled';
2
+
3
+ export interface GraphNode {
4
+ id: string;
5
+ type: NodeType;
6
+ label: string;
7
+ }
8
+
9
+ export interface GraphEdge {
10
+ from: string;
11
+ to: string;
12
+ label?: string;
13
+ backLink?: boolean;
14
+ }
15
+
16
+ export interface GraphIR {
17
+ nodes: GraphNode[];
18
+ edges: GraphEdge[];
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ export type {
2
+ CompletionConfig,
3
+ EmitChain,
4
+ ForEachBuilder,
5
+ GatherBuilder,
6
+ GatherChain,
7
+ HandleChain,
8
+ HandleOptions,
9
+ PhasedBuilder,
10
+ PhasedChain,
11
+ PhasedTerminal,
12
+ Pipeline,
13
+ PipelineBuilder,
14
+ RunBuilder,
15
+ TriggerBuilder,
16
+ } from './builder/define';
17
+ export { define } from './builder/define';
18
+ export type {
19
+ CustomHandlerDescriptor,
20
+ EmitHandlerDescriptor,
21
+ EventPredicate,
22
+ FailureContext,
23
+ ForEachPhasedDescriptor,
24
+ GatherEventConfig,
25
+ HandlerDescriptor,
26
+ KeyExtractor,
27
+ PipelineDescriptor,
28
+ RunAwaitHandlerDescriptor,
29
+ SuccessContext,
30
+ } from './core/descriptors';
31
+ export type { Command, CommandDispatch, Event } from './core/types';
32
+ export { dispatch } from './core/types';
33
+
34
+ export type { GraphEdge, GraphIR, GraphNode, NodeType } from './graph/types';
35
+ export type { EventLoggerOptions, LogEntry } from './logging/event-logger';
36
+ export { EventLogger } from './logging/event-logger';
37
+
38
+ export { AwaitTracker } from './runtime/await-tracker';
39
+ export type { PipelineContext, RuntimeConfig } from './runtime/context';
40
+ export { EventCommandMapper } from './runtime/event-command-map';
41
+ export { PhasedExecutor } from './runtime/phased-executor';
42
+ export { PipelineRuntime } from './runtime/pipeline-runtime';
43
+ export { SettledTracker } from './runtime/settled-tracker';
44
+ export type { CommandHandlerWithMetadata, PipelineServerConfig } from './server/pipeline-server';
45
+ export { PipelineServer } from './server/pipeline-server';
46
+ export { SSEManager } from './server/sse-manager';
47
+ export type { SnapshotDiff, SnapshotResult } from './testing/snapshot-compare';
48
+ export {
49
+ compareEventSequence,
50
+ containsSubsequence,
51
+ findMissingEvents,
52
+ findUnexpectedEvents,
53
+ formatSnapshotDiff,
54
+ } from './testing/snapshot-compare';
@@ -0,0 +1,100 @@
1
+ import type { Event } from '@auto-engineer/message-bus';
2
+ import { beforeEach, describe, expect, it } from 'vitest';
3
+ import { EventLogger } from './event-logger';
4
+
5
+ describe('EventLogger', () => {
6
+ let logger: EventLogger;
7
+
8
+ beforeEach(() => {
9
+ logger = new EventLogger();
10
+ });
11
+
12
+ describe('logging', () => {
13
+ it('should log events', () => {
14
+ const event: Event = { type: 'TestEvent', data: { foo: 'bar' } };
15
+ logger.log(event);
16
+ expect(logger.getEntries()).toHaveLength(1);
17
+ expect(logger.getEntries()[0].event).toEqual(event);
18
+ });
19
+
20
+ it('should add timestamp to entries', () => {
21
+ const event: Event = { type: 'TestEvent', data: {} };
22
+ logger.log(event);
23
+ const entry = logger.getEntries()[0];
24
+ expect(entry.timestamp).toBeDefined();
25
+ expect(new Date(entry.timestamp).getTime()).toBeLessThanOrEqual(Date.now());
26
+ });
27
+
28
+ it('should extract correlationId', () => {
29
+ const event: Event = { type: 'TestEvent', correlationId: 'c1', data: {} };
30
+ logger.log(event);
31
+ expect(logger.getEntries()[0].correlationId).toBe('c1');
32
+ });
33
+
34
+ it('should call onLog callback', () => {
35
+ const logged: Event[] = [];
36
+ logger = new EventLogger({
37
+ onLog: (entry) => logged.push(entry.event),
38
+ });
39
+
40
+ logger.log({ type: 'E1', data: {} });
41
+ logger.log({ type: 'E2', data: {} });
42
+
43
+ expect(logged).toHaveLength(2);
44
+ expect(logged.map((e) => e.type)).toEqual(['E1', 'E2']);
45
+ });
46
+ });
47
+
48
+ describe('queries', () => {
49
+ it('should filter by correlationId', () => {
50
+ logger.log({ type: 'E1', correlationId: 'c1', data: {} });
51
+ logger.log({ type: 'E2', correlationId: 'c2', data: {} });
52
+ logger.log({ type: 'E3', correlationId: 'c1', data: {} });
53
+
54
+ const c1Events = logger.getEntriesByCorrelationId('c1');
55
+ expect(c1Events).toHaveLength(2);
56
+ expect(c1Events.map((e) => e.event.type)).toEqual(['E1', 'E3']);
57
+ });
58
+
59
+ it('should return event types', () => {
60
+ logger.log({ type: 'A', data: {} });
61
+ logger.log({ type: 'B', data: {} });
62
+ logger.log({ type: 'C', data: {} });
63
+
64
+ expect(logger.getEventTypes()).toEqual(['A', 'B', 'C']);
65
+ });
66
+
67
+ it('should preserve event type order', () => {
68
+ logger.log({ type: 'Started', data: {} });
69
+ logger.log({ type: 'Processing', data: {} });
70
+ logger.log({ type: 'Completed', data: {} });
71
+
72
+ expect(logger.getEventTypes()).toEqual(['Started', 'Processing', 'Completed']);
73
+ });
74
+ });
75
+
76
+ describe('management', () => {
77
+ it('should clear entries', () => {
78
+ logger.log({ type: 'E1', data: {} });
79
+ logger.log({ type: 'E2', data: {} });
80
+
81
+ logger.clear();
82
+
83
+ expect(logger.getEntries()).toHaveLength(0);
84
+ });
85
+
86
+ it('should serialize to JSON', () => {
87
+ logger.log({ type: 'E1', data: { x: 1 } });
88
+ const json = logger.toJSON();
89
+ expect(json).toHaveLength(1);
90
+ expect(json[0].event.type).toBe('E1');
91
+ });
92
+
93
+ it('should return copy of entries', () => {
94
+ logger.log({ type: 'E1', data: {} });
95
+ const entries = logger.getEntries();
96
+ entries.push({ timestamp: '', event: { type: 'Fake', data: {} } });
97
+ expect(logger.getEntries()).toHaveLength(1);
98
+ });
99
+ });
100
+ });
@@ -0,0 +1,50 @@
1
+ import type { Event } from '@auto-engineer/message-bus';
2
+
3
+ export interface LogEntry {
4
+ timestamp: string;
5
+ event: Event;
6
+ correlationId?: string;
7
+ }
8
+
9
+ export interface EventLoggerOptions {
10
+ onLog?: (entry: LogEntry) => void;
11
+ }
12
+
13
+ export class EventLogger {
14
+ private entries: LogEntry[] = [];
15
+ private readonly onLog?: (entry: LogEntry) => void;
16
+
17
+ constructor(options?: EventLoggerOptions) {
18
+ this.onLog = options?.onLog;
19
+ }
20
+
21
+ log(event: Event): void {
22
+ const entry: LogEntry = {
23
+ timestamp: new Date().toISOString(),
24
+ event,
25
+ correlationId: event.correlationId,
26
+ };
27
+ this.entries.push(entry);
28
+ this.onLog?.(entry);
29
+ }
30
+
31
+ getEntries(): LogEntry[] {
32
+ return [...this.entries];
33
+ }
34
+
35
+ getEntriesByCorrelationId(correlationId: string): LogEntry[] {
36
+ return this.entries.filter((e) => e.correlationId === correlationId);
37
+ }
38
+
39
+ getEventTypes(): string[] {
40
+ return this.entries.map((e) => e.event.type);
41
+ }
42
+
43
+ clear(): void {
44
+ this.entries = [];
45
+ }
46
+
47
+ toJSON(): LogEntry[] {
48
+ return this.getEntries();
49
+ }
50
+ }