@calmo/task-runner 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +30 -0
  3. package/coverage/coverage-final.json +7 -6
  4. package/coverage/index.html +12 -12
  5. package/coverage/lcov-report/index.html +12 -12
  6. package/coverage/lcov-report/src/EventBus.ts.html +4 -4
  7. package/coverage/lcov-report/src/TaskGraphValidator.ts.html +33 -33
  8. package/coverage/lcov-report/src/TaskRunner.ts.html +30 -18
  9. package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +1 -1
  10. package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +17 -2
  11. package/coverage/lcov-report/src/TaskStateManager.ts.html +35 -35
  12. package/coverage/lcov-report/src/WorkflowExecutor.ts.html +94 -34
  13. package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +1 -1
  14. package/coverage/lcov-report/src/contracts/index.html +1 -1
  15. package/coverage/lcov-report/src/index.html +7 -7
  16. package/coverage/lcov-report/src/strategies/DryRunExecutionStrategy.ts.html +1 -1
  17. package/coverage/lcov-report/src/strategies/RetryingExecutionStrategy.ts.html +355 -0
  18. package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +3 -3
  19. package/coverage/lcov-report/src/strategies/index.html +20 -5
  20. package/coverage/lcov.info +328 -240
  21. package/coverage/src/EventBus.ts.html +4 -4
  22. package/coverage/src/TaskGraphValidator.ts.html +33 -33
  23. package/coverage/src/TaskRunner.ts.html +30 -18
  24. package/coverage/src/TaskRunnerBuilder.ts.html +1 -1
  25. package/coverage/src/TaskRunnerExecutionConfig.ts.html +17 -2
  26. package/coverage/src/TaskStateManager.ts.html +35 -35
  27. package/coverage/src/WorkflowExecutor.ts.html +94 -34
  28. package/coverage/src/contracts/RunnerEvents.ts.html +1 -1
  29. package/coverage/src/contracts/index.html +1 -1
  30. package/coverage/src/index.html +7 -7
  31. package/coverage/src/strategies/DryRunExecutionStrategy.ts.html +1 -1
  32. package/coverage/src/strategies/RetryingExecutionStrategy.ts.html +355 -0
  33. package/coverage/src/strategies/StandardExecutionStrategy.ts.html +3 -3
  34. package/coverage/src/strategies/index.html +20 -5
  35. package/dist/TaskRunner.js +3 -2
  36. package/dist/TaskRunner.js.map +1 -1
  37. package/dist/TaskRunnerExecutionConfig.d.ts +5 -0
  38. package/dist/TaskStep.d.ts +3 -0
  39. package/dist/WorkflowExecutor.d.ts +4 -1
  40. package/dist/WorkflowExecutor.js +19 -4
  41. package/dist/WorkflowExecutor.js.map +1 -1
  42. package/dist/contracts/TaskRetryConfig.d.ts +8 -0
  43. package/dist/contracts/TaskRetryConfig.js +2 -0
  44. package/dist/contracts/TaskRetryConfig.js.map +1 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.js +1 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/strategies/RetryingExecutionStrategy.d.ts +12 -0
  49. package/dist/strategies/RetryingExecutionStrategy.js +74 -0
  50. package/dist/strategies/RetryingExecutionStrategy.js.map +1 -0
  51. package/openspec/changes/archive/2026-01-18-add-task-retry-policy/tasks.md +10 -0
  52. package/openspec/changes/archive/2026-01-18-add-workflow-preview/tasks.md +7 -0
  53. package/openspec/project.md +1 -0
  54. package/package.json +1 -1
  55. package/src/TaskRunner.ts +6 -2
  56. package/src/TaskRunnerExecutionConfig.ts +5 -0
  57. package/src/TaskStep.ts +3 -0
  58. package/src/WorkflowExecutor.ts +24 -4
  59. package/src/contracts/TaskRetryConfig.ts +8 -0
  60. package/src/index.ts +2 -0
  61. package/src/strategies/RetryingExecutionStrategy.ts +90 -0
  62. package/test-report.xml +122 -92
  63. package/openspec/changes/add-task-retry-policy/tasks.md +0 -10
  64. package/openspec/changes/add-workflow-preview/tasks.md +0 -7
  65. /package/openspec/changes/{add-task-retry-policy → archive/2026-01-18-add-task-retry-policy}/proposal.md +0 -0
  66. /package/openspec/changes/{add-workflow-preview → archive/2026-01-18-add-workflow-preview}/proposal.md +0 -0
