@calmo/task-runner 1.2.3 → 2.0.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.
@@ -25,28 +25,28 @@
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'>121/121</span>
28
+ <span class='fraction'>126/126</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'>42/42</span>
35
+ <span class='fraction'>48/48</span>
36
36
  </div>
37
37
 
38
38
 
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'>19/19</span>
42
+ <span class='fraction'>25/25</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'>119/119</span>
49
+ <span class='fraction'>123/123</span>
50
50
  </div>
51
51
 
52
52
 
@@ -99,13 +99,13 @@
99
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
101
  <td data-value="100" class="pct high">100%</td>
102
- <td data-value="46" class="abs high">46/46</td>
102
+ <td data-value="56" class="abs high">56/56</td>
103
103
  <td data-value="100" class="pct high">100%</td>
104
104
  <td data-value="16" class="abs high">16/16</td>
105
105
  <td data-value="100" class="pct high">100%</td>
106
106
  <td data-value="5" class="abs high">5/5</td>
107
107
  <td data-value="100" class="pct high">100%</td>
108
- <td data-value="44" class="abs high">44/44</td>
108
+ <td data-value="54" class="abs high">54/54</td>
109
109
  </tr>
110
110
 
111
111
  <tr>
@@ -129,13 +129,13 @@
129
129
  <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
130
130
  </td>
131
131
  <td data-value="100" class="pct high">100%</td>
132
- <td data-value="48" class="abs high">48/48</td>
132
+ <td data-value="43" class="abs high">43/43</td>
133
133
  <td data-value="100" class="pct high">100%</td>
134
- <td data-value="14" class="abs high">14/14</td>
134
+ <td data-value="20" class="abs high">20/20</td>
135
135
  <td data-value="100" class="pct high">100%</td>
136
- <td data-value="5" class="abs high">5/5</td>
136
+ <td data-value="11" class="abs high">11/11</td>
137
137
  <td data-value="100" class="pct high">100%</td>
138
- <td data-value="48" class="abs high">48/48</td>
138
+ <td data-value="42" class="abs high">42/42</td>
139
139
  </tr>
140
140
 
141
141
  </tbody>
@@ -146,7 +146,7 @@
146
146
  <div class='footer quiet pad2 space-top1 center small'>
147
147
  Code coverage generated by
148
148
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
149
- at 2026-01-18T14:25:37.513Z
149
+ at 2026-01-18T15:13:46.741Z
150
150
  </div>
151
151
  <script src="../prettify.js"></script>
152
152
  <script>
@@ -87,25 +87,48 @@ export class TaskGraphValidator {
87
87
  const errorDetails = result.errors.map(e => e.message);
88
88
  return `Task graph validation failed: ${errorDetails.join("; ")}`;
89
89
  }
