@visulima/task-runner 0.0.1 → 1.0.0-alpha.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 (49) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +170 -36
  4. package/binding.js +204 -0
  5. package/dist/affected.d.ts +48 -0
  6. package/dist/cache.d.ts +103 -0
  7. package/dist/default-task-runner.d.ts +44 -0
  8. package/dist/file-access-tracker.d.ts +53 -0
  9. package/dist/fingerprint.d.ts +45 -0
  10. package/dist/framework-inference.d.ts +35 -0
  11. package/dist/graph-visualizer.d.ts +74 -0
  12. package/dist/incremental-hasher.d.ts +58 -0
  13. package/dist/index.d.ts +34 -0
  14. package/dist/index.js +20 -0
  15. package/dist/life-cycle.d.ts +36 -0
  16. package/dist/lockfile-hasher.d.ts +73 -0
  17. package/dist/native-binding.d.ts +64 -0
  18. package/dist/packem_shared/Cache-IYpTYVUC.js +298 -0
  19. package/dist/packem_shared/CompositeLifeCycle-7AtYw1dv.js +112 -0
  20. package/dist/packem_shared/FileAccessTracker-CrtBAt5D.js +239 -0
  21. package/dist/packem_shared/FingerprintManager-D6Y0erg-.js +227 -0
  22. package/dist/packem_shared/IncrementalFileHasher-Ds3J6dgb.js +151 -0
  23. package/dist/packem_shared/RemoteCache-BDqrnDEi.js +179 -0
  24. package/dist/packem_shared/TaskOrchestrator-BvYs3ONw.js +342 -0
  25. package/dist/packem_shared/TaskScheduler-CJilHDta.js +111 -0
  26. package/dist/packem_shared/TrackedTaskExecutor-BGUKFE-7.js +164 -0
  27. package/dist/packem_shared/collectFiles-ClXHnHhg.js +22 -0
  28. package/dist/packem_shared/computeTaskHash-BoCnnvIJ.js +356 -0
  29. package/dist/packem_shared/createTaskGraph-CcsFaSrz.js +164 -0
  30. package/dist/packem_shared/defaultTaskRunner-CrW4v5Ye.js +79 -0
  31. package/dist/packem_shared/detectFrameworks-CeFzKE6J.js +101 -0
  32. package/dist/packem_shared/extractPackageName-CbVNW-dr.js +189 -0
  33. package/dist/packem_shared/filterAffectedTasks-I-18zPg6.js +135 -0
  34. package/dist/packem_shared/findCycle-DF4_BRdO.js +212 -0
  35. package/dist/packem_shared/generateRunSummary-qn-_jKwt.js +134 -0
  36. package/dist/packem_shared/isNativeAvailable-BWhnZ4ES.js +19 -0
  37. package/dist/packem_shared/projectGraphToDot-VdTjHcVp.js +202 -0
  38. package/dist/packem_shared/utils-zO0ZRgtf.js +390 -0
  39. package/dist/remote-cache.d.ts +55 -0
  40. package/dist/run-summary.d.ts +89 -0
  41. package/dist/task-graph-utils.d.ts +39 -0
  42. package/dist/task-graph.d.ts +22 -0
  43. package/dist/task-hasher.d.ts +67 -0
  44. package/dist/task-orchestrator.d.ts +38 -0
  45. package/dist/task-scheduler.d.ts +18 -0
  46. package/dist/tracked-executor.d.ts +46 -0
  47. package/dist/types.d.ts +385 -0
  48. package/dist/utils.d.ts +39 -0
  49. package/package.json +72 -7
