@calmo/task-runner 3.8.0 → 3.8.2

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 (59) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/coverage/coverage-final.json +5 -4
  3. package/coverage/index.html +18 -18
  4. package/coverage/lcov-report/index.html +18 -18
  5. package/coverage/lcov-report/src/EventBus.ts.html +70 -34
  6. package/coverage/lcov-report/src/TaskGraphValidationError.ts.html +1 -1
  7. package/coverage/lcov-report/src/TaskGraphValidator.ts.html +120 -60
  8. package/coverage/lcov-report/src/TaskRunner.ts.html +78 -63
  9. package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +1 -1
  10. package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +1 -1
  11. package/coverage/lcov-report/src/TaskStateManager.ts.html +17 -29
  12. package/coverage/lcov-report/src/WorkflowExecutor.ts.html +1 -1
  13. package/coverage/lcov-report/src/contracts/ErrorTypes.ts.html +103 -0
  14. package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +1 -1
  15. package/coverage/lcov-report/src/contracts/index.html +23 -8
  16. package/coverage/lcov-report/src/index.html +13 -13
  17. package/coverage/lcov-report/src/strategies/DryRunExecutionStrategy.ts.html +1 -1
  18. package/coverage/lcov-report/src/strategies/RetryingExecutionStrategy.ts.html +1 -1
  19. package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +1 -1
  20. package/coverage/lcov-report/src/strategies/index.html +1 -1
  21. package/coverage/lcov.info +238 -213
  22. package/coverage/src/EventBus.ts.html +70 -34
  23. package/coverage/src/TaskGraphValidationError.ts.html +1 -1
  24. package/coverage/src/TaskGraphValidator.ts.html +120 -60
  25. package/coverage/src/TaskRunner.ts.html +78 -63
  26. package/coverage/src/TaskRunnerBuilder.ts.html +1 -1
  27. package/coverage/src/TaskRunnerExecutionConfig.ts.html +1 -1
  28. package/coverage/src/TaskStateManager.ts.html +17 -29
  29. package/coverage/src/WorkflowExecutor.ts.html +1 -1
  30. package/coverage/src/contracts/ErrorTypes.ts.html +103 -0
  31. package/coverage/src/contracts/RunnerEvents.ts.html +1 -1
  32. package/coverage/src/contracts/index.html +23 -8
  33. package/coverage/src/index.html +13 -13
  34. package/coverage/src/strategies/DryRunExecutionStrategy.ts.html +1 -1
  35. package/coverage/src/strategies/RetryingExecutionStrategy.ts.html +1 -1
  36. package/coverage/src/strategies/StandardExecutionStrategy.ts.html +1 -1
  37. package/coverage/src/strategies/index.html +1 -1
  38. package/dist/EventBus.js +11 -2
  39. package/dist/EventBus.js.map +1 -1
  40. package/dist/TaskGraphValidator.d.ts +3 -0
  41. package/dist/TaskGraphValidator.js +33 -26
  42. package/dist/TaskGraphValidator.js.map +1 -1
  43. package/dist/TaskRunner.d.ts +4 -0
  44. package/dist/TaskRunner.js +38 -44
  45. package/dist/TaskRunner.js.map +1 -1
  46. package/dist/TaskStateManager.js +1 -5
  47. package/dist/TaskStateManager.js.map +1 -1
  48. package/dist/contracts/ErrorTypes.d.ts +6 -0
  49. package/dist/contracts/ErrorTypes.js +7 -0
  50. package/dist/contracts/ErrorTypes.js.map +1 -0
  51. package/dist/contracts/ValidationError.d.ts +2 -1
  52. package/package.json +1 -1
  53. package/src/EventBus.ts +27 -15
  54. package/src/TaskGraphValidator.ts +52 -32
  55. package/src/TaskRunner.ts +51 -46
  56. package/src/TaskStateManager.ts +1 -5
  57. package/src/contracts/ErrorTypes.ts +6 -0
  58. package/src/contracts/ValidationError.ts +10 -1
  59. package/test-report.xml +152 -128