@@ -25,14 +25,14 @@
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'>38/38</span>
28
+ <span class='fraction'>46/46</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
33
  <span class="strong">100% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>12/12</span>
35
+ <span class='fraction'>16/16</span>
36
36
  </div>
37
37
 
38
38
 
@@ -46,7 +46,7 @@
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'>38/38</span>
49
+ <span class='fraction'>46/46</span>
50
50
  </div>
51
51
 
52
52
 
@@ -183,7 +183,27 @@
183
183
  <a name='L118'></a><a href='#L118'>118</a>
184
184
  <a name='L119'></a><a href='#L119'>119</a>
185
185
  <a name='L120'></a><a href='#L120'>120</a>
186
- <a name='L121'></a><a href='#L121'>121</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
186
+ <a name='L121'></a><a href='#L121'>121</a>
187
+ <a name='L122'></a><a href='#L122'>122</a>
188
+ <a name='L123'></a><a href='#L123'>123</a>
189
+ <a name='L124'></a><a href='#L124'>124</a>
190
+ <a name='L125'></a><a href='#L125'>125</a>
191
+ <a name='L126'></a><a href='#L126'>126</a>
192
+ <a name='L127'></a><a href='#L127'>127</a>
193
+ <a name='L128'></a><a href='#L128'>128</a>
194
+ <a name='L129'></a><a href='#L129'>129</a>
195
+ <a name='L130'></a><a href='#L130'>130</a>
196
+ <a name='L131'></a><a href='#L131'>131</a>
197
+ <a name='L132'></a><a href='#L132'>132</a>
198
+ <a name='L133'></a><a href='#L133'>133</a>
199
+ <a name='L134'></a><a href='#L134'>134</a>
200
+ <a name='L135'></a><a href='#L135'>135</a>
201
+ <a name='L136'></a><a href='#L136'>136</a>
202
+ <a name='L137'></a><a href='#L137'>137</a>
203
+ <a name='L138'></a><a href='#L138'>138</a>
204
+ <a name='L139'></a><a href='#L139'>139</a>
205
+ <a name='L140'></a><a href='#L140'>140</a>
206
+ <a name='L141'></a><a href='#L141'>141</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
187
207
  <span class="cline-any cline-neutral">&nbsp;</span>
188
208
  <span class="cline-any cline-neutral">&nbsp;</span>
189
209
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -194,6 +214,7 @@
194
214
  <span class="cline-any cline-neutral">&nbsp;</span>
195
215
  <span class="cline-any cline-neutral">&nbsp;</span>
196
216
  <span class="cline-any cline-neutral">&nbsp;</span>
217
+ <span class="cline-any cline-yes">47x</span>
197
218
  <span class="cline-any cline-neutral">&nbsp;</span>
198
219
  <span class="cline-any cline-neutral">&nbsp;</span>
199
220
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -201,12 +222,13 @@
201
222
  <span class="cline-any cline-neutral">&nbsp;</span>
202
223
  <span class="cline-any cline-neutral">&nbsp;</span>
203
224
  <span class="cline-any cline-neutral">&nbsp;</span>
204
- <span class="cline-any cline-yes">43x</span>
205
- <span class="cline-any cline-yes">43x</span>
206
- <span class="cline-any cline-yes">43x</span>
207
- <span class="cline-any cline-yes">43x</span>
208
225
  <span class="cline-any cline-neutral">&nbsp;</span>
209
226
  <span class="cline-any cline-neutral">&nbsp;</span>
227
+ <span class="cline-any cline-yes">47x</span>
228
+ <span class="cline-any cline-yes">47x</span>
229
+ <span class="cline-any cline-yes">47x</span>
230
+ <span class="cline-any cline-yes">47x</span>
231
+ <span class="cline-any cline-yes">47x</span>
210
232
  <span class="cline-any cline-neutral">&nbsp;</span>
211
233
  <span class="cline-any cline-neutral">&nbsp;</span>