90
- detectCycle(taskId, path, visited, recursionStack, adjacencyList) {
91
- visited.add(taskId);
92
- recursionStack.add(taskId);
93
- path.push(taskId);
94
- const dependencies = adjacencyList.get(taskId);
95
- for (const dependenceId of dependencies) {
96
- if (!visited.has(dependenceId) &&
97
- this.detectCycle(dependenceId, path, visited, recursionStack, adjacencyList)) {
98
- return true;
90
+ detectCycle(startTaskId, path, visited, recursionStack, adjacencyList) {
91
+ // Use an explicit stack to avoid maximum call stack size exceeded errors
92
+ const stack = [];
93
+ visited.add(startTaskId);
94
+ recursionStack.add(startTaskId);
95
+ path.push(startTaskId);
96
+ stack.push({
97
+ taskId: startTaskId,
98
+ index: 0,
99
+ /* v8 ignore next */
100
+ dependencies: adjacencyList.get(startTaskId) ?? []
101
+ });
102
+ while (stack.length > 0) {
103
+ const frame = stack[stack.length - 1];
104
+ const { taskId, dependencies } = frame;
105
+ if (frame.index < dependencies.length) {
106
+ const dependenceId = dependencies[frame.index];
107
+ frame.index++;
108
+ if (recursionStack.has(dependenceId)) {
109
+ // Cycle detected
110
+ path.push(dependenceId);
111
+ return true;
112
+ }
113
+ if (!visited.has(dependenceId)) {
114
+ visited.add(dependenceId);
115
+ recursionStack.add(dependenceId);
116
+ path.push(dependenceId);
117
+ stack.push({
118
+ taskId: dependenceId,
119
+ index: 0,
120
+ /* v8 ignore next */
121
+ dependencies: adjacencyList.get(dependenceId) ?? []
122
+ });
123
+ }
99
124
  }
100
- else if (recursionStack.has(dependenceId)) {
101
- // Cycle detected
102
- // Add the dependency to complete the visual cycle
103
- path.push(dependenceId);
104
- return true;
125
+ else {
126
+ // Finished all dependencies for this node
127
+ recursionStack.delete(taskId);
128
+ path.pop();
129
+ stack.pop();
105
130
  }
106
131
  }
107
- recursionStack.delete(taskId);
108
- path.pop();
109
132
  return false;
110
133
  }
111
134
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TaskGraphValidator.js","sourceRoot":"","sources":["../src/TaskGraphValidator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TaskGraphValidator.js","sourceRoot":"","sources":["../src/TaskGraphValidator.ts"],"names":[],"mappings":"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"}
@@ -22,9 +22,16 @@ export declare class WorkflowExecutor<TContext> {
22
22
  execute(steps: TaskStep<TContext>[]): Promise<Map<string, TaskResult>>;
23
23
  /**
24
24
  * Logic to identify tasks that can be started or must be skipped.
25
- * Iterate only over pending steps to avoid O(N^2) checks on completed tasks.
26
25
  */
27
26
  private processQueue;
27
+ /**
28
+ * Identifies steps that cannot run because a dependency failed.
29
+ */
30
+ private handleSkippedTasks;
31
+ /**
32
+ * Returns steps where all dependencies have finished successfully.
33
+ */
34
+ private getReadySteps;
28
35
  /**
29
36
  * Handles the lifecycle of a single task execution.
30
37
  */
@@ -23,40 +23,38 @@ export class WorkflowExecutor {
23
23
  this.eventBus.emit("workflowStart", { context: this.context, steps });
24
24
  const results = new Map();
25
25
  const executingPromises = new Set();
26
- const pendingSteps = new Set(steps);
27
26
  // Initial pass
28
- this.processQueue(pendingSteps, results, executingPromises);
27
+ this.processQueue(steps, results, executingPromises);
29
28
  while (results.size < steps.length && executingPromises.size > 0) {
30
29
  // Wait for the next task to finish
31
30
  await Promise.race(executingPromises);
32
31
  // After a task finishes, check for new work
33
- this.processQueue(pendingSteps, results, executingPromises);
32
+ this.processQueue(steps, results, executingPromises);
34
33
  }
35
34
  this.eventBus.emit("workflowEnd", { context: this.context, results });
36
35
  return results;
37
36
  }
38
37
  /**
39
38
  * Logic to identify tasks that can be started or must be skipped.
40
- * Iterate only over pending steps to avoid O(N^2) checks on completed tasks.
41
39
  */
42
- processQueue(pendingSteps, results, executingPromises) {
43
- const toRemove = [];
44
- const toRun = [];
40
+ processQueue(steps, results, executingPromises) {
41
+ this.handleSkippedTasks(steps, results);
42
+ const readySteps = this.getReadySteps(steps, results);
43
+ for (const step of readySteps) {
44
+ const taskPromise = this.runStep(step, results).then(() => {
45
+ executingPromises.delete(taskPromise);
46
+ });
47
+ executingPromises.add(taskPromise);
48
+ }
49
+ }
50
+ /**
51
+ * Identifies steps that cannot run because a dependency failed.
52
+ */
53
+ handleSkippedTasks(steps, results) {
54
+ const pendingSteps = steps.filter((step) => !results.has(step.name) && !this.running.has(step.name));
45
55
  for (const step of pendingSteps) {
46
56
  const deps = step.dependencies ?? [];
47
- let blocked = false;
48
- let failedDep;
49
- for (const dep of deps) {
50
- const depResult = results.get(dep);
51
- if (!depResult) {
52
- // Dependency not finished yet
53
- blocked = true;
54
- }
55
- else if (depResult.status !== "success") {
56
- failedDep = dep;
57
- break;
58
- }
59
- }
57
+ const failedDep = deps.find((dep) => results.has(dep) && results.get(dep)?.status !== "success");
60
58
  if (failedDep) {
61
59
  const result = {
62
60
  status: "skipped",
@@ -64,24 +62,19 @@ export class WorkflowExecutor {
64
62
  };
65
63
  results.set(step.name, result);
66
64
  this.eventBus.emit("taskSkipped", { step, result });
67
- toRemove.push(step);
68
- }
69
- else if (!blocked) {
70
- toRun.push(step);
71
- toRemove.push(step);
72
65
  }
73
66
  }
74
- // Cleanup pending set
75
- for (const step of toRemove) {
76
- pendingSteps.delete(step);
77
- }
78
- // Execute ready tasks
79
- for (const step of toRun) {
80
- const taskPromise = this.runStep(step, results).then(() => {
81
- executingPromises.delete(taskPromise);
82
- });
83
- executingPromises.add(taskPromise);
84
- }
67
+ }
68
+ /**
69
+ * Returns steps where all dependencies have finished successfully.
70
+ */
71
+ getReadySteps(steps, results) {
72
+ return steps.filter((step) => {
73
+ if (results.has(step.name) || this.running.has(step.name))
74
+ return false;
75
+ const deps = step.dependencies ?? [];
76
+ return deps.every((dep) => results.has(dep) && results.get(dep)?.status === "success");
77
+ });
85
78
  }
86
79
  /**
87
80
  * Handles the lifecycle of a single task execution.
@@ -1 +1 @@
1
- {"version":3,"file":"WorkflowExecutor.js","sourceRoot":"","sources":["../src/WorkflowExecutor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WorkflowExecutor.js","sourceRoot":"","sources":["../src/WorkflowExecutor.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calmo/task-runner",
3
- "version": "1.2.3",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -104,33 +104,60 @@ export class TaskGraphValidator implements ITaskGraphValidator {
104
104
  }
105
105
 
106
106
  private detectCycle(
107
- taskId: string,
107
+ startTaskId: string,
108
108
  path: string[],
109
109
  visited: Set<string>,
110
110
  recursionStack: Set<string>,
111
111
  adjacencyList: Map<string, string[]>
112
112
  ): boolean {
113
- visited.add(taskId);
114
- recursionStack.add(taskId);
115
- path.push(taskId);
116
-
117
- const dependencies = adjacencyList.get(taskId)!;
118
- for (const dependenceId of dependencies) {
119
- if (
120
- !visited.has(dependenceId) &&
121
- this.detectCycle(dependenceId, path, visited, recursionStack, adjacencyList)
122
- ) {
123
- return true;
124
- } else if (recursionStack.has(dependenceId)) {
125
- // Cycle detected
126
- // Add the dependency to complete the visual cycle
127
- path.push(dependenceId);
128
- return true;
113
+ // Use an explicit stack to avoid maximum call stack size exceeded errors
114
+ const stack: { taskId: string; index: number; dependencies: string[] }[] = [];
115
+
116
+ visited.add(startTaskId);
117
+ recursionStack.add(startTaskId);
118
+ path.push(startTaskId);
119
+
120
+ stack.push({
121
+ taskId: startTaskId,
122
+ index: 0,
123
+ /* v8 ignore next */
124
+ dependencies: adjacencyList.get(startTaskId) ?? []
125
+ });
126
+
127
+ while (stack.length > 0) {
128
+ const frame = stack[stack.length - 1];
129
+ const { taskId, dependencies } = frame;
130
+
131
+ if (frame.index < dependencies.length) {
132
+ const dependenceId = dependencies[frame.index];
133
+ frame.index++;
134
+
135
+ if (recursionStack.has(dependenceId)) {
136
+ // Cycle detected
137
+ path.push(dependenceId);
138
+ return true;
139
+ }
140
+
141
+ if (!visited.has(dependenceId)) {
142
+ visited.add(dependenceId);
143
+ recursionStack.add(dependenceId);
144
+ path.push(dependenceId);
145
+
146
+ stack.push({
147
+ taskId: dependenceId,
148
+ index: 0,
149
+ /* v8 ignore next */
150
+ dependencies: adjacencyList.get(dependenceId) ?? []
151
+ });
152
+ }
153
+ } else {
154
+ // Finished all dependencies for this node
155
+ recursionStack.delete(taskId);
156
+ path.pop();
157
+ stack.pop();
129
158
  }
130
159
  }
131
160
 
132
- recursionStack.delete(taskId);
133
- path.pop();
134
161
  return false;
135
162
  }
136
163
  }
@@ -28,16 +28,15 @@ export class WorkflowExecutor<TContext> {
28
28
 
29
29
  const results = new Map<string, TaskResult>();
30
30
  const executingPromises = new Set<Promise<void>>();
31
- const pendingSteps = new Set(steps);
32
31
 
33
32
  // Initial pass
34
- this.processQueue(pendingSteps, results, executingPromises);
33
+ this.processQueue(steps, results, executingPromises);
35
34
 
36
35
  while (results.size < steps.length && executingPromises.size > 0) {
37
36
  // Wait for the next task to finish
38
37
  await Promise.race(executingPromises);
39
38
  // After a task finishes, check for new work
40
- this.processQueue(pendingSteps, results, executingPromises);
39
+ this.processQueue(steps, results, executingPromises);
41
40
  }
42
41
 
43
42
  this.eventBus.emit("workflowEnd", { context: this.context, results });
@@ -46,31 +45,37 @@ export class WorkflowExecutor<TContext> {
46
45
 
47
46
  /**
48
47
  * Logic to identify tasks that can be started or must be skipped.
49
- * Iterate only over pending steps to avoid O(N^2) checks on completed tasks.
50
48
  */
51
49
  private processQueue(
52
- pendingSteps: Set<TaskStep<TContext>>,
50
+ steps: TaskStep<TContext>[],
53
51
  results: Map<string, TaskResult>,
54
52
  executingPromises: Set<Promise<void>>
55
53
  ): void {
56
- const toRemove: TaskStep<TContext>[] = [];
57
- const toRun: TaskStep<TContext>[] = [];
54
+ this.handleSkippedTasks(steps, results);
55
+
56
+ const readySteps = this.getReadySteps(steps, results);
57
+
58
+ for (const step of readySteps) {
59
+ const taskPromise = this.runStep(step, results).then(() => {
60
+ executingPromises.delete(taskPromise);
61
+ });
62
+ executingPromises.add(taskPromise);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Identifies steps that cannot run because a dependency failed.
68
+ */
69
+ private handleSkippedTasks(steps: TaskStep<TContext>[], results: Map<string, TaskResult>): void {
70
+ const pendingSteps = steps.filter(
71
+ (step) => !results.has(step.name) && !this.running.has(step.name)
72
+ );
58
73
 
59
74
  for (const step of pendingSteps) {
60
75
  const deps = step.dependencies ?? [];
61
- let blocked = false;
62
- let failedDep: string | undefined;
63
-
64
- for (const dep of deps) {
65
- const depResult = results.get(dep);
66
- if (!depResult) {
67
- // Dependency not finished yet
68
- blocked = true;
69
- } else if (depResult.status !== "success") {
70
- failedDep = dep;
71
- break;
72
- }
73
- }
76
+ const failedDep = deps.find(
77
+ (dep) => results.has(dep) && results.get(dep)?.status !== "success"
78
+ );
74
79
 
75
80
  if (failedDep) {
76
81
  const result: TaskResult = {
@@ -79,25 +84,22 @@ export class WorkflowExecutor<TContext> {
79
84
  };
80
85
  results.set(step.name, result);
81
86
  this.eventBus.emit("taskSkipped", { step, result });
82
- toRemove.push(step);
83
- } else if (!blocked) {
84
- toRun.push(step);
85
- toRemove.push(step);
86
87
  }
87
88
  }
89
+ }
88
90
 
89
- // Cleanup pending set
90
- for (const step of toRemove) {
91
- pendingSteps.delete(step);
92
- }
91
+ /**
92
+ * Returns steps where all dependencies have finished successfully.
93
+ */
94
+ private getReadySteps(steps: TaskStep<TContext>[], results: Map<string, TaskResult>): TaskStep<TContext>[] {
95
+ return steps.filter((step) => {
96
+ if (results.has(step.name) || this.running.has(step.name)) return false;
93
97
 
94
- // Execute ready tasks
95
- for (const step of toRun) {
96
- const taskPromise = this.runStep(step, results).then(() => {
97
- executingPromises.delete(taskPromise);
98
- });
99
- executingPromises.add(taskPromise);
100
- }
98
+ const deps = step.dependencies ?? [];
99
+ return deps.every(
100
+ (dep) => results.has(dep) && results.get(dep)?.status === "success"
101
+ );
102
+ });
101
103
  }
102
104
 
103
105
  /**