@@ -25,7 +25,7 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>295/295</span>
28
+ <span class='fraction'>302/302</span>
29
29
  </div>
30
30
 
31
31
 
@@ -39,14 +39,14 @@
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>56/56</span>
42
+ <span class='fraction'>60/60</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>291/291</span>
49
+ <span class='fraction'>299/299</span>
50
50
  </div>
51
51
 
52
52
 
@@ -84,28 +84,28 @@
84
84
  <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
85
85
  </td>
86
86
  <td data-value="100" class="pct high">100%</td>
87
- <td data-value="253" class="abs high">253/253</td>
87
+ <td data-value="257" class="abs high">257/257</td>
88
88
  <td data-value="100" class="pct high">100%</td>
89
89
  <td data-value="97" class="abs high">97/97</td>
90
90
  <td data-value="100" class="pct high">100%</td>
91
- <td data-value="47" class="abs high">47/47</td>
91
+ <td data-value="51" class="abs high">51/51</td>
92
92
  <td data-value="100" class="pct high">100%</td>
93
- <td data-value="249" class="abs high">249/249</td>
93
+ <td data-value="254" class="abs high">254/254</td>
94
94
  </tr>
95
95
 
96
96
  <tr>
97
- <td class="file empty" data-value="src/contracts"><a href="src/contracts/index.html">src/contracts</a></td>
98
- <td data-value="0" class="pic empty">
99
- <div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
97
+ <td class="file high" data-value="src/contracts"><a href="src/contracts/index.html">src/contracts</a></td>
98
+ <td data-value="100" class="pic high">
99
+ <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
100
100
  </td>
101
- <td data-value="0" class="pct empty">0%</td>
102
- <td data-value="0" class="abs empty">0/0</td>
103
- <td data-value="0" class="pct empty">0%</td>
104
- <td data-value="0" class="abs empty">0/0</td>
105
- <td data-value="0" class="pct empty">0%</td>
106
- <td data-value="0" class="abs empty">0/0</td>
107
- <td data-value="0" class="pct empty">0%</td>
108
- <td data-value="0" class="abs empty">0/0</td>
101
+ <td data-value="100" class="pct high">100%</td>
102
+ <td data-value="3" class="abs high">3/3</td>
103
+ <td data-value="100" class="pct high">100%</td>
104
+ <td data-value="0" class="abs high">0/0</td>
105
+ <td data-value="100" class="pct high">100%</td>
106
+ <td data-value="0" class="abs high">0/0</td>
107
+ <td data-value="100" class="pct high">100%</td>
108
+ <td data-value="3" class="abs high">3/3</td>
109
109
  </tr>
110
110
 
111
111
  <tr>
@@ -131,7 +131,7 @@
131
131
  <div class='footer quiet pad2 space-top1 center small'>
132
132
  Code coverage generated by
133
133
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
134
- at 2026-01-21T00:01:01.006Z
134
+ at 2026-01-21T01:16:02.348Z
135
135
  </div>
136
136
  <script src="prettify.js"></script>
137
137
  <script>
@@ -25,7 +25,7 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>16/16</span>
28
+ <span class='fraction'>17/17</span>
29
29
  </div>
30
30
 
31
31
 
@@ -39,14 +39,14 @@
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>5/5</span>
42
+ <span class='fraction'>6/6</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>16/16</span>
49
+ <span class='fraction'>17/17</span>
50
50
  </div>
51
51
 
52
52
 
@@ -149,7 +149,19 @@
149
149
  <a name='L84'></a><a href='#L84'>84</a>
150
150
  <a name='L85'></a><a href='#L85'>85</a>
151
151
  <a name='L86'></a><a href='#L86'>86</a>
