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
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import { copyFileSync, constants, existsSync, lstatSync, mkdirSync, openSync, closeSync, fsyncSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, symlinkSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { atomicRenameSync, atomicWriteFileSync } from "./atomic.js";
|
|
5
|
+
const TERMINAL = new Set([
|
|
6
|
+
"complete",
|
|
7
|
+
"failed",
|
|
8
|
+
"interrupted",
|
|
9
|
+
"abandoned",
|
|
10
|
+
]);
|
|
11
|
+
const ROOT_EXCLUSIONS = new Set([
|
|
12
|
+
".git",
|
|
13
|
+
".supercov",
|
|
14
|
+
".mcdc-pool",
|
|
15
|
+
"node_modules",
|
|
16
|
+
"build",
|
|
17
|
+
"dist",
|
|
18
|
+
".next",
|
|
19
|
+
".nuxt",
|
|
20
|
+
".output",
|
|
21
|
+
"coverage",
|
|
22
|
+
"playwright-report",
|
|
23
|
+
"test-results",
|
|
24
|
+
]);
|
|
25
|
+
function processExists(pid) {
|
|
26
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
27
|
+
return false;
|
|
28
|
+
try {
|
|
29
|
+
process.kill(pid, 0);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return error.code === "EPERM";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function readJson(path) {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function statePath(root, runId) {
|
|
45
|
+
return resolve(root, ".supercov/work", runId, "state.json");
|
|
46
|
+
}
|
|
47
|
+
export function isolatedWorkspacePath(root, runId) {
|
|
48
|
+
return resolve(root, ".supercov/work", runId, basename(root));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Stable isolated namespace used as a live mount by VM/container runners.
|
|
52
|
+
* Its path must remain stable so providers whose snapshot key includes mount
|
|
53
|
+
* paths can reuse an instrumented snapshot across equivalent coverage runs.
|
|
54
|
+
* The project lock makes refreshes single-writer.
|
|
55
|
+
*/
|
|
56
|
+
export function cachedWorkspacePath(root) {
|
|
57
|
+
return resolve(root, ".supercov/cache/instrumented-workspace", basename(root));
|
|
58
|
+
}
|
|
59
|
+
function cacheTransactionPrefix(root, kind) {
|
|
60
|
+
return `.${basename(root)}.${kind}-`;
|
|
61
|
+
}
|
|
62
|
+
function cacheTransactionPath(root, kind) {
|
|
63
|
+
const workspace = cachedWorkspacePath(root);
|
|
64
|
+
return resolve(dirname(workspace), `${cacheTransactionPrefix(root, kind)}${process.pid}-${randomUUID()}`);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Recover the stable cache at transaction boundaries that SIGKILL or a host
|
|
68
|
+
* crash can interrupt. A staging tree is never live. A previous tree is
|
|
69
|
+
* restored only when the stable name is absent; otherwise it is obsolete.
|
|
70
|
+
*/
|
|
71
|
+
export function recoverCachedWorkspace(root) {
|
|
72
|
+
const workspace = cachedWorkspacePath(root);
|
|
73
|
+
const parent = dirname(workspace);
|
|
74
|
+
if (!existsSync(parent))
|
|
75
|
+
return { restoredPrevious: false, removedStaging: 0, removedPrevious: 0 };
|
|
76
|
+
const entries = readdirSync(parent, { withFileTypes: true });
|
|
77
|
+
const stagingPrefix = cacheTransactionPrefix(root, "staging");
|
|
78
|
+
const previousPrefix = cacheTransactionPrefix(root, "previous");
|
|
79
|
+
const staging = entries
|
|
80
|
+
.filter((entry) => entry.name.startsWith(stagingPrefix))
|
|
81
|
+
.map((entry) => resolve(parent, entry.name));
|
|
82
|
+
const previous = entries
|
|
83
|
+
.filter((entry) => entry.name.startsWith(previousPrefix) && entry.isDirectory())
|
|
84
|
+
.map((entry) => resolve(parent, entry.name))
|
|
85
|
+
.sort((left, right) => lstatSync(right).mtimeMs - lstatSync(left).mtimeMs);
|
|
86
|
+
const invalidPrevious = entries
|
|
87
|
+
.filter((entry) => entry.name.startsWith(previousPrefix) && !entry.isDirectory())
|
|
88
|
+
.map((entry) => resolve(parent, entry.name));
|
|
89
|
+
let restoredPrevious = false;
|
|
90
|
+
if (!existsSync(workspace) && previous[0]) {
|
|
91
|
+
atomicRenameSync(previous[0], workspace);
|
|
92
|
+
previous.shift();
|
|
93
|
+
restoredPrevious = true;
|
|
94
|
+
}
|
|
95
|
+
for (const path of staging)
|
|
96
|
+
rmSync(path, { recursive: true, force: true });
|
|
97
|
+
for (const path of previous)
|
|
98
|
+
rmSync(path, { recursive: true, force: true });
|
|
99
|
+
for (const path of invalidPrevious)
|
|
100
|
+
rmSync(path, { recursive: true, force: true });
|
|
101
|
+
return {
|
|
102
|
+
restoredPrevious,
|
|
103
|
+
removedStaging: staging.length,
|
|
104
|
+
removedPrevious: previous.length + invalidPrevious.length,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export function writeRunState(root, runId, state) {
|
|
108
|
+
const complete = { ...state, updatedAt: new Date().toISOString() };
|
|
109
|
+
atomicWriteFileSync(statePath(root, runId), `${JSON.stringify(complete, null, 2)}\n`);
|
|
110
|
+
return complete;
|
|
111
|
+
}
|
|
112
|
+
export function updateRunState(root, runId, update) {
|
|
113
|
+
const current = readJson(statePath(root, runId));
|
|
114
|
+
if (!current)
|
|
115
|
+
throw new Error(`Run state is missing for ${runId}`);
|
|
116
|
+
const next = {
|
|
117
|
+
...current,
|
|
118
|
+
...update,
|
|
119
|
+
updatedAt: new Date().toISOString(),
|
|
120
|
+
};
|
|
121
|
+
atomicWriteFileSync(statePath(root, runId), `${JSON.stringify(next, null, 2)}\n`);
|
|
122
|
+
return next;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A published run is self-contained only once its raw-evidence archive exists.
|
|
126
|
+
* At that point loose evidence and per-run state are transactional leftovers,
|
|
127
|
+
* not user history. Derive every deletion target from root + run ID.
|
|
128
|
+
*/
|
|
129
|
+
export function finalizePublishedRunStorage(root, runId) {
|
|
130
|
+
const runDirectory = resolve(root, ".supercov/runs", runId);
|
|
131
|
+
const publishedRun = readJson(resolve(runDirectory, "run.json"));
|
|
132
|
+
if (publishedRun?.id !== runId ||
|
|
133
|
+
!existsSync(resolve(runDirectory, "evidence.raw.gz"))) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
rmSync(resolve(root, ".supercov/evidence", runId), {
|
|
137
|
+
recursive: true,
|
|
138
|
+
force: true,
|
|
139
|
+
});
|
|
140
|
+
rmSync(resolve(root, ".supercov/work", runId), {
|
|
141
|
+
recursive: true,
|
|
142
|
+
force: true,
|
|
143
|
+
});
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
/** Recover dead runs and finish any publication whose atomic rename landed. */
|
|
147
|
+
export function recoverAbandonedRuns(root) {
|
|
148
|
+
const workRoot = resolve(root, ".supercov/work");
|
|
149
|
+
if (!existsSync(workRoot))
|
|
150
|
+
return [];
|
|
151
|
+
const recovered = [];
|
|
152
|
+
for (const entry of readdirSync(workRoot, { withFileTypes: true })) {
|
|
153
|
+
if (!entry.isDirectory())
|
|
154
|
+
continue;
|
|
155
|
+
const path = statePath(root, entry.name);
|
|
156
|
+
const state = readJson(path);
|
|
157
|
+
if (!state)
|
|
158
|
+
continue;
|
|
159
|
+
if (TERMINAL.has(state.status)) {
|
|
160
|
+
finalizePublishedRunStorage(root, entry.name);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (processExists(state.pid))
|
|
164
|
+
continue;
|
|
165
|
+
// Never trust a persisted path as a deletion target. A partially written
|
|
166
|
+
// or manually edited state file cannot widen cleanup beyond this run's
|
|
167
|
+
// deterministic workspace namespace.
|
|
168
|
+
rmSync(isolatedWorkspacePath(root, entry.name), {
|
|
169
|
+
recursive: true,
|
|
170
|
+
force: true,
|
|
171
|
+
});
|
|
172
|
+
rmSync(resolve(root, ".supercov/work", entry.name, "run-publication"), {
|
|
173
|
+
recursive: true,
|
|
174
|
+
force: true,
|
|
175
|
+
});
|
|
176
|
+
if (finalizePublishedRunStorage(root, entry.name)) {
|
|
177
|
+
// The run directory is published by one atomic rename before the run
|
|
178
|
+
// state flips terminal. Its run.json is the durable terminal record, so
|
|
179
|
+
// recovery completes cleanup without recreating disposable state.
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Evidence moved out of the disposable workspace before run
|
|
183
|
+
// generation is not a visible run. Remove that orphan on recovery so a
|
|
184
|
+
// hard kill cannot accumulate partial run data indefinitely.
|
|
185
|
+
rmSync(resolve(root, ".supercov/evidence", entry.name), {
|
|
186
|
+
recursive: true,
|
|
187
|
+
force: true,
|
|
188
|
+
});
|
|
189
|
+
updateRunState(root, entry.name, {
|
|
190
|
+
status: "abandoned",
|
|
191
|
+
error: `Recovered after process ${state.pid} exited without cleanup`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
recovered.push(entry.name);
|
|
195
|
+
}
|
|
196
|
+
return recovered.sort();
|
|
197
|
+
}
|
|
198
|
+
export function acquireProjectLock(root, runId) {
|
|
199
|
+
const lockPath = resolve(root, ".supercov/locks/active.json");
|
|
200
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
201
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
202
|
+
let descriptor;
|
|
203
|
+
try {
|
|
204
|
+
descriptor = openSync(lockPath, "wx", 0o600);
|
|
205
|
+
const payload = `${JSON.stringify({ runId, pid: process.pid, startedAt: new Date().toISOString() }, null, 2)}\n`;
|
|
206
|
+
writeFileSync(descriptor, payload);
|
|
207
|
+
fsyncSync(descriptor);
|
|
208
|
+
closeSync(descriptor);
|
|
209
|
+
descriptor = undefined;
|
|
210
|
+
let released = false;
|
|
211
|
+
return {
|
|
212
|
+
path: lockPath,
|
|
213
|
+
release() {
|
|
214
|
+
if (released)
|
|
215
|
+
return;
|
|
216
|
+
released = true;
|
|
217
|
+
const owner = readJson(lockPath);
|
|
218
|
+
if (owner?.runId === runId && owner.pid === process.pid)
|
|
219
|
+
rmSync(lockPath, { force: true });
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
if (descriptor !== undefined)
|
|
225
|
+
closeSync(descriptor);
|
|
226
|
+
const failure = error;
|
|
227
|
+
if (failure.code !== "EEXIST")
|
|
228
|
+
throw error;
|
|
229
|
+
const owner = readJson(lockPath);
|
|
230
|
+
if (owner?.pid && processExists(owner.pid)) {
|
|
231
|
+
throw new Error(`Coverage run ${owner.runId ?? "unknown"} is already active in this project (pid ${owner.pid})`);
|
|
232
|
+
}
|
|
233
|
+
if (!owner) {
|
|
234
|
+
const ageMs = Date.now() - statSync(lockPath).mtimeMs;
|
|
235
|
+
if (ageMs < 30_000)
|
|
236
|
+
throw new Error("A coverage run is currently acquiring the project lock");
|
|
237
|
+
}
|
|
238
|
+
rmSync(lockPath, { force: true });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
throw new Error("Could not acquire the Supercov project lock");
|
|
242
|
+
}
|
|
243
|
+
function inside(root, path) {
|
|
244
|
+
const local = relative(root, path);
|
|
245
|
+
return (local === "" ||
|
|
246
|
+
(!local.startsWith(`..${sep}`) && local !== ".." && !isAbsolute(local)));
|
|
247
|
+
}
|
|
248
|
+
function copyTree(source, destination, root = false, sourceRoot = source, destinationRoot = destination, hooks = {}, finalDestinationRoot = destinationRoot) {
|
|
249
|
+
mkdirSync(destination, { recursive: true });
|
|
250
|
+
for (const entry of readdirSync(source, { withFileTypes: true })) {
|
|
251
|
+
if (root && ROOT_EXCLUSIONS.has(entry.name))
|
|
252
|
+
continue;
|
|
253
|
+
const from = resolve(source, entry.name);
|
|
254
|
+
const to = resolve(destination, entry.name);
|
|
255
|
+
const stat = lstatSync(from);
|
|
256
|
+
if (stat.isDirectory())
|
|
257
|
+
copyTree(from, to, false, sourceRoot, destinationRoot, hooks, finalDestinationRoot);
|
|
258
|
+
else if (stat.isSymbolicLink()) {
|
|
259
|
+
const link = readlinkSync(from);
|
|
260
|
+
const lexicalTarget = isAbsolute(link)
|
|
261
|
+
? resolve(link)
|
|
262
|
+
: resolve(dirname(from), link);
|
|
263
|
+
let finalTarget;
|
|
264
|
+
try {
|
|
265
|
+
finalTarget = realpathSync(from);
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
if (error.code !== "ENOENT")
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
if (!inside(sourceRoot, lexicalTarget) ||
|
|
272
|
+
(finalTarget && !inside(realpathSync(sourceRoot), finalTarget))) {
|
|
273
|
+
throw new Error(`Refusing to preserve symlink outside the isolated project: ${relative(sourceRoot, from)} -> ${link}`);
|
|
274
|
+
}
|
|
275
|
+
const targetIsDirectory = statSync(from).isDirectory();
|
|
276
|
+
const relocatedAbsoluteTarget = resolve(destinationRoot, relative(sourceRoot, lexicalTarget));
|
|
277
|
+
// Windows junctions are absolute. Point them at the stable name that the
|
|
278
|
+
// staging tree will have after publication, not the staging name that is
|
|
279
|
+
// about to disappear. POSIX links remain relative and survive rename.
|
|
280
|
+
const isolatedLink = process.platform === "win32" && targetIsDirectory
|
|
281
|
+
? resolve(finalDestinationRoot, relative(sourceRoot, lexicalTarget))
|
|
282
|
+
: isAbsolute(link)
|
|
283
|
+
? relative(dirname(to), relocatedAbsoluteTarget)
|
|
284
|
+
: link;
|
|
285
|
+
symlinkSync(isolatedLink, to, process.platform === "win32"
|
|
286
|
+
? targetIsDirectory
|
|
287
|
+
? "junction"
|
|
288
|
+
: "file"
|
|
289
|
+
: undefined);
|
|
290
|
+
}
|
|
291
|
+
else if (stat.isFile()) {
|
|
292
|
+
if (hooks.copyFile)
|
|
293
|
+
hooks.copyFile(from, to);
|
|
294
|
+
else
|
|
295
|
+
copyFileSync(from, to, constants.COPYFILE_FICLONE);
|
|
296
|
+
}
|
|
297
|
+
else
|
|
298
|
+
throw new Error(`Unsupported filesystem entry in isolated project: ${relative(sourceRoot, from)}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/** Create a copy-on-write project snapshot whose build outputs are disposable. */
|
|
302
|
+
export function prepareIsolatedWorkspace(root, runId) {
|
|
303
|
+
const workspace = isolatedWorkspacePath(root, runId);
|
|
304
|
+
rmSync(workspace, { recursive: true, force: true });
|
|
305
|
+
copyTree(root, workspace, true);
|
|
306
|
+
const nodeModules = resolve(root, "node_modules");
|
|
307
|
+
if (existsSync(nodeModules)) {
|
|
308
|
+
// Keep the node_modules mount point itself as a real directory. VM-based
|
|
309
|
+
// runners commonly layer a Linux dependency cache onto the mounted
|
|
310
|
+
// workspace's node_modules path;
|
|
311
|
+
// making the mount point an external symlink causes virtio-fs's safe
|
|
312
|
+
// `opaque` policy to reject LOOKUP with EACCES before the nested mount can
|
|
313
|
+
// be attached. Per-entry links still give host-side builds zero-copy
|
|
314
|
+
// access to the user's installed dependencies and are hidden by the VM's
|
|
315
|
+
// nested mount.
|
|
316
|
+
const isolatedNodeModules = resolve(workspace, "node_modules");
|
|
317
|
+
mkdirSync(isolatedNodeModules, { recursive: true });
|
|
318
|
+
for (const entry of readdirSync(nodeModules, { withFileTypes: true })) {
|
|
319
|
+
const target = resolve(nodeModules, entry.name);
|
|
320
|
+
symlinkSync(target, resolve(isolatedNodeModules, entry.name), process.platform === "win32"
|
|
321
|
+
? entry.isDirectory()
|
|
322
|
+
? "junction"
|
|
323
|
+
: "file"
|
|
324
|
+
: undefined);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return workspace;
|
|
328
|
+
}
|
|
329
|
+
/** Refresh the stable, disposable instrumented-build namespace. */
|
|
330
|
+
export function prepareCachedWorkspace(root, hooks = {}) {
|
|
331
|
+
const workspace = cachedWorkspacePath(root);
|
|
332
|
+
recoverCachedWorkspace(root);
|
|
333
|
+
const staging = cacheTransactionPath(root, "staging");
|
|
334
|
+
const previous = cacheTransactionPath(root, "previous");
|
|
335
|
+
let movedPrevious = false;
|
|
336
|
+
try {
|
|
337
|
+
copyTree(root, staging, true, root, staging, hooks, workspace);
|
|
338
|
+
const nodeModules = resolve(root, "node_modules");
|
|
339
|
+
if (existsSync(nodeModules)) {
|
|
340
|
+
const isolatedNodeModules = resolve(staging, "node_modules");
|
|
341
|
+
mkdirSync(isolatedNodeModules, { recursive: true });
|
|
342
|
+
for (const entry of readdirSync(nodeModules, { withFileTypes: true })) {
|
|
343
|
+
symlinkSync(resolve(nodeModules, entry.name), resolve(isolatedNodeModules, entry.name), process.platform === "win32"
|
|
344
|
+
? entry.isDirectory()
|
|
345
|
+
? "junction"
|
|
346
|
+
: "file"
|
|
347
|
+
: undefined);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
for (const requested of hooks.reusePaths ?? []) {
|
|
351
|
+
const from = resolve(workspace, requested);
|
|
352
|
+
const to = resolve(staging, requested);
|
|
353
|
+
if (!inside(workspace, from) ||
|
|
354
|
+
from === workspace ||
|
|
355
|
+
!inside(staging, to) ||
|
|
356
|
+
to === staging ||
|
|
357
|
+
!existsSync(from)) {
|
|
358
|
+
throw new Error(`Refusing to reuse unexpected build path: ${requested}`);
|
|
359
|
+
}
|
|
360
|
+
const stat = lstatSync(from);
|
|
361
|
+
if (stat.isDirectory())
|
|
362
|
+
copyTree(from, to, false, workspace, staging, hooks, workspace);
|
|
363
|
+
else if (stat.isFile()) {
|
|
364
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
365
|
+
if (hooks.copyFile)
|
|
366
|
+
hooks.copyFile(from, to);
|
|
367
|
+
else
|
|
368
|
+
copyFileSync(from, to, constants.COPYFILE_FICLONE);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
throw new Error(`Unsupported reusable build entry: ${requested}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
hooks.beforePublish?.(staging);
|
|
375
|
+
// Keep the last complete cache available for the whole refresh. These two
|
|
376
|
+
// same-filesystem renames are the only publication boundary. Recovery
|
|
377
|
+
// restores `previous` if the process is killed in the narrow gap.
|
|
378
|
+
const publishRename = hooks.rename ?? atomicRenameSync;
|
|
379
|
+
if (existsSync(workspace)) {
|
|
380
|
+
publishRename(workspace, previous);
|
|
381
|
+
movedPrevious = true;
|
|
382
|
+
hooks.afterPreviousMoved?.(previous);
|
|
383
|
+
}
|
|
384
|
+
publishRename(staging, workspace);
|
|
385
|
+
hooks.afterPublished?.(workspace);
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
if (movedPrevious && !existsSync(workspace) && existsSync(previous)) {
|
|
389
|
+
try {
|
|
390
|
+
atomicRenameSync(previous, workspace);
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
// Leave the previous tree for deterministic recovery next invocation.
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
rmSync(staging, { recursive: true, force: true });
|
|
400
|
+
}
|
|
401
|
+
// Failure to remove an obsolete previous generation must not invalidate the
|
|
402
|
+
// newly published cache. The next invocation and `supercov clean` both
|
|
403
|
+
// remove it deterministically.
|
|
404
|
+
try {
|
|
405
|
+
rmSync(previous, { recursive: true, force: true });
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
// Deliberately retained for recoverCachedWorkspace().
|
|
409
|
+
}
|
|
410
|
+
return workspace;
|
|
411
|
+
}
|
|
412
|
+
export function removeIsolatedWorkspace(root, runId) {
|
|
413
|
+
const workspace = isolatedWorkspacePath(root, runId);
|
|
414
|
+
const relativePath = relative(resolve(root, ".supercov/work"), workspace);
|
|
415
|
+
if (relativePath.startsWith("..") || relativePath.split(sep).length < 2) {
|
|
416
|
+
throw new Error(`Refusing to remove unexpected workspace path: ${workspace}`);
|
|
417
|
+
}
|
|
418
|
+
rmSync(workspace, { recursive: true, force: true });
|
|
419
|
+
}
|
|
420
|
+
/** Deterministic retention: IDs sort newest-first because run IDs are UTC. */
|
|
421
|
+
function cleanCoverageStorageLocked(root, options, removeBuildCache) {
|
|
422
|
+
recoverAbandonedRuns(root);
|
|
423
|
+
const runsRoot = resolve(root, ".supercov/runs");
|
|
424
|
+
const workRoot = resolve(root, ".supercov/work");
|
|
425
|
+
const evidenceRoot = resolve(root, ".supercov/evidence");
|
|
426
|
+
const bases = [runsRoot, workRoot, evidenceRoot];
|
|
427
|
+
const ids = new Set();
|
|
428
|
+
for (const base of bases) {
|
|
429
|
+
if (!existsSync(base))
|
|
430
|
+
continue;
|
|
431
|
+
for (const entry of readdirSync(base, { withFileTypes: true }))
|
|
432
|
+
if (entry.isDirectory())
|
|
433
|
+
ids.add(entry.name);
|
|
434
|
+
}
|
|
435
|
+
const ordered = [...ids].sort((left, right) => right.localeCompare(left));
|
|
436
|
+
const active = new Set(ordered.filter((id) => {
|
|
437
|
+
const state = readJson(resolve(root, ".supercov/work", id, "state.json"));
|
|
438
|
+
return Boolean(state && !TERMINAL.has(state.status));
|
|
439
|
+
}));
|
|
440
|
+
const published = existsSync(runsRoot)
|
|
441
|
+
? readdirSync(runsRoot, { withFileTypes: true })
|
|
442
|
+
.filter((entry) => entry.isDirectory())
|
|
443
|
+
.map((entry) => entry.name)
|
|
444
|
+
.sort((left, right) => right.localeCompare(left))
|
|
445
|
+
: [];
|
|
446
|
+
const retained = new Set(published.filter((id) => !active.has(id)).slice(0, Math.max(0, options.keep)));
|
|
447
|
+
const removedRuns = [];
|
|
448
|
+
const removedWorkspaces = [];
|
|
449
|
+
const removedEvidence = [];
|
|
450
|
+
for (const id of ordered) {
|
|
451
|
+
const work = resolve(root, ".supercov/work", id);
|
|
452
|
+
const state = readJson(resolve(work, "state.json"));
|
|
453
|
+
if (active.has(id))
|
|
454
|
+
continue;
|
|
455
|
+
const hasPublishedRun = existsSync(resolve(runsRoot, id));
|
|
456
|
+
const removeHistory = hasPublishedRun && !retained.has(id);
|
|
457
|
+
const removeTransientWork = existsSync(work) && (!state || TERMINAL.has(state.status));
|
|
458
|
+
if (removeTransientWork) {
|
|
459
|
+
removedWorkspaces.push(id);
|
|
460
|
+
if (!options.dryRun)
|
|
461
|
+
rmSync(work, { recursive: true, force: true });
|
|
462
|
+
}
|
|
463
|
+
const hasLooseEvidence = existsSync(resolve(evidenceRoot, id));
|
|
464
|
+
if (hasLooseEvidence && (!hasPublishedRun || removeHistory)) {
|
|
465
|
+
removedEvidence.push(id);
|
|
466
|
+
if (!options.dryRun)
|
|
467
|
+
rmSync(resolve(evidenceRoot, id), { recursive: true, force: true });
|
|
468
|
+
}
|
|
469
|
+
if (removeHistory) {
|
|
470
|
+
removedRuns.push(id);
|
|
471
|
+
if (!options.dryRun)
|
|
472
|
+
rmSync(resolve(runsRoot, id), { recursive: true, force: true });
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const buildCache = resolve(root, ".supercov/cache/instrumented-workspace");
|
|
476
|
+
const removedBuildCache = removeBuildCache && active.size === 0 && existsSync(buildCache);
|
|
477
|
+
if (removedBuildCache && !options.dryRun)
|
|
478
|
+
rmSync(buildCache, { recursive: true, force: true });
|
|
479
|
+
return { removedRuns, removedWorkspaces, removedEvidence, removedBuildCache };
|
|
480
|
+
}
|
|
481
|
+
/** Cleanup is itself a project-wide transaction, so it cannot race a run. */
|
|
482
|
+
export function cleanCoverageStorage(root, options) {
|
|
483
|
+
const lock = acquireProjectLock(root, `clean-${process.pid}-${randomUUID()}`);
|
|
484
|
+
try {
|
|
485
|
+
return cleanCoverageStorageLocked(root, options, true);
|
|
486
|
+
}
|
|
487
|
+
finally {
|
|
488
|
+
lock.release();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/** Explicit history retention that deliberately preserves the shared cache. */
|
|
492
|
+
export function pruneCoverageStorage(root, options) {
|
|
493
|
+
const lock = acquireProjectLock(root, `prune-${process.pid}-${randomUUID()}`);
|
|
494
|
+
try {
|
|
495
|
+
return cleanCoverageStorageLocked(root, options, false);
|
|
496
|
+
}
|
|
497
|
+
finally {
|
|
498
|
+
lock.release();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,WAAW,EACX,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAwBpE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAiB;IACvC,UAAU;IACV,QAAQ;IACR,aAAa;IACb,WAAW;CACZ,CAAC,CAAC;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,MAAM;IACN,WAAW;IACX,YAAY;IACZ,cAAc;IACd,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;IACP,SAAS;IACT,UAAU;IACV,mBAAmB;IACnB,cAAc;CACf,CAAC,CAAC;AAEH,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAI,IAAY;IAC/B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,KAAa;IAC5C,OAAO,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,KAAa;IAC/D,OAAO,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,OAAO,CAAC,IAAI,EAAE,wCAAwC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,sBAAsB,CAC7B,IAAY,EACZ,IAA4B;IAE5B,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC;AACvC,CAAC;AAED,SAAS,oBAAoB,CAC3B,IAAY,EACZ,IAA4B;IAE5B,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC5C,OAAO,OAAO,CACZ,OAAO,CAAC,SAAS,CAAC,EAClB,GAAG,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,EAAE,CACtE,CAAC;AACJ,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QACrB,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;IAE5E,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC9D,MAAM,cAAc,GAAG,sBAAsB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,OAAO;SACpB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;SACvD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,OAAO;SACrB,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,CACxE;SACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;SAC3C,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;IAC7E,MAAM,eAAe,GAAG,OAAO;SAC5B,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CACzE;SACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAE/C,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1C,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QACzC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,gBAAgB,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,OAAO;QAAE,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,KAAK,MAAM,IAAI,IAAI,QAAQ;QAAE,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5E,KAAK,MAAM,IAAI,IAAI,eAAe;QAChC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,OAAO;QACL,gBAAgB;QAChB,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,eAAe,EAAE,QAAQ,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,KAAa,EACb,KAAkC;IAElC,MAAM,QAAQ,GAAG,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IACnE,mBAAmB,CACjB,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,EACtB,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACzC,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,KAAa,EACb,MAA0E;IAE1E,MAAM,OAAO,GAAG,QAAQ,CAAW,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;IACnE,MAAM,IAAI,GAAa;QACrB,GAAG,OAAO;QACV,GAAG,MAAM;QACT,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,mBAAmB,CACjB,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,EACtB,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACrC,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CACzC,IAAY,EACZ,KAAa;IAEb,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,QAAQ,CAAkB,OAAO,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;IAClF,IACE,YAAY,EAAE,EAAE,KAAK,KAAK;QAC1B,CAAC,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,EACrD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,CAAC,EAAE;QACjD,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE;QAC7C,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QACnC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,QAAQ,CAAW,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,2BAA2B,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9C,SAAS;QACX,CAAC;QACD,IAAI,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;YAAE,SAAS;QACvC,yEAAyE;QACzE,uEAAuE;QACvE,qCAAqC;QACrC,MAAM,CAAC,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;YAC9C,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE;YACrE,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QACH,IAAI,2BAA2B,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAClD,qEAAqE;YACrE,wEAAwE;YACxE,kEAAkE;QACpE,CAAC;aAAM,CAAC;YACN,4DAA4D;YAC5D,uEAAuE;YACvE,6DAA6D;YAC7D,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;gBACtD,SAAS,EAAE,IAAI;gBACf,KAAK,EAAE,IAAI;aACZ,CAAC,CAAC;YACH,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE;gBAC/B,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,2BAA2B,KAAK,CAAC,GAAG,yBAAyB;aACrE,CAAC,CAAC;QACL,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,SAAS,CAAC,IAAI,EAAE,CAAC;AAC1B,CAAC;AAOD,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,KAAa;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC;IAC9D,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,UAA8B,CAAC;QACnC,IAAI,CAAC;YACH,UAAU,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAC7C,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;YACjH,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACnC,SAAS,CAAC,UAAU,CAAC,CAAC;YACtB,SAAS,CAAC,UAAU,CAAC,CAAC;YACtB,UAAU,GAAG,SAAS,CAAC;YACvB,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,OAAO;oBACL,IAAI,QAAQ;wBAAE,OAAO;oBACrB,QAAQ,GAAG,IAAI,CAAC;oBAChB,MAAM,KAAK,GAAG,QAAQ,CAAmC,QAAQ,CAAC,CAAC;oBACnE,IAAI,KAAK,EAAE,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;wBACrD,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACtC,CAAC;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,UAAU,KAAK,SAAS;gBAAE,SAAS,CAAC,UAAU,CAAC,CAAC;YACpD,MAAM,OAAO,GAAG,KAA8B,CAAC;YAC/C,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YAC3C,MAAM,KAAK,GAAG,QAAQ,CAAmC,QAAQ,CAAC,CAAC;YACnE,IAAI,KAAK,EAAE,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,KAAK,CACb,gBAAgB,KAAK,CAAC,KAAK,IAAI,SAAS,2CAA2C,KAAK,CAAC,GAAG,GAAG,CAChG,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;gBACtD,IAAI,KAAK,GAAG,MAAM;oBAChB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;YAC9E,CAAC;YACD,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,MAAM,CAAC,IAAY,EAAE,IAAY;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,OAAO,CACL,KAAK,KAAK,EAAE;QACZ,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CACxE,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CACf,MAAc,EACd,WAAmB,EACnB,IAAI,GAAG,KAAK,EACZ,UAAU,GAAG,MAAM,EACnB,eAAe,GAAG,WAAW,EAC7B,KAAK,GAA0B,EAAE,EACjC,oBAAoB,GAAG,eAAe;IAEtC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjE,IAAI,IAAI,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QACtD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,WAAW,EAAE;YACpB,QAAQ,CACN,IAAI,EACJ,EAAE,EACF,KAAK,EACL,UAAU,EACV,eAAe,EACf,KAAK,EACL,oBAAoB,CACrB,CAAC;aACC,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC;gBACpC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;gBACf,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;YACjC,IAAI,WAA+B,CAAC;YACpC,IAAI,CAAC;gBACH,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,KAAK,CAAC;YACtE,CAAC;YACD,IACE,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC;gBAClC,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC,EAC/D,CAAC;gBACD,MAAM,IAAI,KAAK,CACb,8DAA8D,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE,CACtG,CAAC;YACJ,CAAC;YACD,MAAM,iBAAiB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YACvD,MAAM,uBAAuB,GAAG,OAAO,CACrC,eAAe,EACf,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,CACpC,CAAC;YACF,yEAAyE;YACzE,yEAAyE;YACzE,sEAAsE;YACtE,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,iBAAiB;gBACpE,CAAC,CAAC,OAAO,CACL,oBAAoB,EACpB,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,CACpC;gBACH,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;oBAClB,CAAC,CAAC,QAAQ,CACN,OAAO,CAAC,EAAE,CAAC,EACX,uBAAuB,CACxB;oBACH,CAAC,CAAC,IAAI,CAAC;YACT,WAAW,CACT,YAAY,EACZ,EAAE,EACF,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC1B,CAAC,CAAC,iBAAiB;oBACjB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,MAAM;gBACV,CAAC,CAAC,SAAS,CACd,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACzB,IAAI,KAAK,CAAC,QAAQ;gBAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;;gBACxC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC1D,CAAC;;YAEC,MAAM,IAAI,KAAK,CACb,qDAAqD,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAClF,CAAC;IACN,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,wBAAwB,CAAC,IAAY,EAAE,KAAa;IAClE,MAAM,SAAS,GAAG,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrD,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAClD,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,yEAAyE;QACzE,mEAAmE;QACnE,iCAAiC;QACjC,qEAAqE;QACrE,2EAA2E;QAC3E,qEAAqE;QACrE,yEAAyE;QACzE,gBAAgB;QAChB,MAAM,mBAAmB,GAAG,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAC/D,SAAS,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACtE,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAChD,WAAW,CACT,MAAM,EACN,OAAO,CAAC,mBAAmB,EAAE,KAAK,CAAC,IAAI,CAAC,EACxC,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC1B,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE;oBACnB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,MAAM;gBACV,CAAC,CAAC,SAAS,CACd,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAiBD,mEAAmE;AACnE,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,KAAK,GAA0B,EAAE;IAEjC,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC5C,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACxD,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,CAAC;QACH,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAC/D,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAClD,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5B,MAAM,mBAAmB,GAAG,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YAC7D,SAAS,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBACtE,WAAW,CACT,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,EAChC,OAAO,CAAC,mBAAmB,EAAE,KAAK,CAAC,IAAI,CAAC,EACxC,OAAO,CAAC,QAAQ,KAAK,OAAO;oBAC1B,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE;wBACnB,CAAC,CAAC,UAAU;wBACZ,CAAC,CAAC,MAAM;oBACV,CAAC,CAAC,SAAS,CACd,CAAC;YACJ,CAAC;QACH,CAAC;QACD,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAC3C,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACvC,IACE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC;gBACxB,IAAI,KAAK,SAAS;gBAClB,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;gBACpB,EAAE,KAAK,OAAO;gBACd,CAAC,UAAU,CAAC,IAAI,CAAC,EACjB,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,4CAA4C,SAAS,EAAE,CAAC,CAAC;YAC3E,CAAC;YACD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,IAAI,CAAC,WAAW,EAAE;gBACpB,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;iBAC7D,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;gBACvB,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC5C,IAAI,KAAK,CAAC,QAAQ;oBAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;;oBACxC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAC;YAC1D,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,qCAAqC,SAAS,EAAE,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;QACD,KAAK,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,CAAC;QAE/B,0EAA0E;QAC1E,sEAAsE;QACtE,kEAAkE;QAClE,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,IAAI,gBAAgB,CAAC;QACvD,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACnC,aAAa,GAAG,IAAI,CAAC;YACrB,KAAK,CAAC,kBAAkB,EAAE,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC;QACD,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAClC,KAAK,CAAC,cAAc,EAAE,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,aAAa,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpE,IAAI,CAAC;gBACH,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,sEAAsE;YACxE,CAAC;QACH,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,+BAA+B;IAC/B,IAAI,CAAC;QACH,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,KAAa;IACjE,MAAM,SAAS,GAAG,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,EAAE,SAAS,CAAC,CAAC;IAC1E,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,iDAAiD,SAAS,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC;AAcD,8EAA8E;AAC9E,SAAS,0BAA0B,CACjC,IAAY,EACZ,OAAuB,EACvB,gBAAyB;IAEzB,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAChC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;YAC5D,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;QACpB,MAAM,KAAK,GAAG,QAAQ,CAAW,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;QACpF,OAAO,OAAO,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CACH,CAAC;IACF,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC;QACpC,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aAC3C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;aACtC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAC1B,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACrD,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAC9E,CAAC;IACF,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,iBAAiB,GAAa,EAAE,CAAC;IACvC,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,QAAQ,CAAW,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;QAC9D,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAC7B,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;QAC1D,MAAM,aAAa,GAAG,eAAe,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,mBAAmB,GACvB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7D,IAAI,mBAAmB,EAAE,CAAC;YACxB,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/D,IAAI,gBAAgB,IAAI,CAAC,CAAC,eAAe,IAAI,aAAa,CAAC,EAAE,CAAC;YAC5D,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,MAAM;gBACjB,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,aAAa,EAAE,CAAC;YAClB,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACrB,IAAI,CAAC,OAAO,CAAC,MAAM;gBACjB,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,wCAAwC,CAAC,CAAC;IAC3E,MAAM,iBAAiB,GACrB,gBAAgB,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IAClE,IAAI,iBAAiB,IAAI,CAAC,OAAO,CAAC,MAAM;QACtC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,CAAC;AAChF,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,OAAuB;IAEvB,MAAM,IAAI,GAAG,kBAAkB,CAC7B,IAAI,EACJ,SAAS,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,EAAE,CACvC,CAAC;IACF,IAAI,CAAC;QACH,OAAO,0BAA0B,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,OAAuB;IAEvB,MAAM,IAAI,GAAG,kBAAkB,CAC7B,IAAI,EACJ,SAAS,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,EAAE,CACvC,CAAC;IACF,IAAI,CAAC;QACH,OAAO,0BAA0B,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Performance and storage
|
|
2
|
+
|
|
3
|
+
Supercov separates timings that it can measure safely from overhead that
|
|
4
|
+
requires an explicit control run. It never runs a user's command a second time
|
|
5
|
+
automatically: an arbitrary test command can write data, call paid services, or
|
|
6
|
+
be intentionally non-repeatable.
|
|
7
|
+
|
|
8
|
+
## Per-run measurements
|
|
9
|
+
|
|
10
|
+
Every coverage run prints and stores monotonic durations for:
|
|
11
|
+
|
|
12
|
+
| Phase | Includes |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `initializationMs` | recovery, locking, project discovery, and integrity fingerprints |
|
|
15
|
+
| `workspacePreparationMs` | transactional refresh of the isolated namespace |
|
|
16
|
+
| `adapterSetupMs` | generated adapters, configs, manifests, and runtime files |
|
|
17
|
+
| `instrumentedBuildMs` | the coverage-aware build/direct pass, or near-zero when an exact-fingerprint build is reused |
|
|
18
|
+
| `testCommandMs` | the user's unchanged command, including any runner or remote infrastructure latency |
|
|
19
|
+
| `evidencePublicationMs` | evidence collection, validation/summary analysis, lossless archive generation, and atomic run staging |
|
|
20
|
+
|
|
21
|
+
The fields are stored in `.supercov/runs/<run-id>/run.json` and returned by
|
|
22
|
+
`supercov runs --json`. Total duration is stored separately as `durationMs`.
|
|
23
|
+
|
|
24
|
+
The non-test phases are not automatically labelled “overhead.” For example, a
|
|
25
|
+
test script that ordinarily performs its own build may overlap work with the
|
|
26
|
+
instrumented-build phase. True end-to-end overhead must compare equivalent
|
|
27
|
+
cold runs or equivalent warm runs of the same command.
|
|
28
|
+
|
|
29
|
+
## Reproducible comparison
|
|
30
|
+
|
|
31
|
+
Run the command without and with Supercov under the same cache state. Use at
|
|
32
|
+
least three alternating pairs and report the medians. Never compare a cold VM,
|
|
33
|
+
browser, package-manager, or build-cache run with a warm one.
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
/usr/bin/time -p npm test
|
|
37
|
+
/usr/bin/time -p npx supercov -- npm test
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Package acquisition is a separate user-interface cost. A cold `npx` download
|
|
41
|
+
depends on the registry and network; a cached `npx` resolution should be
|
|
42
|
+
reported separately from Supercov's recorded phases.
|
|
43
|
+
|
|
44
|
+
## Reference measurement, not a guarantee
|
|
45
|
+
|
|
46
|
+
On 2026-08-24, the 29-test Essential SEO offline suite on the development Mac
|
|
47
|
+
produced this warm pair:
|
|
48
|
+
|
|
49
|
+
| Measurement | Duration |
|
|
50
|
+
| --- | ---: |
|
|
51
|
+
| unchanged command | 39.57 s |
|
|
52
|
+
| Supercov total | 45.38 s |
|
|
53
|
+
| end-to-end difference | +5.81 s (+14.7%) |
|
|
54
|
+
| initialization | 0.06 s |
|
|
55
|
+
| workspace preparation | 0.35 s |
|
|
56
|
+
| adapter setup | 0.05 s |
|
|
57
|
+
| instrumented build | 4.87 s |
|
|
58
|
+
| test command inside Supercov | 39.60 s |
|
|
59
|
+
| evidence/report preparation (historical format) | 0.40 s |
|
|
60
|
+
|
|
61
|
+
The test-command durations were effectively identical in this pair. The extra
|
|
62
|
+
instrumented build accounted for about 84% of the measured difference. A
|
|
63
|
+
seven-sample isolated workspace refresh had a 270 ms median and 447 ms maximum.
|
|
64
|
+
The built output grew from 2,781,273 to 3,094,366 logical bytes (+11.3%). A
|
|
65
|
+
cached `npx supercov help` added a 686 ms median over direct CLI startup; the
|
|
66
|
+
first observed `npx` resolution took 2.22 s.
|
|
67
|
+
|
|
68
|
+
Cold VM-image runs were 170.34 s without Supercov and 175.44 s with Supercov in
|
|
69
|
+
the same session, but a single cold pair is too noisy for a general percentage.
|
|
70
|
+
Both spent approximately 124 seconds preparing their VM image.
|
|
71
|
+
|
|
72
|
+
Before raw-evidence-only storage, the reference run retained 4.5 MB of reports
|
|
73
|
+
and 1.7 MB across 178 loose evidence files. Its canonical compressed JSON was
|
|
74
|
+
0.9 MB. The execution evidence alone packed to about 121 KiB; current archives
|
|
75
|
+
also embed the exact denominator manifest and are the sole coverage artifact.
|
|
76
|
+
Every CLI query derives its view from the archive on demand without writing a
|
|
77
|
+
cache.
|
|
78
|
+
These numbers are application- and filesystem-specific optimization baselines.
|
|
79
|
+
An exact matching Vite build is also reused across runs, removing the measured
|
|
80
|
+
4.87-second repeated build; any source/configuration/toolchain-key change falls
|
|
81
|
+
back to a fresh isolated build.
|
|
82
|
+
|
|
83
|
+
The evidence-only Essential SEO validation packed the exact manifest plus 178
|
|
84
|
+
execution-evidence files (2.66 MB uncompressed) into a 248 KiB archive. With
|
|
85
|
+
the 4 KiB `run.json`, the complete immutable run occupies 252 KiB and contains
|
|
86
|
+
no derived report. Its identical warm 29-test run recorded 0 ms for the build
|
|
87
|
+
phase and 40.49 seconds total: 0.07 seconds initialization, 0.36 seconds
|
|
88
|
+
workspace refresh, 0.05 seconds adapter setup, 39.84 seconds in the unchanged
|
|
89
|
+
test command, and 0.12 seconds evidence validation/archive publication. Fresh
|
|
90
|
+
process queries for summary, files, and gaps each took 0.16–0.20 seconds on
|
|
91
|
+
this run while writing no cache. Test execution is still the dominant and
|
|
92
|
+
naturally variable part of the total.
|
|
93
|
+
|
|
94
|
+
## Isolation strategy trade-offs
|
|
95
|
+
|
|
96
|
+
| Strategy | Arbitrary-runner compatibility | Failure isolation | Startup/storage |
|
|
97
|
+
| --- | --- | --- | --- |
|
|
98
|
+
| Transactional physical namespace | Highest; ordinary filesystem consumers and opaque mounts see real files | Strong when staging, publication, recovery, locking, and same-filesystem renames are enforced | Recreates directory entries and may copy bytes when reflinks are unavailable |
|
|
99
|
+
| Node loader/Vite plugins | High for observed Node and bundler graphs, incomplete for native readers and hidden remote mounts | Strong because transformed source need not be persisted | Lowest retained storage and usually fastest |
|
|
100
|
+
| FUSE/OS overlay | Potentially broad local read interception, but not portable or zero-install | Adds mount, privilege, kernel/extension, and teardown failure boundaries | Low duplicated storage but operationally expensive |
|
|
101
|
+
| Adaptive hybrid | Fast path where capability is proven; transactional namespace otherwise | Inherits the physical fallback's guarantees when detection is conservative | Best practical balance; more implementation paths must be tested |
|
|
102
|
+
|
|
103
|
+
The safe default remains the transactional physical namespace. The intended
|
|
104
|
+
optimization is an adaptive hybrid that selects a proven loader/plugin path
|
|
105
|
+
and lazily materializes the same transactional fallback whenever an opaque
|
|
106
|
+
runner needs real files. FUSE is not an appropriate portable default for a
|
|
107
|
+
zero-install `npx` tool.
|