212
234
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -217,33 +239,35 @@
217
239
  <span class="cline-any cline-neutral">&nbsp;</span>
218
240
  <span class="cline-any cline-neutral">&nbsp;</span>
219
241
  <span class="cline-any cline-neutral">&nbsp;</span>
220
- <span class="cline-any cline-yes">43x</span>
221
- <span class="cline-any cline-yes">43x</span>
222
242
  <span class="cline-any cline-neutral">&nbsp;</span>
223
243
  <span class="cline-any cline-neutral">&nbsp;</span>
224
- <span class="cline-any cline-yes">43x</span>
244
+ <span class="cline-any cline-yes">47x</span>
245
+ <span class="cline-any cline-yes">47x</span>
246
+ <span class="cline-any cline-neutral">&nbsp;</span>
247
+ <span class="cline-any cline-neutral">&nbsp;</span>
248
+ <span class="cline-any cline-yes">47x</span>
225
249
  <span class="cline-any cline-yes">2x</span>
226
250
  <span class="cline-any cline-yes">2x</span>
227
251
  <span class="cline-any cline-yes">2x</span>
228
252
  <span class="cline-any cline-yes">2x</span>
229
253
  <span class="cline-any cline-neutral">&nbsp;</span>
230
254
  <span class="cline-any cline-neutral">&nbsp;</span>
231
- <span class="cline-any cline-yes">41x</span>
255
+ <span class="cline-any cline-yes">45x</span>
232
256
  <span class="cline-any cline-neutral">&nbsp;</span>
233
- <span class="cline-any cline-yes">41x</span>
257
+ <span class="cline-any cline-yes">45x</span>
234
258
  <span class="cline-any cline-neutral">&nbsp;</span>
235
259
  <span class="cline-any cline-yes">9x</span>
236
260
  <span class="cline-any cline-neutral">&nbsp;</span>
237
261
  <span class="cline-any cline-neutral">&nbsp;</span>
238
- <span class="cline-any cline-yes">41x</span>
262
+ <span class="cline-any cline-yes">45x</span>
239
263
  <span class="cline-any cline-yes">9x</span>
240
264
  <span class="cline-any cline-neutral">&nbsp;</span>
241
265
  <span class="cline-any cline-neutral">&nbsp;</span>
242
- <span class="cline-any cline-yes">41x</span>
266
+ <span class="cline-any cline-yes">45x</span>
243
267
  <span class="cline-any cline-neutral">&nbsp;</span>
244
- <span class="cline-any cline-yes">41x</span>
268
+ <span class="cline-any cline-yes">45x</span>
245
269
  <span class="cline-any cline-neutral">&nbsp;</span>
246
- <span class="cline-any cline-yes">41x</span>
270
+ <span class="cline-any cline-yes">45x</span>
247
271
  <span class="cline-any cline-neutral">&nbsp;</span>
248
272
  <span class="cline-any cline-neutral">&nbsp;</span>
249
273
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -266,13 +290,13 @@
266
290
  <span class="cline-any cline-neutral">&nbsp;</span>
267
291
  <span class="cline-any cline-neutral">&nbsp;</span>
268
292
  <span class="cline-any cline-neutral">&nbsp;</span>
269
- <span class="cline-any cline-yes">41x</span>
293
+ <span class="cline-any cline-yes">45x</span>
270
294
  <span class="cline-any cline-neutral">&nbsp;</span>
271
- <span class="cline-any cline-yes">41x</span>
272
- <span class="cline-any cline-yes">41x</span>
273
- <span class="cline-any cline-yes">41x</span>
295
+ <span class="cline-any cline-yes">45x</span>
296
+ <span class="cline-any cline-yes">45x</span>
297
+ <span class="cline-any cline-yes">45x</span>
274
298
  <span class="cline-any cline-neutral">&nbsp;</span>
275
- <span class="cline-any cline-yes">41x</span>
299
+ <span class="cline-any cline-yes">45x</span>
276
300
  <span class="cline-any cline-yes">9x</span>
277
301
  <span class="cline-any cline-neutral">&nbsp;</span>
278
302
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -285,21 +309,37 @@
285
309
  <span class="cline-any cline-neutral">&nbsp;</span>
286
310
  <span class="cline-any cline-neutral">&nbsp;</span>
