@codyswann/lisa 4.28.5 → 4.29.0
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/all/copy-overwrite/scripts/lisa-gates.mjs +2 -1
- package/all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh +3 -2
- package/cdk/create-only/vitest.config.local.ts +5 -1
- package/cdk/package-lisa/package.lisa.json +14 -7
- package/dist/cli/lisa-test-run-bootstrap.d.ts +3 -0
- package/dist/cli/lisa-test-run-bootstrap.d.ts.map +1 -0
- package/dist/cli/lisa-test-run-bootstrap.js +201 -0
- package/dist/cli/lisa-test-run-bootstrap.js.map +1 -0
- package/dist/cli/lisa-test-run-ipc.d.ts +42 -0
- package/dist/cli/lisa-test-run-ipc.d.ts.map +1 -0
- package/dist/cli/lisa-test-run-ipc.js +200 -0
- package/dist/cli/lisa-test-run-ipc.js.map +1 -0
- package/dist/cli/lisa-test-run-process-group.d.ts +30 -0
- package/dist/cli/lisa-test-run-process-group.d.ts.map +1 -0
- package/dist/cli/lisa-test-run-process-group.js +123 -0
- package/dist/cli/lisa-test-run-process-group.js.map +1 -0
- package/dist/cli/lisa-test-run-reaper.d.ts +3 -0
- package/dist/cli/lisa-test-run-reaper.d.ts.map +1 -0
- package/dist/cli/lisa-test-run-reaper.js +195 -0
- package/dist/cli/lisa-test-run-reaper.js.map +1 -0
- package/dist/cli/lisa-test-run-runtime.d.ts +46 -0
- package/dist/cli/lisa-test-run-runtime.d.ts.map +1 -0
- package/dist/cli/lisa-test-run-runtime.js +123 -0
- package/dist/cli/lisa-test-run-runtime.js.map +1 -0
- package/dist/cli/lisa-test-run-supervisor.d.ts +13 -0
- package/dist/cli/lisa-test-run-supervisor.d.ts.map +1 -0
- package/dist/cli/lisa-test-run-supervisor.js +268 -0
- package/dist/cli/lisa-test-run-supervisor.js.map +1 -0
- package/dist/cli/lisa-test-run.d.ts +3 -0
- package/dist/cli/lisa-test-run.d.ts.map +1 -0
- package/dist/cli/lisa-test-run.js +66 -0
- package/dist/cli/lisa-test-run.js.map +1 -0
- package/dist/configs/vitest/base.d.ts.map +1 -1
- package/dist/configs/vitest/base.js +5 -2
- package/dist/configs/vitest/base.js.map +1 -1
- package/dist/configs/vitest/cdk.d.ts +4 -0
- package/dist/configs/vitest/cdk.d.ts.map +1 -1
- package/dist/configs/vitest/cdk.js +5 -0
- package/dist/configs/vitest/cdk.js.map +1 -1
- package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
- package/dist/configs/vitest/harper-fabric.js +1 -0
- package/dist/configs/vitest/harper-fabric.js.map +1 -1
- package/dist/configs/vitest/nestjs.d.ts.map +1 -1
- package/dist/configs/vitest/nestjs.js +1 -0
- package/dist/configs/vitest/nestjs.js.map +1 -1
- package/dist/configs/vitest/phaser.d.ts.map +1 -1
- package/dist/configs/vitest/phaser.js +1 -0
- package/dist/configs/vitest/phaser.js.map +1 -1
- package/dist/configs/vitest/scratch-authority.d.ts +25 -0
- package/dist/configs/vitest/scratch-authority.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-authority.js +107 -0
- package/dist/configs/vitest/scratch-authority.js.map +1 -0
- package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts +5 -0
- package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-bound-cleanup-programs.js +219 -0
- package/dist/configs/vitest/scratch-bound-cleanup-programs.js.map +1 -0
- package/dist/configs/vitest/scratch-bound-cleanup.d.ts +28 -0
- package/dist/configs/vitest/scratch-bound-cleanup.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-bound-cleanup.js +155 -0
- package/dist/configs/vitest/scratch-bound-cleanup.js.map +1 -0
- package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts +17 -0
- package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-bound-root-cleanup.js +149 -0
- package/dist/configs/vitest/scratch-bound-root-cleanup.js.map +1 -0
- package/dist/configs/vitest/scratch-global-setup.d.ts +17 -9
- package/dist/configs/vitest/scratch-global-setup.d.ts.map +1 -1
- package/dist/configs/vitest/scratch-global-setup.js +103 -35
- package/dist/configs/vitest/scratch-global-setup.js.map +1 -1
- package/dist/configs/vitest/scratch-leak-setup.d.ts +2 -0
- package/dist/configs/vitest/scratch-leak-setup.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-leak-setup.js +108 -0
- package/dist/configs/vitest/scratch-leak-setup.js.map +1 -0
- package/dist/configs/vitest/scratch-namespace-authority.d.ts +80 -0
- package/dist/configs/vitest/scratch-namespace-authority.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-namespace-authority.js +291 -0
- package/dist/configs/vitest/scratch-namespace-authority.js.map +1 -0
- package/dist/configs/vitest/scratch-owner.d.ts +131 -0
- package/dist/configs/vitest/scratch-owner.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-owner.js +297 -0
- package/dist/configs/vitest/scratch-owner.js.map +1 -0
- package/dist/configs/vitest/scratch-route-profile.d.ts +36 -0
- package/dist/configs/vitest/scratch-route-profile.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-route-profile.js +217 -0
- package/dist/configs/vitest/scratch-route-profile.js.map +1 -0
- package/dist/configs/vitest/scratch-run-root-compatibility.d.ts +33 -0
- package/dist/configs/vitest/scratch-run-root-compatibility.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-run-root-compatibility.js +137 -0
- package/dist/configs/vitest/scratch-run-root-compatibility.js.map +1 -0
- package/dist/configs/vitest/scratch-run-root-intent.d.ts +66 -0
- package/dist/configs/vitest/scratch-run-root-intent.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-run-root-intent.js +183 -0
- package/dist/configs/vitest/scratch-run-root-intent.js.map +1 -0
- package/dist/configs/vitest/scratch-setup.d.ts.map +1 -1
- package/dist/configs/vitest/scratch-setup.js +45 -15
- package/dist/configs/vitest/scratch-setup.js.map +1 -1
- package/dist/configs/vitest/scratch-supervision-intent.d.ts +8 -0
- package/dist/configs/vitest/scratch-supervision-intent.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-supervision-intent.js +125 -0
- package/dist/configs/vitest/scratch-supervision-intent.js.map +1 -0
- package/dist/configs/vitest/scratch-supervision-protocol.d.ts +12 -0
- package/dist/configs/vitest/scratch-supervision-protocol.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-supervision-protocol.js +230 -0
- package/dist/configs/vitest/scratch-supervision-protocol.js.map +1 -0
- package/dist/configs/vitest/scratch-supervision.d.ts +64 -0
- package/dist/configs/vitest/scratch-supervision.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-supervision.js +209 -0
- package/dist/configs/vitest/scratch-supervision.js.map +1 -0
- package/dist/configs/vitest/scratch-sweep.d.ts +66 -0
- package/dist/configs/vitest/scratch-sweep.d.ts.map +1 -0
- package/dist/configs/vitest/scratch-sweep.js +82 -0
- package/dist/configs/vitest/scratch-sweep.js.map +1 -0
- package/dist/configs/vitest/scratch.d.ts +14 -187
- package/dist/configs/vitest/scratch.d.ts.map +1 -1
- package/dist/configs/vitest/scratch.js +18 -285
- package/dist/configs/vitest/scratch.js.map +1 -1
- package/dist/configs/vitest/typescript.d.ts.map +1 -1
- package/dist/configs/vitest/typescript.js +1 -0
- package/dist/configs/vitest/typescript.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +4 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +59 -13
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/expo/package-lisa/package.lisa.json +14 -14
- package/harper-fabric/package-lisa/package.lisa.json +8 -3
- package/nestjs/create-only/vitest.config.local.ts +5 -1
- package/nestjs/package-lisa/package.lisa.json +14 -8
- package/npm-package/package-lisa/package.lisa.json +8 -0
- package/package.json +17 -13
- package/phaser/package-lisa/package.lisa.json +9 -3
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/sonar-secrets.sh +3 -2
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/sonar-secrets.sh +3 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/sonar-secrets.sh +3 -2
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/sonar-secrets.sh +3 -2
- package/typescript/create-only/vitest.config.local.ts +5 -1
- package/typescript/package-lisa/package.lisa.json +14 -8
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/** Kernel-bound removal of one token-authorized scratch run root. */
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { SCRATCH_QUARANTINE_PREFIX, assertScratchNamespaceAuthority, } from "./scratch-namespace-authority.js";
|
|
7
|
+
import { BOUND_DIRECTORY_CLEANUP_PROGRAM } from "./scratch-bound-cleanup-programs.js";
|
|
8
|
+
import { readScratchOwnerRecord, } from "./scratch-owner.js";
|
|
9
|
+
/** Exit status used when a child cwd does not match its expected inode. */
|
|
10
|
+
const BOUND_CLEANUP_IDENTITY_EXIT = 73;
|
|
11
|
+
/**
|
|
12
|
+
* Demand one inert direct basename, never a path.
|
|
13
|
+
* @param basename - Candidate direct-child name
|
|
14
|
+
*/
|
|
15
|
+
function validateBasename(basename) {
|
|
16
|
+
if (basename === "" ||
|
|
17
|
+
basename === "." ||
|
|
18
|
+
basename === ".." ||
|
|
19
|
+
path.basename(basename) !== basename ||
|
|
20
|
+
basename.includes("/") ||
|
|
21
|
+
basename.includes("\\")) {
|
|
22
|
+
throw new Error(`Scratch candidate must be a direct basename: ${basename}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Read one candidate without converting absence into an exception.
|
|
27
|
+
* @param candidate - Candidate path
|
|
28
|
+
* @returns Candidate metadata, or undefined when already absent
|
|
29
|
+
*/
|
|
30
|
+
function lstatIfPresent(candidate) {
|
|
31
|
+
try {
|
|
32
|
+
return fs.lstatSync(candidate);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error.code === "ENOENT")
|
|
36
|
+
return undefined;
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Clear a directory through a cwd bound to the inspected inode.
|
|
42
|
+
* @param candidate - Directory path
|
|
43
|
+
* @param expected - Pinned directory identity
|
|
44
|
+
*/
|
|
45
|
+
function clearDirectoryThroughBoundCwd(candidate, expected) {
|
|
46
|
+
const result = spawnSync(process.execPath, [
|
|
47
|
+
"--input-type=commonjs",
|
|
48
|
+
"--eval",
|
|
49
|
+
BOUND_DIRECTORY_CLEANUP_PROGRAM,
|
|
50
|
+
String(expected.dev),
|
|
51
|
+
String(expected.ino),
|
|
52
|
+
], { cwd: candidate, encoding: "utf8", maxBuffer: 64 * 1024 });
|
|
53
|
+
if (result.error !== undefined)
|
|
54
|
+
throw result.error;
|
|
55
|
+
if (result.status === BOUND_CLEANUP_IDENTITY_EXIT) {
|
|
56
|
+
throw new Error("Scratch directory identity changed before bound cleanup");
|
|
57
|
+
}
|
|
58
|
+
if (result.status !== 0 || result.signal !== null) {
|
|
59
|
+
throw new Error(`Scratch bound cleanup failed: ${result.stderr.trim() || result.signal || String(result.status)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Bind an optional armed inode identity to the inspected candidate.
|
|
64
|
+
* @param options - Authorized root-removal options
|
|
65
|
+
* @param stat - Candidate metadata when present
|
|
66
|
+
* @returns Validated metadata, or undefined when already absent
|
|
67
|
+
*/
|
|
68
|
+
function validateExpectedRootIdentity(options, stat) {
|
|
69
|
+
if (stat !== undefined &&
|
|
70
|
+
options.expectedIdentity !== undefined &&
|
|
71
|
+
(stat.dev !== options.expectedIdentity.dev ||
|
|
72
|
+
stat.ino !== options.expectedIdentity.ino)) {
|
|
73
|
+
throw new Error("Scratch root identity does not match armed intent");
|
|
74
|
+
}
|
|
75
|
+
return stat;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate authority before reading a destructive candidate.
|
|
79
|
+
* @param options - Authorized root-removal options
|
|
80
|
+
* @param candidate - Candidate path
|
|
81
|
+
* @returns Candidate metadata, or undefined when already absent
|
|
82
|
+
*/
|
|
83
|
+
function inspectAuthorizedCandidate(options, candidate) {
|
|
84
|
+
validateBasename(options.basename);
|
|
85
|
+
assertScratchNamespaceAuthority(options.authority);
|
|
86
|
+
return validateExpectedRootIdentity(options, lstatIfPresent(candidate));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Revalidate an expected marker token at a specific path.
|
|
90
|
+
* @param candidate - Candidate path
|
|
91
|
+
* @param expectedToken - Token authorized by the caller
|
|
92
|
+
* @param phase - Whether validation occurs before or after quarantine
|
|
93
|
+
*/
|
|
94
|
+
function assertOwnerToken(candidate, expectedToken, phase) {
|
|
95
|
+
if (expectedToken === undefined)
|
|
96
|
+
return;
|
|
97
|
+
const owner = readScratchOwnerRecord(candidate);
|
|
98
|
+
if (owner.token !== expectedToken) {
|
|
99
|
+
throw new Error(`Scratch owner token changed ${phase} quarantine`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Revalidate and unlink the renamed candidate.
|
|
104
|
+
* @param quarantine - Quarantined path
|
|
105
|
+
* @param before - Pinned pre-rename metadata
|
|
106
|
+
* @param expectedToken - Token authorized by the caller
|
|
107
|
+
* @param afterIdentityCheck - Optional adversarial test hook
|
|
108
|
+
* @returns True after successful removal
|
|
109
|
+
*/
|
|
110
|
+
function removeVerifiedQuarantine(quarantine, before, expectedToken, afterIdentityCheck) {
|
|
111
|
+
const after = fs.lstatSync(quarantine);
|
|
112
|
+
if (after.isSymbolicLink() ||
|
|
113
|
+
after.dev !== before.dev ||
|
|
114
|
+
after.ino !== before.ino) {
|
|
115
|
+
throw new Error("Scratch root identity changed during quarantine");
|
|
116
|
+
}
|
|
117
|
+
afterIdentityCheck?.(quarantine);
|
|
118
|
+
assertOwnerToken(quarantine, expectedToken, "after");
|
|
119
|
+
clearDirectoryThroughBoundCwd(quarantine, after);
|
|
120
|
+
fs.rmdirSync(quarantine);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Quarantine and delete one direct owned root.
|
|
125
|
+
* @param options - Authorized root-removal options
|
|
126
|
+
* @returns Whether an existing root was removed
|
|
127
|
+
*/
|
|
128
|
+
export function removeAuthorizedScratchRoot(options) {
|
|
129
|
+
const candidate = path.join(options.authority.namespace.canonicalPath, options.basename);
|
|
130
|
+
const quarantineName = `${SCRATCH_QUARANTINE_PREFIX}${randomBytes(16).toString("hex")}`;
|
|
131
|
+
const quarantine = path.join(options.authority.namespace.canonicalPath, quarantineName);
|
|
132
|
+
const before = inspectAuthorizedCandidate(options, candidate);
|
|
133
|
+
if (before === undefined)
|
|
134
|
+
return false;
|
|
135
|
+
if (before.isSymbolicLink() || !before.isDirectory()) {
|
|
136
|
+
throw new Error(`Scratch root authority refuses non-directory or symlink: ${candidate}`);
|
|
137
|
+
}
|
|
138
|
+
assertOwnerToken(candidate, options.expectedToken, "before");
|
|
139
|
+
try {
|
|
140
|
+
fs.renameSync(candidate, quarantine);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (error.code === "ENOENT")
|
|
144
|
+
return false;
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
return removeVerifiedQuarantine(quarantine, before, options.expectedToken, options.afterIdentityCheck);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=scratch-bound-root-cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-bound-root-cleanup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-root-cleanup.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { processBirthFingerprintSnapshot } from "./scratch-owner.js";
|
|
1
2
|
/**
|
|
2
3
|
* Upper bound on entries the namespace may hold that **nobody owns**.
|
|
3
4
|
*
|
|
@@ -35,7 +36,7 @@ export declare const MAX_NAMESPACE_ENTRIES = 512;
|
|
|
35
36
|
export interface NamespaceResidue {
|
|
36
37
|
/** Entries whose owning process is gone but which were not removed */
|
|
37
38
|
readonly orphaned: readonly string[];
|
|
38
|
-
/** Entries
|
|
39
|
+
/** Entries whose exact dead ownership cannot be established */
|
|
39
40
|
readonly unrecognised: readonly string[];
|
|
40
41
|
/** Total entries present */
|
|
41
42
|
readonly total: number;
|
|
@@ -50,26 +51,33 @@ export interface NamespaceResidue {
|
|
|
50
51
|
* that regression into a failure instead of a slow leak.
|
|
51
52
|
* @param dir - Namespace directory to inspect
|
|
52
53
|
* @param alive - Liveness probe, overridable for tests
|
|
54
|
+
* @param birth - Process-birth probe, overridable for tests
|
|
53
55
|
* @returns The classified residue.
|
|
54
56
|
*/
|
|
55
|
-
export declare const inspectNamespace: (dir?: string, alive?: (pid: number) => boolean) => NamespaceResidue;
|
|
57
|
+
export declare const inspectNamespace: (dir?: string, alive?: (pid: number) => boolean, birth?: (pid: number) => string | undefined) => NamespaceResidue;
|
|
56
58
|
/**
|
|
57
59
|
* Builds the failure message for a namespace that is accumulating.
|
|
58
60
|
*
|
|
59
61
|
* Every branch names the directory and the offending entries, because the
|
|
60
62
|
* failure this replaces was a 60-second timeout that named nothing.
|
|
61
63
|
*
|
|
62
|
-
* `unrecognised` is deliberately
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* did not, and a namespace growing without bound.
|
|
64
|
+
* `unrecognised` is deliberately a fail-closed refusal. A foreign, malformed,
|
|
65
|
+
* or identity-mismatched entry is preserved because age and naming are not
|
|
66
|
+
* deletion authority; proceeding would silently permit unbounded residue, and
|
|
67
|
+
* deleting it could destroy a live sibling's work. The diagnostic therefore
|
|
68
|
+
* names the bounded entries and gives the only safe manual recovery rule.
|
|
68
69
|
* @param dir - Namespace directory inspected
|
|
69
70
|
* @param residue - What the inspection found
|
|
70
71
|
* @returns The message, or `undefined` when the namespace is healthy.
|
|
71
72
|
*/
|
|
72
73
|
export declare const describeResidueFailure: (dir: string, residue: NamespaceResidue) => string | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Sweeps the namespace and reports what survived.
|
|
76
|
+
* @param alive - Process liveness probe shared by both phases
|
|
77
|
+
* @param snapshot - One bulk process-birth snapshot provider
|
|
78
|
+
* @returns The residue remaining after the sweep.
|
|
79
|
+
*/
|
|
80
|
+
export declare const sweepThenInspect: (alive?: (pid: number) => boolean, snapshot?: typeof processBirthFingerprintSnapshot) => NamespaceResidue;
|
|
73
81
|
/**
|
|
74
82
|
* Renders the banner printed at the moment of refusal.
|
|
75
83
|
*
|
|
@@ -193,7 +201,7 @@ export declare const announceRefusal: (failure: string, writeNotice?: (text: str
|
|
|
193
201
|
* A refusal speaks twice — a banner before collection and a summary line at
|
|
194
202
|
* exit — and the two are not redundant. They are the top and the bottom of a
|
|
195
203
|
* transcript nobody reads in full.
|
|
196
|
-
* @throws When residue is present that the sweep cannot or did not reclaim.
|
|
204
|
+
* @throws {Error} When residue is present that the sweep cannot or did not reclaim.
|
|
197
205
|
*/
|
|
198
206
|
export declare const setup: () => void;
|
|
199
207
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scratch-global-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scratch-global-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"AAmDA,OAAO,EAGL,+BAA+B,EAGhC,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AA4DD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAK,MAA8B,EACnC,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAmC,KACnE,gBAgBF,CAAC;AAeF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,MAAM,EACX,SAAS,gBAAgB,KACxB,MAAM,GAAG,SAsCX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,WAAU,OAAO,+BAAiE,KACjF,gBAQF,CAAC;AAgBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAcxC,CAAC;AAKf;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MAG/B,CAAC;AAEzB;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAQhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,QAAO,CAAC,IAAI,EAAE,MAAM,KAAK,IAExB,KACA,IAIF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EACf,cAAa,CAAC,IAAI,EAAE,MAAM,KAAK,IAE9B,KACA,IAMF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,QAAO,IAexB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,QAAO,IAE3B,CAAC"}
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs once in the main Vitest process, on either side of the whole run.
|
|
5
5
|
*
|
|
6
|
-
* `setup` reclaims residue
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* `setup` reclaims residue only when a valid owner marker proves the exact root
|
|
7
|
+
* belongs to a dead process. It REFUSES TO START when a root cannot be removed
|
|
8
|
+
* with that authority or when proven-dead residue survives the sweep. That
|
|
9
|
+
* fail-closed distinction prevents a cleanup guard from deleting a live
|
|
10
|
+
* sibling's work. `teardown` reclaims what this run allocated.
|
|
10
11
|
*
|
|
11
12
|
* The check is deliberately a hard failure. A previous version of this problem
|
|
12
13
|
* was invisible for a working day because every instrument that met the
|
|
@@ -29,16 +30,18 @@
|
|
|
29
30
|
* a hook the runner ignores is the exact defect this file was written to stop,
|
|
30
31
|
* reproduced inside the guard itself.
|
|
31
32
|
*
|
|
32
|
-
* Checking at the start rather than the end costs nothing that matters:
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* is still visible one run later.
|
|
33
|
+
* Checking at the start rather than the end costs nothing that matters: the
|
|
34
|
+
* next run reclaims roots whose exact dead ownership it can prove, preserves
|
|
35
|
+
* uncertain paths, and refuses before collection if anything unsafe remains.
|
|
36
|
+
* Accumulation is a trend, and a trend is still visible one run later.
|
|
36
37
|
* @see {@link module:configs/vitest/scratch} for the reclaim rules
|
|
37
38
|
* @module configs/vitest/scratch-global-setup
|
|
38
39
|
*/
|
|
39
|
-
import { writeSync } from "node:fs";
|
|
40
|
+
import { lstatSync, realpathSync, writeSync } from "node:fs";
|
|
41
|
+
import * as path from "node:path";
|
|
40
42
|
import { env } from "node:process";
|
|
41
|
-
import { isProcessAlive,
|
|
43
|
+
import { isProcessAlive, liveOwnerBirthProbe, readNamespaceEntries, scratchNamespaceDir, sweepScratchNamespace, } from "./scratch.js";
|
|
44
|
+
import { classifyScratchOwner, processBirthFingerprint, processBirthFingerprintSnapshot, readScratchOwnerRecord, } from "./scratch-owner.js";
|
|
42
45
|
/**
|
|
43
46
|
* Upper bound on entries the namespace may hold that **nobody owns**.
|
|
44
47
|
*
|
|
@@ -72,6 +75,50 @@ import { isProcessAlive, parseRunRootName, readNamespaceEntries, scratchNamespac
|
|
|
72
75
|
* against measured harm and belongs in its own ticket.
|
|
73
76
|
*/
|
|
74
77
|
export const MAX_NAMESPACE_ENTRIES = 512;
|
|
78
|
+
/**
|
|
79
|
+
* Verify that a durable marker names the physical root and namespace inspected.
|
|
80
|
+
* @param dir - Canonical namespace path
|
|
81
|
+
* @param root - Direct child root
|
|
82
|
+
* @param owner - Valid owner marker
|
|
83
|
+
* @returns True when device, inode, and canonical identities all match
|
|
84
|
+
*/
|
|
85
|
+
function markerMatchesInspectedPath(dir, root, owner) {
|
|
86
|
+
const namespaceStat = lstatSync(dir);
|
|
87
|
+
const rootStat = lstatSync(root);
|
|
88
|
+
return (!rootStat.isSymbolicLink() &&
|
|
89
|
+
rootStat.isDirectory() &&
|
|
90
|
+
owner.namespace.canonicalPath === realpathSync(dir) &&
|
|
91
|
+
owner.namespace.dev === namespaceStat.dev &&
|
|
92
|
+
owner.namespace.ino === namespaceStat.ino &&
|
|
93
|
+
owner.root.canonicalPath === realpathSync(root) &&
|
|
94
|
+
owner.root.dev === rootStat.dev &&
|
|
95
|
+
owner.root.ino === rootStat.ino);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Classify one direct namespace child without treating age as authority.
|
|
99
|
+
* @param dir - Canonical namespace path
|
|
100
|
+
* @param name - Direct child basename
|
|
101
|
+
* @param alive - Pid liveness probe
|
|
102
|
+
* @param birth - Process-birth probe
|
|
103
|
+
* @returns Fail-closed entry disposition
|
|
104
|
+
*/
|
|
105
|
+
function classifyNamespaceEntry(dir, name, alive, birth) {
|
|
106
|
+
const root = path.join(dir, name);
|
|
107
|
+
try {
|
|
108
|
+
const owner = readScratchOwnerRecord(root);
|
|
109
|
+
if (!markerMatchesInspectedPath(dir, root, owner))
|
|
110
|
+
return "unrecognised";
|
|
111
|
+
return classifyScratchOwner(owner, {
|
|
112
|
+
isProcessAlive: alive,
|
|
113
|
+
processBirthFingerprint: birth,
|
|
114
|
+
}) === "preserve"
|
|
115
|
+
? "live"
|
|
116
|
+
: "orphaned";
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return "unrecognised";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
75
122
|
/**
|
|
76
123
|
* Classifies what is left in the namespace.
|
|
77
124
|
*
|
|
@@ -82,16 +129,22 @@ export const MAX_NAMESPACE_ENTRIES = 512;
|
|
|
82
129
|
* that regression into a failure instead of a slow leak.
|
|
83
130
|
* @param dir - Namespace directory to inspect
|
|
84
131
|
* @param alive - Liveness probe, overridable for tests
|
|
132
|
+
* @param birth - Process-birth probe, overridable for tests
|
|
85
133
|
* @returns The classified residue.
|
|
86
134
|
*/
|
|
87
|
-
export const inspectNamespace = (dir = scratchNamespaceDir(), alive = isProcessAlive) => {
|
|
135
|
+
export const inspectNamespace = (dir = scratchNamespaceDir(), alive = isProcessAlive, birth = processBirthFingerprint) => {
|
|
88
136
|
const entries = readNamespaceEntries(dir);
|
|
137
|
+
const classified = entries.map(name => ({
|
|
138
|
+
name,
|
|
139
|
+
disposition: classifyNamespaceEntry(dir, name, alive, birth),
|
|
140
|
+
}));
|
|
89
141
|
return {
|
|
90
|
-
orphaned:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
142
|
+
orphaned: classified
|
|
143
|
+
.filter(entry => entry.disposition === "orphaned")
|
|
144
|
+
.map(entry => entry.name),
|
|
145
|
+
unrecognised: classified
|
|
146
|
+
.filter(entry => entry.disposition === "unrecognised")
|
|
147
|
+
.map(entry => entry.name),
|
|
95
148
|
total: entries.length,
|
|
96
149
|
};
|
|
97
150
|
};
|
|
@@ -101,18 +154,20 @@ export const inspectNamespace = (dir = scratchNamespaceDir(), alive = isProcessA
|
|
|
101
154
|
* @returns A comma-separated sample, elided beyond five.
|
|
102
155
|
*/
|
|
103
156
|
const sample = (names) => names.slice(0, 5).join(", ") + (names.length > 5 ? ", …" : "");
|
|
157
|
+
/** Safe operator recovery when automatic deletion authority is unavailable. */
|
|
158
|
+
const MANUAL_RECOVERY_GUIDANCE = "Remove only an exact entry whose dead owner you can independently verify. " +
|
|
159
|
+
"Do not clear the shared namespace or remove a live sibling's root.";
|
|
104
160
|
/**
|
|
105
161
|
* Builds the failure message for a namespace that is accumulating.
|
|
106
162
|
*
|
|
107
163
|
* Every branch names the directory and the offending entries, because the
|
|
108
164
|
* failure this replaces was a 60-second timeout that named nothing.
|
|
109
165
|
*
|
|
110
|
-
* `unrecognised` is deliberately
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* did not, and a namespace growing without bound.
|
|
166
|
+
* `unrecognised` is deliberately a fail-closed refusal. A foreign, malformed,
|
|
167
|
+
* or identity-mismatched entry is preserved because age and naming are not
|
|
168
|
+
* deletion authority; proceeding would silently permit unbounded residue, and
|
|
169
|
+
* deleting it could destroy a live sibling's work. The diagnostic therefore
|
|
170
|
+
* names the bounded entries and gives the only safe manual recovery rule.
|
|
116
171
|
* @param dir - Namespace directory inspected
|
|
117
172
|
* @param residue - What the inspection found
|
|
118
173
|
* @returns The message, or `undefined` when the namespace is healthy.
|
|
@@ -122,7 +177,15 @@ export const describeResidueFailure = (dir, residue) => {
|
|
|
122
177
|
return (`Test scratch namespace ${dir} still holds ${String(residue.orphaned.length)} ` +
|
|
123
178
|
`root(s) whose owning process is gone, after a sweep that should have ` +
|
|
124
179
|
`removed them: ${sample(residue.orphaned)}. Reclaim-on-start is not ` +
|
|
125
|
-
`working, so a killed run's residue is now permanent
|
|
180
|
+
`working, so a killed run's residue is now permanent. ${MANUAL_RECOVERY_GUIDANCE}`);
|
|
181
|
+
}
|
|
182
|
+
if (residue.unrecognised.length > 0) {
|
|
183
|
+
return (`Test scratch namespace ${dir} holds ${String(residue.unrecognised.length)} ` +
|
|
184
|
+
`root(s) without valid owner-marker authority: ${sample(residue.unrecognised)}. ` +
|
|
185
|
+
`Lisa preserved the uncertain residue instead of deleting a path that ` +
|
|
186
|
+
`cannot be bound to a token and process-birth fingerprint. ` +
|
|
187
|
+
`There are ${String(residue.total)} entries in total; the historical ` +
|
|
188
|
+
`accumulating-residue ceiling is ${String(MAX_NAMESPACE_ENTRIES)}. ${MANUAL_RECOVERY_GUIDANCE}`);
|
|
126
189
|
}
|
|
127
190
|
// Entries no live process owns. Written as the general expression rather
|
|
128
191
|
// than as `unrecognised` alone: the orphaned branch above returns first
|
|
@@ -134,18 +197,24 @@ export const describeResidueFailure = (dir, residue) => {
|
|
|
134
197
|
`live process owns, past the ceiling of ${String(MAX_NAMESPACE_ENTRIES)} ` +
|
|
135
198
|
`(${String(residue.total)} entries in total, the rest being work in ` +
|
|
136
199
|
`flight). Scratch space is accumulating rather than being reclaimed — ` +
|
|
137
|
-
`the condition this guard exists to prevent
|
|
200
|
+
`the condition this guard exists to prevent. ${MANUAL_RECOVERY_GUIDANCE}`);
|
|
138
201
|
}
|
|
139
202
|
return undefined;
|
|
140
203
|
};
|
|
141
204
|
/**
|
|
142
205
|
* Sweeps the namespace and reports what survived.
|
|
143
|
-
* @param
|
|
206
|
+
* @param alive - Process liveness probe shared by both phases
|
|
207
|
+
* @param snapshot - One bulk process-birth snapshot provider
|
|
144
208
|
* @returns The residue remaining after the sweep.
|
|
145
209
|
*/
|
|
146
|
-
const sweepThenInspect = (
|
|
147
|
-
|
|
148
|
-
|
|
210
|
+
export const sweepThenInspect = (alive = isProcessAlive, snapshot = processBirthFingerprintSnapshot) => {
|
|
211
|
+
const dir = scratchNamespaceDir();
|
|
212
|
+
const birth = liveOwnerBirthProbe(dir, alive, snapshot);
|
|
213
|
+
sweepScratchNamespace({
|
|
214
|
+
isProcessAlive: alive,
|
|
215
|
+
processBirthFingerprint: birth,
|
|
216
|
+
});
|
|
217
|
+
return inspectNamespace(dir, alive, birth);
|
|
149
218
|
};
|
|
150
219
|
/**
|
|
151
220
|
* Audits the namespace, tolerating a sibling run's worker exiting mid-audit.
|
|
@@ -154,12 +223,11 @@ const sweepThenInspect = (dir) => {
|
|
|
154
223
|
* inspection, through no fault of this run. Sweeping once more before treating
|
|
155
224
|
* an orphan as a defect keeps a guard built to catch a permanent leak from
|
|
156
225
|
* failing on a transient one.
|
|
157
|
-
* @param dir - Namespace directory
|
|
158
226
|
* @returns The residue to judge.
|
|
159
227
|
*/
|
|
160
|
-
const auditNamespace = (
|
|
161
|
-
const first = sweepThenInspect(
|
|
162
|
-
return first.orphaned.length > 0 ? sweepThenInspect(
|
|
228
|
+
const auditNamespace = () => {
|
|
229
|
+
const first = sweepThenInspect();
|
|
230
|
+
return first.orphaned.length > 0 ? sweepThenInspect() : first;
|
|
163
231
|
};
|
|
164
232
|
/**
|
|
165
233
|
* Renders the banner printed at the moment of refusal.
|
|
@@ -321,7 +389,7 @@ export const announceRefusal = (failure, writeNotice = text => {
|
|
|
321
389
|
* A refusal speaks twice — a banner before collection and a summary line at
|
|
322
390
|
* exit — and the two are not redundant. They are the top and the bottom of a
|
|
323
391
|
* transcript nobody reads in full.
|
|
324
|
-
* @throws When residue is present that the sweep cannot or did not reclaim.
|
|
392
|
+
* @throws {Error} When residue is present that the sweep cannot or did not reclaim.
|
|
325
393
|
*/
|
|
326
394
|
export const setup = () => {
|
|
327
395
|
// The namespace directory is not created here. `createRunRoot` makes it
|
|
@@ -329,7 +397,7 @@ export const setup = () => {
|
|
|
329
397
|
// inspection treat an absent namespace as an empty one — so creating it would
|
|
330
398
|
// be a side effect ahead of the audit that buys nothing.
|
|
331
399
|
const dir = scratchNamespaceDir();
|
|
332
|
-
const residue = auditNamespace(
|
|
400
|
+
const residue = auditNamespace();
|
|
333
401
|
const failure = describeResidueFailure(dir, residue);
|
|
334
402
|
if (failure !== undefined) {
|
|
335
403
|
// Both halves of the announcement happen before the throw, because the
|
|
@@ -354,6 +422,6 @@ export const setup = () => {
|
|
|
354
422
|
* approximately true.
|
|
355
423
|
*/
|
|
356
424
|
export const teardown = () => {
|
|
357
|
-
sweepScratchNamespace(
|
|
425
|
+
sweepScratchNamespace();
|
|
358
426
|
};
|
|
359
427
|
//# sourceMappingURL=scratch-global-setup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scratch-global-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"scratch-global-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-leak-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-leak-setup.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-suite backstop that makes a leaked fixture fail the suite that leaked it.
|
|
3
|
+
*
|
|
4
|
+
* The worker-wide run root is still removed wholesale on exit and reclaimed by
|
|
5
|
+
* a successor after SIGKILL. This hook is the attribution layer: it snapshots
|
|
6
|
+
* the run-root children before collection, runs last after every user
|
|
7
|
+
* `afterAll`, removes anything added by the suite, and fails on additions whose
|
|
8
|
+
* prefix was not registered before collection.
|
|
9
|
+
* @module configs/vitest/scratch-leak-setup
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { afterAll } from "vitest";
|
|
15
|
+
import { readBoundedScratchNames, removeAuthorizedScratchChildren, } from "./scratch-authority.js";
|
|
16
|
+
import { SCRATCH_OWNER_FILE, readScratchOwnerRecord } from "./scratch-owner.js";
|
|
17
|
+
/** Run root whose children this suite owns. */
|
|
18
|
+
const runRoot = tmpdir();
|
|
19
|
+
/** Fail immediately if the setup order did not install scratch first. */
|
|
20
|
+
if (!path.basename(runRoot).startsWith("worker-")) {
|
|
21
|
+
throw new Error(`Scratch leak guard started before scratch redirection: ${runRoot}`);
|
|
22
|
+
}
|
|
23
|
+
/** Owner record binds the prefix registry and path identity to this root. */
|
|
24
|
+
const owner = readScratchOwnerRecord(runRoot);
|
|
25
|
+
/** Direct children present before this suite imports any test module. */
|
|
26
|
+
const baseline = new Set([
|
|
27
|
+
...readBoundedScratchNames(runRoot),
|
|
28
|
+
SCRATCH_OWNER_FILE,
|
|
29
|
+
]);
|
|
30
|
+
/**
|
|
31
|
+
* Render the prefix a mkdtemp-shaped basename came from.
|
|
32
|
+
* @param name - Direct scratch child basename
|
|
33
|
+
* @returns Display prefix
|
|
34
|
+
*/
|
|
35
|
+
function displayedPrefix(name) {
|
|
36
|
+
return /^[\s\S]*[A-Za-z0-9]{6}$/u.test(name) ? `${name.slice(0, -6)}*` : name;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Remove every classified addition through one inode-bound cleanup process.
|
|
40
|
+
* @param names - Direct scratch child basenames
|
|
41
|
+
*/
|
|
42
|
+
function removeInternalChildren(names) {
|
|
43
|
+
for (const name of names) {
|
|
44
|
+
if (path.basename(name) !== name || name === "." || name === "..") {
|
|
45
|
+
throw new Error(`Scratch leak guard refused non-basename child: ${name}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const before = fs.lstatSync(runRoot);
|
|
49
|
+
if (before.dev !== owner.root.dev ||
|
|
50
|
+
before.ino !== owner.root.ino ||
|
|
51
|
+
fs.realpathSync(runRoot) !== owner.root.canonicalPath) {
|
|
52
|
+
throw new Error("Scratch run-root identity changed before leak cleanup");
|
|
53
|
+
}
|
|
54
|
+
const marker = readScratchOwnerRecord(runRoot);
|
|
55
|
+
if (marker.token !== owner.token) {
|
|
56
|
+
throw new Error("Scratch owner marker changed before leak cleanup");
|
|
57
|
+
}
|
|
58
|
+
removeAuthorizedScratchChildren({
|
|
59
|
+
parent: owner.root,
|
|
60
|
+
basenames: names,
|
|
61
|
+
});
|
|
62
|
+
if (readScratchOwnerRecord(runRoot).token !== owner.token) {
|
|
63
|
+
throw new Error("Scratch owner marker changed during leak cleanup");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read the run root or throw a suite-attributed diagnostic.
|
|
68
|
+
* @returns Current direct-child basenames
|
|
69
|
+
*/
|
|
70
|
+
function readRunRootNames() {
|
|
71
|
+
try {
|
|
72
|
+
return readBoundedScratchNames(runRoot);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new Error(`Scratch leak guard could not read suite root ${runRoot}: ${String(error)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Whether one addition belongs to a prefix registered before collection.
|
|
80
|
+
* @param name - Added direct-child basename
|
|
81
|
+
* @returns Whether the stack registered ownership
|
|
82
|
+
*/
|
|
83
|
+
function isRegisteredAddition(name) {
|
|
84
|
+
return owner.registeredPrefixes.some(prefix => name.startsWith(prefix));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Audit direct additions after user hooks, clean every one, and fail on any
|
|
88
|
+
* prefix the stack did not register before collection.
|
|
89
|
+
*/
|
|
90
|
+
afterAll(() => {
|
|
91
|
+
const names = readRunRootNames();
|
|
92
|
+
const additions = names.filter(name => !baseline.has(name));
|
|
93
|
+
const unregistered = additions.filter(name => !isRegisteredAddition(name));
|
|
94
|
+
try {
|
|
95
|
+
removeInternalChildren(additions);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new Error(`Scratch leak guard could not clean ${additions.join(", ")} for suite ${owner.suiteLabel}: ${String(error)}`);
|
|
99
|
+
}
|
|
100
|
+
if (unregistered.length > 0) {
|
|
101
|
+
const details = [...unregistered]
|
|
102
|
+
.sort((left, right) => left.localeCompare(right))
|
|
103
|
+
.map(name => `${name} (${displayedPrefix(name)})`)
|
|
104
|
+
.join(", ");
|
|
105
|
+
throw new Error(`Suite ${owner.suiteLabel} leaked ${String(unregistered.length)} unregistered scratch fixture(s): ${details}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=scratch-leak-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-leak-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-leak-setup.ts"],"names":[],"mappings":"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"}
|