@visulima/task-runner 1.0.0-alpha.5 → 1.0.0-alpha.6
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 +31 -0
- package/dist/archive.d.ts +38 -0
- package/dist/cache.d.ts +31 -3
- package/dist/chrome-trace.d.ts +53 -0
- package/dist/file-access-tracker.d.ts +7 -1
- package/dist/fingerprint.d.ts +9 -0
- package/dist/incremental-hasher.d.ts +18 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.js +22 -19
- package/dist/life-cycle.d.ts +2 -0
- package/dist/log-reporter.d.ts +34 -0
- package/dist/output-resolver.d.ts +20 -0
- package/dist/packem_shared/{Cache-iAjRMV2d.js → Cache-CWaX_c8U.js} +135 -45
- package/dist/packem_shared/{CompositeLifeCycle-7AtYw1dv.js → CompositeLifeCycle-CSVbRC_5.js} +10 -0
- package/dist/packem_shared/{FileAccessTracker-CrtBAt5D.js → FileAccessTracker-CQ5Ot7Hd.js} +68 -16
- package/dist/packem_shared/{FingerprintManager-Cu-ta9ee.js → FingerprintManager-CV7U4f4f.js} +22 -1
- package/dist/packem_shared/{IncrementalFileHasher-Cm_kJY5V.js → IncrementalFileHasher-BRS76-mb.js} +26 -0
- package/dist/packem_shared/LogReporter-BDt52HLu.js +44 -0
- package/dist/packem_shared/{RemoteCache-BFceSe4a.js → RemoteCache-DSU3lc87.js} +77 -37
- package/dist/packem_shared/{TaskOrchestrator-lLn-PH1m.js → TaskOrchestrator-rf45vW5c.js} +94 -15
- package/dist/packem_shared/{TerminalBuffer-CnPyFgPB.js → TerminalBuffer-qVJvbRQZ.js} +1 -1
- package/dist/packem_shared/{TrackedTaskExecutor-BGUKFE-7.js → TrackedTaskExecutor-CFPpQfXF.js} +1 -1
- package/dist/packem_shared/archive-UQHAnZUa.js +102 -0
- package/dist/packem_shared/{buildForwardDependencyMap-0BJFMMPv.js → buildForwardDependencyMap-DLPgKEto.js} +2 -1
- package/dist/packem_shared/{computeTaskHash-B2SVZqgp.js → computeTaskHash-DYqfrDGq.js} +122 -6
- package/dist/packem_shared/{createTaskGraph-CcsFaSrz.js → createTaskGraph-B7nH0kY_.js} +2 -2
- package/dist/packem_shared/{defaultTaskRunner-BdFTifsh.js → defaultTaskRunner-Cp7jCmIl.js} +28 -6
- package/dist/packem_shared/{extractPackageName-CbVNW-dr.js → extractPackageName-BllKetnz.js} +2 -1
- package/dist/packem_shared/{generateRunSummary-qn-_jKwt.js → generateRunSummary-BE1jnQ3H.js} +19 -1
- package/dist/packem_shared/{parsePartition-C4-P5RjK.js → parsePartition-BfLbHGAx.js} +18 -0
- package/dist/packem_shared/{projectGraphToDot-C8uYeaPo.js → projectGraphToDot-DU1lSe-c.js} +1 -1
- package/dist/packem_shared/resolveOutputs-n6MCKoTe.js +111 -0
- package/dist/packem_shared/{runConcurrentFallback-CGHz_f-Q.js → runConcurrentFallback-BTmgGV1H.js} +1 -1
- package/dist/packem_shared/{runConcurrently-qrkWyzXW.js → runConcurrently-CmfC4r-f.js} +1 -1
- package/dist/packem_shared/toChromeTrace-B2tZoJ-7.js +121 -0
- package/dist/remote-cache.d.ts +45 -0
- package/dist/run-summary.d.ts +26 -4
- package/dist/task-hasher.d.ts +37 -0
- package/dist/task-orchestrator.d.ts +2 -2
- package/dist/types.d.ts +137 -3
- package/index.js +52 -52
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
## @visulima/task-runner [1.0.0-alpha.6](https://github.com/visulima/visulima/compare/@visulima/task-runner@1.0.0-alpha.5...@visulima/task-runner@1.0.0-alpha.6) (2026-04-21)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **task-runner:** output globs, auto-writes, parallel cache IO ([137f53f](https://github.com/visulima/visulima/commit/137f53f7f5a4d8c16df511c9d145b2c158025a32))
|
|
6
|
+
* **task-runner:** vite-task parity + plugin-ready lifecycle hooks ([cfc7360](https://github.com/visulima/visulima/commit/cfc7360abf00524fbfc37b60df27970c325f91e1)), closes [pkg#task](https://github.com/visulima/pkg/issues/task)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **task-runner:** resolve eslint errors in chrome-trace and task-hasher ([2cf6266](https://github.com/visulima/visulima/commit/2cf6266b8252bc24a6d900f49f97611d4d629ff3))
|
|
11
|
+
|
|
12
|
+
### Miscellaneous Chores
|
|
13
|
+
|
|
14
|
+
* **api-platform:** apply pending lint and source updates ([3fb0043](https://github.com/visulima/visulima/commit/3fb0043a4cf35f752ca89a09a077100ae0142da8))
|
|
15
|
+
* bump engines.node to ^22.14.0 || >=24.10.0 ([c3d0931](https://github.com/visulima/visulima/commit/c3d0931d1504e4f21ebf50ea680cfa7ce4ba15ce))
|
|
16
|
+
* fixed jsr.json ([5d85e51](https://github.com/visulima/visulima/commit/5d85e5179de38e284ec433b14d77c71a1619c8d6))
|
|
17
|
+
* **task-runner:** apply formatter and lint fixes ([70b4641](https://github.com/visulima/visulima/commit/70b4641b394897ebfc021425b992db936e320d8a))
|
|
18
|
+
* **task-runner:** apply formatter and lint fixes ([aee664f](https://github.com/visulima/visulima/commit/aee664f0a03ed9c1eb8bf7a6a91e62f601a3d5ff))
|
|
19
|
+
* **task-runner:** apply pending changes ([9d92517](https://github.com/visulima/visulima/commit/9d92517f1a54bc28a19cb8cbdb937cdc234e152d))
|
|
20
|
+
* **task-runner:** apply pending lint and source updates ([c01eb39](https://github.com/visulima/visulima/commit/c01eb393f991d8fc96f8ed87bfc71d90902ee659))
|
|
21
|
+
* **task-runner:** enforce curly braces and apply lint fixes ([4fbd8ee](https://github.com/visulima/visulima/commit/4fbd8eefa1b56f428528b495b074d5c266fb6733))
|
|
22
|
+
|
|
23
|
+
### Code Refactoring
|
|
24
|
+
|
|
25
|
+
* replace inline import() types with top-level imports ([4569a4c](https://github.com/visulima/visulima/commit/4569a4ca04723da069f985855dcfab292f7347e1))
|
|
26
|
+
|
|
27
|
+
### Tests
|
|
28
|
+
|
|
29
|
+
* **task-runner:** remove native binding guard from tests ([90f0dff](https://github.com/visulima/visulima/commit/90f0dffe85f4b83e76905a26d53b917365116b45))
|
|
30
|
+
* **task-runner:** skip default excludes for tmpdir-backed tracker tests ([0db0620](https://github.com/visulima/visulima/commit/0db06206173f0799244fafcc578b9fb0be9d2fb6))
|
|
31
|
+
|
|
1
32
|
## @visulima/task-runner [1.0.0-alpha.5](https://github.com/visulima/visulima/compare/@visulima/task-runner@1.0.0-alpha.4...@visulima/task-runner@1.0.0-alpha.5) (2026-04-15)
|
|
2
33
|
|
|
3
34
|
### Features
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tar + brotli archive helpers.
|
|
3
|
+
*
|
|
4
|
+
* Both the local cache (`cache.ts`) and remote cache (`remote-cache.ts`)
|
|
5
|
+
* archive file trees into compressed tarballs. This module consolidates
|
|
6
|
+
* the compression parameters and stream plumbing so both paths stay in
|
|
7
|
+
* sync — same brotli quality, same error-handling shape, one place to
|
|
8
|
+
* audit.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Brotli quality 4 hits a sweet spot for cache tarballs: ~15–20% smaller
|
|
12
|
+
* than gzip on typical source/dist payloads at comparable throughput.
|
|
13
|
+
* Higher qualities (8+) reach diminishing returns and noticeably slow
|
|
14
|
+
* down cache writes; lower qualities (1–3) give up ratio for speed we
|
|
15
|
+
* don't need on IO-bound workloads.
|
|
16
|
+
*/
|
|
17
|
+
export declare const BROTLI_COMPRESS_OPTIONS: {
|
|
18
|
+
params: Record<number, number>;
|
|
19
|
+
};
|
|
20
|
+
/** Plain tar: `sourceDir` → `outputPath`, no compression. */
|
|
21
|
+
export declare const createTar: (sourceDirectory: string, outputPath: string) => Promise<void>;
|
|
22
|
+
/** Plain tar extract into `destinationDirectory`. */
|
|
23
|
+
export declare const extractTar: (archivePath: string, destinationDirectory: string) => Promise<void>;
|
|
24
|
+
/** tar + gzip (`-czf`). Used when Turborepo-protocol compatibility matters. */
|
|
25
|
+
export declare const createTarGz: (sourceDirectory: string, outputPath: string) => Promise<void>;
|
|
26
|
+
export declare const extractTarGz: (archivePath: string, destinationDirectory: string) => Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Creates an uncompressed tar, then streams it through brotli into
|
|
29
|
+
* `outputPath`. Two-step (tar-to-temp, brotli-stream) to avoid
|
|
30
|
+
* shelling out to an external `brotli` binary that may not exist.
|
|
31
|
+
* Cleans up the intermediate tar even when compression fails.
|
|
32
|
+
*/
|
|
33
|
+
export declare const createTarBrotli: (sourceDirectory: string, outputPath: string) => Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Inverse of {@link createTarBrotli}: decompresses into a temp tar,
|
|
36
|
+
* then extracts. Temp is cleaned in `finally`.
|
|
37
|
+
*/
|
|
38
|
+
export declare const extractTarBrotli: (archivePath: string, destinationDirectory: string) => Promise<void>;
|
package/dist/cache.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TaskFingerprint } from "./fingerprint.d.ts";
|
|
2
|
+
import type { OutputSpec } from "./types.d.ts";
|
|
2
3
|
/**
|
|
3
4
|
* Represents a cached task result.
|
|
4
5
|
*/
|
|
@@ -18,6 +19,17 @@ interface CachedResult {
|
|
|
18
19
|
interface CacheOptions {
|
|
19
20
|
/** The cache directory (defaults to `{workspaceRoot}/.task-runner-cache`) */
|
|
20
21
|
cacheDirectory?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional isolation namespace appended to the cache directory
|
|
24
|
+
* as `<cacheDir>/ns/<namespace>`. When the caller derives the
|
|
25
|
+
* namespace from the resolved global-env fingerprint, flipping an
|
|
26
|
+
* env var sends writes into a new namespace while keeping the old
|
|
27
|
+
* namespace intact — rolling the env back restores the old hits.
|
|
28
|
+
*
|
|
29
|
+
* Filesystem-safe segment; callers are responsible for sanitising
|
|
30
|
+
* (slashes/colons would break path resolution).
|
|
31
|
+
*/
|
|
32
|
+
cacheNamespace?: string;
|
|
21
33
|
/** Maximum age of cache entries in milliseconds (default: 7 days) */
|
|
22
34
|
maxCacheAge?: number;
|
|
23
35
|
/** Maximum cache size (e.g., "500MB", "1GB") */
|
|
@@ -78,12 +90,28 @@ declare class Cache {
|
|
|
78
90
|
*
|
|
79
91
|
* Uses atomic write: builds the entry in a temporary directory,
|
|
80
92
|
* then renames into place to avoid partial reads by concurrent processes.
|
|
93
|
+
*
|
|
94
|
+
* `outputs` accepts the richer `OutputSpec[]` shape — glob
|
|
95
|
+
* patterns, negative globs, and `{ auto: true }` entries. Pass
|
|
96
|
+
* `autoWrites` alongside `{ auto: true }` so the resolver knows
|
|
97
|
+
* which files the task actually wrote; otherwise auto entries
|
|
98
|
+
* contribute nothing.
|
|
81
99
|
*/
|
|
82
|
-
put(hash: string, terminalOutput: string, outputs:
|
|
100
|
+
put(hash: string, terminalOutput: string, outputs: OutputSpec[], code: number, fingerprint?: TaskFingerprint, autoWrites?: ReadonlyArray<string>): Promise<void>;
|
|
83
101
|
/**
|
|
84
|
-
* Restores cached outputs
|
|
102
|
+
* Restores cached outputs from the compressed `outputs.tar.br`
|
|
103
|
+
* archive. Returns `true` either when the archive was extracted
|
|
104
|
+
* successfully OR when the entry simply has no outputs to restore.
|
|
105
|
+
*
|
|
106
|
+
* The restore flow stages into a temp directory, then swaps each
|
|
107
|
+
* top-level entry into place (see {@link restoreOutputsCompressed})
|
|
108
|
+
* so a mid-restore failure never destroys the user's working tree.
|
|
109
|
+
* The `outputs` parameter is no longer consulted at restore time —
|
|
110
|
+
* the archive is authoritative, and top-level entries in the
|
|
111
|
+
* extracted staging become the set of swap roots. Still accepted
|
|
112
|
+
* for backward compat.
|
|
85
113
|
*/
|
|
86
|
-
restoreOutputs(hash: string,
|
|
114
|
+
restoreOutputs(hash: string, _outputs?: OutputSpec[]): Promise<boolean>;
|
|
87
115
|
/**
|
|
88
116
|
* Retrieves the most recent cached result for a task by its ID.
|
|
89
117
|
* Used in auto-fingerprint mode where the hash is derived from
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { RunSummary } from "./run-summary.d.ts";
|
|
2
|
+
/**
|
|
3
|
+
* A single event in the Chrome Tracing JSON format. Chrome's
|
|
4
|
+
* chrome://tracing viewer and Perfetto both accept an array of these.
|
|
5
|
+
*
|
|
6
|
+
* Fields track the subset we actually emit:
|
|
7
|
+
* - `ph: "X"` — "complete" span (has duration)
|
|
8
|
+
* - `ph: "s"` / `ph: "f"` — flow start / finish (connects two spans)
|
|
9
|
+
*
|
|
10
|
+
* See the Chrome Trace Event Format spec on docs.google.com
|
|
11
|
+
* (document id: 1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU)
|
|
12
|
+
* for the full specification.
|
|
13
|
+
*/
|
|
14
|
+
export interface ChromeTraceEvent {
|
|
15
|
+
args?: Record<string, unknown>;
|
|
16
|
+
/** Event category — grouped in the viewer's search/filter UI. */
|
|
17
|
+
cat: string;
|
|
18
|
+
/** Duration in microseconds — only set for `ph: "X"` events. */
|
|
19
|
+
dur?: number;
|
|
20
|
+
/** Flow ID — used to draw an arrow from flow-start to flow-finish. */
|
|
21
|
+
id?: number;
|
|
22
|
+
/** Human label shown on the timeline. */
|
|
23
|
+
name: string;
|
|
24
|
+
/**
|
|
25
|
+
* Event phase:
|
|
26
|
+
* - `"X"` — complete (span with duration)
|
|
27
|
+
* - `"s"` — flow start
|
|
28
|
+
* - `"f"` — flow finish
|
|
29
|
+
* - `"M"` — metadata
|
|
30
|
+
*/
|
|
31
|
+
ph: "f" | "M" | "s" | "X";
|
|
32
|
+
pid: number;
|
|
33
|
+
tid: number;
|
|
34
|
+
/** Timestamp in microseconds. */
|
|
35
|
+
ts: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Converts a {@link RunSummary} into a Chrome Tracing event list that
|
|
39
|
+
* renders as a gantt chart in chrome://tracing or Perfetto.
|
|
40
|
+
*
|
|
41
|
+
* Each task becomes a `"X"` span; dependency edges become flow arrows
|
|
42
|
+
* from the dependency's finish to the dependent task's start.
|
|
43
|
+
* Parallel tasks are assigned synthetic `tid` values (lane 0, 1, 2, …)
|
|
44
|
+
* based on the smallest free lane at the task's start time, so the
|
|
45
|
+
* timeline clearly shows concurrency without requiring real thread IDs.
|
|
46
|
+
*/
|
|
47
|
+
export declare const toChromeTrace: (summary: RunSummary) => ChromeTraceEvent[];
|
|
48
|
+
/**
|
|
49
|
+
* Writes a Chrome Tracing JSON file at `outputPath`. Consumers (e.g.
|
|
50
|
+
* a CLI `--profile out.json` flag) call this after the run completes
|
|
51
|
+
* with a RunSummary produced by the task orchestrator.
|
|
52
|
+
*/
|
|
53
|
+
export declare const writeChromeTrace: (summary: RunSummary, outputPath: string) => Promise<void>;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Represents a file access recorded during task execution.
|
|
3
|
+
*
|
|
4
|
+
* Write-intent accesses (`"write"`) are emitted when a task opens a file
|
|
5
|
+
* with `O_WRONLY`/`O_RDWR`/`O_CREAT`/`O_TRUNC` flags (strace) or calls
|
|
6
|
+
* `writeFile`/`appendFile`/`unlink`/`rename` (preload script).
|
|
7
|
+
* The orchestrator uses the overlap of reads and writes to the same
|
|
8
|
+
* workspace path to detect self-modifying tasks and skip caching.
|
|
3
9
|
*/
|
|
4
10
|
export interface FileAccess {
|
|
5
11
|
/** The absolute path of the file */
|
|
6
12
|
path: string;
|
|
7
13
|
/** The type of access */
|
|
8
|
-
type: "
|
|
14
|
+
type: "missing" | "read" | "readdir" | "stat" | "write";
|
|
9
15
|
}
|
|
10
16
|
/**
|
|
11
17
|
* Result of tracking file accesses during a command execution.
|
package/dist/fingerprint.d.ts
CHANGED
|
@@ -13,6 +13,15 @@ export interface TaskFingerprint {
|
|
|
13
13
|
fileHashes: Record<string, string>;
|
|
14
14
|
/** Paths of files that were probed but didn't exist (ENOENT) */
|
|
15
15
|
missingFiles: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Workspace-relative paths that were both read **and** written
|
|
18
|
+
* during execution. Populated when the tracker emits
|
|
19
|
+
* {@link FileAccess} entries with `"write"` type. The orchestrator
|
|
20
|
+
* uses a non-empty value here to skip caching a self-modifying
|
|
21
|
+
* task, whose fingerprint would otherwise capture post-write state
|
|
22
|
+
* and trigger false cache hits.
|
|
23
|
+
*/
|
|
24
|
+
modifiedInputs?: string[];
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
27
|
* Describes why a cache miss occurred.
|
|
@@ -53,6 +53,24 @@ declare class IncrementalFileHasher {
|
|
|
53
53
|
* Clears the in-memory snapshot.
|
|
54
54
|
*/
|
|
55
55
|
clear(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Looks up the cached hash for `absolutePath` when its mtime and
|
|
58
|
+
* size match the snapshot; returns `undefined` otherwise. Callers
|
|
59
|
+
* that already have a `stat` result (e.g. `InProcessTaskHasher`)
|
|
60
|
+
* skip an extra syscall by passing it through directly.
|
|
61
|
+
*
|
|
62
|
+
* The snapshot is considered loaded on first call — lazy-load is
|
|
63
|
+
* synchronous-friendly here because the caller already performed
|
|
64
|
+
* an async `stat` before calling this method.
|
|
65
|
+
*/
|
|
66
|
+
getSnapshotHash(absolutePath: string, mtimeMs: number, size: number): string | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Writes a fresh snapshot entry after the caller has computed the
|
|
69
|
+
* hash. Pairs with {@link IncrementalFileHasher.getSnapshotHash}
|
|
70
|
+
* — after a miss, the caller hashes the file and records the
|
|
71
|
+
* result here so the next run can reuse it.
|
|
72
|
+
*/
|
|
73
|
+
recordSnapshot(absolutePath: string, hash: string, mtimeMs: number, size: number): void;
|
|
56
74
|
}
|
|
57
75
|
export type { FileSnapshot, IncrementalHasherOptions };
|
|
58
76
|
export { IncrementalFileHasher };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export type { AffectedOptions, AffectedResult } from "./affected.d.ts";
|
|
|
2
2
|
export { buildForwardDependencyMap, buildReverseDependencyMap, expandAffected, filterAffectedTasks, getAffectedProjects, getChangedFiles } from "./affected.d.ts";
|
|
3
3
|
export type { CachedResult, CacheOptions } from "./cache.d.ts";
|
|
4
4
|
export { Cache, DEFAULT_CACHE_DIRECTORY_NAME, formatCacheSize, parseCacheSize } from "./cache.d.ts";
|
|
5
|
+
export type { ChromeTraceEvent } from "./chrome-trace.d.ts";
|
|
6
|
+
export { toChromeTrace, writeChromeTrace } from "./chrome-trace.d.ts";
|
|
5
7
|
export type { ParseCommandsOptions } from "./command-parser/index.d.ts";
|
|
6
8
|
export { expandArguments, expandShortcut, expandWildcard, parseCommands, stripQuotes } from "./command-parser/index.d.ts";
|
|
7
9
|
export { runConcurrently } from "./concurrent.d.ts";
|
|
@@ -23,12 +25,15 @@ export { IncrementalFileHasher } from "./incremental-hasher.d.ts";
|
|
|
23
25
|
export { CompositeLifeCycle, ConsoleLifeCycle, EmptyLifeCycle } from "./life-cycle.d.ts";
|
|
24
26
|
export type { PackageLockfileHash, ResolvedDependency } from "./lockfile-hasher.d.ts";
|
|
25
27
|
export { extractPackageName, LockfileHasher, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile } from "./lockfile-hasher.d.ts";
|
|
28
|
+
export type { LogMode } from "./log-reporter.d.ts";
|
|
29
|
+
export { createLogReporter, LogReporter } from "./log-reporter.d.ts";
|
|
26
30
|
export { isNativeAvailable, loadNativeBindings } from "./native-binding.d.ts";
|
|
31
|
+
export { resolveOutputs } from "./output-resolver.d.ts";
|
|
27
32
|
export { enforceProjectConstraints } from "./project-constraints.d.ts";
|
|
28
|
-
export type { RemoteCacheOptions } from "./remote-cache.d.ts";
|
|
33
|
+
export type { RemoteCacheCompression, RemoteCacheOptions, RemoteCacheSigning } from "./remote-cache.d.ts";
|
|
29
34
|
export { RemoteCache } from "./remote-cache.d.ts";
|
|
30
35
|
export type { RunSummary, TaskSummary } from "./run-summary.d.ts";
|
|
31
|
-
export { generateRunSummary, writeRunSummary } from "./run-summary.d.ts";
|
|
36
|
+
export { generateRunSummary, getLastRunSummaryPath, readLastRunSummary, writeLastRunSummary, writeRunSummary } from "./run-summary.d.ts";
|
|
32
37
|
export { createTaskGraph, getTaskId, parseTaskId } from "./task-graph.d.ts";
|
|
33
38
|
export { findCycle, findCycles, getDependentTasks, getLeafTasks, getTransitiveDependencies, makeAcyclic, reverseTaskGraph, walkTaskGraph, } from "./task-graph-utils.d.ts";
|
|
34
39
|
export type { TaskHasher, TaskHasherOptions } from "./task-hasher.d.ts";
|
|
@@ -40,5 +45,5 @@ export { parsePartition, TaskScheduler } from "./task-scheduler.d.ts";
|
|
|
40
45
|
export { TerminalBuffer } from "./terminal-buffer.d.ts";
|
|
41
46
|
export type { TrackedExecutionResult } from "./tracked-executor.d.ts";
|
|
42
47
|
export { TrackedTaskExecutor } from "./tracked-executor.d.ts";
|
|
43
|
-
export type { AffectedScope, ConcurrentCloseEvent, ConcurrentCommandConfig, ConcurrentCommandInput, ConcurrentRunnerOptions, ConcurrentRunResult, ConstraintsConfig, ConstraintViolation, DependencyKindRules, DependencyType, EnvironmentInput, ExternalDependencyInput, FileSetInput, InputDefinition, LifeCycleInterface, NamedInputs, ProcessEvent, ProjectConfiguration, ProjectGraph, ProjectGraphDependency, ProjectGraphProjectNode, RuntimeInput, TagRelationships, TargetConfiguration, TargetDependencyConfig, Task, TaskExecutionOptions, TaskExecutor, TaskGraph, TaskHashDetails, TaskResult, TaskResults, TaskRunnerContext, TaskRunnerOptions, TasksRunner, TaskStatus, TaskTarget, TypeBoundaries, WorkspaceConfiguration, } from "./types.d.ts";
|
|
48
|
+
export type { AffectedScope, ConcurrentCloseEvent, ConcurrentCommandConfig, ConcurrentCommandInput, ConcurrentRunnerOptions, ConcurrentRunResult, ConstraintsConfig, ConstraintViolation, DependencyKindRules, DependencyType, EnvironmentInput, ExternalDependencyInput, FileSetBase, FileSetInput, FileSetPattern, InputDefinition, LifeCycleInterface, NamedInputs, OutputSpec, ProcessEvent, ProjectConfiguration, ProjectGraph, ProjectGraphDependency, ProjectGraphProjectNode, RuntimeInput, TagRelationships, TargetConfiguration, TargetDependencyConfig, Task, TaskExecutionOptions, TaskExecutor, TaskGraph, TaskHashDetails, TaskPriority, TaskResult, TaskResults, TaskRunnerContext, TaskRunnerOptions, TasksRunner, TaskStatus, TaskTarget, TypeBoundaries, WorkspaceConfiguration, } from "./types.d.ts";
|
|
44
49
|
export { collectFiles, createFailureResult, hashFile, hashStrings, readPackageDeps, resolveTaskCwd, sortObjectKeys, uniqueId } from "./utils.d.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
export { buildForwardDependencyMap, buildReverseDependencyMap, expandAffected, filterAffectedTasks, getAffectedProjects, getChangedFiles } from './packem_shared/buildForwardDependencyMap-
|
|
2
|
-
export { Cache, DEFAULT_CACHE_DIRECTORY_NAME, formatCacheSize, parseCacheSize } from './packem_shared/Cache-
|
|
1
|
+
export { buildForwardDependencyMap, buildReverseDependencyMap, expandAffected, filterAffectedTasks, getAffectedProjects, getChangedFiles } from './packem_shared/buildForwardDependencyMap-DLPgKEto.js';
|
|
2
|
+
export { Cache, DEFAULT_CACHE_DIRECTORY_NAME, formatCacheSize, parseCacheSize } from './packem_shared/Cache-CWaX_c8U.js';
|
|
3
|
+
export { toChromeTrace, writeChromeTrace } from './packem_shared/toChromeTrace-B2tZoJ-7.js';
|
|
3
4
|
export { parseCommands } from './packem_shared/parseCommands-D-IgF8Zh.js';
|
|
4
|
-
export { runConcurrently } from './packem_shared/runConcurrently-
|
|
5
|
-
export { runConcurrentFallback } from './packem_shared/runConcurrentFallback-
|
|
6
|
-
export { defaultTaskRunner } from './packem_shared/defaultTaskRunner-
|
|
5
|
+
export { runConcurrently } from './packem_shared/runConcurrently-CmfC4r-f.js';
|
|
6
|
+
export { runConcurrentFallback } from './packem_shared/runConcurrentFallback-BTmgGV1H.js';
|
|
7
|
+
export { defaultTaskRunner } from './packem_shared/defaultTaskRunner-Cp7jCmIl.js';
|
|
7
8
|
export { detectScriptShell } from './packem_shared/detectScriptShell-CR-xXKA4.js';
|
|
8
|
-
export { FileAccessTracker, generatePreloadScript } from './packem_shared/FileAccessTracker-
|
|
9
|
-
export { FingerprintManager } from './packem_shared/FingerprintManager-
|
|
9
|
+
export { FileAccessTracker, generatePreloadScript } from './packem_shared/FileAccessTracker-CQ5Ot7Hd.js';
|
|
10
|
+
export { FingerprintManager } from './packem_shared/FingerprintManager-CV7U4f4f.js';
|
|
10
11
|
export { detectFrameworks, getFrameworkEnvVariables, inferFrameworkEnvPatterns } from './packem_shared/detectFrameworks-CeFzKE6J.js';
|
|
11
|
-
export { projectGraphToDot, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot } from './packem_shared/projectGraphToDot-
|
|
12
|
-
export { IncrementalFileHasher } from './packem_shared/IncrementalFileHasher-
|
|
13
|
-
export { CompositeLifeCycle, ConsoleLifeCycle, EmptyLifeCycle } from './packem_shared/CompositeLifeCycle-
|
|
14
|
-
export { LockfileHasher, extractPackageName, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile } from './packem_shared/extractPackageName-
|
|
12
|
+
export { projectGraphToDot, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot } from './packem_shared/projectGraphToDot-DU1lSe-c.js';
|
|
13
|
+
export { IncrementalFileHasher } from './packem_shared/IncrementalFileHasher-BRS76-mb.js';
|
|
14
|
+
export { CompositeLifeCycle, ConsoleLifeCycle, EmptyLifeCycle } from './packem_shared/CompositeLifeCycle-CSVbRC_5.js';
|
|
15
|
+
export { LockfileHasher, extractPackageName, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile } from './packem_shared/extractPackageName-BllKetnz.js';
|
|
16
|
+
export { LogReporter, createLogReporter } from './packem_shared/LogReporter-BDt52HLu.js';
|
|
15
17
|
export { isNativeAvailable, loadNativeBindings } from './packem_shared/isNativeAvailable-BpD28A6Z.js';
|
|
18
|
+
export { resolveOutputs } from './packem_shared/resolveOutputs-n6MCKoTe.js';
|
|
16
19
|
export { enforceProjectConstraints } from './packem_shared/enforceProjectConstraints-C5Jp_C3u.js';
|
|
17
|
-
export { RemoteCache } from './packem_shared/RemoteCache-
|
|
18
|
-
export { generateRunSummary, writeRunSummary } from './packem_shared/generateRunSummary-
|
|
19
|
-
export { createTaskGraph, getTaskId, parseTaskId } from './packem_shared/createTaskGraph-
|
|
20
|
+
export { RemoteCache } from './packem_shared/RemoteCache-DSU3lc87.js';
|
|
21
|
+
export { generateRunSummary, getLastRunSummaryPath, readLastRunSummary, writeLastRunSummary, writeRunSummary } from './packem_shared/generateRunSummary-BE1jnQ3H.js';
|
|
22
|
+
export { createTaskGraph, getTaskId, parseTaskId } from './packem_shared/createTaskGraph-B7nH0kY_.js';
|
|
20
23
|
export { findCycle, findCycles, getDependentTasks, getLeafTasks, getTransitiveDependencies, makeAcyclic, reverseTaskGraph, walkTaskGraph } from './packem_shared/findCycle-DefgNYhg.js';
|
|
21
|
-
export { InProcessTaskHasher, computeTaskHash } from './packem_shared/computeTaskHash-
|
|
22
|
-
export { TaskOrchestrator } from './packem_shared/TaskOrchestrator-
|
|
23
|
-
export { TaskScheduler, parsePartition } from './packem_shared/parsePartition-
|
|
24
|
-
export { TerminalBuffer } from './packem_shared/TerminalBuffer-
|
|
25
|
-
export { TrackedTaskExecutor } from './packem_shared/TrackedTaskExecutor-
|
|
24
|
+
export { InProcessTaskHasher, computeTaskHash } from './packem_shared/computeTaskHash-DYqfrDGq.js';
|
|
25
|
+
export { TaskOrchestrator } from './packem_shared/TaskOrchestrator-rf45vW5c.js';
|
|
26
|
+
export { TaskScheduler, parsePartition } from './packem_shared/parsePartition-BfLbHGAx.js';
|
|
27
|
+
export { TerminalBuffer } from './packem_shared/TerminalBuffer-qVJvbRQZ.js';
|
|
28
|
+
export { TrackedTaskExecutor } from './packem_shared/TrackedTaskExecutor-CFPpQfXF.js';
|
|
26
29
|
export { c as collectFiles, a as createFailureResult, h as hashFile, b as hashStrings, r as readPackageDeps, d as resolveTaskCwd, s as sortObjectKeys, u as uniqueId } from './packem_shared/utils-zO0ZRgtf.js';
|
|
27
30
|
export { createInputHandler } from './packem_shared/createInputHandler-DTfePcTG.js';
|
|
28
31
|
export { expandArguments } from './packem_shared/expandArguments-0AwD2BIA.js';
|
package/dist/life-cycle.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ declare class CompositeLifeCycle implements LifeCycleInterface {
|
|
|
13
13
|
endTasks(taskResults: TaskResult[]): void;
|
|
14
14
|
printTaskTerminalOutput(task: Task, status: TaskStatus, terminalOutput: string): void;
|
|
15
15
|
printCacheMiss(task: Task, reasons: string): void;
|
|
16
|
+
onTaskStdout(task: Task, chunk: string): void;
|
|
17
|
+
onTaskStderr(task: Task, chunk: string): void;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* A lifecycle handler that logs task progress to the console.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { LifeCycleInterface, Task, TaskResult, TaskStatus } from "./types.d.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Output formatting mode for task terminal output.
|
|
4
|
+
*
|
|
5
|
+
* - `interleaved` **(default)**: emit each task's buffered output as-is
|
|
6
|
+
* — lines from parallel tasks may intermix when streamed live.
|
|
7
|
+
* - `labeled`: prefix every line with `[project#target]` so parallel
|
|
8
|
+
* tasks remain distinguishable.
|
|
9
|
+
* - `grouped`: buffer each task's output and print it as a single block
|
|
10
|
+
* bracketed by `── project#target ──` header and blank-line footer.
|
|
11
|
+
*
|
|
12
|
+
* Matches the three modes exposed by vite-task's `--log` flag.
|
|
13
|
+
*/
|
|
14
|
+
export type LogMode = "grouped" | "interleaved" | "labeled";
|
|
15
|
+
/**
|
|
16
|
+
* A lifecycle handler that renders task terminal output per {@link LogMode}.
|
|
17
|
+
*
|
|
18
|
+
* Operates on the buffered `printTaskTerminalOutput` signal the orchestrator
|
|
19
|
+
* emits at task-completion. Line-by-line streaming is the consumer's
|
|
20
|
+
* responsibility — a streaming reporter can wrap this one and emit buffered
|
|
21
|
+
* output at the end of each task regardless of streaming choices.
|
|
22
|
+
*/
|
|
23
|
+
export declare class LogReporter implements LifeCycleInterface {
|
|
24
|
+
#private;
|
|
25
|
+
constructor(mode: LogMode, write?: (chunk: string) => void);
|
|
26
|
+
printTaskTerminalOutput(task: Task, _status: TaskStatus, terminalOutput: string): void;
|
|
27
|
+
endTasks(_taskResults: TaskResult[]): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Convenience factory matching vite-task's `createLogReporter(mode)` surface.
|
|
31
|
+
* Consumers that already compose their own lifecycle handlers can instantiate
|
|
32
|
+
* {@link LogReporter} directly.
|
|
33
|
+
*/
|
|
34
|
+
export declare const createLogReporter: (mode: LogMode, write?: (chunk: string) => void) => LogReporter;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { OutputSpec } from "./types.d.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Expands a task's `OutputSpec[]` into the concrete file list to
|
|
4
|
+
* archive:
|
|
5
|
+
*
|
|
6
|
+
* - literal paths → kept as-is (the archiver recursively copies
|
|
7
|
+
* directories, so `"dist"` captures its whole tree);
|
|
8
|
+
* - positive globs → expanded via `fs.glob`, filtered to files only;
|
|
9
|
+
* - negatives (`!pattern`) → applied to the combined result;
|
|
10
|
+
* - `{ auto: true }` → pulls in `autoWrites` entries that fall inside
|
|
11
|
+
* the workspace.
|
|
12
|
+
*
|
|
13
|
+
* Returns deduped, sorted workspace-relative paths so archives are
|
|
14
|
+
* byte-reproducible across invocations.
|
|
15
|
+
*
|
|
16
|
+
* Silent degradation: missing literal paths, empty glob matches, and
|
|
17
|
+
* `{ auto: true }` without tracked writes all contribute nothing
|
|
18
|
+
* rather than throwing.
|
|
19
|
+
*/
|
|
20
|
+
export declare const resolveOutputs: (workspaceRoot: string, outputs: OutputSpec[] | undefined, autoWrites?: ReadonlyArray<string>) => Promise<string[]>;
|
|
@@ -22,17 +22,30 @@ const {
|
|
|
22
22
|
mkdir,
|
|
23
23
|
writeFile,
|
|
24
24
|
rename,
|
|
25
|
-
stat,
|
|
26
25
|
rm,
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
readdir,
|
|
27
|
+
stat,
|
|
28
|
+
cp
|
|
29
29
|
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
30
30
|
import { formatBytes, parseBytes } from '@visulima/humanizer';
|
|
31
31
|
import { join, resolve, dirname } from '@visulima/path';
|
|
32
|
+
import { e as extractTarBrotli, c as createTarBrotli } from './archive-UQHAnZUa.js';
|
|
33
|
+
import { resolveOutputs } from './resolveOutputs-n6MCKoTe.js';
|
|
32
34
|
import { u as uniqueId } from './utils-zO0ZRgtf.js';
|
|
33
35
|
|
|
34
36
|
const DEFAULT_MAX_CACHE_AGE = 7 * 24 * 60 * 60 * 1e3;
|
|
35
37
|
const DEFAULT_CACHE_DIRECTORY_NAME = ".task-runner-cache";
|
|
38
|
+
const assertSafeNamespace = (namespace) => {
|
|
39
|
+
if (namespace.includes("\0")) {
|
|
40
|
+
throw new Error("cacheNamespace: null bytes are not allowed.");
|
|
41
|
+
}
|
|
42
|
+
if (namespace.includes("/") || namespace.includes("\\")) {
|
|
43
|
+
throw new Error(`cacheNamespace: path separators are not allowed (received ${JSON.stringify(namespace)}).`);
|
|
44
|
+
}
|
|
45
|
+
if (namespace === "." || namespace === "..") {
|
|
46
|
+
throw new Error(`cacheNamespace: "${namespace}" would escape the cache subtree.`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
36
49
|
const removeEntry = async (entryPath) => {
|
|
37
50
|
try {
|
|
38
51
|
await rm(entryPath, { force: true, recursive: true });
|
|
@@ -73,7 +86,11 @@ class Cache {
|
|
|
73
86
|
#indexWriteQueue = Promise.resolve();
|
|
74
87
|
constructor(options) {
|
|
75
88
|
this.#workspaceRoot = options.workspaceRoot;
|
|
76
|
-
|
|
89
|
+
const baseDirectory = options.cacheDirectory ?? join(options.workspaceRoot, DEFAULT_CACHE_DIRECTORY_NAME);
|
|
90
|
+
if (options.cacheNamespace !== void 0 && options.cacheNamespace.length > 0) {
|
|
91
|
+
assertSafeNamespace(options.cacheNamespace);
|
|
92
|
+
}
|
|
93
|
+
this.#cacheDirectory = options.cacheNamespace && options.cacheNamespace.length > 0 ? join(baseDirectory, "ns", options.cacheNamespace) : baseDirectory;
|
|
77
94
|
this.#maxCacheAge = options.maxCacheAge ?? DEFAULT_MAX_CACHE_AGE;
|
|
78
95
|
this.#maxCacheSize = options.maxCacheSize ? parseCacheSize(options.maxCacheSize) : void 0;
|
|
79
96
|
}
|
|
@@ -117,8 +134,14 @@ class Cache {
|
|
|
117
134
|
*
|
|
118
135
|
* Uses atomic write: builds the entry in a temporary directory,
|
|
119
136
|
* then renames into place to avoid partial reads by concurrent processes.
|
|
137
|
+
*
|
|
138
|
+
* `outputs` accepts the richer `OutputSpec[]` shape — glob
|
|
139
|
+
* patterns, negative globs, and `{ auto: true }` entries. Pass
|
|
140
|
+
* `autoWrites` alongside `{ auto: true }` so the resolver knows
|
|
141
|
+
* which files the task actually wrote; otherwise auto entries
|
|
142
|
+
* contribute nothing.
|
|
120
143
|
*/
|
|
121
|
-
async put(hash, terminalOutput, outputs, code, fingerprint) {
|
|
144
|
+
async put(hash, terminalOutput, outputs, code, fingerprint, autoWrites) {
|
|
122
145
|
const cacheEntryDirectory = join(this.#cacheDirectory, hash);
|
|
123
146
|
const temporaryDirectory = join(this.#cacheDirectory, `.tmp-${hash}-${uniqueId()}`);
|
|
124
147
|
try {
|
|
@@ -126,7 +149,7 @@ class Cache {
|
|
|
126
149
|
const writes = [
|
|
127
150
|
writeFile(join(temporaryDirectory, "code"), String(code)),
|
|
128
151
|
writeFile(join(temporaryDirectory, "terminalOutput"), terminalOutput),
|
|
129
|
-
this.#archiveOutputs(temporaryDirectory, outputs)
|
|
152
|
+
this.#archiveOutputs(temporaryDirectory, outputs, autoWrites)
|
|
130
153
|
];
|
|
131
154
|
if (fingerprint) {
|
|
132
155
|
writes.push(writeFile(join(temporaryDirectory, "fingerprint.json"), JSON.stringify(fingerprint)));
|
|
@@ -140,36 +163,21 @@ class Cache {
|
|
|
140
163
|
}
|
|
141
164
|
}
|
|
142
165
|
/**
|
|
143
|
-
* Restores cached outputs
|
|
166
|
+
* Restores cached outputs from the compressed `outputs.tar.br`
|
|
167
|
+
* archive. Returns `true` either when the archive was extracted
|
|
168
|
+
* successfully OR when the entry simply has no outputs to restore.
|
|
169
|
+
*
|
|
170
|
+
* The restore flow stages into a temp directory, then swaps each
|
|
171
|
+
* top-level entry into place (see {@link restoreOutputsCompressed})
|
|
172
|
+
* so a mid-restore failure never destroys the user's working tree.
|
|
173
|
+
* The `outputs` parameter is no longer consulted at restore time —
|
|
174
|
+
* the archive is authoritative, and top-level entries in the
|
|
175
|
+
* extracted staging become the set of swap roots. Still accepted
|
|
176
|
+
* for backward compat.
|
|
144
177
|
*/
|
|
145
|
-
async restoreOutputs(hash,
|
|
178
|
+
async restoreOutputs(hash, _outputs) {
|
|
146
179
|
const cacheEntryDirectory = join(this.#cacheDirectory, hash);
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
await stat(outputsDirectory);
|
|
150
|
-
} catch {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
try {
|
|
154
|
-
await Promise.all(
|
|
155
|
-
outputs.map(async (output) => {
|
|
156
|
-
const absoluteOutput = resolve(this.#workspaceRoot, output);
|
|
157
|
-
const cachedOutput = join(outputsDirectory, output);
|
|
158
|
-
try {
|
|
159
|
-
await stat(cachedOutput);
|
|
160
|
-
} catch {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const parentDirectory = dirname(absoluteOutput);
|
|
164
|
-
await mkdir(parentDirectory, { recursive: true });
|
|
165
|
-
await rm(absoluteOutput, { force: true, recursive: true });
|
|
166
|
-
await cp(cachedOutput, absoluteOutput, { recursive: true });
|
|
167
|
-
})
|
|
168
|
-
);
|
|
169
|
-
return true;
|
|
170
|
-
} catch {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
180
|
+
return restoreOutputsCompressed(cacheEntryDirectory, this.#workspaceRoot);
|
|
173
181
|
}
|
|
174
182
|
/**
|
|
175
183
|
* Retrieves the most recent cached result for a task by its ID.
|
|
@@ -276,23 +284,105 @@ class Cache {
|
|
|
276
284
|
}
|
|
277
285
|
}
|
|
278
286
|
/**
|
|
279
|
-
* Archives task output files into the cache
|
|
287
|
+
* Archives task output files into the cache as a single
|
|
288
|
+
* brotli-compressed tarball (`outputs.tar.br`). See
|
|
289
|
+
* {@link archiveOutputsCompressed} for the staging + compression
|
|
290
|
+
* flow.
|
|
280
291
|
*/
|
|
281
|
-
async #archiveOutputs(cacheEntryDirectory, outputs) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
292
|
+
async #archiveOutputs(cacheEntryDirectory, outputs, autoWrites) {
|
|
293
|
+
await archiveOutputsCompressed(this.#workspaceRoot, cacheEntryDirectory, outputs, autoWrites);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const OUTPUTS_ARCHIVE_FILENAME = "outputs.tar.br";
|
|
297
|
+
const archiveOutputsCompressed = async (workspaceRoot, cacheEntryDirectory, outputs, autoWrites) => {
|
|
298
|
+
if (outputs.length === 0) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const resolvedPaths = await resolveOutputs(workspaceRoot, outputs, autoWrites);
|
|
302
|
+
if (resolvedPaths.length === 0) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const stagingDirectory = join(cacheEntryDirectory, `.outputs-stage-${uniqueId()}`);
|
|
306
|
+
const finalPath = join(cacheEntryDirectory, OUTPUTS_ARCHIVE_FILENAME);
|
|
307
|
+
try {
|
|
308
|
+
await mkdir(stagingDirectory, { recursive: true });
|
|
309
|
+
const stagedFlags = await runBounded(STAGE_CONCURRENCY, resolvedPaths, async (relativePath) => {
|
|
310
|
+
const absoluteOutput = resolve(workspaceRoot, relativePath);
|
|
311
|
+
const stagedOutput = join(stagingDirectory, relativePath);
|
|
312
|
+
try {
|
|
313
|
+
await mkdir(dirname(stagedOutput), { recursive: true });
|
|
314
|
+
await cp(absoluteOutput, stagedOutput, { recursive: true });
|
|
315
|
+
return true;
|
|
316
|
+
} catch {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
const stagedCount = stagedFlags.filter(Boolean).length;
|
|
321
|
+
if (stagedCount === 0) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
await createTarBrotli(stagingDirectory, finalPath);
|
|
325
|
+
} finally {
|
|
326
|
+
await rm(stagingDirectory, { force: true, recursive: true }).catch(() => {
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const STAGE_CONCURRENCY = 16;
|
|
331
|
+
const runBounded = async (limit, items, fn) => {
|
|
332
|
+
const results = Array.from({ length: items.length });
|
|
333
|
+
let cursor = 0;
|
|
334
|
+
const worker = async () => {
|
|
335
|
+
while (cursor < items.length) {
|
|
336
|
+
const index = cursor;
|
|
337
|
+
cursor += 1;
|
|
338
|
+
results[index] = await fn(items[index], index);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
|
|
342
|
+
await Promise.all(workers);
|
|
343
|
+
return results;
|
|
344
|
+
};
|
|
345
|
+
const restoreOutputsCompressed = async (cacheEntryDirectory, workspaceRoot) => {
|
|
346
|
+
const archivePath = join(cacheEntryDirectory, OUTPUTS_ARCHIVE_FILENAME);
|
|
347
|
+
try {
|
|
348
|
+
await stat(archivePath);
|
|
349
|
+
} catch {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
const stagingDirectory = join(cacheEntryDirectory, `.restore-${uniqueId()}`);
|
|
353
|
+
const backups = [];
|
|
354
|
+
try {
|
|
355
|
+
await mkdir(stagingDirectory, { recursive: true });
|
|
356
|
+
await extractTarBrotli(archivePath, stagingDirectory);
|
|
357
|
+
const topLevel = await readdir(stagingDirectory);
|
|
358
|
+
await runBounded(STAGE_CONCURRENCY, topLevel, async (entry) => {
|
|
359
|
+
const absoluteOutput = resolve(workspaceRoot, entry);
|
|
360
|
+
const stagedOutput = join(stagingDirectory, entry);
|
|
361
|
+
await mkdir(dirname(absoluteOutput), { recursive: true });
|
|
287
362
|
try {
|
|
288
363
|
await stat(absoluteOutput);
|
|
289
|
-
const
|
|
290
|
-
await
|
|
291
|
-
|
|
364
|
+
const backup = `${absoluteOutput}.pre-restore-${uniqueId()}`;
|
|
365
|
+
await rename(absoluteOutput, backup);
|
|
366
|
+
backups.push({ backup, original: absoluteOutput });
|
|
292
367
|
} catch {
|
|
293
368
|
}
|
|
369
|
+
await cp(stagedOutput, absoluteOutput, { recursive: true });
|
|
370
|
+
});
|
|
371
|
+
await Promise.all(backups.map(({ backup }) => rm(backup, { force: true, recursive: true }).catch(() => {
|
|
372
|
+
})));
|
|
373
|
+
return true;
|
|
374
|
+
} catch {
|
|
375
|
+
for (const { backup, original } of backups) {
|
|
376
|
+
await rm(original, { force: true, recursive: true }).catch(() => {
|
|
377
|
+
});
|
|
378
|
+
await rename(backup, original).catch(() => {
|
|
379
|
+
});
|
|
294
380
|
}
|
|
381
|
+
return false;
|
|
382
|
+
} finally {
|
|
383
|
+
await rm(stagingDirectory, { force: true, recursive: true }).catch(() => {
|
|
384
|
+
});
|
|
295
385
|
}
|
|
296
|
-
}
|
|
386
|
+
};
|
|
297
387
|
|
|
298
388
|
export { Cache, DEFAULT_CACHE_DIRECTORY_NAME, formatCacheSize, parseCacheSize };
|