supercov 0.0.2 → 0.0.4
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/LICENSE +21 -0
- package/README.md +159 -30
- package/bin/supercov.js +10 -0
- package/dist/atomic.d.ts +6 -0
- package/dist/atomic.d.ts.map +1 -0
- package/dist/atomic.js +47 -0
- package/dist/atomic.js.map +1 -0
- package/dist/buildCache.d.ts +14 -0
- package/dist/buildCache.d.ts.map +1 -0
- package/dist/buildCache.js +84 -0
- package/dist/buildCache.js.map +1 -0
- package/dist/cli.js +536 -215
- package/dist/cli.js.map +1 -1
- package/dist/directInstrumenter.d.ts +8 -0
- package/dist/directInstrumenter.d.ts.map +1 -0
- package/dist/directInstrumenter.js +70 -0
- package/dist/directInstrumenter.js.map +1 -0
- package/dist/evidenceArchive.d.ts +35 -0
- package/dist/evidenceArchive.d.ts.map +1 -0
- package/dist/evidenceArchive.js +101 -0
- package/dist/evidenceArchive.js.map +1 -0
- package/dist/instrumenter.d.ts.map +1 -1
- package/dist/instrumenter.js +251 -41
- package/dist/instrumenter.js.map +1 -1
- package/dist/integrity.d.ts.map +1 -1
- package/dist/integrity.js +27 -0
- package/dist/integrity.js.map +1 -1
- package/dist/launchSupervisor.d.ts +24 -0
- package/dist/launchSupervisor.d.ts.map +1 -0
- package/dist/launchSupervisor.js +397 -0
- package/dist/launchSupervisor.js.map +1 -0
- package/dist/playwright.d.ts +0 -4
- package/dist/playwright.d.ts.map +1 -1
- package/dist/playwright.js +33 -17
- package/dist/playwright.js.map +1 -1
- package/dist/playwrightReporter.d.ts.map +1 -1
- package/dist/playwrightReporter.js +3 -2
- package/dist/playwrightReporter.js.map +1 -1
- package/dist/project.d.ts +5 -2
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +192 -27
- package/dist/project.js.map +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +29 -21
- package/dist/query.js.map +1 -1
- package/dist/register.mjs +18 -13
- package/dist/register.mjs.map +1 -1
- package/dist/reporter.d.ts +6 -10
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +23 -82
- package/dist/reporter.js.map +1 -1
- package/dist/resolve-loader.d.mts.map +1 -1
- package/dist/resolve-loader.mjs +11 -3
- package/dist/resolve-loader.mjs.map +1 -1
- package/dist/runAnalysis.d.ts +19 -0
- package/dist/runAnalysis.d.ts.map +1 -0
- package/dist/runAnalysis.js +112 -0
- package/dist/runAnalysis.js.map +1 -0
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +35 -7
- package/dist/runtime.js.map +1 -1
- package/dist/transport.d.ts +2 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +13 -5
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vitePlugin.d.ts +1 -0
- package/dist/vitePlugin.d.ts.map +1 -1
- package/dist/vitePlugin.js +35 -3
- package/dist/vitePlugin.js.map +1 -1
- package/dist/vitest.js +3 -2
- package/dist/vitest.js.map +1 -1
- package/dist/vitestReporter.d.ts +17 -2
- package/dist/vitestReporter.d.ts.map +1 -1
- package/dist/vitestReporter.js +51 -2
- package/dist/vitestReporter.js.map +1 -1
- package/dist/workspace.d.ts +81 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +501 -0
- package/dist/workspace.js.map +1 -0
- package/docs/performance.md +107 -0
- package/docs/workspace-isolation.md +118 -0
- package/package.json +21 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Supercov contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -18,32 +18,64 @@ npm link
|
|
|
18
18
|
|
|
19
19
|
## Verifying the instrumenter
|
|
20
20
|
|
|
21
|
-
The coverage engine has
|
|
21
|
+
The coverage engine has seven independent release gates:
|
|
22
22
|
|
|
23
23
|
- semantic differential fixtures execute original and instrumented programs
|
|
24
24
|
in isolated scopes and compare return values, thrown errors, and observable
|
|
25
25
|
side-effect order;
|
|
26
26
|
- a deterministic generated corpus exercises 160 nested combinations of
|
|
27
27
|
short-circuiting, ternaries, coercion, and thrown expressions on every run;
|
|
28
|
+
- seeded `fast-check` properties exercise another 500 generated nested
|
|
29
|
+
expressions and 300 generated control-flow executions, with shrinking and a
|
|
30
|
+
reproducible seed on failure;
|
|
28
31
|
- coverage oracles assert exact decision vectors, MC/DC witnesses, and branch
|
|
29
|
-
alternatives independently of program behavior
|
|
32
|
+
alternatives independently of program behavior;
|
|
33
|
+
- the same three-condition masking-MC/DC golden cases must report 100% for a
|
|
34
|
+
complete witness set and 33.33% for an incomplete one under both Supercov
|
|
35
|
+
and Clang/LLVM source-based MC/DC;
|
|
36
|
+
- release CI shards the pinned TC39 Test262 corpus across 16 workers, runs the
|
|
37
|
+
official Test262 harness on original and instrumented sources, and rejects
|
|
38
|
+
any scenario that passes originally but fails after transformation; and
|
|
39
|
+
- checked performance budgets cover transform latency, transactional workspace
|
|
40
|
+
preparation, output expansion, and runtime probe overhead.
|
|
30
41
|
|
|
31
42
|
```sh
|
|
32
43
|
npm test
|
|
44
|
+
npm run test:clang-mcdc
|
|
45
|
+
npm run benchmark:check
|
|
46
|
+
TEST262_DIR=/path/to/test262 npm run test:test262
|
|
33
47
|
```
|
|
34
48
|
|
|
35
49
|
The differential suite includes getters, proxies, optional calls and `this`,
|
|
36
50
|
computed logical assignments, defaults, `try`/`catch`/`finally`, iterator
|
|
37
51
|
closing, switch fallthrough, labeled loops, async functions, and generators.
|
|
38
|
-
|
|
52
|
+
The compatibility workflow additionally runs Node 22/24/25, Playwright
|
|
53
|
+
1.55/current, Vite 5/current, Vitest 2/current, Chromium, Firefox, WebKit, and
|
|
54
|
+
modern JavaScript/JSX/TypeScript/TSX syntax fixtures. Filesystem publication,
|
|
55
|
+
symlink, copy fallback, ENOSPC, failed rename, and forced-termination recovery
|
|
56
|
+
also run on Ubuntu, macOS, and Windows. Test262's module, async,
|
|
57
|
+
raw, parse/resolution-negative, Annex B sloppy-script extension, and explicit
|
|
58
|
+
`Function.prototype.toString`/function-source-coercion tests are intentionally
|
|
59
|
+
excluded from the source-rewrite comparison, with reason counts printed for
|
|
60
|
+
every shard. Annex B does not apply to the Vite application modules Supercov
|
|
61
|
+
instruments; exact source reflection necessarily observes a source transform.
|
|
62
|
+
When application code directly coerces or observes a function's source,
|
|
63
|
+
Supercov leaves that function body uninstrumented and records a visible
|
|
64
|
+
`semantic-safety` completeness blocker. The release corpus covers every other eligible synchronous
|
|
65
|
+
script and runtime-negative scenario, while dedicated differential fixtures
|
|
66
|
+
cover async functions and generators. Every semantic-equivalence failure
|
|
67
|
+
blocks the trusted-publishing workflow.
|
|
39
68
|
|
|
40
69
|
## Agent query workflow
|
|
41
70
|
|
|
42
|
-
Each run is stored locally as
|
|
43
|
-
`.supercov/runs/<run-id
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
71
|
+
Each run is stored locally as two immutable files under
|
|
72
|
+
`.supercov/runs/<run-id>/`: `evidence.raw.gz` and `run.json`. The archive
|
|
73
|
+
contains the exact coverage denominator manifest plus raw per-worker and
|
|
74
|
+
background evidence. Every query reconstructs its requested coverage view
|
|
75
|
+
directly from that archive; no derived report or query cache is retained.
|
|
76
|
+
Loose evidence is removed only after the whole run directory is atomically
|
|
77
|
+
visible. HTML is not generated during a test run; agents should use bounded
|
|
78
|
+
CLI queries instead of loading the complete derived model into context.
|
|
47
79
|
|
|
48
80
|
```sh
|
|
49
81
|
# Orient using only a few lines.
|
|
@@ -75,8 +107,8 @@ Use `--filter passed` for verified coverage from successful attempts of
|
|
|
75
107
|
ultimately passing tests, or `--filter failed` to inspect only execution from
|
|
76
108
|
failed attempts (including failed retries of flaky tests). Reports record
|
|
77
109
|
attempt status and classify each test as passed, failed, flaky, skipped, timed
|
|
78
|
-
out, interrupted, or unknown. The
|
|
79
|
-
|
|
110
|
+
out, interrupted, or unknown. The passed and failed subsets are stored inside
|
|
111
|
+
the same compressed report rather than duplicated into presentation files.
|
|
80
112
|
|
|
81
113
|
The run ID is positional because all coverage queries operate on one immutable
|
|
82
114
|
run. `latest` is a convenience selector for interactive use. Every query
|
|
@@ -87,31 +119,128 @@ Text output is concise for an interactive agent; JSON is the stable machine
|
|
|
87
119
|
interface that can later back hosted coverage tools without changing the
|
|
88
120
|
stored schema.
|
|
89
121
|
|
|
90
|
-
For a conventional Vite project, the
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
122
|
+
For a conventional Vite project, or a Node project with no build step, the
|
|
123
|
+
CLI:
|
|
124
|
+
|
|
125
|
+
1. refreshes a stable isolated source namespace under
|
|
126
|
+
`.supercov/cache/instrumented-workspace/<project>/`, links the existing
|
|
127
|
+
dependency tree, and creates all Vite, Vitest, Playwright, and build output
|
|
128
|
+
only there; file data uses copy-on-write reflinks where the filesystem
|
|
129
|
+
supports them, and falls back to copying where it does not; the stable path
|
|
130
|
+
lets VM/container snapshot systems reuse a coverage build without touching
|
|
131
|
+
the application's ordinary build; when the complete source/config/toolchain
|
|
132
|
+
fingerprint is unchanged, the prior instrumented output and manifest are
|
|
133
|
+
carried into the refreshed source snapshot and the build is skipped;
|
|
94
134
|
2. inventories every `app/**/*.ts(x)` and `src/**/*.ts(x)` file for the
|
|
95
|
-
denominator, then
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
135
|
+
denominator, then instruments modules loaded by Vite without changing the
|
|
136
|
+
project's config; when no build script exists, it instruments only the
|
|
137
|
+
disposable source copy and supplies a module-format-neutral runtime through
|
|
138
|
+
the inherited Node preload;
|
|
139
|
+
3. discovers the Playwright-compatible fixture provider, test export, and
|
|
140
|
+
additional named exports from the suite's existing imports, then redirects
|
|
141
|
+
that provider at module-load time; Vitest setup is injected through the
|
|
142
|
+
child runner's generated config;
|
|
143
|
+
4. runs the exact command following `--`, propagating coverage through every
|
|
144
|
+
Node child process it launches;
|
|
101
145
|
5. attributes every source hit and decision vector to its individual test,
|
|
102
146
|
automatically wraps Playwright actions and assertions, and records the
|
|
103
147
|
action/assertion phase responsible for each correlated hit; then merges
|
|
104
|
-
server and browser evidence into
|
|
105
|
-
6.
|
|
148
|
+
server and browser evidence into one compressed JSON report; and
|
|
149
|
+
6. atomically publishes the compressed evidence/report into `.supercov/`, then
|
|
150
|
+
removes loose evidence and terminal per-run work state, retaining only the
|
|
151
|
+
immutable run and disposable isolated build namespace. The
|
|
152
|
+
ordinary application build is never read as an input, overwritten, or
|
|
153
|
+
rebuilt afterward.
|
|
154
|
+
|
|
155
|
+
Only `.supercov/` is modified in the user's checkout. A per-project lock
|
|
156
|
+
rejects overlapping runs before either can build. Run state is durably written
|
|
157
|
+
through preparing/building/testing/reporting phases; SIGINT, SIGTERM,
|
|
158
|
+
and SIGHUP are forwarded to the entire child process group. If the process is
|
|
159
|
+
killed without a cleanup opportunity, the next invocation marks the dead PID's
|
|
160
|
+
run abandoned and refreshes the isolated namespace before using it. Cache
|
|
161
|
+
refresh is transactional: a new sibling generation is prepared completely,
|
|
162
|
+
the stable name is switched only at publication, and the prior complete
|
|
163
|
+
generation is retained until that switch succeeds. The next invocation
|
|
164
|
+
discards orphan staging trees or restores the prior generation if a host crash
|
|
165
|
+
landed between the two same-filesystem renames. Report, evidence archive, and
|
|
166
|
+
state writes use sibling-temp files, fsync, and atomic rename; lock acquisition
|
|
167
|
+
uses exclusive creation and fsync. Published `run.json` is the durable terminal
|
|
168
|
+
record, so terminal work state is not retained.
|
|
169
|
+
|
|
170
|
+
Retention is deterministic because UTC run IDs sort chronologically:
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
npx supercov prune --keep 20
|
|
174
|
+
npx supercov prune --keep 20 --dry-run
|
|
175
|
+
npx supercov clean --keep 20 # also removes the shared build cache
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Neither operation runs automatically. `prune` removes explicit history beyond
|
|
179
|
+
the requested retention and orphan/terminal transient data while preserving
|
|
180
|
+
the shared cache. `clean` also removes that cache. Both acquire the same lock
|
|
181
|
+
as a coverage run, refuse to race an active run, and never touch files outside
|
|
182
|
+
`.supercov/`.
|
|
183
|
+
|
|
184
|
+
The complete ownership, crash-recovery, symlink, and future copy-free design is
|
|
185
|
+
documented in [Workspace isolation](docs/workspace-isolation.md).
|
|
186
|
+
|
|
187
|
+
Every run prints and stores monotonic phase timings for initialization,
|
|
188
|
+
workspace preparation, adapter setup, the instrumented build, the unchanged
|
|
189
|
+
test command, and report preparation. They are available in
|
|
190
|
+
`.supercov/runs/<run-id>/run.json` and in the JSON form of `supercov runs`.
|
|
191
|
+
These phase timings do not pretend to be end-to-end overhead: that percentage
|
|
192
|
+
requires an explicit comparison with the same command run without Supercov,
|
|
193
|
+
which Supercov never executes automatically because an arbitrary test command
|
|
194
|
+
may have side effects or external cost. See
|
|
195
|
+
[Performance and storage](docs/performance.md) for the comparison methodology,
|
|
196
|
+
strategy trade-offs, and a measured real-suite reference.
|
|
106
197
|
|
|
107
198
|
The automatic adapters currently support standard Playwright suites (ESM and
|
|
108
|
-
CommonJS specs in arbitrary project directories),
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
199
|
+
CommonJS specs in arbitrary project directories), project-owned Playwright
|
|
200
|
+
fixture packages, Vitest, no-build Node commands, and Node coordinators that
|
|
201
|
+
launch tests inside a mounted VM/container workspace. A single command such as
|
|
202
|
+
`supercov -- npm test` can collect Vitest and Playwright evidence into the same
|
|
203
|
+
run. No-build Node execution is retained as background/unattributed evidence
|
|
204
|
+
until a recognized test-runner adapter supplies exact test boundaries. Jest,
|
|
205
|
+
exact `node:test` attribution, non-Vite application builds, browser component
|
|
206
|
+
runners, and distributed multi-host merging still require adapters; they are
|
|
207
|
+
not silently reported as per-test coverage.
|
|
208
|
+
|
|
209
|
+
Remote execution discovery is structural rather than provider-specific. The
|
|
210
|
+
preload observes CommonJS exports for a static `build(options)` capability,
|
|
211
|
+
activates only when those options contain a host-to-guest mount that includes
|
|
212
|
+
the isolated project, scopes an existing cache/snapshot identity to the run's
|
|
213
|
+
source fingerprint, and follows the opaque returned object graph. A method
|
|
214
|
+
whose options contain `argv`, `cmd`, or `command` receives guest-translated
|
|
215
|
+
Supercov paths and a guest-valid Node preload. The execution log records this
|
|
216
|
+
process/capability graph but hashes long or multiline arguments so embedded
|
|
217
|
+
shell bodies and credentials are never persisted.
|
|
218
|
+
|
|
219
|
+
This first zero-edit mechanism has explicit boundaries. It follows Node child
|
|
220
|
+
processes, not arbitrary non-Node supervisors or a remote control plane that
|
|
221
|
+
never exposes launches to the local process. The remote SDK must currently be
|
|
222
|
+
visible through CommonJS loading, its build options must expose the workspace
|
|
223
|
+
mount, and its execution call must accept an environment. Pure-ESM executor
|
|
224
|
+
SDKs, positional-only remote exec APIs, and providers that hide all launch
|
|
225
|
+
state behind an RPC need additional interception layers. Supercov reports
|
|
226
|
+
missing evidence rather than claiming those paths are covered.
|
|
227
|
+
|
|
228
|
+
The public regression suite includes a provider-neutral opaque executor. Its
|
|
229
|
+
CommonJS SDK exposes only a static build capability, a host-to-guest mount,
|
|
230
|
+
an existing snapshot key, an opaque image/pool/machine chain, and an
|
|
231
|
+
argv-shaped execution method. CI requires Supercov to discover that structure,
|
|
232
|
+
scope the cache identity, translate paths and the Node preload into the guest,
|
|
233
|
+
run nested Vitest and Playwright commands, parse every concurrent trace shard,
|
|
234
|
+
and produce 100% fixture coverage. A separate clean-room gate packs the npm
|
|
235
|
+
tarball and invokes it through `npx` in a project with no build step, asserting
|
|
236
|
+
that no source or configuration file changes.
|
|
237
|
+
|
|
238
|
+
Before the isolated build, Supercov also compares the invoked npm/pnpm/yarn/bun
|
|
239
|
+
script with explicit string-valued `process.env` mode checks in the project's
|
|
240
|
+
build config. A semantic match such as `test:preview` and
|
|
241
|
+
`process.env.TEST_PREVIEW === "true"` activates that build-only flag and is
|
|
242
|
+
printed before the build. It never guesses values for unrelated environment
|
|
243
|
+
variables.
|
|
115
244
|
|
|
116
245
|
Each test carries two independent provenance fields:
|
|
117
246
|
|
|
@@ -147,7 +276,7 @@ phase travels on browser requests into automatically wrapped Remix loaders,
|
|
|
147
276
|
actions, and the server document renderer. Node async context preserves that
|
|
148
277
|
ID through awaited helpers. An assertion also retains the preceding action ID,
|
|
149
278
|
making chains such as “click -> application lines/decisions -> visible
|
|
150
|
-
assertion” queryable in JSON
|
|
279
|
+
assertion” queryable in JSON.
|
|
151
280
|
|
|
152
281
|
Server evidence is safe when Playwright uses multiple workers against one
|
|
153
282
|
application server. Every routed request carries a run/worker/test/retry scope;
|
package/bin/supercov.js
CHANGED
|
@@ -13,6 +13,16 @@ const child = spawn(
|
|
|
13
13
|
},
|
|
14
14
|
);
|
|
15
15
|
|
|
16
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
17
|
+
process.once(signal, () => {
|
|
18
|
+
try {
|
|
19
|
+
child.kill(signal);
|
|
20
|
+
} catch {
|
|
21
|
+
// The child may already have completed.
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
child.on("error", (error) => {
|
|
17
27
|
console.error("[supercov] failed to start", error);
|
|
18
28
|
process.exitCode = 1;
|
package/dist/atomic.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type WriteFileOptions } from "node:fs";
|
|
2
|
+
/** Write a complete sibling file and atomically replace the destination. */
|
|
3
|
+
export declare function atomicWriteFileSync(path: string, data: string | NodeJS.ArrayBufferView, options?: WriteFileOptions): void;
|
|
4
|
+
/** Atomically publish a fully prepared file or directory and persist its entry. */
|
|
5
|
+
export declare function atomicRenameSync(source: string, destination: string): void;
|
|
6
|
+
//# sourceMappingURL=atomic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic.d.ts","sourceRoot":"","sources":["../src/atomic.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,gBAAgB,EACtB,MAAM,SAAS,CAAC;AAiBjB,4EAA4E;AAC5E,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,eAAe,EACrC,OAAO,CAAC,EAAE,gBAAgB,GACzB,IAAI,CAmBN;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAI1E"}
|
package/dist/atomic.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { mkdirSync, openSync, closeSync, fsyncSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
function fsyncDirectory(path) {
|
|
5
|
+
try {
|
|
6
|
+
const directory = openSync(path, "r");
|
|
7
|
+
try {
|
|
8
|
+
fsyncSync(directory);
|
|
9
|
+
}
|
|
10
|
+
finally {
|
|
11
|
+
closeSync(directory);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Some platforms (notably Windows) do not permit opening directories.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Write a complete sibling file and atomically replace the destination. */
|
|
19
|
+
export function atomicWriteFileSync(path, data, options) {
|
|
20
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
21
|
+
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
22
|
+
let descriptor;
|
|
23
|
+
try {
|
|
24
|
+
descriptor = openSync(temporary, "wx", 0o600);
|
|
25
|
+
writeFileSync(descriptor, data, options);
|
|
26
|
+
fsyncSync(descriptor);
|
|
27
|
+
closeSync(descriptor);
|
|
28
|
+
descriptor = undefined;
|
|
29
|
+
renameSync(temporary, path);
|
|
30
|
+
// Persist the directory entry as well as the file contents on POSIX. Some
|
|
31
|
+
// platforms (notably Windows) do not permit opening directories, so file
|
|
32
|
+
// fsync + atomic rename remains the portable fallback there.
|
|
33
|
+
fsyncDirectory(dirname(path));
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
if (descriptor !== undefined)
|
|
37
|
+
closeSync(descriptor);
|
|
38
|
+
rmSync(temporary, { force: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Atomically publish a fully prepared file or directory and persist its entry. */
|
|
42
|
+
export function atomicRenameSync(source, destination) {
|
|
43
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
44
|
+
renameSync(source, destination);
|
|
45
|
+
fsyncDirectory(dirname(destination));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=atomic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic.js","sourceRoot":"","sources":["../src/atomic.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,MAAM,EACN,aAAa,GAEd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC;YACH,SAAS,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;IACxE,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,IAAqC,EACrC,OAA0B;IAE1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,MAAM,CAAC;IAC/D,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,UAAU,GAAG,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC9C,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACzC,SAAS,CAAC,UAAU,CAAC,CAAC;QACtB,SAAS,CAAC,UAAU,CAAC,CAAC;QACtB,UAAU,GAAG,SAAS,CAAC;QACvB,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC5B,0EAA0E;QAC1E,yEAAyE;QACzE,6DAA6D;QAC7D,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC;YAAS,CAAC;QACT,IAAI,UAAU,KAAK,SAAS;YAAE,SAAS,CAAC,UAAU,CAAC,CAAC;QACpD,MAAM,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,WAAmB;IAClE,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAChC,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CoverageRunIntegrity } from "./types.ts";
|
|
2
|
+
import type { CoverageProject } from "./project.ts";
|
|
3
|
+
export declare const INSTRUMENTED_BUILD_CACHE_SCHEMA_VERSION = 1;
|
|
4
|
+
export interface InstrumentedBuildCache {
|
|
5
|
+
schemaVersion: typeof INSTRUMENTED_BUILD_CACHE_SCHEMA_VERSION;
|
|
6
|
+
key: string;
|
|
7
|
+
createdAt: string;
|
|
8
|
+
artifactPaths: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function instrumentedBuildCacheKey(integrity: CoverageRunIntegrity, project: CoverageProject): string;
|
|
11
|
+
export declare function readInstrumentedBuildCache(workspace: string, key: string): InstrumentedBuildCache | undefined;
|
|
12
|
+
export declare function writeInstrumentedBuildCache(workspace: string, key: string): InstrumentedBuildCache | undefined;
|
|
13
|
+
export declare function buildCacheReusePaths(metadata: InstrumentedBuildCache): string[];
|
|
14
|
+
//# sourceMappingURL=buildCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildCache.d.ts","sourceRoot":"","sources":["../src/buildCache.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,eAAO,MAAM,uCAAuC,IAAI,CAAC;AAUzD,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,OAAO,uCAAuC,CAAC;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAgBD,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,eAAe,GACvB,MAAM,CAeR;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,sBAAsB,GAAG,SAAS,CAqBpC;AAED,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,sBAAsB,GAAG,SAAS,CAqCpC;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,sBAAsB,GAC/B,MAAM,EAAE,CAEV"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { isAbsolute, resolve, sep } from "node:path";
|
|
4
|
+
import { atomicWriteFileSync } from "./atomic.js";
|
|
5
|
+
export const INSTRUMENTED_BUILD_CACHE_SCHEMA_VERSION = 1;
|
|
6
|
+
const BUILD_OUTPUT_CANDIDATES = [
|
|
7
|
+
"build",
|
|
8
|
+
"dist",
|
|
9
|
+
".next",
|
|
10
|
+
".nuxt",
|
|
11
|
+
".output",
|
|
12
|
+
];
|
|
13
|
+
function metadataPath(workspace) {
|
|
14
|
+
return resolve(workspace, ".supercov/build-cache.json");
|
|
15
|
+
}
|
|
16
|
+
function safeRelativePath(path) {
|
|
17
|
+
return Boolean(path &&
|
|
18
|
+
!isAbsolute(path) &&
|
|
19
|
+
path !== ".." &&
|
|
20
|
+
!path.startsWith(`..${sep}`) &&
|
|
21
|
+
!path.split(/[\\/]/).includes(".."));
|
|
22
|
+
}
|
|
23
|
+
export function instrumentedBuildCacheKey(integrity, project) {
|
|
24
|
+
return createHash("sha256")
|
|
25
|
+
.update(JSON.stringify({
|
|
26
|
+
schemaVersion: INSTRUMENTED_BUILD_CACHE_SCHEMA_VERSION,
|
|
27
|
+
executionFingerprint: integrity.fingerprint.execution,
|
|
28
|
+
adapter: project.buildAdapter,
|
|
29
|
+
command: project.buildCommand,
|
|
30
|
+
environment: project.buildEnvironment,
|
|
31
|
+
node: process.versions.node,
|
|
32
|
+
platform: process.platform,
|
|
33
|
+
architecture: process.arch,
|
|
34
|
+
}))
|
|
35
|
+
.digest("hex");
|
|
36
|
+
}
|
|
37
|
+
export function readInstrumentedBuildCache(workspace, key) {
|
|
38
|
+
let metadata;
|
|
39
|
+
try {
|
|
40
|
+
metadata = JSON.parse(readFileSync(metadataPath(workspace), "utf8"));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
if (metadata.schemaVersion !== INSTRUMENTED_BUILD_CACHE_SCHEMA_VERSION ||
|
|
46
|
+
metadata.key !== key ||
|
|
47
|
+
!Array.isArray(metadata.artifactPaths) ||
|
|
48
|
+
metadata.artifactPaths.length === 0 ||
|
|
49
|
+
metadata.artifactPaths.some((path) => !safeRelativePath(path) || !existsSync(resolve(workspace, path)))) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return metadata;
|
|
53
|
+
}
|
|
54
|
+
export function writeInstrumentedBuildCache(workspace, key) {
|
|
55
|
+
let declared = {};
|
|
56
|
+
try {
|
|
57
|
+
declared = JSON.parse(readFileSync(resolve(workspace, ".supercov/build-outputs.json"), "utf8"));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Older/native Vite integrations use the conservative known outputs.
|
|
61
|
+
}
|
|
62
|
+
const existingOutputs = [
|
|
63
|
+
...new Set([
|
|
64
|
+
...BUILD_OUTPUT_CANDIDATES,
|
|
65
|
+
...(declared.paths ?? []).filter(safeRelativePath),
|
|
66
|
+
]),
|
|
67
|
+
].filter((path) => existsSync(resolve(workspace, path)));
|
|
68
|
+
const outputs = existingOutputs.filter((path) => !existingOutputs.some((parent) => parent !== path && path.startsWith(`${parent}/`)));
|
|
69
|
+
const manifest = ".supercov/manifest.json";
|
|
70
|
+
if (outputs.length === 0 || !existsSync(resolve(workspace, manifest)))
|
|
71
|
+
return undefined;
|
|
72
|
+
const metadata = {
|
|
73
|
+
schemaVersion: INSTRUMENTED_BUILD_CACHE_SCHEMA_VERSION,
|
|
74
|
+
key,
|
|
75
|
+
createdAt: new Date().toISOString(),
|
|
76
|
+
artifactPaths: [...outputs, manifest],
|
|
77
|
+
};
|
|
78
|
+
atomicWriteFileSync(metadataPath(workspace), `${JSON.stringify(metadata, null, 2)}\n`);
|
|
79
|
+
return metadata;
|
|
80
|
+
}
|
|
81
|
+
export function buildCacheReusePaths(metadata) {
|
|
82
|
+
return [...metadata.artifactPaths, ".supercov/build-cache.json"];
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=buildCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildCache.js","sourceRoot":"","sources":["../src/buildCache.ts"],"names":[],"mappings":"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"}
|