@@ -0,0 +1,189 @@
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
4
+
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
+
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
15
+ }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
19
+
20
+ const {
21
+ readFile
22
+ } = __cjs_getBuiltinModule("node:fs/promises");
23
+ import { r as readPackageDeps, e as createXxh3Hasher } from './utils-zO0ZRgtf.js';
24
+ import { join } from '@visulima/path';
25
+
26
+ const extractPackageName = (path) => {
27
+ const match = /.*node_modules\/((?:@[^/]+\/)?[^/]+)/.exec(path);
28
+ if (!match?.[1]) {
29
+ return void 0;
30
+ }
31
+ const name = match[1];
32
+ if (name.startsWith(".")) {
33
+ return void 0;
34
+ }
35
+ return name;
36
+ };
37
+ const parseNpmLockfile = (content) => {
38
+ const versions = /* @__PURE__ */ new Map();
39
+ try {
40
+ const lockfile = JSON.parse(content);
41
+ if (lockfile.packages) {
42
+ for (const [path, entry] of Object.entries(lockfile.packages)) {
43
+ if (!path || !entry.version) {
44
+ continue;
45
+ }
46
+ const name = extractPackageName(path);
47
+ if (name && !versions.has(name)) {
48
+ versions.set(name, entry.version);
49
+ }
50
+ }
51
+ }
52
+ if (versions.size === 0 && lockfile.dependencies) {
53
+ for (const [name, entry] of Object.entries(lockfile.dependencies)) {
54
+ if (entry.version) {
55
+ versions.set(name, entry.version);
56
+ }
57
+ }
58
+ }
59
+ } catch {
60
+ }
61
+ return versions;
62
+ };
63
+ const parsePnpmLockfile = (content) => {
64
+ const versions = /* @__PURE__ */ new Map();
65
+ const depVersionRegex = /^\s{4,}(\S+):\n\s+specifier:.*\n\s+version:\s*'?([^'\n(]+)/gm;
66
+ let match;
67
+ while ((match = depVersionRegex.exec(content) ?? void 0) !== void 0) {
68
+ const name = match[1].replaceAll(/^['"]|['"]$/g, "");
69
+ let version = match[2].trim();
70
+ const parenIndex = version.indexOf("(");
71
+ if (parenIndex > 0) {
72
+ version = version.slice(0, parenIndex).trim();
73
+ }
74
+ if (!versions.has(name)) {
75
+ versions.set(name, version);
76
+ }
77
+ }
78
+ if (versions.size === 0) {
79
+ const packagesRegex = /^\s{2}[/'"]?(?:@([^/@']+)\/)?([^@']+)@(\d[^:'"\s]*)/gm;
80
+ while ((match = packagesRegex.exec(content) ?? void 0) !== void 0) {
81
+ const scope = match[1];
82
+ const name = scope ? `@${scope}/${match[2]}` : match[2];
83
+ const version = match[3];
84
+ if (!versions.has(name)) {
85
+ versions.set(name, version);
86
+ }
87
+ }
88
+ }
89
+ return versions;
90
+ };
91
+ const parseYarnLockfile = (content) => {
92
+ const versions = /* @__PURE__ */ new Map();
93
+ const entryRegex = /^["']?(?:@([^/@"']+)\/)?([^@"']+)@[^"'\n]+["']?:?[\t\v\f\r \u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*\n\s+version:?\s+"?([^"\n]+)"?/gm;
94
+ let match;
95
+ while ((match = entryRegex.exec(content) ?? void 0) !== void 0) {
96
+ const scope = match[1];
97
+ const name = scope ? `@${scope}/${match[2]}` : match[2];
98
+ const version = match[3].trim();
99
+ if (!versions.has(name)) {
100
+ versions.set(name, version);
101
+ }
102
+ }
103
+ return versions;
104
+ };
105
+ class LockfileHasher {
106
+ #workspaceRoot;
107
+ #lockfileCache;
108
+ #lockfileType;
109
+ constructor(workspaceRoot) {
110
+ this.#workspaceRoot = workspaceRoot;
111
+ }
112
+ /**
113
+ * Computes a hash based only on the resolved dependency versions
114
+ * relevant to a specific package.
115
+ * @param packageJsonPath Path to the package.json (relative to workspace root)
116
+ * @returns Hash of the relevant lockfile entries, or undefined if no lockfile found
117
+ */
118
+ async hashForPackage(packageJsonPath) {
119
+ const fullPath = join(this.#workspaceRoot, packageJsonPath);
120
+ const deps = await readPackageDeps(fullPath);
121
+ if (!deps || deps.size === 0) {
122
+ return void 0;
123
+ }
124
+ const resolvedVersions = await this.#getResolvedVersions();
125
+ if (!resolvedVersions) {
126
+ return void 0;
127
+ }
128
+ const resolved = [];
129
+ for (const depName of deps) {
130
+ const version = resolvedVersions.get(depName);
131
+ if (version) {
132
+ resolved.push({ name: depName, version });
133
+ }
134
+ }
135
+ if (resolved.length === 0) {
136
+ return void 0;
137
+ }
138
+ resolved.sort((a, b) => a.name.localeCompare(b.name));
139
+ const hash = createXxh3Hasher();
140
+ for (const dependency of resolved) {
141
+ hash.update(`${dependency.name}@${dependency.version}`);
142
+ }
143
+ return {
144
+ dependencies: resolved,
145
+ hash: hash.digest()
146
+ };
147
+ }
148
+ /**
149
+ * Returns the type of lockfile detected, or undefined if none found.
150
+ */
151
+ get lockfileType() {
152
+ return this.#lockfileType;
153
+ }
154
+ /**
155
+ * Clears the cached lockfile data.
156
+ */
157
+ clearCache() {
158
+ this.#lockfileCache = void 0;
159
+ this.#lockfileType = void 0;
160
+ }
161
+ /**
162
+ * Parses the workspace lockfile and returns a map of package name → resolved version.
163
+ * Results are cached for subsequent calls.
164
+ */
165
+ async #getResolvedVersions() {
166
+ if (this.#lockfileCache !== void 0) {
167
+ return this.#lockfileCache.size > 0 ? this.#lockfileCache : void 0;
168
+ }
169
+ const parsers = [
170
+ { file: "package-lock.json", parser: parseNpmLockfile, type: "npm" },
171
+ { file: "pnpm-lock.yaml", parser: parsePnpmLockfile, type: "pnpm" },
172
+ { file: "yarn.lock", parser: parseYarnLockfile, type: "yarn" }
173
+ ];
174
+ for (const { file, parser, type } of parsers) {
175
+ try {
176
+ const content = await readFile(join(this.#workspaceRoot, file), "utf8");
177
+ const versions = parser(content);
178
+ this.#lockfileCache = versions;
179
+ this.#lockfileType = type;
180
+ return versions;
181
+ } catch {
182
+ }
183
+ }
184
+ this.#lockfileCache = /* @__PURE__ */ new Map();
185
+ return void 0;
186
+ }
187
+ }
188
+
189
+ export { LockfileHasher, extractPackageName, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile };
@@ -0,0 +1,135 @@
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
4
+
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
+
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
15
+ }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
19
+
20
+ const {
21
+ execFile
22
+ } = __cjs_getBuiltinModule("node:child_process");
23
+
24
+ const validateGitRef = (ref) => {
25
+ if (!/^[a-zA-Z0-9._\-/~^@{}]+$/.test(ref)) {
26
+ throw new Error(`Invalid git ref: "${ref}". Only alphanumeric characters, dots, dashes, underscores, slashes, tildes, carets, and @ are allowed.`);
27
+ }
28
+ };
29
+ const findProjectForFile = (filePath, projects) => {
30
+ let bestMatch;
31
+ let bestLength = 0;
32
+ for (const [name, config] of Object.entries(projects)) {
33
+ const { root } = config;
34
+ if ((filePath.startsWith(`${root}/`) || filePath === root) && root.length > bestLength) {
35
+ bestMatch = name;
36
+ bestLength = root.length;
37
+ }
38
+ }
39
+ return bestMatch;
40
+ };
41
+ const expandAffected = (affectedProjects, projectGraph) => {
42
+ const reverseDependencies = /* @__PURE__ */ new Map();
43
+ for (const [project, dependencies] of Object.entries(projectGraph.dependencies)) {
44
+ for (const dependency of dependencies) {
45
+ let set = reverseDependencies.get(dependency.target);
46
+ if (!set) {
47
+ set = /* @__PURE__ */ new Set();
48
+ reverseDependencies.set(dependency.target, set);
49
+ }
50
+ set.add(project);
51
+ }
52
+ }
53
+ const queue = [...affectedProjects];
54
+ while (queue.length > 0) {
55
+ const project = queue.shift();
56
+ if (project === void 0) {
57
+ continue;
58
+ }
59
+ const dependents = reverseDependencies.get(project);
60
+ if (dependents) {
61
+ for (const dependent of dependents) {
62
+ if (!affectedProjects.has(dependent)) {
63
+ affectedProjects.add(dependent);
64
+ queue.push(dependent);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ };
70
+ const getMergeBase = (workspaceRoot, base, head) => new Promise((resolve, reject) => {
71
+ execFile("git", ["merge-base", base, head], { cwd: workspaceRoot }, (error, stdout) => {
72
+ if (error) {
73
+ reject(error);
74
+ } else {
75
+ resolve(stdout.trim());
76
+ }
77
+ });
78
+ });
79
+ const getChangedFiles = async (workspaceRoot, base, head) => {
80
+ validateGitRef(base);
81
+ validateGitRef(head);
82
+ try {
83
+ const mergeBase = await getMergeBase(workspaceRoot, base, head);
84
+ return await new Promise((resolve, reject) => {
85
+ execFile("git", ["diff", "--name-only", mergeBase, head], { cwd: workspaceRoot }, (error, stdout) => {
86
+ if (error) {
87
+ reject(error);
88
+ } else {
89
+ resolve(stdout.trim().split("\n").filter(Boolean));
90
+ }
91
+ });
92
+ });
93
+ } catch {
94
+ return new Promise((resolve, reject) => {
95
+ execFile("git", ["diff", "--name-only", `${base}...${head}`], { cwd: workspaceRoot }, (error, stdout) => {
96
+ if (error) {
97
+ reject(error);
98
+ } else {
99
+ resolve(stdout.trim().split("\n").filter(Boolean));
100
+ }
101
+ });
102
+ });
103
+ }
104
+ };
105
+ const getAffectedProjects = async (options) => {
106
+ const { base = "main", head = "HEAD", projectGraph, projects, workspaceRoot } = options;
107
+ const changedFiles = await getChangedFiles(workspaceRoot, base, head);
108
+ const changedProjects = /* @__PURE__ */ new Set();
109
+ for (const file of changedFiles) {
110
+ const project = findProjectForFile(file, projects);
111
+ if (project) {
112
+ changedProjects.add(project);
113
+ } else {
114
+ return {
115
+ affectedProjects: Object.keys(projects),
116
+ changedFiles,
117
+ changedProjects: Object.keys(projects)
118
+ };
119
+ }
120
+ }
121
+ const affectedProjects = new Set(changedProjects);
122
+ expandAffected(affectedProjects, projectGraph);
123
+ return {
124
+ affectedProjects: [...affectedProjects],
125
+ changedFiles,
126
+ changedProjects: [...changedProjects]
127
+ };
128
+ };
129
+ const filterAffectedTasks = (taskIds, affectedProjects) => taskIds.filter((taskId) => {
130
+ const parts = taskId.split(":");
131
+ const project = parts[0];
132
+ return affectedProjects.has(project);
133
+ });
134
+
135
+ export { filterAffectedTasks, getAffectedProjects, getChangedFiles };
@@ -0,0 +1,212 @@
1
+ const findCycle = (taskGraph) => {
2
+ const visited = /* @__PURE__ */ new Set();
3
+ const inStack = /* @__PURE__ */ new Set();
4
+ const parent = /* @__PURE__ */ new Map();
5
+ for (const taskId of Object.keys(taskGraph.tasks)) {
6
+ if (visited.has(taskId)) {
7
+ continue;
8
+ }
9
+ const stack = [taskId];
10
+ while (stack.length > 0) {
11
+ const current = stack[stack.length - 1];
12
+ if (!visited.has(current)) {
13
+ visited.add(current);
14
+ inStack.add(current);
15
+ }
16
+ const deps = taskGraph.dependencies[current] ?? [];
17
+ let foundUnvisited = false;
18
+ for (const dep of deps) {
19
+ if (inStack.has(dep)) {
20
+ const cycle = [dep];
21
+ let node = current;
22
+ while (node !== dep) {
23
+ cycle.push(node);
24
+ node = parent.get(node) ?? dep;
25
+ }
26
+ cycle.push(dep);
27
+ cycle.reverse();
28
+ return cycle;
29
+ }
30
+ if (!visited.has(dep)) {
31
+ parent.set(dep, current);
32
+ stack.push(dep);
33
+ foundUnvisited = true;
34
+ break;
35
+ }
36
+ }
37
+ if (!foundUnvisited) {
38
+ stack.pop();
39
+ inStack.delete(current);
40
+ }
41
+ }
42
+ }
43
+ return void 0;
44
+ };
45
+ const findCycles = (taskGraph) => {
46
+ const cycles = [];
47
+ const visited = /* @__PURE__ */ new Set();
48
+ const inStack = /* @__PURE__ */ new Set();
49
+ const stack = [];
50
+ const dfs = (taskId) => {
51
+ visited.add(taskId);
52
+ inStack.add(taskId);
53
+ stack.push(taskId);
54
+ const deps = taskGraph.dependencies[taskId] ?? [];
55
+ for (const dep of deps) {
56
+ if (inStack.has(dep)) {
57
+ const cycleStart = stack.indexOf(dep);
58
+ const cycle = [...stack.slice(cycleStart), dep];
59
+ cycles.push(cycle);
60
+ } else if (!visited.has(dep)) {
61
+ dfs(dep);
62
+ }
63
+ }
64
+ stack.pop();
65
+ inStack.delete(taskId);
66
+ };
67
+ for (const taskId of Object.keys(taskGraph.tasks)) {
68
+ if (!visited.has(taskId)) {
69
+ dfs(taskId);
70
+ }
71
+ }
72
+ return cycles;
73
+ };
74
+ const walkTaskGraph = (taskGraph, callback) => {
75
+ const dependencyCount = /* @__PURE__ */ new Map();
76
+ for (const taskId of Object.keys(taskGraph.tasks)) {
77
+ dependencyCount.set(taskId, (taskGraph.dependencies[taskId] ?? []).length);
78
+ }
79
+ const dependents = /* @__PURE__ */ new Map();
80
+ for (const [taskId, deps] of Object.entries(taskGraph.dependencies)) {
81
+ for (const dep of deps) {
82
+ let list = dependents.get(dep);
83
+ if (!list) {
84
+ list = [];
85
+ dependents.set(dep, list);
86
+ }
87
+ list.push(taskId);
88
+ }
89
+ }
90
+ const queue = [];
91
+ for (const [taskId, count] of dependencyCount) {
92
+ if (count === 0) {
93
+ queue.push(taskId);
94
+ }
95
+ }
96
+ while (queue.length > 0) {
97
+ const taskId = queue.shift();
98
+ callback(taskId);
99
+ const taskDependents = dependents.get(taskId) ?? [];
100
+ for (const dependent of taskDependents) {
101
+ const newCount = (dependencyCount.get(dependent) ?? 1) - 1;
102
+ dependencyCount.set(dependent, newCount);
103
+ if (newCount === 0) {
104
+ queue.push(dependent);
105
+ }
106
+ }
107
+ }
108
+ };
109
+ const reverseTaskGraph = (taskGraph) => {
110
+ const reversedDependencies = {};
111
+ for (const taskId of Object.keys(taskGraph.tasks)) {
112
+ reversedDependencies[taskId] = [];
113
+ }
114
+ for (const [taskId, deps] of Object.entries(taskGraph.dependencies)) {
115
+ for (const dep of deps) {
116
+ reversedDependencies[dep]?.push(taskId);
117
+ }
118
+ }
119
+ const roots = Object.keys(taskGraph.tasks).filter((taskId) => (reversedDependencies[taskId]?.length ?? 0) === 0);
120
+ return {
121
+ dependencies: reversedDependencies,
122
+ roots,
123
+ tasks: { ...taskGraph.tasks }
124
+ };
125
+ };
126
+ const getLeafTasks = (taskGraph) => Object.keys(taskGraph.tasks).filter((taskId) => (taskGraph.dependencies[taskId]?.length ?? 0) === 0);
127
+ const makeAcyclic = (taskGraph) => {
128
+ const visited = /* @__PURE__ */ new Set();
129
+ const inStack = /* @__PURE__ */ new Set();
130
+ const edgesToRemove = [];
131
+ const dfs = (taskId) => {
132
+ visited.add(taskId);
133
+ inStack.add(taskId);
134
+ const deps = taskGraph.dependencies[taskId] ?? [];
135
+ for (const dep of deps) {
136
+ if (inStack.has(dep)) {
137
+ edgesToRemove.push({ from: taskId, to: dep });
138
+ } else if (!visited.has(dep)) {
139
+ dfs(dep);
140
+ }
141
+ }
142
+ inStack.delete(taskId);
143
+ };
144
+ for (const taskId of Object.keys(taskGraph.tasks)) {
145
+ if (!visited.has(taskId)) {
146
+ dfs(taskId);
147
+ }
148
+ }
149
+ const newDependencies = {};
150
+ for (const [taskId, deps] of Object.entries(taskGraph.dependencies)) {
151
+ newDependencies[taskId] = deps.filter((dep) => !edgesToRemove.some((edge) => edge.from === taskId && edge.to === dep));
152
+ }
153
+ const allDeps = /* @__PURE__ */ new Set();
154
+ for (const deps of Object.values(newDependencies)) {
155
+ for (const dep of deps) {
156
+ allDeps.add(dep);
157
+ }
158
+ }
159
+ const roots = Object.keys(taskGraph.tasks).filter((taskId) => !allDeps.has(taskId));
160
+ return {
161
+ dependencies: newDependencies,
162
+ roots,
163
+ tasks: { ...taskGraph.tasks }
164
+ };
165
+ };
166
+ const getDependentTasks = (taskGraph, taskId) => {
167
+ const reversed = reverseTaskGraph(taskGraph);
168
+ const result = [];
169
+ const visited = /* @__PURE__ */ new Set();
170
+ const queue = [taskId];
171
+ while (queue.length > 0) {
172
+ const current = queue.shift();
173
+ if (visited.has(current)) {
174
+ continue;
175
+ }
176
+ visited.add(current);
177
+ if (current !== taskId) {
178
+ result.push(current);
179
+ }
180
+ const deps = reversed.dependencies[current] ?? [];
181
+ for (const dep of deps) {
182
+ if (!visited.has(dep)) {
183
+ queue.push(dep);
184
+ }
185
+ }
186
+ }
187
+ return result;
188
+ };
189
+ const getTransitiveDependencies = (taskGraph, taskId) => {
190
+ const result = [];
191
+ const visited = /* @__PURE__ */ new Set();
192
+ const queue = [taskId];
193
+ while (queue.length > 0) {
194
+ const current = queue.shift();
195
+ if (visited.has(current)) {
196
+ continue;
197
+ }
198
+ visited.add(current);
199
+ if (current !== taskId) {
200
+ result.push(current);
201
+ }
202
+ const deps = taskGraph.dependencies[current] ?? [];
203
+ for (const dep of deps) {
204
+ if (!visited.has(dep)) {
205
+ queue.push(dep);
206
+ }
207
+ }
208
+ }
209
+ return result;
210
+ };
211
+
212
+ export { findCycle, findCycles, getDependentTasks, getLeafTasks, getTransitiveDependencies, makeAcyclic, reverseTaskGraph, walkTaskGraph };
@@ -0,0 +1,134 @@
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
4
+
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
+
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
15
+ }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
19
+
20
+ const {
21
+ mkdir,
22
+ writeFile
23
+ } = __cjs_getBuiltinModule("node:fs/promises");
24
+ import { join } from '@visulima/path';
25
+ import { u as uniqueId } from './utils-zO0ZRgtf.js';
26
+
27
+ const getCacheStatus = (result) => {
28
+ switch (result.status) {
29
+ case "local-cache":
30
+ case "local-cache-kept-existing": {
31
+ return "HIT";
32
+ }
33
+ case "remote-cache": {
34
+ return "REMOTE_HIT";
35
+ }
36
+ case "skipped": {
37
+ return "SKIPPED";
38
+ }
39
+ default: {
40
+ return "MISS";
41
+ }
42
+ }
43
+ };
44
+ const generateRunSummary = (results, taskGraph, startTime) => {
45
+ const endTime = Date.now();
46
+ const id = `${new Date(startTime).toISOString().replaceAll(/[:.]/g, "-")}_${uniqueId()}`;
47
+ const tasks = [];
48
+ let succeeded = 0;
49
+ let failed = 0;
50
+ let cached = 0;
51
+ let skipped = 0;
52
+ for (const [taskId, result] of results) {
53
+ const taskDeps = taskGraph.dependencies[taskId] ?? [];
54
+ const summary = {
55
+ cacheable: result.task.cache !== false,
56
+ cacheStatus: getCacheStatus(result),
57
+ dependencies: taskDeps,
58
+ duration: result.startTime && result.endTime ? result.endTime - result.startTime : void 0,
59
+ endTime: result.endTime ? new Date(result.endTime).toISOString() : void 0,
60
+ exitCode: result.code,
61
+ hash: result.task.hash,
62
+ hashDetails: result.task.hashDetails,
63
+ outputs: result.task.outputs,
64
+ startTime: result.startTime ? new Date(result.startTime).toISOString() : void 0,
65
+ target: {
66
+ configuration: result.task.target.configuration,
67
+ project: result.task.target.project,
68
+ target: result.task.target.target
69
+ },
70
+ taskId
71
+ };
72
+ tasks.push(summary);
73
+ switch (result.status) {
74
+ case "failure": {
75
+ failed += 1;
76
+ break;
77
+ }
78
+ case "local-cache":
79
+ // falls through
80
+ case "local-cache-kept-existing":
81
+ case "remote-cache": {
82
+ cached += 1;
83
+ break;
84
+ }
85
+ case "skipped": {
86
+ skipped += 1;
87
+ break;
88
+ }
89
+ case "success": {
90
+ succeeded += 1;
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ const sortedTasks = tasks.toSorted((a, b) => {
96
+ if (!a.startTime || !b.startTime) {
97
+ return 0;
98
+ }
99
+ return a.startTime.localeCompare(b.startTime);
100
+ });
101
+ return {
102
+ duration: endTime - startTime,
103
+ endTime: new Date(endTime).toISOString(),
104
+ environment: {
105
+ arch: process.arch,
106
+ nodeVersion: process.version,
107
+ platform: process.platform
108
+ },
109
+ id,
110
+ startTime: new Date(startTime).toISOString(),
111
+ stats: {
112
+ cached,
113
+ failed,
114
+ skipped,
115
+ succeeded,
116
+ total: results.size
117
+ },
118
+ taskGraph: {
119
+ dependencies: taskGraph.dependencies,
120
+ roots: taskGraph.roots
121
+ },
122
+ tasks: sortedTasks
123
+ };
124
+ };
125
+ const writeRunSummary = async (summary, workspaceRoot) => {
126
+ const runsDirectory = join(workspaceRoot, ".task-runner", "runs");
127
+ await mkdir(runsDirectory, { recursive: true });
128
+ const filename = `${summary.id}.json`;
129
+ const filePath = join(runsDirectory, filename);
130
+ await writeFile(filePath, JSON.stringify(summary, void 0, 2));
131
+ return filePath;
132
+ };
133
+
134
+ export { generateRunSummary, writeRunSummary };
@@ -0,0 +1,19 @@
1
+ import __cjs_mod__ from "node:module"; // -- packem CommonJS require shim --
2
+ const require = __cjs_mod__.createRequire(import.meta.url);
3
+ let nativeBindings;
4
+ let loadAttempted = false;
5
+ const loadNativeBindings = () => {
6
+ if (loadAttempted) {
7
+ return nativeBindings;
8
+ }
9
+ loadAttempted = true;
10
+ try {
11
+ nativeBindings = require("../binding.js");
12
+ } catch {
13
+ nativeBindings = void 0;
14
+ }
15
+ return nativeBindings;
16
+ };
17
+ const isNativeAvailable = () => loadNativeBindings() !== void 0;
18
+
19
+ export { isNativeAvailable, loadNativeBindings };