152
- <a name='L87'></a><a href='#L87'>87</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
152
+ <a name='L87'></a><a href='#L87'>87</a>
153
+ <a name='L88'></a><a href='#L88'>88</a>
154
+ <a name='L89'></a><a href='#L89'>89</a>
155
+ <a name='L90'></a><a href='#L90'>90</a>
156
+ <a name='L91'></a><a href='#L91'>91</a>
157
+ <a name='L92'></a><a href='#L92'>92</a>
158
+ <a name='L93'></a><a href='#L93'>93</a>
159
+ <a name='L94'></a><a href='#L94'>94</a>
160
+ <a name='L95'></a><a href='#L95'>95</a>
161
+ <a name='L96'></a><a href='#L96'>96</a>
162
+ <a name='L97'></a><a href='#L97'>97</a>
163
+ <a name='L98'></a><a href='#L98'>98</a>
164
+ <a name='L99'></a><a href='#L99'>99</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
153
165
  <span class="cline-any cline-neutral">&nbsp;</span>
154
166
  <span class="cline-any cline-neutral">&nbsp;</span>
155
167
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -160,7 +172,7 @@
160
172
  <span class="cline-any cline-neutral">&nbsp;</span>
161
173
  <span class="cline-any cline-neutral">&nbsp;</span>
162
174
  <span class="cline-any cline-neutral">&nbsp;</span>
163
- <span class="cline-any cline-yes">80x</span>
175
+ <span class="cline-any cline-yes">90x</span>
164
176
  <span class="cline-any cline-neutral">&nbsp;</span>
165
177
  <span class="cline-any cline-neutral">&nbsp;</span>
166
178
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -171,13 +183,13 @@
171
183
  <span class="cline-any cline-neutral">&nbsp;</span>
172
184
  <span class="cline-any cline-neutral">&nbsp;</span>
173
185
  <span class="cline-any cline-neutral">&nbsp;</span>
174
- <span class="cline-any cline-yes">16x</span>
186
+ <span class="cline-any cline-yes">24x</span>
175
187
  <span class="cline-any cline-neutral">&nbsp;</span>
176
188
  <span class="cline-any cline-neutral">&nbsp;</span>
177
- <span class="cline-any cline-yes">14x</span>
189
+ <span class="cline-any cline-yes">21x</span>
178
190
  <span class="cline-any cline-neutral">&nbsp;</span>
179
191
  <span class="cline-any cline-neutral">&nbsp;</span>
180
- <span class="cline-any cline-yes">16x</span>
192
+ <span class="cline-any cline-yes">24x</span>
181
193
  <span class="cline-any cline-neutral">&nbsp;</span>
182
194
  <span class="cline-any cline-neutral">&nbsp;</span>
183
195
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -191,8 +203,11 @@
191
203
  <span class="cline-any cline-neutral">&nbsp;</span>
192
204
  <span class="cline-any cline-neutral">&nbsp;</span>
193
205
  <span class="cline-any cline-neutral">&nbsp;</span>
194
- <span class="cline-any cline-yes">3x</span>
195
- <span class="cline-any cline-yes">2x</span>
206
+ <span class="cline-any cline-yes">8x</span>
207
+ <span class="cline-any cline-yes">6x</span>
208
+ <span class="cline-any cline-neutral">&nbsp;</span>
209
+ <span class="cline-any cline-neutral">&nbsp;</span>
210
+ <span class="cline-any cline-neutral">&nbsp;</span>
196
211
  <span class="cline-any cline-neutral">&nbsp;</span>
197
212
  <span class="cline-any cline-neutral">&nbsp;</span>
198
213
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -204,18 +219,20 @@
204
219
  <span class="cline-any cline-neutral">&nbsp;</span>
205
220
  <span class="cline-any cline-neutral">&nbsp;</span>
206
221
  <span class="cline-any cline-neutral">&nbsp;</span>
222
+ <span class="cline-any cline-yes">443x</span>
207
223
  <span class="cline-any cline-neutral">&nbsp;</span>
208
224
  <span class="cline-any cline-neutral">&nbsp;</span>
225
+ <span class="cline-any cline-yes">443x</span>
226
+ <span class="cline-any cline-yes">19x</span>
209
227
  <span class="cline-any cline-neutral">&nbsp;</span>
210
- <span class="cline-any cline-yes">430x</span>
211
228
  <span class="cline-any cline-neutral">&nbsp;</span>
212
229
  <span class="cline-any cline-neutral">&nbsp;</span>
