@calmo/task-runner 3.8.0 → 3.8.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.
- package/CHANGELOG.md +6 -0
- package/coverage/coverage-final.json +5 -4
- package/coverage/index.html +18 -18
- package/coverage/lcov-report/index.html +18 -18
- package/coverage/lcov-report/src/EventBus.ts.html +4 -4
- package/coverage/lcov-report/src/TaskGraphValidationError.ts.html +1 -1
- package/coverage/lcov-report/src/TaskGraphValidator.ts.html +120 -60
- package/coverage/lcov-report/src/TaskRunner.ts.html +78 -63
- package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +1 -1
- package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +1 -1
- package/coverage/lcov-report/src/TaskStateManager.ts.html +17 -29
- package/coverage/lcov-report/src/WorkflowExecutor.ts.html +1 -1
- package/coverage/lcov-report/src/contracts/ErrorTypes.ts.html +103 -0
- package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +1 -1
- package/coverage/lcov-report/src/contracts/index.html +23 -8
- package/coverage/lcov-report/src/index.html +10 -10
- package/coverage/lcov-report/src/strategies/DryRunExecutionStrategy.ts.html +1 -1
- package/coverage/lcov-report/src/strategies/RetryingExecutionStrategy.ts.html +1 -1
- package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +1 -1
- package/coverage/lcov-report/src/strategies/index.html +1 -1
- package/coverage/lcov.info +206 -184
- package/coverage/src/EventBus.ts.html +4 -4
- package/coverage/src/TaskGraphValidationError.ts.html +1 -1
- package/coverage/src/TaskGraphValidator.ts.html +120 -60
- package/coverage/src/TaskRunner.ts.html +78 -63
- package/coverage/src/TaskRunnerBuilder.ts.html +1 -1
- package/coverage/src/TaskRunnerExecutionConfig.ts.html +1 -1
- package/coverage/src/TaskStateManager.ts.html +17 -29
- package/coverage/src/WorkflowExecutor.ts.html +1 -1
- package/coverage/src/contracts/ErrorTypes.ts.html +103 -0
- package/coverage/src/contracts/RunnerEvents.ts.html +1 -1
- package/coverage/src/contracts/index.html +23 -8
- package/coverage/src/index.html +10 -10
- package/coverage/src/strategies/DryRunExecutionStrategy.ts.html +1 -1
- package/coverage/src/strategies/RetryingExecutionStrategy.ts.html +1 -1
- package/coverage/src/strategies/StandardExecutionStrategy.ts.html +1 -1
- package/coverage/src/strategies/index.html +1 -1
- package/dist/TaskGraphValidator.d.ts +3 -0
- package/dist/TaskGraphValidator.js +33 -26
- package/dist/TaskGraphValidator.js.map +1 -1
- package/dist/TaskRunner.d.ts +4 -0
- package/dist/TaskRunner.js +38 -44
- package/dist/TaskRunner.js.map +1 -1
- package/dist/TaskStateManager.js +1 -5
- package/dist/TaskStateManager.js.map +1 -1
- package/dist/contracts/ErrorTypes.d.ts +6 -0
- package/dist/contracts/ErrorTypes.js +7 -0
- package/dist/contracts/ErrorTypes.js.map +1 -0
- package/dist/contracts/ValidationError.d.ts +2 -1
- package/package.json +1 -1
- package/src/TaskGraphValidator.ts +52 -32
- package/src/TaskRunner.ts +51 -46
- package/src/TaskStateManager.ts +1 -5
- package/src/contracts/ErrorTypes.ts +6 -0
- package/src/contracts/ValidationError.ts +10 -1
- package/test-report.xml +134 -128
package/dist/TaskRunner.js
CHANGED
|
@@ -56,21 +56,10 @@ export class TaskRunner {
|
|
|
56
56
|
*/
|
|
57
57
|
static getMermaidGraph(steps) {
|
|
58
58
|
const graphLines = ["graph TD"];
|
|
59
|
-
// Helper to sanitize node names or wrap them if needed
|
|
60
|
-
// For simplicity, we just wrap in quotes and use the name as ID if it's simple
|
|
61
|
-
// or generate an ID if strictly needed. Here we assume names are unique IDs.
|
|
62
|
-
// We will wrap names in quotes for the label, but use the name as the ID.
|
|
63
|
-
// Actually, Mermaid ID cannot have spaces without quotes.
|
|
64
|
-
const safeId = (name) => JSON.stringify(name);
|
|
65
59
|
const sanitize = (name) => this.sanitizeMermaidId(name);
|
|
66
|
-
// Add all nodes first to ensure they exist
|
|
67
60
|
for (const step of steps) {
|
|
68
|
-
|
|
69
|
-
// Format: ID["Label"]
|
|
70
|
-
// safeId returns a quoted string (e.g. "Task Name"), so we use it directly as the label
|
|
71
|
-
graphLines.push(` ${sanitize(step.name)}[${safeId(step.name)}]`);
|
|
61
|
+
graphLines.push(` ${sanitize(step.name)}[${JSON.stringify(step.name)}]`);
|
|
72
62
|
}
|
|
73
|
-
// Add edges
|
|
74
63
|
for (const step of steps) {
|
|
75
64
|
if (step.dependencies) {
|
|
76
65
|
for (const dep of step.dependencies) {
|
|
@@ -114,43 +103,48 @@ export class TaskRunner {
|
|
|
114
103
|
strategy = new DryRunExecutionStrategy();
|
|
115
104
|
}
|
|
116
105
|
const executor = new WorkflowExecutor(this.context, this.eventBus, stateManager, strategy, config?.concurrency);
|
|
117
|
-
// We need to handle the timeout cleanup properly.
|
|
118
106
|
if (config?.timeout !== undefined) {
|
|
119
|
-
|
|
120
|
-
const timeoutId = setTimeout(() => {
|
|
121
|
-
controller.abort(new Error(`Workflow timed out after ${config.timeout}ms`));
|
|
122
|
-
}, config.timeout);
|
|
123
|
-
let effectiveSignal = controller.signal;
|
|
124
|
-
let onAbort;
|
|
125
|
-
// Handle combination of signals if user provided one
|
|
126
|
-
if (config.signal) {
|
|
127
|
-
if (config.signal.aborted) {
|
|
128
|
-
// If already aborted, use it directly (WorkflowExecutor handles early abort)
|
|
129
|
-
// We can cancel timeout immediately
|
|
130
|
-
clearTimeout(timeoutId);
|
|
131
|
-
effectiveSignal = config.signal;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
// Listen to user signal to abort our controller
|
|
135
|
-
onAbort = () => {
|
|
136
|
-
controller.abort(config.signal?.reason);
|
|
137
|
-
};
|
|
138
|
-
config.signal.addEventListener("abort", onAbort);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
try {
|
|
142
|
-
return await executor.execute(steps, effectiveSignal);
|
|
143
|
-
}
|
|
144
|
-
finally {
|
|
145
|
-
clearTimeout(timeoutId);
|
|
146
|
-
if (config.signal && onAbort) {
|
|
147
|
-
config.signal.removeEventListener("abort", onAbort);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
107
|
+
return this.executeWithTimeout(executor, steps, config.timeout, config.signal);
|
|
150
108
|
}
|
|
151
109
|
else {
|
|
152
110
|
return executor.execute(steps, config?.signal);
|
|
153
111
|
}
|
|
154
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Executes tasks with a timeout, ensuring resources are cleaned up.
|
|
115
|
+
*/
|
|
116
|
+
async executeWithTimeout(executor, steps, timeout, signal) {
|
|
117
|
+
const controller = new AbortController();
|
|
118
|
+
const timeoutId = setTimeout(() => {
|
|
119
|
+
controller.abort(new Error(`Workflow timed out after ${timeout}ms`));
|
|
120
|
+
}, timeout);
|
|
121
|
+
let effectiveSignal = controller.signal;
|
|
122
|
+
let onAbort;
|
|
123
|
+
// Handle combination of signals if user provided one
|
|
124
|
+
if (signal) {
|
|
125
|
+
if (signal.aborted) {
|
|
126
|
+
// If already aborted, use it directly (WorkflowExecutor handles early abort)
|
|
127
|
+
// We can cancel timeout immediately
|
|
128
|
+
clearTimeout(timeoutId);
|
|
129
|
+
effectiveSignal = signal;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Listen to user signal to abort our controller
|
|
133
|
+
onAbort = () => {
|
|
134
|
+
controller.abort(signal.reason);
|
|
135
|
+
};
|
|
136
|
+
signal.addEventListener("abort", onAbort);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
return await executor.execute(steps, effectiveSignal);
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
if (signal && onAbort) {
|
|
145
|
+
signal.removeEventListener("abort", onAbort);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
155
149
|
}
|
|
156
150
|
//# sourceMappingURL=TaskRunner.js.map
|
package/dist/TaskRunner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskRunner.js","sourceRoot":"","sources":["../src/TaskRunner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAM7D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAKlF;;;;GAIG;AACH,MAAM,OAAO,UAAU;IASD;IARZ,QAAQ,GAAG,IAAI,QAAQ,EAAY,CAAC;IACpC,SAAS,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACrC,iBAAiB,GACvB,IAAI,yBAAyB,CAAC,IAAI,yBAAyB,EAAE,CAAC,CAAC;IAEjE;;OAEG;IACH,YAAoB,OAAiB;QAAjB,YAAO,GAAP,OAAO,CAAU;IAAG,CAAC;IAEzC;;;;OAIG;IACI,EAAE,CACP,KAAQ,EACR,QAA0C;QAE1C,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACI,GAAG,CACR,KAAQ,EACR,QAA0C;QAE1C,sBAAsB;QACtB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACI,oBAAoB,CAAC,QAAsC;QAChE,sBAAsB;QACtB,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,eAAe,CAAI,KAAoB;QACnD,MAAM,UAAU,GAAG,CAAC,UAAU,CAAC,CAAC;QAEhC,
|
|
1
|
+
{"version":3,"file":"TaskRunner.js","sourceRoot":"","sources":["../src/TaskRunner.ts"],"names":[],"mappings":"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"}
|
package/dist/TaskStateManager.js
CHANGED
|
@@ -91,20 +91,16 @@ export class TaskStateManager {
|
|
|
91
91
|
for (const step of this.pendingSteps) {
|
|
92
92
|
// Also check running? No, running tasks are handled by AbortSignal in Executor.
|
|
93
93
|
// We only cancel what is pending and hasn't started.
|
|
94
|
-
/* v8 ignore next 1 */
|
|
95
94
|
if (!this.results.has(step.name) && !this.running.has(step.name)) {
|
|
96
95
|
const result = {
|
|
97
96
|
status: "cancelled",
|
|
98
97
|
message,
|
|
99
98
|
};
|
|
100
99
|
this.results.set(step.name, result);
|
|
100
|
+
this.eventBus.emit("taskEnd", { step, result });
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
// Clear pending set as they are now "done" (cancelled)
|
|
104
|
-
// Wait, if we clear pending steps, processDependencies won't pick them up.
|
|
105
|
-
// The loop in Executor relies on results.size or pendingSteps.
|
|
106
|
-
// The previous implementation iterated `steps` (all steps) to cancel.
|
|
107
|
-
// Here we iterate `pendingSteps`.
|
|
108
104
|
this.pendingSteps.clear();
|
|
109
105
|
}
|
|
110
106
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskStateManager.js","sourceRoot":"","sources":["../src/TaskStateManager.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IAKP;IAJZ,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IACxC,YAAY,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAEpC,YAAoB,QAA4B;QAA5B,aAAQ,GAAR,QAAQ,CAAoB;IAAG,CAAC;IAEpD;;;OAGG;IACH,UAAU,CAAC,KAA2B;QACpC,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,mBAAmB;QACjB,MAAM,QAAQ,GAAyB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAyB,EAAE,CAAC;QAEvC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;YACrC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,SAA6B,CAAC;YAElC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACxC,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,8BAA8B;oBAC9B,OAAO,GAAG,IAAI,CAAC;gBACjB,CAAC;qBAAM,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC1C,SAAS,GAAG,GAAG,CAAC;oBAChB,MAAM;gBACR,CAAC;YACH,CAAC;YAED,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC9C,MAAM,QAAQ,GAAG,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChE,MAAM,MAAM,GAAe;oBACzB,MAAM,EAAE,SAAS;oBACjB,OAAO,EAAE,+BAA+B,SAAS,WAAW,QAAQ,EAAE;iBACvE,CAAC;gBACF,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;iBAAM,IAAI,CAAC,OAAO,EAAE,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,IAAwB;QAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,IAAwB,EAAE,MAAkB;QACxD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAClD,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,OAAe;QAC9B,2CAA2C;QAC3C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACrC,gFAAgF;YAChF,qDAAqD;YACrD,
|
|
1
|
+
{"version":3,"file":"TaskStateManager.js","sourceRoot":"","sources":["../src/TaskStateManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorTypes.js","sourceRoot":"","sources":["../../src/contracts/ErrorTypes.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,gBAAyB,CAAC;AAC9D,MAAM,CAAC,MAAM,wBAAwB,GAAG,oBAA6B,CAAC;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,OAAgB,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { ERROR_CYCLE, ERROR_DUPLICATE_TASK, ERROR_MISSING_DEPENDENCY } from "./ErrorTypes.js";
|
|
1
2
|
/**
|
|
2
3
|
* Describes a specific validation error found in the task graph.
|
|
3
4
|
*/
|
|
4
5
|
export interface ValidationError {
|
|
5
6
|
/** The type of validation error. */
|
|
6
|
-
type:
|
|
7
|
+
type: typeof ERROR_CYCLE | typeof ERROR_MISSING_DEPENDENCY | typeof ERROR_DUPLICATE_TASK;
|
|
7
8
|
/** A human-readable message describing the error. */
|
|
8
9
|
message: string;
|
|
9
10
|
/** Optional detailed information about the error, e.g., the cycle path, or the task with a missing dependency. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calmo/task-runner",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"description": "A lightweight, type-safe, and domain-agnostic task orchestration engine. It resolves a Directed Acyclic Graph (DAG) of steps, executes independent tasks in parallel, and manages a shared context across the pipeline.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -2,6 +2,11 @@ import { ITaskGraphValidator } from "./contracts/ITaskGraphValidator.js";
|
|
|
2
2
|
import { ValidationResult } from "./contracts/ValidationResult.js";
|
|
3
3
|
import { ValidationError } from "./contracts/ValidationError.js";
|
|
4
4
|
import { TaskGraph } from "./TaskGraph.js";
|
|
5
|
+
import {
|
|
6
|
+
ERROR_CYCLE,
|
|
7
|
+
ERROR_DUPLICATE_TASK,
|
|
8
|
+
ERROR_MISSING_DEPENDENCY,
|
|
9
|
+
} from "./contracts/ErrorTypes.js";
|
|
5
10
|
|
|
6
11
|
export class TaskGraphValidator implements ITaskGraphValidator {
|
|
7
12
|
/**
|
|
@@ -18,11 +23,46 @@ export class TaskGraphValidator implements ITaskGraphValidator {
|
|
|
18
23
|
const errors: ValidationError[] = [];
|
|
19
24
|
|
|
20
25
|
// 1. Check for duplicate tasks
|
|
26
|
+
const taskIds = this.checkDuplicateTasks(taskGraph, errors);
|
|
27
|
+
|
|
28
|
+
// 2. Check for missing dependencies
|
|
29
|
+
this.checkMissingDependencies(taskGraph, taskIds, errors);
|
|
30
|
+
|
|
31
|
+
// 3. Check for cycles
|
|
32
|
+
// Only run cycle detection if there are no missing dependencies, otherwise we might chase non-existent nodes.
|
|
33
|
+
const hasMissingDependencies = errors.some(
|
|
34
|
+
(e) => e.type === ERROR_MISSING_DEPENDENCY
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (!hasMissingDependencies) {
|
|
38
|
+
this.checkCycles(taskGraph, errors);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
isValid: errors.length === 0,
|
|
43
|
+
errors,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates a human-readable error message from a validation result.
|
|
49
|
+
* @param result The validation result containing errors.
|
|
50
|
+
* @returns A formatted error string.
|
|
51
|
+
*/
|
|
52
|
+
createErrorMessage(result: ValidationResult): string {
|
|
53
|
+
const errorDetails = result.errors.map((e) => e.message);
|
|
54
|
+
return `Task graph validation failed: ${errorDetails.join("; ")}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private checkDuplicateTasks(
|
|
58
|
+
taskGraph: TaskGraph,
|
|
59
|
+
errors: ValidationError[]
|
|
60
|
+
): Set<string> {
|
|
21
61
|
const taskIds = new Set<string>();
|
|
22
62
|
for (const task of taskGraph.tasks) {
|
|
23
63
|
if (taskIds.has(task.id)) {
|
|
24
64
|
errors.push({
|
|
25
|
-
type:
|
|
65
|
+
type: ERROR_DUPLICATE_TASK,
|
|
26
66
|
message: `Duplicate task detected with ID: ${task.id}`,
|
|
27
67
|
details: { taskId: task.id },
|
|
28
68
|
});
|
|
@@ -30,33 +70,28 @@ export class TaskGraphValidator implements ITaskGraphValidator {
|
|
|
30
70
|
taskIds.add(task.id);
|
|
31
71
|
}
|
|
32
72
|
}
|
|
73
|
+
return taskIds;
|
|
74
|
+
}
|
|
33
75
|
|
|
34
|
-
|
|
76
|
+
private checkMissingDependencies(
|
|
77
|
+
taskGraph: TaskGraph,
|
|
78
|
+
taskIds: Set<string>,
|
|
79
|
+
errors: ValidationError[]
|
|
80
|
+
): void {
|
|
35
81
|
for (const task of taskGraph.tasks) {
|
|
36
82
|
for (const dependenceId of task.dependencies) {
|
|
37
83
|
if (!taskIds.has(dependenceId)) {
|
|
38
84
|
errors.push({
|
|
39
|
-
type:
|
|
85
|
+
type: ERROR_MISSING_DEPENDENCY,
|
|
40
86
|
message: `Task '${task.id}' depends on missing task '${dependenceId}'`,
|
|
41
87
|
details: { taskId: task.id, missingDependencyId: dependenceId },
|
|
42
88
|
});
|
|
43
89
|
}
|
|
44
90
|
}
|
|
45
91
|
}
|
|
92
|
+
}
|
|
46
93
|
|
|
47
|
-
|
|
48
|
-
// Only run cycle detection if there are no missing dependencies, otherwise we might chase non-existent nodes.
|
|
49
|
-
const hasMissingDependencies = errors.some(
|
|
50
|
-
(e) => e.type === "missing_dependency"
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
if (hasMissingDependencies) {
|
|
54
|
-
return {
|
|
55
|
-
isValid: errors.length === 0,
|
|
56
|
-
errors,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
94
|
+
private checkCycles(taskGraph: TaskGraph, errors: ValidationError[]): void {
|
|
60
95
|
// Build adjacency list
|
|
61
96
|
const adjacencyList = new Map<string, string[]>();
|
|
62
97
|
for (const task of taskGraph.tasks) {
|
|
@@ -82,7 +117,7 @@ export class TaskGraphValidator implements ITaskGraphValidator {
|
|
|
82
117
|
const cyclePath = path.slice(cycleStartIndex);
|
|
83
118
|
|
|
84
119
|
errors.push({
|
|
85
|
-
type:
|
|
120
|
+
type: ERROR_CYCLE,
|
|
86
121
|
message: `Cycle detected: ${cyclePath.join(" -> ")}`,
|
|
87
122
|
details: { cyclePath },
|
|
88
123
|
});
|
|
@@ -90,21 +125,6 @@ export class TaskGraphValidator implements ITaskGraphValidator {
|
|
|
90
125
|
break;
|
|
91
126
|
}
|
|
92
127
|
}
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
isValid: errors.length === 0,
|
|
96
|
-
errors,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Creates a human-readable error message from a validation result.
|
|
102
|
-
* @param result The validation result containing errors.
|
|
103
|
-
* @returns A formatted error string.
|
|
104
|
-
*/
|
|
105
|
-
createErrorMessage(result: ValidationResult): string {
|
|
106
|
-
const errorDetails = result.errors.map((e) => e.message);
|
|
107
|
-
return `Task graph validation failed: ${errorDetails.join("; ")}`;
|
|
108
128
|
}
|
|
109
129
|
|
|
110
130
|
private detectCycle(
|
package/src/TaskRunner.ts
CHANGED
|
@@ -79,23 +79,14 @@ export class TaskRunner<TContext> {
|
|
|
79
79
|
public static getMermaidGraph<T>(steps: TaskStep<T>[]): string {
|
|
80
80
|
const graphLines = ["graph TD"];
|
|
81
81
|
|
|
82
|
-
// Helper to sanitize node names or wrap them if needed
|
|
83
|
-
// For simplicity, we just wrap in quotes and use the name as ID if it's simple
|
|
84
|
-
// or generate an ID if strictly needed. Here we assume names are unique IDs.
|
|
85
|
-
// We will wrap names in quotes for the label, but use the name as the ID.
|
|
86
|
-
// Actually, Mermaid ID cannot have spaces without quotes.
|
|
87
|
-
const safeId = (name: string) => JSON.stringify(name);
|
|
88
82
|
const sanitize = (name: string) => this.sanitizeMermaidId(name);
|
|
89
83
|
|
|
90
|
-
// Add all nodes first to ensure they exist
|
|
91
84
|
for (const step of steps) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
graphLines.push(` ${sanitize(step.name)}[${safeId(step.name)}]`);
|
|
85
|
+
graphLines.push(
|
|
86
|
+
` ${sanitize(step.name)}[${JSON.stringify(step.name)}]`
|
|
87
|
+
);
|
|
96
88
|
}
|
|
97
89
|
|
|
98
|
-
// Add edges
|
|
99
90
|
for (const step of steps) {
|
|
100
91
|
if (step.dependencies) {
|
|
101
92
|
for (const dep of step.dependencies) {
|
|
@@ -159,44 +150,58 @@ export class TaskRunner<TContext> {
|
|
|
159
150
|
config?.concurrency
|
|
160
151
|
);
|
|
161
152
|
|
|
162
|
-
// We need to handle the timeout cleanup properly.
|
|
163
153
|
if (config?.timeout !== undefined) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// Handle combination of signals if user provided one
|
|
175
|
-
if (config.signal) {
|
|
176
|
-
if (config.signal.aborted) {
|
|
177
|
-
// If already aborted, use it directly (WorkflowExecutor handles early abort)
|
|
178
|
-
// We can cancel timeout immediately
|
|
179
|
-
clearTimeout(timeoutId);
|
|
180
|
-
effectiveSignal = config.signal;
|
|
181
|
-
} else {
|
|
182
|
-
// Listen to user signal to abort our controller
|
|
183
|
-
onAbort = () => {
|
|
184
|
-
controller.abort(config.signal?.reason);
|
|
185
|
-
};
|
|
186
|
-
config.signal.addEventListener("abort", onAbort);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
154
|
+
return this.executeWithTimeout(
|
|
155
|
+
executor,
|
|
156
|
+
steps,
|
|
157
|
+
config.timeout,
|
|
158
|
+
config.signal
|
|
159
|
+
);
|
|
160
|
+
} else {
|
|
161
|
+
return executor.execute(steps, config?.signal);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
189
164
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
165
|
+
/**
|
|
166
|
+
* Executes tasks with a timeout, ensuring resources are cleaned up.
|
|
167
|
+
*/
|
|
168
|
+
private async executeWithTimeout(
|
|
169
|
+
executor: WorkflowExecutor<TContext>,
|
|
170
|
+
steps: TaskStep<TContext>[],
|
|
171
|
+
timeout: number,
|
|
172
|
+
signal?: AbortSignal
|
|
173
|
+
): Promise<Map<string, TaskResult>> {
|
|
174
|
+
const controller = new AbortController();
|
|
175
|
+
const timeoutId = setTimeout(() => {
|
|
176
|
+
controller.abort(new Error(`Workflow timed out after ${timeout}ms`));
|
|
177
|
+
}, timeout);
|
|
178
|
+
|
|
179
|
+
let effectiveSignal = controller.signal;
|
|
180
|
+
let onAbort: (() => void) | undefined;
|
|
181
|
+
|
|
182
|
+
// Handle combination of signals if user provided one
|
|
183
|
+
if (signal) {
|
|
184
|
+
if (signal.aborted) {
|
|
185
|
+
// If already aborted, use it directly (WorkflowExecutor handles early abort)
|
|
186
|
+
// We can cancel timeout immediately
|
|
193
187
|
clearTimeout(timeoutId);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
effectiveSignal = signal;
|
|
189
|
+
} else {
|
|
190
|
+
// Listen to user signal to abort our controller
|
|
191
|
+
onAbort = () => {
|
|
192
|
+
controller.abort(signal.reason);
|
|
193
|
+
};
|
|
194
|
+
signal.addEventListener("abort", onAbort);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
return await executor.execute(steps, effectiveSignal);
|
|
200
|
+
} finally {
|
|
201
|
+
clearTimeout(timeoutId);
|
|
202
|
+
if (signal && onAbort) {
|
|
203
|
+
signal.removeEventListener("abort", onAbort);
|
|
197
204
|
}
|
|
198
|
-
} else {
|
|
199
|
-
return executor.execute(steps, config?.signal);
|
|
200
205
|
}
|
|
201
206
|
}
|
|
202
207
|
}
|
package/src/TaskStateManager.ts
CHANGED
|
@@ -101,20 +101,16 @@ export class TaskStateManager<TContext> {
|
|
|
101
101
|
for (const step of this.pendingSteps) {
|
|
102
102
|
// Also check running? No, running tasks are handled by AbortSignal in Executor.
|
|
103
103
|
// We only cancel what is pending and hasn't started.
|
|
104
|
-
/* v8 ignore next 1 */
|
|
105
104
|
if (!this.results.has(step.name) && !this.running.has(step.name)) {
|
|
106
105
|
const result: TaskResult = {
|
|
107
106
|
status: "cancelled",
|
|
108
107
|
message,
|
|
109
108
|
};
|
|
110
109
|
this.results.set(step.name, result);
|
|
110
|
+
this.eventBus.emit("taskEnd", { step, result });
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
// Clear pending set as they are now "done" (cancelled)
|
|
114
|
-
// Wait, if we clear pending steps, processDependencies won't pick them up.
|
|
115
|
-
// The loop in Executor relies on results.size or pendingSteps.
|
|
116
|
-
// The previous implementation iterated `steps` (all steps) to cancel.
|
|
117
|
-
// Here we iterate `pendingSteps`.
|
|
118
114
|
this.pendingSteps.clear();
|
|
119
115
|
}
|
|
120
116
|
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ERROR_CYCLE,
|
|
3
|
+
ERROR_DUPLICATE_TASK,
|
|
4
|
+
ERROR_MISSING_DEPENDENCY,
|
|
5
|
+
} from "./ErrorTypes.js";
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* Describes a specific validation error found in the task graph.
|
|
3
9
|
*/
|
|
4
10
|
export interface ValidationError {
|
|
5
11
|
/** The type of validation error. */
|
|
6
|
-
type:
|
|
12
|
+
type:
|
|
13
|
+
| typeof ERROR_CYCLE
|
|
14
|
+
| typeof ERROR_MISSING_DEPENDENCY
|
|
15
|
+
| typeof ERROR_DUPLICATE_TASK;
|
|
7
16
|
/** A human-readable message describing the error. */
|
|
8
17
|
message: string;
|
|
9
18
|
/** Optional detailed information about the error, e.g., the cycle path, or the task with a missing dependency. */
|