287
311
  <span class="cline-any cline-neutral">&nbsp;</span>
288
- <span class="cline-any cline-yes">110x</span>
312
+ <span class="cline-any cline-yes">233x</span>
313
+ <span class="cline-any cline-neutral">&nbsp;</span>
314
+ <span class="cline-any cline-neutral">&nbsp;</span>
315
+ <span class="cline-any cline-yes">233x</span>
316
+ <span class="cline-any cline-yes">119x</span>
317
+ <span class="cline-any cline-neutral">&nbsp;</span>
318
+ <span class="cline-any cline-neutral">&nbsp;</span>
319
+ <span class="cline-any cline-neutral">&nbsp;</span>
320
+ <span class="cline-any cline-yes">233x</span>
321
+ <span class="cline-any cline-yes">130x</span>
322
+ <span class="cline-any cline-neutral">&nbsp;</span>
323
+ <span class="cline-any cline-neutral">&nbsp;</span>
324
+ <span class="cline-any cline-neutral">&nbsp;</span>
325
+ <span class="cline-any cline-yes">11x</span>
289
326
  <span class="cline-any cline-neutral">&nbsp;</span>
290
327
  <span class="cline-any cline-neutral">&nbsp;</span>
291
- <span class="cline-any cline-yes">110x</span>
292
- <span class="cline-any cline-yes">105x</span>
328
+ <span class="cline-any cline-yes">119x</span>
293
329
  <span class="cline-any cline-neutral">&nbsp;</span>
294
- <span class="cline-any cline-yes">105x</span>
330
+ <span class="cline-any cline-yes">119x</span>
295
331
  <span class="cline-any cline-neutral">&nbsp;</span>
296
- <span class="cline-any cline-yes">105x</span>
332
+ <span class="cline-any cline-yes">119x</span>
297
333
  <span class="cline-any cline-neutral">&nbsp;</span>
334
+ <span class="cline-any cline-yes">119x</span>
298
335
  <span class="cline-any cline-neutral">&nbsp;</span>
299
- <span class="cline-any cline-yes">105x</span>
300
336
  <span class="cline-any cline-neutral">&nbsp;</span>
337
+ <span class="cline-any cline-yes">119x</span>
301
338
  <span class="cline-any cline-neutral">&nbsp;</span>
302
- <span class="cline-any cline-yes">105x</span>
339
+ <span class="cline-any cline-yes">119x</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-yes">119x</span>
303
343
  <span class="cline-any cline-neutral">&nbsp;</span>
304
344
  <span class="cline-any cline-neutral">&nbsp;</span>
305
345
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -314,17 +354,21 @@ import { IExecutionStrategy } from "./strategies/IExecutionStrategy.js";
314
354
  * @template TContext The shape of the shared context object.
315
355
  */