213
- <span class="cline-any cline-yes">430x</span>
214
- <span class="cline-any cline-yes">14x</span>
215
- <span class="cline-any cline-yes">14x</span>
216
- <span class="cline-any cline-yes">14x</span>
217
- <span class="cline-any cline-yes">14x</span>
218
- <span class="cline-any cline-yes">14x</span>
230
+ <span class="cline-any cline-yes">19x</span>
231
+ <span class="cline-any cline-neutral">&nbsp;</span>
232
+ <span class="cline-any cline-yes">19x</span>
233
+ <span class="cline-any cline-yes">19x</span>
234
+ <span class="cline-any cline-yes">19x</span>
235
+ <span class="cline-any cline-neutral">&nbsp;</span>
219
236
  <span class="cline-any cline-yes">1x</span>
220
237
  <span class="cline-any cline-yes">1x</span>
221
238
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -225,7 +242,7 @@
225
242
  <span class="cline-any cline-neutral">&nbsp;</span>
226
243
  <span class="cline-any cline-neutral">&nbsp;</span>
227
244
  <span class="cline-any cline-neutral">&nbsp;</span>
228
- <span class="cline-any cline-yes">1x</span>
245
+ <span class="cline-any cline-yes">3x</span>
229
246
  <span class="cline-any cline-neutral">&nbsp;</span>
230
247
  <span class="cline-any cline-neutral">&nbsp;</span>
231
248
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -233,6 +250,13 @@
233
250
  <span class="cline-any cline-neutral">&nbsp;</span>
234
251
  <span class="cline-any cline-neutral">&nbsp;</span>
235
252
  <span class="cline-any cline-neutral">&nbsp;</span>
253
+ <span class="cline-any cline-yes">1x</span>
254
+ <span class="cline-any cline-neutral">&nbsp;</span>
255
+ <span class="cline-any cline-neutral">&nbsp;</span>
256
+ <span class="cline-any cline-neutral">&nbsp;</span>
257
+ <span class="cline-any cline-neutral">&nbsp;</span>
258
+ <span class="cline-any cline-neutral">&nbsp;</span>
259
+ <span class="cline-any cline-neutral">&nbsp;</span>
236
260
  <span class="cline-any cline-neutral">&nbsp;</span>
237
261
  <span class="cline-any cline-neutral">&nbsp;</span>
