@visulima/task-runner 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (102) hide show
  1. package/CHANGELOG.md +310 -0
  2. package/README.md +198 -52
  3. package/dist/index.d.ts +3795 -34
  4. package/dist/index.js +1 -20
  5. package/dist/packem_chunks/index.js +31 -0
  6. package/dist/packem_shared/Cache-C8FfeXpg.js +2 -0
  7. package/dist/packem_shared/CompositeLifeCycle-C6aee9GK.js +1 -0
  8. package/dist/packem_shared/FileAccessTracker-DBz_w4wl.js +50 -0
  9. package/dist/packem_shared/FingerprintManager-CYW2EwLc.js +2 -0
  10. package/dist/packem_shared/HttpRemoteCache-CpPl6lzE.js +1 -0
  11. package/dist/packem_shared/INPUT_URI_SCHEMES-Csrd0tlg.js +1 -0
  12. package/dist/packem_shared/IncrementalFileHasher-B-V3i2x-.js +1 -0
  13. package/dist/packem_shared/LogReporter-3R3oWj-Q.js +13 -0
  14. package/dist/packem_shared/ReapiRemoteCache-BXJip5wH.js +251 -0
  15. package/dist/packem_shared/TaskOrchestrator-CYj5MLwz.js +6 -0
  16. package/dist/packem_shared/TerminalBuffer-BtZy7TpT.js +3 -0
  17. package/dist/packem_shared/TrackedTaskExecutor-CtYLL3vS.js +1 -0
  18. package/dist/packem_shared/V2_ROOT-injxWBrl.js +1 -0
  19. package/dist/packem_shared/actionDigestForTaskHash-BOL4fZ9v.js +1 -0
  20. package/dist/packem_shared/archive-CDfGy5Lm.js +1 -0
  21. package/dist/packem_shared/buildForwardDependencyMap-DudUDFze.js +3 -0
  22. package/dist/packem_shared/collectFiles-W4bnBRpb.js +1 -0
  23. package/dist/packem_shared/collectNodeModulesBinDirs-CD-eDrtO.js +1 -0
  24. package/dist/packem_shared/computeTaskHash-CaPdG1BA.js +1 -0
  25. package/dist/packem_shared/containsBlob-DAU8R7GH.js +1 -0
  26. package/dist/packem_shared/createInputHandler-CkDCpPYy.js +1 -0
  27. package/dist/packem_shared/createTaskGraph-D8Jn_Dn9.js +1 -0
  28. package/dist/packem_shared/defaultTaskRunner-DMHpavzm.js +2 -0
  29. package/dist/packem_shared/detectFrameworks-WVZJOPgN.js +1 -0
  30. package/dist/packem_shared/detectScriptShell-CaTDk5cW.js +1 -0
  31. package/dist/packem_shared/digestBuffer-g11aCaDx.js +1 -0
  32. package/dist/packem_shared/enforceProjectConstraints-dNc1SwRi.js +1 -0
  33. package/dist/packem_shared/expandArguments-D7qvc6Rp.js +1 -0
  34. package/dist/packem_shared/expandShortcut-BErNHNXZ.js +1 -0
  35. package/dist/packem_shared/expandTokensInString-DVSFEdWu.js +1 -0
  36. package/dist/packem_shared/expandWildcard-DE0dOOZF.js +1 -0
  37. package/dist/packem_shared/extractPackageName-BeL6Gc3a.js +1 -0
  38. package/dist/packem_shared/findCycle-BY8-jmzB.js +1 -0
  39. package/dist/packem_shared/formatTimingTable-CP3rsDwf.js +7 -0
  40. package/dist/packem_shared/generateRunSummary-L9Z2NfWn.js +1 -0
  41. package/dist/packem_shared/getCurrentBranch-D-qoZByx.js +1 -0
  42. package/dist/packem_shared/getMainWorktreeRoot-DB9P2wDL.js +1 -0
  43. package/dist/packem_shared/isNativeAvailable-CkTjxb7P.js +1 -0
  44. package/dist/packem_shared/parseCommands-BHsXoUCd.js +1 -0
  45. package/dist/packem_shared/parsePartition-Bt1jBjZH.js +1 -0
  46. package/dist/packem_shared/projectGraphToDot-FN6oHDGH.js +250 -0
  47. package/dist/packem_shared/resolveCacheMode--4y60ODd.js +1 -0
  48. package/dist/packem_shared/resolveOutputs-CzGGEbcP.js +1 -0
  49. package/dist/packem_shared/runConcurrentFallback-BhJCT2LA.js +3 -0
  50. package/dist/packem_shared/runConcurrently-D1Ytsjaj.js +1 -0
  51. package/dist/packem_shared/runTeardown-DAn1xFWJ.js +1 -0
  52. package/dist/packem_shared/shell-quote-BhmqDUL1.js +1 -0
  53. package/dist/packem_shared/stripQuotes-jkZb0CL9.js +1 -0
  54. package/dist/packem_shared/toChromeTrace-DxN5NQIU.js +1 -0
  55. package/dist/packem_shared/tracked-executor-B90U4Um3.js +3 -0
  56. package/dist/packem_shared/utils-BH2W5Wml.js +1 -0
  57. package/dist/packem_shared/withRestart-DKtEGsQA.js +1 -0
  58. package/index.js +603 -0
  59. package/package.json +31 -19
  60. package/binding.js +0 -204
  61. package/dist/affected.d.ts +0 -48
  62. package/dist/cache.d.ts +0 -103
  63. package/dist/default-task-runner.d.ts +0 -44
  64. package/dist/file-access-tracker.d.ts +0 -53
  65. package/dist/fingerprint.d.ts +0 -45
  66. package/dist/framework-inference.d.ts +0 -35
  67. package/dist/graph-visualizer.d.ts +0 -74
  68. package/dist/incremental-hasher.d.ts +0 -58
  69. package/dist/life-cycle.d.ts +0 -36
  70. package/dist/lockfile-hasher.d.ts +0 -73
  71. package/dist/native-binding.d.ts +0 -64
  72. package/dist/packem_shared/Cache-IYpTYVUC.js +0 -298
  73. package/dist/packem_shared/CompositeLifeCycle-7AtYw1dv.js +0 -112
  74. package/dist/packem_shared/FileAccessTracker-CrtBAt5D.js +0 -239
  75. package/dist/packem_shared/FingerprintManager-D6Y0erg-.js +0 -227
  76. package/dist/packem_shared/IncrementalFileHasher-Ds3J6dgb.js +0 -151
  77. package/dist/packem_shared/RemoteCache-BDqrnDEi.js +0 -179
  78. package/dist/packem_shared/TaskOrchestrator-BvYs3ONw.js +0 -342
  79. package/dist/packem_shared/TaskScheduler-CJilHDta.js +0 -111
  80. package/dist/packem_shared/TrackedTaskExecutor-BGUKFE-7.js +0 -164
  81. package/dist/packem_shared/collectFiles-ClXHnHhg.js +0 -22
  82. package/dist/packem_shared/computeTaskHash-BoCnnvIJ.js +0 -356
  83. package/dist/packem_shared/createTaskGraph-CcsFaSrz.js +0 -164
  84. package/dist/packem_shared/defaultTaskRunner-CrW4v5Ye.js +0 -79
  85. package/dist/packem_shared/detectFrameworks-CeFzKE6J.js +0 -101
  86. package/dist/packem_shared/extractPackageName-CbVNW-dr.js +0 -189
  87. package/dist/packem_shared/filterAffectedTasks-I-18zPg6.js +0 -135
  88. package/dist/packem_shared/findCycle-DF4_BRdO.js +0 -212
  89. package/dist/packem_shared/generateRunSummary-qn-_jKwt.js +0 -134
  90. package/dist/packem_shared/isNativeAvailable-BWhnZ4ES.js +0 -19
  91. package/dist/packem_shared/projectGraphToDot-VdTjHcVp.js +0 -202
  92. package/dist/packem_shared/utils-zO0ZRgtf.js +0 -390
  93. package/dist/remote-cache.d.ts +0 -55
  94. package/dist/run-summary.d.ts +0 -89
  95. package/dist/task-graph-utils.d.ts +0 -39
  96. package/dist/task-graph.d.ts +0 -22
  97. package/dist/task-hasher.d.ts +0 -67
  98. package/dist/task-orchestrator.d.ts +0 -38
  99. package/dist/task-scheduler.d.ts +0 -18
  100. package/dist/tracked-executor.d.ts +0 -46
  101. package/dist/types.d.ts +0 -385
  102. package/dist/utils.d.ts +0 -39