316
356
  export class WorkflowExecutor&lt;TContext&gt; {
357
+ private readyQueue: TaskStep&lt;TContext&gt;[] = [];
358
+ &nbsp;
317
359
  /**
318
360
  * @param context The shared context object.
319
361
  * @param eventBus The event bus to emit events.
320
362
  * @param stateManager Manages execution state.
321
363
  * @param strategy Execution strategy.
364
+ * @param concurrency Maximum number of concurrent tasks.
322
365
  */
323
366
  constructor(
324
367
  private context: TContext,
325
368
  private eventBus: EventBus&lt;TContext&gt;,
326
369
  private stateManager: TaskStateManager&lt;TContext&gt;,
327
- private strategy: IExecutionStrategy&lt;TContext&gt;
370
+ private strategy: IExecutionStrategy&lt;TContext&gt;,
371
+ private concurrency?: number
328
372
  ) {}
329
373
  &nbsp;
330
374
  /**
@@ -405,10 +449,24 @@ export class WorkflowExecutor&lt;TContext&gt; {
405
449
  executingPromises: Set&lt;Promise&lt;void&gt;&gt;,
406
450
  signal?: AbortSignal
407
451
  ): void {
408
- const toRun = this.stateManager.processDependencies();
452
+ const newlyReady = this.stateManager.processDependencies();
453
+ &nbsp;
454
+ // Add newly ready tasks to the queue
455
+ for (const task of newlyReady) {
456
+ this.readyQueue.push(task);
457
+ }
458
+ &nbsp;
459
+ // Execute ready tasks while respecting concurrency limit
460
+ while (this.readyQueue.length &gt; 0) {
461
+ if (
462
+ this.concurrency !== undefined &amp;&amp;
463
+ executingPromises.size &gt;= this.concurrency
464
+ ) {
465
+ break;
466
+ }
467
+ &nbsp;
468
+ const step = this.readyQueue.shift()!;
409
469
  &nbsp;
410
- // Execute ready tasks
411
- for (const step of toRun) {
412
470
  this.stateManager.markRunning(step);
413
471
  &nbsp;
414
472
  const taskPromise = this.strategy.execute(step, this.context, signal)
@@ -417,6 +475,8 @@ export class WorkflowExecutor&lt;TContext&gt; {
417
475
  })
418
476
  .finally(() =&gt; {
419
477
  executingPromises.delete(taskPromise);
478
+ // When a task finishes, we try to run more
479
+ this.processLoop(executingPromises, signal);
420
480
  });
421
481
  &nbsp;
422
482
  executingPromises.add(taskPromise);
@@ -430,7 +490,7 @@ export class WorkflowExecutor&lt;TContext&gt; {
430
490
  <div class='footer quiet pad2 space-top1 center small'>
431
491
  Code coverage generated by
432
492
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
433
- at 2026-01-18T18:12:39.248Z
493
+ at 2026-01-18T20:12:36.619Z
434
494
  </div>
435
495
  <script src="../prettify.js"></script>
436
496
  <script>
@@ -202,7 +202,7 @@ export type ListenerMap&lt;TContext&gt; = {
202
202
  <div class='footer quiet pad2 space-top1 center small'>
203
203
  Code coverage generated by
204
204
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
205
- at 2026-01-18T18:12:39.248Z
205
+ at 2026-01-18T20:12:36.619Z
206
206
  </div>
207
207
  <script src="../../prettify.js"></script>
208
208
  <script>
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-01-18T18:12:39.248Z
104
+ at 2026-01-18T20:12:36.619Z
105
105
  </div>
106
106
  <script src="../../prettify.js"></script>
107
107
  <script>
@@ -25,14 +25,14 @@
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'>219/219</span>
28
+ <span class='fraction'>227/227</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
33
  <span class="strong">100% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>74/74</span>
35
+ <span class='fraction'>78/78</span>
36
36
  </div>
37
37
 
38
38
 
@@ -46,7 +46,7 @@
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'>215/215</span>
49
+ <span class='fraction'>223/223</span>
50
50
  </div>
51
51
 
52
52
 
@@ -174,13 +174,13 @@
174
174
  <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
175
175
  </td>
176
176
  <td data-value="100" class="pct high">100%</td>
177
- <td data-value="38" class="abs high">38/38</td>
177
+ <td data-value="46" class="abs high">46/46</td>
178
178
  <td data-value="100" class="pct high">100%</td>
179
- <td data-value="12" class="abs high">12/12</td>
179
+ <td data-value="16" class="abs high">16/16</td>
180
180
  <td data-value="100" class="pct high">100%</td>
181
181
  <td data-value="6" class="abs high">6/6</td>
182
182
  <td data-value="100" class="pct high">100%</td>
183
- <td data-value="38" class="abs high">38/38</td>
183
+ <td data-value="46" class="abs high">46/46</td>
184
184
  </tr>
185
185
 
186
186
  </tbody>
@@ -191,7 +191,7 @@
191
191
  <div class='footer quiet pad2 space-top1 center small'>
192
192
  Code coverage generated by
193
193
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
194
- at 2026-01-18T18:12:39.248Z
194
+ at 2026-01-18T20:12:36.619Z
195
195
  </div>
196
196
  <script src="../prettify.js"></script>
197
197
  <script>
@@ -163,7 +163,7 @@ export class DryRunExecutionStrategy&lt;TContext&gt;
163
163
  <div class='footer quiet pad2 space-top1 center small'>
164
164
  Code coverage generated by
165
165
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
166
- at 2026-01-18T18:12:39.248Z
166
+ at 2026-01-18T20:12:36.619Z
167
167
  </div>
168
168
  <script src="../../prettify.js"></script>
169
169
  <script>