238
262
  <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import {
@@ -298,25 +322,37 @@ export class EventBus&lt;TContext&gt; {
298
322
  | undefined;
299
323
  if (listeners) {
300
324
  for (const listener of listeners) {
301
- Promise.resolve().then(() =&gt; {
302
- try {
303
- const result = listener(data);
304
- if (result instanceof Promise) {
305
- result.catch((error) =&gt; {
306
- console.error(
307
- `Error in event listener for ${String(event)}:`,
308
- error
309
- );
310
- });
325
+ // We use Promise.resolve().then() to schedule the listener on the microtask queue,
326
+ // ensuring the emit method remains non-blocking.
327
+ // The final .catch() ensures that any errors in the promise infrastructure itself are logged.
328
+ Promise.resolve()
329
+ .then(() =&gt; {
330
+ try {
331
+ const result = listener(data);
332
+ if (result instanceof Promise) {
333
+ // Handle async listener rejections
334
+ result.catch((error) =&gt; {
335
+ console.error(
336
+ `Error in event listener for ${String(event)}:`,
337
+ error
338
+ );
339
+ });
340
+ }
341
+ } catch (error) {
342
+ // Handle sync listener errors
343
+ console.error(
344
+ `Error in event listener for ${String(event)}:`,
345
+ error
346
+ );
311
347
  }
312
- } catch (error) {
313
- // Prevent listener errors from bubbling up
348
+ })
349
+ .catch((error) =&gt; {
350
+ // detailed handling for the promise chain itself
314
351
  console.error(
315
- `Error in event listener for ${String(event)}:`,
352
+ `Unexpected error in event bus execution for ${String(event)}:`,
316
353
  error
317
354
  );
318
- }
319
- });
355
+ });
320
356
  }
321
357
  }
322
358
  }
@@ -328,7 +364,7 @@ export class EventBus&lt;TContext&gt; {
328
364
  <div class='footer quiet pad2 space-top1 center small'>
329
365
  Code coverage generated by
330
366
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
331
- at 2026-01-21T00:01:01.006Z
367
+ at 2026-01-21T01:16:02.348Z
332
368
  </div>
333
369
  <script src="../prettify.js"></script>
334
370
  <script>
@@ -115,7 +115,7 @@ export class TaskGraphValidationError extends Error {
115
115
  <div class='footer quiet pad2 space-top1 center small'>
116
116
  Code coverage generated by
117
117
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
118
- at 2026-01-21T00:01:01.006Z
118
+ at 2026-01-21T01:16:02.348Z
119
119
  </div>
120
120
  <script src="../prettify.js"></script>
121
121
  <script>
@@ -25,7 +25,7 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>56/56</span>
28
+ <span class='fraction'>59/59</span>
29
29
  </div>
30
30
 
31
31
 
@@ -39,14 +39,14 @@
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>5/5</span>
42
+ <span class='fraction'>8/8</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>55/55</span>
49
+ <span class='fraction'>58/58</span>
50
50
  </div>
51
51
 
52
52
 
@@ -231,7 +231,32 @@
231
231
  <a name='L166'></a><a href='#L166'>166</a>
232
232
  <a name='L167'></a><a href='#L167'>167</a>
233
233
  <a name='L168'></a><a href='#L168'>168</a>
234
- <a name='L169'></a><a href='#L169'>169</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
234
+ <a name='L169'></a><a href='#L169'>169</a>
235
+ <a name='L170'></a><a href='#L170'>170</a>
236
+ <a name='L171'></a><a href='#L171'>171</a>
237
+ <a name='L172'></a><a href='#L172'>172</a>
238
+ <a name='L173'></a><a href='#L173'>173</a>
239
+ <a name='L174'></a><a href='#L174'>174</a>
240
+ <a name='L175'></a><a href='#L175'>175</a>
241
+ <a name='L176'></a><a href='#L176'>176</a>
242
+ <a name='L177'></a><a href='#L177'>177</a>
243
+ <a name='L178'></a><a href='#L178'>178</a>
244
+ <a name='L179'></a><a href='#L179'>179</a>
245
+ <a name='L180'></a><a href='#L180'>180</a>
246
+ <a name='L181'></a><a href='#L181'>181</a>
247
+ <a name='L182'></a><a href='#L182'>182</a>
248
+ <a name='L183'></a><a href='#L183'>183</a>
249
+ <a name='L184'></a><a href='#L184'>184</a>
250
+ <a name='L185'></a><a href='#L185'>185</a>
251
+ <a name='L186'></a><a href='#L186'>186</a>
252
+ <a name='L187'></a><a href='#L187'>187</a>
253
+ <a name='L188'></a><a href='#L188'>188</a>
254
+ <a name='L189'></a><a href='#L189'>189</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
255
+ <span class="cline-any cline-neutral">&nbsp;</span>
256
+ <span class="cline-any cline-neutral">&nbsp;</span>
257
+ <span class="cline-any cline-neutral">&nbsp;</span>
258
+ <span class="cline-any cline-neutral">&nbsp;</span>
259
+ <span class="cline-any cline-neutral">&nbsp;</span>
235
260
  <span class="cline-any cline-neutral">&nbsp;</span>
236
261
  <span class="cline-any cline-neutral">&nbsp;</span>
237
262
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -252,39 +277,69 @@
252
277
  <span class="cline-any cline-neutral">&nbsp;</span>
253
278
  <span class="cline-any cline-neutral">&nbsp;</span>
254
279
  <span class="cline-any cline-yes">67x</span>
280
+ <span class="cline-any cline-neutral">&nbsp;</span>
281
+ <span class="cline-any cline-neutral">&nbsp;</span>
255
282
  <span class="cline-any cline-yes">67x</span>
256
- <span class="cline-any cline-yes">20178x</span>
257
- <span class="cline-any cline-yes">3x</span>
258
283
  <span class="cline-any cline-neutral">&nbsp;</span>
259
284
  <span class="cline-any cline-neutral">&nbsp;</span>
260
285
  <span class="cline-any cline-neutral">&nbsp;</span>
286
+ <span class="cline-any cline-yes">67x</span>
287
+ <span class="cline-any cline-yes">9x</span>
261
288
  <span class="cline-any cline-neutral">&nbsp;</span>
262
289
  <span class="cline-any cline-neutral">&nbsp;</span>
263
- <span class="cline-any cline-yes">20175x</span>
290
+ <span class="cline-any cline-yes">67x</span>
291
+ <span class="cline-any cline-yes">61x</span>
292
+ <span class="cline-any cline-neutral">&nbsp;</span>
293
+ <span class="cline-any cline-neutral">&nbsp;</span>
294
+ <span class="cline-any cline-yes">67x</span>
295
+ <span class="cline-any cline-neutral">&nbsp;</span>
296
+ <span class="cline-any cline-neutral">&nbsp;</span>
297
+ <span class="cline-any cline-neutral">&nbsp;</span>
298
+ <span class="cline-any cline-neutral">&nbsp;</span>
299
+ <span class="cline-any cline-neutral">&nbsp;</span>
300
+ <span class="cline-any cline-neutral">&nbsp;</span>
301
+ <span class="cline-any cline-neutral">&nbsp;</span>
302
+ <span class="cline-any cline-neutral">&nbsp;</span>
303
+ <span class="cline-any cline-neutral">&nbsp;</span>
304
+ <span class="cline-any cline-neutral">&nbsp;</span>
305
+ <span class="cline-any cline-neutral">&nbsp;</span>
306
+ <span class="cline-any cline-yes">11x</span>
307
+ <span class="cline-any cline-yes">10x</span>
308
+ <span class="cline-any cline-neutral">&nbsp;</span>
264
309
  <span class="cline-any cline-neutral">&nbsp;</span>
265
310
  <span class="cline-any cline-neutral">&nbsp;</span>
266
311
  <span class="cline-any cline-neutral">&nbsp;</span>
267
312
  <span class="cline-any cline-neutral">&nbsp;</span>
313
+ <span class="cline-any cline-neutral">&nbsp;</span>
314
+ <span class="cline-any cline-yes">67x</span>
268
315
  <span class="cline-any cline-yes">67x</span>
269
316
  <span class="cline-any cline-yes">20178x</span>
270
- <span class="cline-any cline-yes">20172x</span>
271
- <span class="cline-any cline-yes">7x</span>
317
+ <span class="cline-any cline-yes">3x</span>
318
+ <span class="cline-any cline-neutral">&nbsp;</span>
319
+ <span class="cline-any cline-neutral">&nbsp;</span>
272
320
  <span class="cline-any cline-neutral">&nbsp;</span>
273
321
  <span class="cline-any cline-neutral">&nbsp;</span>
274
322
  <span class="cline-any cline-neutral">&nbsp;</span>
323
+ <span class="cline-any cline-yes">20175x</span>
275
324
  <span class="cline-any cline-neutral">&nbsp;</span>
276
325
  <span class="cline-any cline-neutral">&nbsp;</span>
326
+ <span class="cline-any cline-yes">67x</span>
277
327
  <span class="cline-any cline-neutral">&nbsp;</span>
278
328
  <span class="cline-any cline-neutral">&nbsp;</span>
279
329
  <span class="cline-any cline-neutral">&nbsp;</span>
280
330
  <span class="cline-any cline-neutral">&nbsp;</span>
281
331
  <span class="cline-any cline-neutral">&nbsp;</span>
282
- <span class="cline-any cline-yes">67x</span>
283
- <span class="cline-any cline-yes">9x</span>
284
332
  <span class="cline-any cline-neutral">&nbsp;</span>
285
333
  <span class="cline-any cline-neutral">&nbsp;</span>
286
334
  <span class="cline-any cline-yes">67x</span>
287
- <span class="cline-any cline-yes">6x</span>
335
+ <span class="cline-any cline-yes">20178x</span>
336
+ <span class="cline-any cline-yes">20172x</span>
337
+ <span class="cline-any cline-yes">7x</span>
338
+ <span class="cline-any cline-neutral">&nbsp;</span>
339
+ <span class="cline-any cline-neutral">&nbsp;</span>
340
+ <span class="cline-any cline-neutral">&nbsp;</span>
341
+ <span class="cline-any cline-neutral">&nbsp;</span>
342
+ <span class="cline-any cline-neutral">&nbsp;</span>
288
343
  <span class="cline-any cline-neutral">&nbsp;</span>
289
344
  <span class="cline-any cline-neutral">&nbsp;</span>
290
345
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -324,21 +379,6 @@
324
379
  <span class="cline-any cline-neutral">&nbsp;</span>
325
380
  <span class="cline-any cline-neutral">&nbsp;</span>
326
381
  <span class="cline-any cline-neutral">&nbsp;</span>
327
- <span class="cline-any cline-yes">61x</span>
328
- <span class="cline-any cline-neutral">&nbsp;</span>
329
- <span class="cline-any cline-neutral">&nbsp;</span>
330
- <span class="cline-any cline-neutral">&nbsp;</span>
331
- <span class="cline-any cline-neutral">&nbsp;</span>
332
- <span class="cline-any cline-neutral">&nbsp;</span>
333
- <span class="cline-any cline-neutral">&nbsp;</span>
334
- <span class="cline-any cline-neutral">&nbsp;</span>
335
- <span class="cline-any cline-neutral">&nbsp;</span>
336
- <span class="cline-any cline-neutral">&nbsp;</span>
337
- <span class="cline-any cline-neutral">&nbsp;</span>
338
- <span class="cline-any cline-neutral">&nbsp;</span>
339
- <span class="cline-any cline-yes">9x</span>
340
- <span class="cline-any cline-yes">9x</span>
341
- <span class="cline-any cline-neutral">&nbsp;</span>
342
382
  <span class="cline-any cline-neutral">&nbsp;</span>
343
383
  <span class="cline-any cline-neutral">&nbsp;</span>
344
384
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -403,6 +443,11 @@
403
443
  import { ValidationResult } from "./contracts/ValidationResult.js";
404
444
  import { ValidationError } from "./contracts/ValidationError.js";
405
445
  import { TaskGraph } from "./TaskGraph.js";
446
+ import {
447
+ ERROR_CYCLE,
448
+ ERROR_DUPLICATE_TASK,
449
+ ERROR_MISSING_DEPENDENCY,
450
+ } from "./contracts/ErrorTypes.js";
406
451
  &nbsp;
407
452
  export class TaskGraphValidator implements ITaskGraphValidator {
408
453
  /**
@@ -419,11 +464,46 @@ export class TaskGraphValidator implements ITaskGraphValidator {
419
464
  const errors: ValidationError[] = [];
420
465
  &nbsp;
421
466
  // 1. Check for duplicate tasks
467
+ const taskIds = this.checkDuplicateTasks(taskGraph, errors);
468
+ &nbsp;
469
+ // 2. Check for missing dependencies
470
+ this.checkMissingDependencies(taskGraph, taskIds, errors);
471
+ &nbsp;
472
+ // 3. Check for cycles
473
+ // Only run cycle detection if there are no missing dependencies, otherwise we might chase non-existent nodes.
474
+ const hasMissingDependencies = errors.some(
475
+ (e) =&gt; e.type === ERROR_MISSING_DEPENDENCY
476
+ );
477
+ &nbsp;
478
+ if (!hasMissingDependencies) {
479
+ this.checkCycles(taskGraph, errors);
480
+ }
481
+ &nbsp;
482
+ return {
483
+ isValid: errors.length === 0,
484
+ errors,
485
+ };
486
+ }
487
+ &nbsp;
488
+ /**
489
+ * Creates a human-readable error message from a validation result.
490
+ * @param result The validation result containing errors.
491
+ * @returns A formatted error string.
492
+ */
493
+ createErrorMessage(result: ValidationResult): string {
494
+ const errorDetails = result.errors.map((e) =&gt; e.message);
495
+ return `Task graph validation failed: ${errorDetails.join("; ")}`;
496
+ }
497
+ &nbsp;
498
+ private checkDuplicateTasks(
499
+ taskGraph: TaskGraph,
500
+ errors: ValidationError[]
501
+ ): Set&lt;string&gt; {
422
502
  const taskIds = new Set&lt;string&gt;();
423
503
  for (const task of taskGraph.tasks) {
424
504
  if (taskIds.has(task.id)) {
425
505
  errors.push({
426
- type: "duplicate_task",
506
+ type: ERROR_DUPLICATE_TASK,
427
507
  message: `Duplicate task detected with ID: ${task.id}`,
428
508
  details: { taskId: task.id },
429
509
  });
@@ -431,33 +511,28 @@ export class TaskGraphValidator implements ITaskGraphValidator {
431
511
  taskIds.add(task.id);
432
512
  }
433
513
  }
514
+ return taskIds;
515
+ }
434
516
  &nbsp;
435
- // 2. Check for missing dependencies
517
+ private checkMissingDependencies(
518
+ taskGraph: TaskGraph,
519
+ taskIds: Set&lt;string&gt;,
520
+ errors: ValidationError[]
521
+ ): void {
436
522
  for (const task of taskGraph.tasks) {
437
523
  for (const dependenceId of task.dependencies) {
438
524
  if (!taskIds.has(dependenceId)) {
439
525
  errors.push({
440
- type: "missing_dependency",
526
+ type: ERROR_MISSING_DEPENDENCY,
441
527
  message: `Task '${task.id}' depends on missing task '${dependenceId}'`,
442
528
  details: { taskId: task.id, missingDependencyId: dependenceId },
443
529
  });
444
530
  }
445
531
  }
446
532
  }
533
+ }
447
534
  &nbsp;
448
- // 3. Check for cycles
449
- // Only run cycle detection if there are no missing dependencies, otherwise we might chase non-existent nodes.
450
- const hasMissingDependencies = errors.some(
451
- (e) =&gt; e.type === "missing_dependency"
452
- );
453
- &nbsp;
454
- if (hasMissingDependencies) {
455
- return {
456
- isValid: errors.length === 0,
457
- errors,
458
- };
459
- }
460
- &nbsp;
535
+ private checkCycles(taskGraph: TaskGraph, errors: ValidationError[]): void {
461
536
  // Build adjacency list
462
537
  const adjacencyList = new Map&lt;string, string[]&gt;();
463
538
  for (const task of taskGraph.tasks) {
@@ -483,7 +558,7 @@ export class TaskGraphValidator implements ITaskGraphValidator {
483
558
  const cyclePath = path.slice(cycleStartIndex);
484
559
  &nbsp;
485
560
  errors.push({
486
- type: "cycle",
561
+ type: ERROR_CYCLE,
487
562
  message: `Cycle detected: ${cyclePath.join(" -&gt; ")}`,
488
563
  details: { cyclePath },
489
564
  });
@@ -491,21 +566,6 @@ export class TaskGraphValidator implements ITaskGraphValidator {
491
566
  break;
492
567
  }
493
568
  }
494
- &nbsp;
495
- return {
496
- isValid: errors.length === 0,
497
- errors,
498
- };
499
- }
500
- &nbsp;
501
- /**
502
- * Creates a human-readable error message from a validation result.
503
- * @param result The validation result containing errors.
504
- * @returns A formatted error string.
505
- */
506
- createErrorMessage(result: ValidationResult): string {
507
- const errorDetails = result.errors.map((e) =&gt; e.message);
508
- return `Task graph validation failed: ${errorDetails.join("; ")}`;
509
569
  }
510
570
  &nbsp;
511
571
  private detectCycle(
@@ -574,7 +634,7 @@ export class TaskGraphValidator implements ITaskGraphValidator {
574
634
  <div class='footer quiet pad2 space-top1 center small'>
575
635
  Code coverage generated by
576
636
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
577
- at 2026-01-21T00:01:01.006Z
637
+ at 2026-01-21T01:16:02.348Z
578
638
  </div>
579
639
  <script src="../prettify.js"></script>
580
640
  <script>