@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,700 @@
1
+ import type { Event } from '@auto-engineer/message-bus';
2
+ import type {
3
+ CustomHandlerDescriptor,
4
+ EmitHandlerDescriptor,
5
+ EventPredicate,
6
+ FailureContext,
7
+ ForEachPhasedDescriptor,
8
+ GatherEventConfig,
9
+ HandlerDescriptor,
10
+ KeyExtractor,
11
+ PipelineDescriptor,
12
+ RunAwaitHandlerDescriptor,
13
+ SettledHandler,
14
+ SettledHandlerDescriptor,
15
+ SuccessContext,
16
+ } from '../core/descriptors';
17
+ import type { CommandDispatch } from '../core/types';
18
+ import type { GraphEdge, GraphIR, GraphNode } from '../graph/types';
19
+ import type { PipelineContext } from '../runtime/context';
20
+
21
+ export interface Pipeline {
22
+ descriptor: Readonly<PipelineDescriptor>;
23
+ toGraph(): GraphIR;
24
+ }
25
+
26
+ export interface PipelineBuilder {
27
+ version(v: string): PipelineBuilder;
28
+ description(d: string): PipelineBuilder;
29
+ key<E>(name: string, extractor: (event: E) => string): PipelineBuilder;
30
+ on(eventType: string): TriggerBuilder;
31
+ settled(commandTypes: readonly string[]): SettledBuilder;
32
+ build(): Pipeline;
33
+ }
34
+
35
+ export interface DispatchOptions<D extends readonly string[] = readonly string[]> {
36
+ dispatches: D;
37
+ }
38
+
39
+ export interface SettledBuilder {
40
+ dispatch<const D extends readonly string[]>(
41
+ options: DispatchOptions<D>,
42
+ handler: (
43
+ events: Record<string, Event[]>,
44
+ send: (commandType: D[number], data: unknown) => void,
45
+ ) => void | { persist: boolean },
46
+ ): SettledChain;
47
+ }
48
+
49
+ export interface SettledChain {
50
+ on(eventType: string): TriggerBuilder;
51
+ settled(commandTypes: readonly string[]): SettledBuilder;
52
+ build(): Pipeline;
53
+ }
54
+
55
+ export interface HandleOptions {
56
+ emits?: string[];
57
+ }
58
+
59
+ export interface TriggerBuilder {
60
+ when<E>(predicate: (event: E) => boolean): TriggerBuilder;
61
+ emit(commandType: string, data: unknown): EmitChain;
62
+ run(commands: CommandDispatch[]): RunBuilder;
63
+ run<E>(factory: (event: E) => CommandDispatch[]): RunBuilder;
64
+ forEach<E, T>(itemsSelector: (event: E) => T[]): ForEachBuilder<T>;
65
+ handle<E>(handler: (event: E, ctx: PipelineContext) => void | Promise<void>, options?: HandleOptions): HandleChain;
66
+ }
67
+
68
+ export interface ForEachBuilder<T> {
69
+ groupInto<P extends string>(phases: readonly P[], classifier: (item: T) => P): PhasedBuilder<T>;
70
+ }
71
+
72
+ export interface PhasedBuilder<T> {
73
+ process(commandType: string, dataFactory: (item: T) => Record<string, unknown>): PhasedChain<T>;
74
+ }
75
+
76
+ export interface CompletionConfig {
77
+ success: string;
78
+ failure: string;
79
+ itemKey: (event: Event) => string;
80
+ }
81
+
82
+ export interface PhasedChain<T> {
83
+ stopOnFailure(): PhasedChain<T>;
84
+ onComplete(config: CompletionConfig): PhasedTerminal;
85
+ build(): Pipeline;
86
+ }
87
+
88
+ export interface PhasedTerminal {
89
+ on(eventType: string): TriggerBuilder;
90
+ build(): Pipeline;
91
+ }
92
+
93
+ export interface RunBuilder {
94
+ awaitAll<E>(keyName: string, keyExtractor: (event: E) => string, options?: { timeout?: number }): GatherBuilder;
95
+ }
96
+
97
+ export interface GatherBuilder {
98
+ onSuccess<T = unknown>(
99
+ eventType: string,
100
+ dataFactory: (ctx: SuccessContext<T>) => Record<string, unknown>,
101
+ ): GatherChain;
102
+ onFailure<T = unknown>(
103
+ eventType: string,
104
+ dataFactory: (ctx: FailureContext<T>) => Record<string, unknown>,
105
+ ): GatherChain;
106
+ on(eventType: string): TriggerBuilder;
107
+ build(): Pipeline;
108
+ }
109
+
110
+ export interface GatherChain {
111
+ onSuccess<T = unknown>(
112
+ eventType: string,
113
+ dataFactory: (ctx: SuccessContext<T>) => Record<string, unknown>,
114
+ ): GatherChain;
115
+ onFailure<T = unknown>(
116
+ eventType: string,
117
+ dataFactory: (ctx: FailureContext<T>) => Record<string, unknown>,
118
+ ): GatherChain;
119
+ on(eventType: string): TriggerBuilder;
120
+ build(): Pipeline;
121
+ }
122
+
123
+ export interface EmitChain {
124
+ emit(commandType: string, data: unknown): EmitChain;
125
+ on(eventType: string): TriggerBuilder;
126
+ settled(commandTypes: readonly string[]): SettledBuilder;
127
+ build(): Pipeline;
128
+ }
129
+
130
+ export interface HandleChain {
131
+ on(eventType: string): TriggerBuilder;
132
+ build(): Pipeline;
133
+ }
134
+
135
+ class PipelineBuilderImpl implements PipelineBuilder {
136
+ private readonly name: string;
137
+ private versionValue?: string;
138
+ private descriptionValue?: string;
139
+ private readonly keys: Map<string, KeyExtractor> = new Map();
140
+ private readonly handlers: HandlerDescriptor[] = [];
141
+
142
+ constructor(name: string) {
143
+ this.name = name;
144
+ }
145
+
146
+ version(v: string): PipelineBuilder {
147
+ this.versionValue = v;
148
+ return this;
149
+ }
150
+
151
+ description(d: string): PipelineBuilder {
152
+ this.descriptionValue = d;
153
+ return this;
154
+ }
155
+
156
+ key<E>(name: string, extractor: (event: E) => string): PipelineBuilder {
157
+ this.keys.set(name, extractor as KeyExtractor);
158
+ return this;
159
+ }
160
+
161
+ on(eventType: string): TriggerBuilder {
162
+ return new TriggerBuilderImpl(this, eventType);
163
+ }
164
+
165
+ settled(commandTypes: readonly string[]): SettledBuilder {
166
+ return new SettledBuilderImpl(this, commandTypes);
167
+ }
168
+
169
+ addHandler(handler: HandlerDescriptor): void {
170
+ this.handlers.push(handler);
171
+ }
172
+
173
+ build(): Pipeline {
174
+ const descriptor: PipelineDescriptor = {
175
+ name: this.name,
176
+ version: this.versionValue,
177
+ description: this.descriptionValue,
178
+ keys: this.keys,
179
+ handlers: this.handlers,
180
+ };
181
+ const frozenDescriptor = Object.freeze(descriptor);
182
+ return {
183
+ descriptor: frozenDescriptor,
184
+ toGraph: () => extractGraph(frozenDescriptor),
185
+ };
186
+ }
187
+ }
188
+
189
+ type GraphBuilderContext = {
190
+ nodeMap: Map<string, GraphNode>;
191
+ edges: GraphEdge[];
192
+ };
193
+
194
+ function addNode(ctx: GraphBuilderContext, id: string, type: 'event' | 'command' | 'settled', label: string): void {
195
+ if (!ctx.nodeMap.has(id)) {
196
+ ctx.nodeMap.set(id, { id, type, label });
197
+ }
198
+ }
199
+
200
+ function processEmitHandler(ctx: GraphBuilderContext, handler: EmitHandlerDescriptor): void {
201
+ addNode(ctx, `evt:${handler.eventType}`, 'event', handler.eventType);
202
+ for (const cmd of handler.commands) {
203
+ addNode(ctx, `cmd:${cmd.commandType}`, 'command', cmd.commandType);
204
+ ctx.edges.push({ from: `evt:${handler.eventType}`, to: `cmd:${cmd.commandType}` });
205
+ }
206
+ }
207
+
208
+ function processRunAwaitHandler(ctx: GraphBuilderContext, handler: RunAwaitHandlerDescriptor): void {
209
+ addNode(ctx, `evt:${handler.eventType}`, 'event', handler.eventType);
210
+ const commands = Array.isArray(handler.commands) ? handler.commands : [];
211
+ for (const cmd of commands) {
212
+ addNode(ctx, `cmd:${cmd.commandType}`, 'command', cmd.commandType);
213
+ ctx.edges.push({ from: `evt:${handler.eventType}`, to: `cmd:${cmd.commandType}` });
214
+ }
215
+ if (handler.onSuccess) {
216
+ addNode(ctx, `evt:${handler.onSuccess.eventType}`, 'event', handler.onSuccess.eventType);
217
+ }
218
+ if (handler.onFailure) {
219
+ addNode(ctx, `evt:${handler.onFailure.eventType}`, 'event', handler.onFailure.eventType);
220
+ }
221
+ }
222
+
223
+ function processForEachPhasedHandler(ctx: GraphBuilderContext, handler: ForEachPhasedDescriptor): void {
224
+ addNode(ctx, `evt:${handler.eventType}`, 'event', handler.eventType);
225
+ const sampleCmd = handler.emitFactory({}, '', { type: '', data: {} });
226
+ addNode(ctx, `cmd:${sampleCmd.commandType}`, 'command', sampleCmd.commandType);
227
+ ctx.edges.push({ from: `evt:${handler.eventType}`, to: `cmd:${sampleCmd.commandType}` });
228
+ addNode(ctx, `evt:${handler.completion.successEvent}`, 'event', handler.completion.successEvent);
229
+ addNode(ctx, `evt:${handler.completion.failureEvent}`, 'event', handler.completion.failureEvent);
230
+ ctx.edges.push({ from: `cmd:${sampleCmd.commandType}`, to: `evt:${handler.completion.successEvent}` });
231
+ ctx.edges.push({ from: `cmd:${sampleCmd.commandType}`, to: `evt:${handler.completion.failureEvent}` });
232
+ }
233
+
234
+ function processCustomHandler(ctx: GraphBuilderContext, handler: CustomHandlerDescriptor): void {
235
+ addNode(ctx, `evt:${handler.eventType}`, 'event', handler.eventType);
236
+ if (handler.declaredEmits) {
237
+ for (const emittedEvent of handler.declaredEmits) {
238
+ addNode(ctx, `evt:${emittedEvent}`, 'event', emittedEvent);
239
+ ctx.edges.push({ from: `evt:${handler.eventType}`, to: `evt:${emittedEvent}` });
240
+ }
241
+ }
242
+ }
243
+
244
+ function processSettledHandler(ctx: GraphBuilderContext, handler: SettledHandlerDescriptor): void {
245
+ const settledNodeId = `settled:${handler.commandTypes.join(',')}`;
246
+ addNode(ctx, settledNodeId, 'settled', `settled(${handler.commandTypes.join(', ')})`);
247
+
248
+ for (const commandType of handler.commandTypes) {
249
+ addNode(ctx, `cmd:${commandType}`, 'command', commandType);
250
+ ctx.edges.push({ from: `cmd:${commandType}`, to: settledNodeId });
251
+ }
252
+
253
+ if (handler.dispatches) {
254
+ for (const dispatchedCommand of handler.dispatches) {
255
+ addNode(ctx, `cmd:${dispatchedCommand}`, 'command', dispatchedCommand);
256
+ ctx.edges.push({ from: settledNodeId, to: `cmd:${dispatchedCommand}`, backLink: true });
257
+ }
258
+ }
259
+ }
260
+
261
+ function extractGraph(descriptor: PipelineDescriptor): GraphIR {
262
+ const ctx: GraphBuilderContext = {
263
+ nodeMap: new Map<string, GraphNode>(),
264
+ edges: [],
265
+ };
266
+
267
+ for (const handler of descriptor.handlers) {
268
+ switch (handler.type) {
269
+ case 'emit':
270
+ processEmitHandler(ctx, handler);
271
+ break;
272
+ case 'run-await':
273
+ processRunAwaitHandler(ctx, handler);
274
+ break;
275
+ case 'foreach-phased':
276
+ processForEachPhasedHandler(ctx, handler);
277
+ break;
278
+ case 'custom':
279
+ processCustomHandler(ctx, handler);
280
+ break;
281
+ case 'settled':
282
+ processSettledHandler(ctx, handler);
283
+ break;
284
+ }
285
+ }
286
+
287
+ return {
288
+ nodes: Array.from(ctx.nodeMap.values()),
289
+ edges: ctx.edges,
290
+ };
291
+ }
292
+
293
+ class TriggerBuilderImpl implements TriggerBuilder {
294
+ private predicate?: EventPredicate;
295
+
296
+ constructor(
297
+ private readonly parent: PipelineBuilderImpl,
298
+ private readonly eventType: string,
299
+ ) {}
300
+
301
+ when<E>(predicate: (event: E) => boolean): TriggerBuilder {
302
+ this.predicate = predicate as EventPredicate;
303
+ return this;
304
+ }
305
+
306
+ emit(commandType: string, data: unknown): EmitChain {
307
+ return new EmitChainImpl(this.parent, this.eventType, [{ commandType, data }], this.predicate);
308
+ }
309
+
310
+ run(commandsOrFactory: CommandDispatch[] | ((event: Event) => CommandDispatch[])): RunBuilder {
311
+ return new RunBuilderImpl(this.parent, this.eventType, commandsOrFactory, this.predicate);
312
+ }
313
+
314
+ forEach<E, T>(itemsSelector: (event: E) => T[]): ForEachBuilder<T> {
315
+ return new ForEachBuilderImpl<T>(
316
+ this.parent,
317
+ this.eventType,
318
+ itemsSelector as (event: Event) => unknown[],
319
+ this.predicate,
320
+ );
321
+ }
322
+
323
+ handle<E>(handler: (event: E, ctx: PipelineContext) => void | Promise<void>, options?: HandleOptions): HandleChain {
324
+ return new HandleChainImpl(
325
+ this.parent,
326
+ this.eventType,
327
+ handler as (event: Event, ctx: PipelineContext) => void | Promise<void>,
328
+ this.predicate,
329
+ options?.emits,
330
+ );
331
+ }
332
+ }
333
+
334
+ class EmitChainImpl implements EmitChain {
335
+ constructor(
336
+ private readonly parent: PipelineBuilderImpl,
337
+ private readonly eventType: string,
338
+ private readonly commands: Array<{ commandType: string; data: unknown }>,
339
+ private readonly predicate?: EventPredicate,
340
+ ) {}
341
+
342
+ emit(commandType: string, data: unknown): EmitChain {
343
+ return new EmitChainImpl(this.parent, this.eventType, [...this.commands, { commandType, data }], this.predicate);
344
+ }
345
+
346
+ on(eventType: string): TriggerBuilder {
347
+ this.finalizeHandler();
348
+ return new TriggerBuilderImpl(this.parent, eventType);
349
+ }
350
+
351
+ settled(commandTypes: readonly string[]): SettledBuilder {
352
+ this.finalizeHandler();
353
+ return new SettledBuilderImpl(this.parent, commandTypes);
354
+ }
355
+
356
+ build(): Pipeline {
357
+ this.finalizeHandler();
358
+ return this.parent.build();
359
+ }
360
+
361
+ private finalizeHandler(): void {
362
+ this.parent.addHandler({
363
+ type: 'emit',
364
+ eventType: this.eventType,
365
+ predicate: this.predicate,
366
+ commands: this.commands.map((c) => ({
367
+ commandType: c.commandType,
368
+ data: c.data as Record<string, unknown>,
369
+ })),
370
+ });
371
+ }
372
+ }
373
+
374
+ class HandleChainImpl implements HandleChain {
375
+ constructor(
376
+ private readonly parent: PipelineBuilderImpl,
377
+ private readonly eventType: string,
378
+ private readonly handler: (event: Event, ctx: PipelineContext) => void | Promise<void>,
379
+ private readonly predicate?: EventPredicate,
380
+ private readonly declaredEmits?: string[],
381
+ ) {}
382
+
383
+ on(eventType: string): TriggerBuilder {
384
+ this.finalizeHandler();
385
+ return new TriggerBuilderImpl(this.parent, eventType);
386
+ }
387
+
388
+ build(): Pipeline {
389
+ this.finalizeHandler();
390
+ return this.parent.build();
391
+ }
392
+
393
+ private finalizeHandler(): void {
394
+ this.parent.addHandler({
395
+ type: 'custom',
396
+ eventType: this.eventType,
397
+ predicate: this.predicate,
398
+ handler: this.handler,
399
+ declaredEmits: this.declaredEmits,
400
+ });
401
+ }
402
+ }
403
+
404
+ class RunBuilderImpl implements RunBuilder {
405
+ constructor(
406
+ private readonly parent: PipelineBuilderImpl,
407
+ private readonly eventType: string,
408
+ private readonly commands: CommandDispatch[] | ((event: Event) => CommandDispatch[]),
409
+ private readonly predicate?: EventPredicate,
410
+ ) {}
411
+
412
+ awaitAll<E>(keyName: string, keyExtractor: (event: E) => string, options?: { timeout?: number }): GatherBuilder {
413
+ return new GatherBuilderImpl(
414
+ this.parent,
415
+ this.eventType,
416
+ this.commands,
417
+ this.predicate,
418
+ keyName,
419
+ keyExtractor as KeyExtractor,
420
+ options?.timeout,
421
+ );
422
+ }
423
+ }
424
+
425
+ class GatherBuilderImpl implements GatherBuilder {
426
+ private successConfig?: GatherEventConfig<SuccessContext>;
427
+ private failureConfig?: GatherEventConfig<FailureContext>;
428
+
429
+ constructor(
430
+ private readonly parent: PipelineBuilderImpl,
431
+ private readonly eventType: string,
432
+ private readonly commands: CommandDispatch[] | ((event: Event) => CommandDispatch[]),
433
+ private readonly predicate: EventPredicate | undefined,
434
+ private readonly keyName: string,
435
+ private readonly keyExtractor: KeyExtractor,
436
+ private readonly timeout?: number,
437
+ ) {}
438
+
439
+ onSuccess<T = unknown>(
440
+ eventType: string,
441
+ dataFactory: (ctx: SuccessContext<T>) => Record<string, unknown>,
442
+ ): GatherChain {
443
+ this.successConfig = { eventType, dataFactory: dataFactory as (ctx: SuccessContext) => Record<string, unknown> };
444
+ return new GatherChainImpl(this, this.parent);
445
+ }
446
+
447
+ onFailure<T = unknown>(
448
+ eventType: string,
449
+ dataFactory: (ctx: FailureContext<T>) => Record<string, unknown>,
450
+ ): GatherChain {
451
+ this.failureConfig = { eventType, dataFactory: dataFactory as (ctx: FailureContext) => Record<string, unknown> };
452
+ return new GatherChainImpl(this, this.parent);
453
+ }
454
+
455
+ on(eventType: string): TriggerBuilder {
456
+ this.finalizeHandler();
457
+ return new TriggerBuilderImpl(this.parent, eventType);
458
+ }
459
+
460
+ build(): Pipeline {
461
+ this.finalizeHandler();
462
+ return this.parent.build();
463
+ }
464
+
465
+ setSuccessConfig(config: GatherEventConfig<SuccessContext>): void {
466
+ this.successConfig = config;
467
+ }
468
+
469
+ setFailureConfig(config: GatherEventConfig<FailureContext>): void {
470
+ this.failureConfig = config;
471
+ }
472
+
473
+ private finalizeHandler(): void {
474
+ this.parent.addHandler({
475
+ type: 'run-await',
476
+ eventType: this.eventType,
477
+ predicate: this.predicate,
478
+ commands: this.commands,
479
+ awaitConfig: {
480
+ keyName: this.keyName,
481
+ key: this.keyExtractor,
482
+ timeout: this.timeout,
483
+ },
484
+ onSuccess: this.successConfig,
485
+ onFailure: this.failureConfig,
486
+ });
487
+ }
488
+ }
489
+
490
+ class GatherChainImpl implements GatherChain {
491
+ constructor(
492
+ private readonly gatherBuilder: GatherBuilderImpl,
493
+ readonly _parent: PipelineBuilderImpl,
494
+ ) {}
495
+
496
+ onSuccess<T = unknown>(
497
+ eventType: string,
498
+ dataFactory: (ctx: SuccessContext<T>) => Record<string, unknown>,
499
+ ): GatherChain {
500
+ this.gatherBuilder.setSuccessConfig({
501
+ eventType,
502
+ dataFactory: dataFactory as (ctx: SuccessContext) => Record<string, unknown>,
503
+ });
504
+ return this;
505
+ }
506
+
507
+ onFailure<T = unknown>(
508
+ eventType: string,
509
+ dataFactory: (ctx: FailureContext<T>) => Record<string, unknown>,
510
+ ): GatherChain {
511
+ this.gatherBuilder.setFailureConfig({
512
+ eventType,
513
+ dataFactory: dataFactory as (ctx: FailureContext) => Record<string, unknown>,
514
+ });
515
+ return this;
516
+ }
517
+
518
+ on(eventType: string): TriggerBuilder {
519
+ return this.gatherBuilder.on(eventType);
520
+ }
521
+
522
+ build(): Pipeline {
523
+ return this.gatherBuilder.build();
524
+ }
525
+ }
526
+
527
+ class ForEachBuilderImpl<T> implements ForEachBuilder<T> {
528
+ constructor(
529
+ private readonly parent: PipelineBuilderImpl,
530
+ private readonly eventType: string,
531
+ private readonly itemsSelector: (event: Event) => unknown[],
532
+ private readonly predicate?: EventPredicate,
533
+ ) {}
534
+
535
+ groupInto<P extends string>(phases: readonly P[], classifier: (item: T) => P): PhasedBuilder<T> {
536
+ return new PhasedBuilderImpl<T>(
537
+ this.parent,
538
+ this.eventType,
539
+ this.itemsSelector,
540
+ this.predicate,
541
+ phases,
542
+ classifier as (item: unknown) => string,
543
+ );
544
+ }
545
+ }
546
+
547
+ class PhasedBuilderImpl<T> implements PhasedBuilder<T> {
548
+ constructor(
549
+ private readonly parent: PipelineBuilderImpl,
550
+ private readonly eventType: string,
551
+ private readonly itemsSelector: (event: Event) => unknown[],
552
+ private readonly predicate: EventPredicate | undefined,
553
+ private readonly phases: readonly string[],
554
+ private readonly classifier: (item: unknown) => string,
555
+ ) {}
556
+
557
+ process(commandType: string, dataFactory: (item: T) => Record<string, unknown>): PhasedChain<T> {
558
+ return new PhasedChainImpl<T>(
559
+ this.parent,
560
+ this.eventType,
561
+ this.itemsSelector,
562
+ this.predicate,
563
+ this.phases,
564
+ this.classifier,
565
+ commandType,
566
+ dataFactory as (item: unknown) => Record<string, unknown>,
567
+ );
568
+ }
569
+ }
570
+
571
+ class PhasedChainImpl<T> implements PhasedChain<T> {
572
+ private stopOnFailureFlag = false;
573
+ private completionConfig?: {
574
+ successEvent: string;
575
+ failureEvent: string;
576
+ itemKey: KeyExtractor;
577
+ };
578
+
579
+ constructor(
580
+ private readonly parent: PipelineBuilderImpl,
581
+ private readonly eventType: string,
582
+ private readonly itemsSelector: (event: Event) => unknown[],
583
+ private readonly predicate: EventPredicate | undefined,
584
+ private readonly phases: readonly string[],
585
+ private readonly classifier: (item: unknown) => string,
586
+ private readonly commandType: string,
587
+ private readonly dataFactory: (item: unknown) => Record<string, unknown>,
588
+ ) {}
589
+
590
+ stopOnFailure(): PhasedChain<T> {
591
+ this.stopOnFailureFlag = true;
592
+ return this;
593
+ }
594
+
595
+ onComplete(config: CompletionConfig): PhasedTerminal {
596
+ this.completionConfig = {
597
+ successEvent: config.success,
598
+ failureEvent: config.failure,
599
+ itemKey: config.itemKey as KeyExtractor,
600
+ };
601
+ return new PhasedTerminalImpl(this, this.parent);
602
+ }
603
+
604
+ build(): Pipeline {
605
+ this.finalizeHandler();
606
+ return this.parent.build();
607
+ }
608
+
609
+ finalizeHandler(): void {
610
+ if (!this.completionConfig) {
611
+ throw new Error('onComplete() must be called before build()');
612
+ }
613
+ this.parent.addHandler({
614
+ type: 'foreach-phased',
615
+ eventType: this.eventType,
616
+ predicate: this.predicate,
617
+ itemsSelector: this.itemsSelector,
618
+ phases: this.phases,
619
+ classifier: this.classifier,
620
+ stopOnFailure: this.stopOnFailureFlag,
621
+ emitFactory: (item: unknown, _phase: string, _event: Event) => ({
622
+ commandType: this.commandType,
623
+ data: this.dataFactory(item),
624
+ }),
625
+ completion: this.completionConfig,
626
+ });
627
+ }
628
+ }
629
+
630
+ class PhasedTerminalImpl implements PhasedTerminal {
631
+ constructor(
632
+ private readonly phasedChain: PhasedChainImpl<unknown>,
633
+ private readonly parent: PipelineBuilderImpl,
634
+ ) {}
635
+
636
+ on(eventType: string): TriggerBuilder {
637
+ this.phasedChain.finalizeHandler();
638
+ return new TriggerBuilderImpl(this.parent, eventType);
639
+ }
640
+
641
+ build(): Pipeline {
642
+ this.phasedChain.finalizeHandler();
643
+ return this.parent.build();
644
+ }
645
+ }
646
+
647
+ class SettledBuilderImpl implements SettledBuilder {
648
+ constructor(
649
+ private readonly parent: PipelineBuilderImpl,
650
+ private readonly commandTypes: readonly string[],
651
+ ) {}
652
+
653
+ dispatch<const D extends readonly string[]>(
654
+ options: DispatchOptions<D>,
655
+ handler: (
656
+ events: Record<string, Event[]>,
657
+ send: (commandType: D[number], data: unknown) => void,
658
+ ) => undefined | { persist: boolean },
659
+ ): SettledChain {
660
+ return new SettledChainImpl(this.parent, this.commandTypes, handler as SettledHandler, options.dispatches);
661
+ }
662
+ }
663
+
664
+ class SettledChainImpl implements SettledChain {
665
+ constructor(
666
+ private readonly parent: PipelineBuilderImpl,
667
+ private readonly commandTypes: readonly string[],
668
+ private readonly handler: SettledHandler,
669
+ private readonly dispatches?: readonly string[],
670
+ ) {}
671
+
672
+ on(eventType: string): TriggerBuilder {
673
+ this.finalizeHandler();
674
+ return new TriggerBuilderImpl(this.parent, eventType);
675
+ }
676
+
677
+ settled(commandTypes: readonly string[]): SettledBuilder {
678
+ this.finalizeHandler();
679
+ return new SettledBuilderImpl(this.parent, commandTypes);
680
+ }
681
+
682
+ build(): Pipeline {
683
+ this.finalizeHandler();
684
+ return this.parent.build();
685
+ }
686
+
687
+ private finalizeHandler(): void {
688
+ const descriptor: SettledHandlerDescriptor = {
689
+ type: 'settled',
690
+ commandTypes: this.commandTypes,
691
+ handler: this.handler,
692
+ dispatches: this.dispatches,
693
+ };
694
+ this.parent.addHandler(descriptor);
695
+ }
696
+ }
697
+
698
+ export function define(name: string): PipelineBuilder {
699
+ return new PipelineBuilderImpl(name);
700
+ }