@@ -1,36 +0,0 @@
1
- import type { LifeCycleInterface, Task, TaskResult, TaskStatus } from "./types.d.ts";
2
- /**
3
- * Combines multiple lifecycle handlers into one.
4
- * Each event is forwarded to all registered handlers.
5
- */
6
- declare class CompositeLifeCycle implements LifeCycleInterface {
7
- #private;
8
- constructor(lifeCycles: LifeCycleInterface[]);
9
- startCommand(): void;
10
- endCommand(): void;
11
- scheduleTask(task: Task): void;
12
- startTasks(tasks: Task[]): void;
13
- endTasks(taskResults: TaskResult[]): void;
14
- printTaskTerminalOutput(task: Task, status: TaskStatus, terminalOutput: string): void;
15
- printCacheMiss(task: Task, reasons: string): void;
16
- }
17
- /**
18
- * A lifecycle handler that logs task progress to the console.
19
- */
20
- declare class ConsoleLifeCycle implements LifeCycleInterface {
21
- #private;
22
- constructor(verbose?: boolean);
23
- startCommand(): void;
24
- endCommand(): void;
25
- scheduleTask(task: Task): void;
26
- startTasks(tasks: Task[]): void;
27
- endTasks(taskResults: TaskResult[]): void;
28
- printTaskTerminalOutput(_task: Task, _status: TaskStatus, terminalOutput: string): void;
29
- printCacheMiss(task: Task, reasons: string): void;
30
- }
31
- /**
32
- * A no-op lifecycle handler. Useful as a default.
33
- */
34
- declare class EmptyLifeCycle implements LifeCycleInterface {
35
- }
36
- export { CompositeLifeCycle, ConsoleLifeCycle, EmptyLifeCycle };
@@ -1,73 +0,0 @@
1
- /**
2
- * Resolved dependency entry from a lockfile.
3
- */
4
- interface ResolvedDependency {
5
- /** The package name */
6
- name: string;
7
- /** The resolved version */
8
- version: string;
9
- }
10
- /**
11
- * Result of parsing a lockfile for a specific package.
12
- */
13
- interface PackageLockfileHash {
14
- /** The resolved dependencies that were included in the hash */
15
- dependencies: ResolvedDependency[];
16
- /** Hash of the resolved dependencies relevant to this package */
17
- hash: string;
18
- }
19
- /**
20
- * Extracts a package name from a node_modules path.
21
- * E.g., "node_modules/@scope/name" -> "@scope/name",
22
- * "node_modules/name" -> "name",
23
- * "node_modules/.package-lock.json" -> undefined.
24
- */
25
- declare const extractPackageName: (path: string) => string | undefined;
26
- /**
27
- * Parses package-lock.json (npm v2/v3 format) to extract resolved versions.
28
- * The v2/v3 format uses a flat "packages" map with paths like "node_modules/pkg-name".
29
- */
30
- declare const parseNpmLockfile: (content: string) => Map<string, string>;
31
- /**
32
- * Parses pnpm-lock.yaml to extract resolved versions.
33
- * Uses a lightweight regex-based parser to avoid a YAML dependency.
34
- */
35
- declare const parsePnpmLockfile: (content: string) => Map<string, string>;
36
- /**
37
- * Parses yarn.lock to extract resolved versions.
38
- * Works with both Yarn Classic (v1) and Berry (v2+) formats.
39
- */
40
- declare const parseYarnLockfile: (content: string) => Map<string, string>;
41
- /**
42
- * Smart lockfile hasher that only hashes the resolved versions
43
- * of a package's actual dependencies, not the entire lockfile.
44
- *
45
- * This matches Turborepo's smart lockfile hashing behavior:
46
- * changing the lockfile only busts cache for affected packages.
47
- *
48
- * Supports:
49
- * - package-lock.json (npm v2/v3)
50
- * - pnpm-lock.yaml (pnpm)
51
- * - yarn.lock (Yarn Classic + Berry)
52
- */
53
- declare class LockfileHasher {
54
- #private;
55
- constructor(workspaceRoot: string);
56
- /**
57
- * Computes a hash based only on the resolved dependency versions
58
- * relevant to a specific package.
59
- * @param packageJsonPath Path to the package.json (relative to workspace root)
60
- * @returns Hash of the relevant lockfile entries, or undefined if no lockfile found
61
- */
62
- hashForPackage(packageJsonPath: string): Promise<PackageLockfileHash | undefined>;
63
- /**
64
- * Returns the type of lockfile detected, or undefined if none found.
65
- */
66
- get lockfileType(): "npm" | "pnpm" | "yarn" | undefined;
67
- /**
68
- * Clears the cached lockfile data.
69
- */
70
- clearCache(): void;
71
- }
72
- export type { PackageLockfileHash, ResolvedDependency };
73
- export { extractPackageName, LockfileHasher, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile };
@@ -1,64 +0,0 @@
1
- /**
2
- * Optional native bindings for performance-critical operations.
3
- *
4
- * The native addon (Rust via napi-rs) provides:
5
- * - Parallel file hashing using rayon + xxHash xxh3-128
6
- * - Optimized task hash computation
7
- * - Fast graph operations (cycle detection, topological sort)
8
- *
9
- * Falls back to pure TypeScript implementations when the native
10
- * addon is not available (not compiled, wrong platform, etc.).
11
- *
12
- * Build with: pnpm build:native
13
- * The napi v3 CLI outputs the .node file to the package root.
14
- */
15
- interface NativeFileHash {
16
- hash: string;
17
- path: string;
18
- }
19
- interface NativeTaskHashDetails {
20
- command: string;
21
- implicit_deps?: string[][];
22
- nodes: string[][];
23
- runtime?: string[][];
24
- }
25
- interface NativeTaskGraph {
26
- edges: string[][];
27
- task_ids: string[];
28
- }
29
- interface NativeCycleResult {
30
- cycle: string[];
31
- has_cycle: boolean;
32
- }
33
- interface NativeBindings {
34
- collectFiles: (directory: string) => string[];
35
- computeTaskHash: (details: NativeTaskHashDetails) => string;
36
- findAllCycles: (graph: NativeTaskGraph) => string[][];
37
- findBackEdges: (graph: NativeTaskGraph) => string[][];
38
- findCycle: (graph: NativeTaskGraph) => NativeCycleResult;
39
- getDependentTasks: (graph: NativeTaskGraph, taskId: string) => string[];
40
- getTransitiveDeps: (graph: NativeTaskGraph, taskId: string) => string[];
41
- hashCommand: (project: string, target: string, configuration: string | undefined, overridesJson: string) => string;
42
- hashEnvVar: (name: string, value: string) => string;
43
- hashFile: (filePath: string) => string;
44
- hashFilesBatch: (filePaths: string[], workspaceRoot: string) => NativeFileHash[];
45
- hashFilesInDirectory: (directory: string, workspaceRoot: string) => NativeFileHash[];
46
- hashString: (input: string) => string;
47
- hashStrings: (inputs: string[]) => string;
48
- topologicalSort: (graph: NativeTaskGraph) => string[];
49
- }
50
- /**
51
- * Attempts to load the native addon. Returns undefined if unavailable.
52
- * The result is cached after the first attempt.
53
- *
54
- * napi v3 outputs the .node file to the package root as
55
- * `task-runner-native.&lt;platform>.node`. The napi-generated binding.js
56
- * handles platform detection automatically.
57
- */
58
- declare const loadNativeBindings: () => NativeBindings | undefined;
59
- /**
60
- * Returns true if the native addon is loaded and available.
61
- */
62
- declare const isNativeAvailable: () => boolean;
63
- export type { NativeBindings, NativeCycleResult, NativeFileHash, NativeTaskGraph, NativeTaskHashDetails };
64
- export { isNativeAvailable, loadNativeBindings };
@@ -1,298 +0,0 @@
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
- mkdir,
23
- writeFile,
24
- rename,
25
- stat,
26
- rm,
27
- cp,
28
- readdir
29
- } = __cjs_getBuiltinModule("node:fs/promises");
30
- import { join, resolve, dirname } from '@visulima/path';
31
- import { formatBytes, parseBytes } from '@visulima/humanizer';
32
- import { u as uniqueId } from './utils-zO0ZRgtf.js';
33
-
34
- const DEFAULT_MAX_CACHE_AGE = 7 * 24 * 60 * 60 * 1e3;
35
- const removeEntry = async (entryPath) => {
36
- try {
37
- await rm(entryPath, { force: true, recursive: true });
38
- } catch {
39
- }
40
- };
41
- const getDirectorySize = async (directoryPath) => {
42
- let totalSize = 0;
43
- try {
44
- const entries = await readdir(directoryPath, { withFileTypes: true });
45
- for (const entry of entries) {
46
- const fullPath = join(directoryPath, entry.name);
47
- if (entry.isDirectory()) {
48
- totalSize += await getDirectorySize(fullPath);
49
- } else if (entry.isFile()) {
50
- const fileStat = await stat(fullPath);
51
- totalSize += fileStat.size;
52
- }
53
- }
54
- } catch {
55
- }
56
- return totalSize;
57
- };
58
- const parseCacheSize = (sizeString) => {
59
- const result = parseBytes(sizeString.trim());
60
- if (Number.isNaN(result)) {
61
- throw new Error(`Invalid cache size format: "${sizeString}". Expected format like "500MB" or "1GB".`);
62
- }
63
- return result;
64
- };
65
- const formatCacheSize = (bytes) => formatBytes(bytes, { decimals: 1, space: false });
66
- class Cache {
67
- #workspaceRoot;
68
- #cacheDirectory;
69
- #maxCacheAge;
70
- #maxCacheSize;
71
- /** Serializes concurrent setTaskIndex writes to prevent lost updates */
72
- #indexWriteQueue = Promise.resolve();
73
- constructor(options) {
74
- this.#workspaceRoot = options.workspaceRoot;
75
- this.#cacheDirectory = options.cacheDirectory ?? join(options.workspaceRoot, ".task-runner-cache");
76
- this.#maxCacheAge = options.maxCacheAge ?? DEFAULT_MAX_CACHE_AGE;
77
- this.#maxCacheSize = options.maxCacheSize ? parseCacheSize(options.maxCacheSize) : void 0;
78
- }
79
- /**
80
- * Gets the cache directory path.
81
- */
82
- get cacheDirectory() {
83
- return this.#cacheDirectory;
84
- }
85
- /**
86
- * Retrieves a cached result for the given task hash.
87
- * Returns undefined if no valid cache entry exists.
88
- */
89
- async get(hash) {
90
- const cacheEntryDirectory = join(this.#cacheDirectory, hash);
91
- try {
92
- await readFile(join(cacheEntryDirectory, ".commit"));
93
- const codeString = await readFile(join(cacheEntryDirectory, "code"), "utf8");
94
- const code = Number.parseInt(codeString.trim(), 10);
95
- if (Number.isNaN(code)) {
96
- return void 0;
97
- }
98
- let terminalOutput = "";
99
- try {
100
- terminalOutput = await readFile(join(cacheEntryDirectory, "terminalOutput"), "utf8");
101
- } catch {
102
- }
103
- let fingerprint;
104
- try {
105
- const fingerprintContent = await readFile(join(cacheEntryDirectory, "fingerprint.json"), "utf8");
106
- fingerprint = JSON.parse(fingerprintContent);
107
- } catch {
108
- }
109
- return { code, fingerprint, hash, terminalOutput };
110
- } catch {
111
- return void 0;
112
- }
113
- }
114
- /**
115
- * Stores a task result in the cache.
116
- *
117
- * Uses atomic write: builds the entry in a temporary directory,
118
- * then renames into place to avoid partial reads by concurrent processes.
119
- */
120
- async put(hash, terminalOutput, outputs, code, fingerprint) {
121
- const cacheEntryDirectory = join(this.#cacheDirectory, hash);
122
- const temporaryDirectory = join(this.#cacheDirectory, `.tmp-${hash}-${uniqueId()}`);
123
- try {
124
- await mkdir(temporaryDirectory, { recursive: true });
125
- const writes = [
126
- writeFile(join(temporaryDirectory, "code"), String(code)),
127
- writeFile(join(temporaryDirectory, "terminalOutput"), terminalOutput),
128
- this.#archiveOutputs(temporaryDirectory, outputs)
129
- ];
130
- if (fingerprint) {
131
- writes.push(writeFile(join(temporaryDirectory, "fingerprint.json"), JSON.stringify(fingerprint)));
132
- }
133
- await Promise.all(writes);
134
- await writeFile(join(temporaryDirectory, ".commit"), "");
135
- await removeEntry(cacheEntryDirectory);
136
- await rename(temporaryDirectory, cacheEntryDirectory);
137
- } catch {
138
- await removeEntry(temporaryDirectory);
139
- }
140
- }
141
- /**
142
- * Restores cached outputs to their original locations.
143
- */
144
- async restoreOutputs(hash, outputs) {
145
- const cacheEntryDirectory = join(this.#cacheDirectory, hash);
146
- const outputsDirectory = join(cacheEntryDirectory, "outputs");
147
- try {
148
- await stat(outputsDirectory);
149
- } catch {
150
- return true;
151
- }
152
- try {
153
- await Promise.all(
154
- outputs.map(async (output) => {
155
- const absoluteOutput = resolve(this.#workspaceRoot, output);
156
- const cachedOutput = join(outputsDirectory, output);
157
- try {
158
- await stat(cachedOutput);
159
- } catch {
160
- return;
161
- }
162
- const parentDirectory = dirname(absoluteOutput);
163
- await mkdir(parentDirectory, { recursive: true });
164
- await rm(absoluteOutput, { force: true, recursive: true });
165
- await cp(cachedOutput, absoluteOutput, { recursive: true });
166
- })
167
- );
168
- return true;
169
- } catch {
170
- return false;
171
- }
172
- }
173
- /**
174
- * Retrieves the most recent cached result for a task by its ID.
175
- * Used in auto-fingerprint mode where the hash is derived from
176
- * tracked file accesses rather than computed upfront.
177
- */
178
- async getByTaskId(taskId) {
179
- const indexFile = join(this.#cacheDirectory, ".task-index.json");
180
- try {
181
- const indexContent = await readFile(indexFile, "utf8");
182
- const index = JSON.parse(indexContent);
183
- const hash = index[taskId];
184
- if (!hash) {
185
- return void 0;
186
- }
187
- return this.get(hash);
188
- } catch {
189
- return void 0;
190
- }
191
- }
192
- /**
193
- * Stores the mapping from task ID to cache hash.
194
- * Uses a write queue to serialize concurrent writes and prevent lost updates.
195
- * Each write is atomic (temp file + rename).
196
- */
197
- async setTaskIndex(taskId, hash) {
198
- this.#indexWriteQueue = this.#indexWriteQueue.then(() => this.#writeTaskIndex(taskId, hash)).catch(() => {
199
- });
200
- return this.#indexWriteQueue;
201
- }
202
- async #writeTaskIndex(taskId, hash) {
203
- const indexFile = join(this.#cacheDirectory, ".task-index.json");
204
- const temporaryFile = join(this.#cacheDirectory, `.task-index-${uniqueId()}.tmp`);
205
- let index = {};
206
- try {
207
- const indexContent = await readFile(indexFile, "utf8");
208
- index = JSON.parse(indexContent);
209
- } catch {
210
- }
211
- index[taskId] = hash;
212
- await mkdir(this.#cacheDirectory, { recursive: true });
213
- await writeFile(temporaryFile, JSON.stringify(index));
214
- try {
215
- await rename(temporaryFile, indexFile);
216
- } catch {
217
- await writeFile(indexFile, JSON.stringify(index));
218
- await rm(temporaryFile, { force: true });
219
- }
220
- }
221
- /**
222
- * Removes old cache entries that exceed the maximum age,
223
- * and enforces the maximum cache size by evicting oldest entries.
224
- */
225
- // eslint-disable-next-line sonarjs/cognitive-complexity
226
- async removeOldEntries() {
227
- try {
228
- const entries = await readdir(this.#cacheDirectory);
229
- const now = Date.now();
230
- const entryInfos = [];
231
- for (const entry of entries) {
232
- if (entry.startsWith(".")) {
233
- continue;
234
- }
235
- const entryPath = join(this.#cacheDirectory, entry);
236
- try {
237
- const entryStat = await stat(entryPath);
238
- if (now - entryStat.mtimeMs > this.#maxCacheAge) {
239
- await removeEntry(entryPath);
240
- } else {
241
- const size = await getDirectorySize(entryPath);
242
- entryInfos.push({
243
- mtimeMs: entryStat.mtimeMs,
244
- name: entry,
245
- path: entryPath,
246
- size
247
- });
248
- }
249
- } catch {
250
- }
251
- }
252
- if (this.#maxCacheSize !== void 0) {
253
- const sortedEntries = entryInfos.toSorted((a, b) => a.mtimeMs - b.mtimeMs);
254
- let totalSize = 0;
255
- for (const info of sortedEntries) {
256
- totalSize += info.size;
257
- }
258
- for (const info of sortedEntries) {
259
- if (totalSize <= this.#maxCacheSize) {
260
- break;
261
- }
262
- await removeEntry(info.path);
263
- totalSize -= info.size;
264
- }
265
- }
266
- } catch {
267
- }
268
- }
269
- /**
270
- * Clears the entire cache.
271
- */
272
- async clear() {
273
- try {
274
- await rm(this.#cacheDirectory, { force: true, recursive: true });
275
- } catch {
276
- }
277
- }
278
- /**
279
- * Archives task output files into the cache.
280
- */
281
- async #archiveOutputs(cacheEntryDirectory, outputs) {
282
- const outputsDirectory = join(cacheEntryDirectory, "outputs");
283
- await mkdir(outputsDirectory, { recursive: true });
284
- for (const output of outputs) {
285
- const absoluteOutput = resolve(this.#workspaceRoot, output);
286
- const cachedOutput = join(outputsDirectory, output);
287
- try {
288
- await stat(absoluteOutput);
289
- const cachedOutputParent = join(cachedOutput, "..");
290
- await mkdir(cachedOutputParent, { recursive: true });
291
- await cp(absoluteOutput, cachedOutput, { recursive: true });
292
- } catch {
293
- }
294
- }
295
- }
296
- }
297
-
298
- export { Cache, formatCacheSize, parseCacheSize };
@@ -1,112 +0,0 @@
1
- const getStatusIcon = (status) => {
2
- switch (status) {
3
- case "failure": {
4
- return "[failure]";
5
- }
6
- case "local-cache":
7
- case "local-cache-kept-existing":
8
- case "remote-cache": {
9
- return "[cache]";
10
- }
11
- case "skipped": {
12
- return "[skipped]";
13
- }
14
- case "success": {
15
- return "[success]";
16
- }
17
- default: {
18
- return "[unknown]";
19
- }
20
- }
21
- };
22
- class CompositeLifeCycle {
23
- #lifeCycles;
24
- constructor(lifeCycles) {
25
- this.#lifeCycles = lifeCycles;
26
- }
27
- startCommand() {
28
- for (const lc of this.#lifeCycles) {
29
- lc.startCommand?.();
30
- }
31
- }
32
- endCommand() {
33
- for (const lc of this.#lifeCycles) {
34
- lc.endCommand?.();
35
- }
36
- }
37
- scheduleTask(task) {
38
- for (const lc of this.#lifeCycles) {
39
- lc.scheduleTask?.(task);
40
- }
41
- }
42
- startTasks(tasks) {
43
- for (const lc of this.#lifeCycles) {
44
- lc.startTasks?.(tasks);
45
- }
46
- }
47
- endTasks(taskResults) {
48
- for (const lc of this.#lifeCycles) {
49
- lc.endTasks?.(taskResults);
50
- }
51
- }
52
- printTaskTerminalOutput(task, status, terminalOutput) {
53
- for (const lc of this.#lifeCycles) {
54
- lc.printTaskTerminalOutput?.(task, status, terminalOutput);
55
- }
56
- }
57
- printCacheMiss(task, reasons) {
58
- for (const lc of this.#lifeCycles) {
59
- lc.printCacheMiss?.(task, reasons);
60
- }
61
- }
62
- }
63
- class ConsoleLifeCycle {
64
- #verbose;
65
- constructor(verbose = false) {
66
- this.#verbose = verbose;
67
- }
68
- startCommand() {
69
- if (this.#verbose) {
70
- console.log("[task-runner] Starting command execution");
71
- }
72
- }
73
- endCommand() {
74
- if (this.#verbose) {
75
- console.log("[task-runner] Command execution complete");
76
- }
77
- }
78
- scheduleTask(task) {
79
- if (this.#verbose) {
80
- console.log(`[task-runner] Scheduled: ${task.id}`);
81
- }
82
- }
83
- // eslint-disable-next-line class-methods-use-this
84
- startTasks(tasks) {
85
- for (const task of tasks) {
86
- console.log(`> ${task.id}`);
87
- }
88
- }
89
- // eslint-disable-next-line class-methods-use-this
90
- endTasks(taskResults) {
91
- for (const result of taskResults) {
92
- const duration = result.startTime && result.endTime ? ` (${result.endTime - result.startTime}ms)` : "";
93
- const statusIcon = getStatusIcon(result.status);
94
- console.log(`${statusIcon} ${result.task.id}${duration}`);
95
- }
96
- }
97
- // eslint-disable-next-line class-methods-use-this
98
- printTaskTerminalOutput(_task, _status, terminalOutput) {
99
- if (terminalOutput.trim()) {
100
- console.log(terminalOutput);
101
- }
102
- }
103
- printCacheMiss(task, reasons) {
104
- if (this.#verbose) {
105
- console.log(`[task-runner] ${task.id}: ${reasons}`);
106
- }
107
- }
108
- }
109
- class EmptyLifeCycle {
110
- }
111
-
112
- export { CompositeLifeCycle, ConsoleLifeCycle, EmptyLifeCycle };