@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,209 @@
|
|
|
1
|
+
/** Versioned supervision leases and nested Vitest worker scratch scopes. */
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { removeAuthorizedScratchChild } from "./scratch-authority.js";
|
|
6
|
+
import { createScratchOwnerRecord, readScratchOwnerRecord, scratchPathIdentity, writeScratchOwnerRecord, } from "./scratch-owner.js";
|
|
7
|
+
import { openOwnedScratchRunRoot, } from "./scratch.js";
|
|
8
|
+
/** Private environment variable inherited only by a supervised test payload. */
|
|
9
|
+
export const SCRATCH_SUPERVISION_LEASE_ENV = "LISA_TEST_RUN_LEASE";
|
|
10
|
+
/** Maximum serialized lease accepted from an inherited environment. */
|
|
11
|
+
const MAX_LEASE_BYTES = 16 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* Whether a value contains an ASCII control code.
|
|
14
|
+
* @param value - Candidate string
|
|
15
|
+
* @returns Whether a control code is present
|
|
16
|
+
*/
|
|
17
|
+
function hasControlCode(value) {
|
|
18
|
+
return [...value].some(character => {
|
|
19
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
20
|
+
return codePoint <= 31 || codePoint === 127;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate one inert direct basename.
|
|
25
|
+
* @param value - Candidate basename
|
|
26
|
+
* @returns Whether the value is a direct basename
|
|
27
|
+
*/
|
|
28
|
+
function validBasename(value) {
|
|
29
|
+
return (typeof value === "string" &&
|
|
30
|
+
value !== "" &&
|
|
31
|
+
value !== "." &&
|
|
32
|
+
value !== ".." &&
|
|
33
|
+
path.basename(value) === value &&
|
|
34
|
+
!value.includes("/") &&
|
|
35
|
+
!value.includes("\\") &&
|
|
36
|
+
Buffer.byteLength(value, "utf8") <= 1_024 &&
|
|
37
|
+
!hasControlCode(value));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate one serialized filesystem identity.
|
|
41
|
+
* @param value - Candidate identity
|
|
42
|
+
* @returns Whether all identity fields are bounded and typed
|
|
43
|
+
*/
|
|
44
|
+
function validIdentity(value) {
|
|
45
|
+
if (typeof value !== "object" || value === null)
|
|
46
|
+
return false;
|
|
47
|
+
const candidate = value;
|
|
48
|
+
return (typeof candidate["canonicalPath"] === "string" &&
|
|
49
|
+
path.isAbsolute(candidate["canonicalPath"]) &&
|
|
50
|
+
Number.isSafeInteger(candidate["dev"]) &&
|
|
51
|
+
Number.isSafeInteger(candidate["ino"]));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Construct a bounded lease only after the suite root has been independently opened.
|
|
55
|
+
* @param intent - Armed suite-root intent
|
|
56
|
+
* @param configuration - Suite attribution captured before collection
|
|
57
|
+
* @param configuration.suiteLabel - Opaque suite label
|
|
58
|
+
* @param configuration.registeredPrefixes - Pre-collection prefix registry
|
|
59
|
+
* @returns Immutable lease
|
|
60
|
+
*/
|
|
61
|
+
export function createScratchSupervisionLease(intent, configuration) {
|
|
62
|
+
const opened = openOwnedScratchRunRoot(intent);
|
|
63
|
+
if (opened === undefined)
|
|
64
|
+
throw new Error("Supervised suite root is absent");
|
|
65
|
+
return Object.freeze({
|
|
66
|
+
schema: 1,
|
|
67
|
+
token: intent.token,
|
|
68
|
+
suiteRootBasename: intent.basename,
|
|
69
|
+
baseCanonicalPath: intent.authority.baseCanonicalPath,
|
|
70
|
+
namespace: intent.authority.namespace,
|
|
71
|
+
suiteRoot: opened.owner.root,
|
|
72
|
+
suiteLabel: configuration.suiteLabel,
|
|
73
|
+
registeredPrefixes: [...configuration.registeredPrefixes],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Parse and validate the private bounded lease contract.
|
|
78
|
+
* @param raw - Serialized lease
|
|
79
|
+
* @returns Validated version-one lease
|
|
80
|
+
*/
|
|
81
|
+
export function parseScratchSupervisionLease(raw) {
|
|
82
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_LEASE_BYTES) {
|
|
83
|
+
throw new Error("Scratch supervision lease exceeds its byte bound");
|
|
84
|
+
}
|
|
85
|
+
const value = (() => {
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new Error("Scratch supervision lease must be valid JSON");
|
|
91
|
+
}
|
|
92
|
+
})();
|
|
93
|
+
if (typeof value !== "object" || value === null) {
|
|
94
|
+
throw new Error("Scratch supervision lease must be an object");
|
|
95
|
+
}
|
|
96
|
+
const lease = value;
|
|
97
|
+
const prefixes = lease["registeredPrefixes"];
|
|
98
|
+
if (lease["schema"] !== 1 ||
|
|
99
|
+
typeof lease["token"] !== "string" ||
|
|
100
|
+
!/^[a-f0-9]{32}$/u.test(lease["token"]) ||
|
|
101
|
+
!validBasename(lease["suiteRootBasename"]) ||
|
|
102
|
+
typeof lease["baseCanonicalPath"] !== "string" ||
|
|
103
|
+
!path.isAbsolute(lease["baseCanonicalPath"]) ||
|
|
104
|
+
!validIdentity(lease["namespace"]) ||
|
|
105
|
+
!validIdentity(lease["suiteRoot"]) ||
|
|
106
|
+
typeof lease["suiteLabel"] !== "string" ||
|
|
107
|
+
lease["suiteLabel"] === "" ||
|
|
108
|
+
!Array.isArray(prefixes) ||
|
|
109
|
+
prefixes.length > 64 ||
|
|
110
|
+
!prefixes.every(validBasename)) {
|
|
111
|
+
throw new Error("Invalid scratch supervision lease schema");
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Resolve and validate the suite root from inert lease fields.
|
|
117
|
+
* @param lease - Validated lease
|
|
118
|
+
* @returns Canonical suite-root path and identity
|
|
119
|
+
*/
|
|
120
|
+
function validateSuiteRoot(lease) {
|
|
121
|
+
const namespace = scratchPathIdentity(lease.namespace.canonicalPath);
|
|
122
|
+
if (namespace.dev !== lease.namespace.dev ||
|
|
123
|
+
namespace.ino !== lease.namespace.ino ||
|
|
124
|
+
path.dirname(namespace.canonicalPath) !== lease.baseCanonicalPath) {
|
|
125
|
+
throw new Error("Scratch supervision namespace identity changed");
|
|
126
|
+
}
|
|
127
|
+
const rootPath = path.join(namespace.canonicalPath, lease.suiteRootBasename);
|
|
128
|
+
const identity = scratchPathIdentity(rootPath);
|
|
129
|
+
const owner = readScratchOwnerRecord(rootPath);
|
|
130
|
+
if (identity.dev !== lease.suiteRoot.dev ||
|
|
131
|
+
identity.ino !== lease.suiteRoot.ino ||
|
|
132
|
+
owner.token !== lease.token ||
|
|
133
|
+
owner.root.dev !== identity.dev ||
|
|
134
|
+
owner.root.ino !== identity.ino) {
|
|
135
|
+
throw new Error("Scratch supervision suite token or identity changed");
|
|
136
|
+
}
|
|
137
|
+
return { path: rootPath, identity };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Allocate one random worker scope under a token-validated suite root.
|
|
141
|
+
* @param lease - Validated suite lease
|
|
142
|
+
* @param options - Optional deterministic persistence seam
|
|
143
|
+
* @returns Durable worker-scope handle
|
|
144
|
+
*/
|
|
145
|
+
export function createSupervisedWorkerScope(lease, options = {}) {
|
|
146
|
+
const suite = validateSuiteRoot(lease);
|
|
147
|
+
const basename = `worker-${String(process.pid)}-${randomBytes(8).toString("hex")}`;
|
|
148
|
+
const workerPath = path.join(suite.path, basename);
|
|
149
|
+
fs.mkdirSync(workerPath, { mode: 0o700 });
|
|
150
|
+
try {
|
|
151
|
+
const owner = createScratchOwnerRecord({
|
|
152
|
+
authority: { namespace: suite.identity },
|
|
153
|
+
root: workerPath,
|
|
154
|
+
suiteLabel: lease.suiteLabel,
|
|
155
|
+
registeredPrefixes: lease.registeredPrefixes,
|
|
156
|
+
});
|
|
157
|
+
(options.writeOwnerRecord ?? writeScratchOwnerRecord)(workerPath, owner);
|
|
158
|
+
validateSuiteRoot(lease);
|
|
159
|
+
return {
|
|
160
|
+
path: workerPath,
|
|
161
|
+
basename,
|
|
162
|
+
parent: suite.identity,
|
|
163
|
+
suiteToken: lease.token,
|
|
164
|
+
owner,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
try {
|
|
169
|
+
(options.removeAuthorizedChild ?? removeAuthorizedScratchChild)({
|
|
170
|
+
parent: suite.identity,
|
|
171
|
+
basename,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (cleanupError) {
|
|
175
|
+
throw new AggregateError([error, cleanupError], "Failed to persist and roll back a supervised worker owner marker");
|
|
176
|
+
}
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Remove one worker scope while revalidating its parent lease around deletion.
|
|
182
|
+
* @param scope - Durable worker handle
|
|
183
|
+
* @param lease - Optional parent lease for pre/post validation
|
|
184
|
+
*/
|
|
185
|
+
export function removeSupervisedWorkerScope(scope, lease) {
|
|
186
|
+
if (lease !== undefined)
|
|
187
|
+
validateSuiteRoot(lease);
|
|
188
|
+
try {
|
|
189
|
+
const owner = readScratchOwnerRecord(scope.path);
|
|
190
|
+
if (owner.token !== scope.owner.token ||
|
|
191
|
+
owner.root.dev !== scope.owner.root.dev ||
|
|
192
|
+
owner.root.ino !== scope.owner.root.ino) {
|
|
193
|
+
throw new Error("Supervised worker token or identity changed");
|
|
194
|
+
}
|
|
195
|
+
removeAuthorizedScratchChild({
|
|
196
|
+
parent: scope.parent,
|
|
197
|
+
basename: scope.basename,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
if (error.code !== "ENOENT")
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
if (lease !== undefined)
|
|
205
|
+
validateSuiteRoot(lease);
|
|
206
|
+
}
|
|
207
|
+
export { parseScratchProtocolMessage } from "./scratch-supervision-protocol.js";
|
|
208
|
+
export { validateScratchRunRootIntent } from "./scratch-supervision-intent.js";
|
|
209
|
+
//# sourceMappingURL=scratch-supervision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-supervision.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-supervision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { processBirthFingerprintSnapshot } from "./scratch-owner.js";
|
|
2
|
+
/** Inputs deciding whether one namespace entry may be removed. */
|
|
3
|
+
export interface ReclaimDecisionInput {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly now: number;
|
|
6
|
+
readonly isProcessAlive: (pid: number) => boolean;
|
|
7
|
+
readonly maxAgeMs?: number;
|
|
8
|
+
readonly selfName?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Outcome of one sweep of the namespace. */
|
|
11
|
+
export interface SweepResult {
|
|
12
|
+
readonly removed: readonly string[];
|
|
13
|
+
readonly kept: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
/** Options controlling one sweep. */
|
|
16
|
+
export interface SweepOptions {
|
|
17
|
+
readonly now?: number;
|
|
18
|
+
readonly isProcessAlive?: (pid: number) => boolean;
|
|
19
|
+
readonly maxAgeMs?: number;
|
|
20
|
+
readonly selfName?: string;
|
|
21
|
+
readonly processBirthFingerprint?: (pid: number) => string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Decide whether a recognized entry belongs to a dead process.
|
|
25
|
+
* @param input - Entry and liveness facts
|
|
26
|
+
* @param input.name - Direct namespace basename
|
|
27
|
+
* @param input.isProcessAlive - Recorded-owner liveness probe
|
|
28
|
+
* @param input.selfName - Optional caller-owned basename
|
|
29
|
+
* @returns True only for a recognized dead-process name
|
|
30
|
+
*/
|
|
31
|
+
export declare const isReclaimable: ({ name, isProcessAlive: alive, selfName, }: ReclaimDecisionInput) => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Report whether a pid resolves to a running process.
|
|
34
|
+
* @param pid - Process id to probe
|
|
35
|
+
* @returns True when the kernel still resolves the process
|
|
36
|
+
*/
|
|
37
|
+
export declare const isProcessAlive: (pid: number) => boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Refuse to authorize a foreign entry using age alone.
|
|
40
|
+
* @param _entryPath - Retained compatibility path
|
|
41
|
+
* @param _now - Retained compatibility clock
|
|
42
|
+
* @param _maxAgeMs - Retained compatibility age
|
|
43
|
+
* @returns Always false
|
|
44
|
+
*/
|
|
45
|
+
export declare const isStaleForeignEntry: (_entryPath: string, _now: number, _maxAgeMs: number) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Read bounded direct namespace entries.
|
|
48
|
+
* @param dir - Exact namespace directory
|
|
49
|
+
* @returns Direct basenames, or an empty list when absent
|
|
50
|
+
*/
|
|
51
|
+
export declare const readNamespaceEntries: (dir: string) => readonly string[];
|
|
52
|
+
/**
|
|
53
|
+
* Capture birth fingerprints for the currently live marked owners.
|
|
54
|
+
* @param dir - Exact namespace path
|
|
55
|
+
* @param alive - Process liveness probe
|
|
56
|
+
* @param snapshot - One bounded bulk process-birth snapshot provider
|
|
57
|
+
* @returns Snapshot-backed process-birth probe
|
|
58
|
+
*/
|
|
59
|
+
export declare function liveOwnerBirthProbe(dir: string, alive: (pid: number) => boolean, snapshot?: typeof processBirthFingerprintSnapshot): (pid: number) => string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Sweep abandoned marked roots using process-birth authority.
|
|
62
|
+
* @param options - Sweep inputs
|
|
63
|
+
* @returns Removed and preserved names
|
|
64
|
+
*/
|
|
65
|
+
export declare const sweepScratchNamespace: (options?: SweepOptions) => SweepResult;
|
|
66
|
+
//# sourceMappingURL=scratch-sweep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-sweep.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-sweep.ts"],"names":[],"mappings":"AASA,OAAO,EACL,+BAA+B,EAEhC,MAAM,oBAAoB,CAAC;AAE5B,kEAAkE;AAClE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACxE;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,4CAI3B,oBAAoB,KAAG,OAIzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,OAO5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,YAAY,MAAM,EAClB,MAAM,MAAM,EACZ,WAAW,MAAM,KAChB,OAAgB,CAAC;AAEpB;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,KAAG,SAAS,MAAM,EAChC,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC/B,QAAQ,GAAE,OAAO,+BAAiE,GACjF,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAWrC;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,UAAS,YAAiB,KACzB,WAUF,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Bounded, ownership-aware startup sweep for the scratch namespace. */
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { parseRunRootName, readBoundedScratchNamespace, scratchNamespaceDir, sweepAuthorizedScratchNamespace, } from "./scratch-authority.js";
|
|
4
|
+
import { processBirthFingerprintSnapshot, readScratchOwnerRecord, } from "./scratch-owner.js";
|
|
5
|
+
/**
|
|
6
|
+
* Decide whether a recognized entry belongs to a dead process.
|
|
7
|
+
* @param input - Entry and liveness facts
|
|
8
|
+
* @param input.name - Direct namespace basename
|
|
9
|
+
* @param input.isProcessAlive - Recorded-owner liveness probe
|
|
10
|
+
* @param input.selfName - Optional caller-owned basename
|
|
11
|
+
* @returns True only for a recognized dead-process name
|
|
12
|
+
*/
|
|
13
|
+
export const isReclaimable = ({ name, isProcessAlive: alive, selfName, }) => {
|
|
14
|
+
if (selfName !== undefined && name === selfName)
|
|
15
|
+
return false;
|
|
16
|
+
const owner = parseRunRootName(name);
|
|
17
|
+
return owner !== undefined && !alive(owner.pid);
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Report whether a pid resolves to a running process.
|
|
21
|
+
* @param pid - Process id to probe
|
|
22
|
+
* @returns True when the kernel still resolves the process
|
|
23
|
+
*/
|
|
24
|
+
export const isProcessAlive = (pid) => {
|
|
25
|
+
try {
|
|
26
|
+
process.kill(pid, 0);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return error.code === "EPERM";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Refuse to authorize a foreign entry using age alone.
|
|
35
|
+
* @param _entryPath - Retained compatibility path
|
|
36
|
+
* @param _now - Retained compatibility clock
|
|
37
|
+
* @param _maxAgeMs - Retained compatibility age
|
|
38
|
+
* @returns Always false
|
|
39
|
+
*/
|
|
40
|
+
export const isStaleForeignEntry = (_entryPath, _now, _maxAgeMs) => false;
|
|
41
|
+
/**
|
|
42
|
+
* Read bounded direct namespace entries.
|
|
43
|
+
* @param dir - Exact namespace directory
|
|
44
|
+
* @returns Direct basenames, or an empty list when absent
|
|
45
|
+
*/
|
|
46
|
+
export const readNamespaceEntries = (dir) => readBoundedScratchNamespace(dir);
|
|
47
|
+
/**
|
|
48
|
+
* Capture birth fingerprints for the currently live marked owners.
|
|
49
|
+
* @param dir - Exact namespace path
|
|
50
|
+
* @param alive - Process liveness probe
|
|
51
|
+
* @param snapshot - One bounded bulk process-birth snapshot provider
|
|
52
|
+
* @returns Snapshot-backed process-birth probe
|
|
53
|
+
*/
|
|
54
|
+
export function liveOwnerBirthProbe(dir, alive, snapshot = processBirthFingerprintSnapshot) {
|
|
55
|
+
const livePids = readNamespaceEntries(dir).flatMap(name => {
|
|
56
|
+
try {
|
|
57
|
+
const pid = readScratchOwnerRecord(path.join(dir, name)).pid;
|
|
58
|
+
return alive(pid) ? [pid] : [];
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const births = snapshot(livePids);
|
|
65
|
+
return (pid) => births.get(pid);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Sweep abandoned marked roots using process-birth authority.
|
|
69
|
+
* @param options - Sweep inputs
|
|
70
|
+
* @returns Removed and preserved names
|
|
71
|
+
*/
|
|
72
|
+
export const sweepScratchNamespace = (options = {}) => {
|
|
73
|
+
const dir = scratchNamespaceDir();
|
|
74
|
+
const alive = options.isProcessAlive ?? isProcessAlive;
|
|
75
|
+
const birthProbe = options.processBirthFingerprint ?? liveOwnerBirthProbe(dir, alive);
|
|
76
|
+
return sweepAuthorizedScratchNamespace({
|
|
77
|
+
isProcessAlive: alive,
|
|
78
|
+
processBirthFingerprint: birthProbe,
|
|
79
|
+
...(options.selfName === undefined ? {} : { selfName: options.selfName }),
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=scratch-sweep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-sweep.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-sweep.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,mBAAmB,EACnB,+BAA+B,GAChC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AA0B5B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAC5B,IAAI,EACJ,cAAc,EAAE,KAAK,EACrB,QAAQ,GACa,EAAW,EAAE;IAClC,IAAI,QAAQ,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAW,EAAW,EAAE;IACrD,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,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,UAAkB,EAClB,IAAY,EACZ,SAAiB,EACR,EAAE,CAAC,KAAK,CAAC;AAEpB;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAW,EAAqB,EAAE,CACrE,2BAA2B,CAAC,GAAG,CAAC,CAAC;AAEnC;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAW,EACX,KAA+B,EAC/B,WAAmD,+BAA+B;IAElF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACxD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;YAC7D,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,CAAC,GAAW,EAAsB,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,UAAwB,EAAE,EACb,EAAE;IACf,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc,IAAI,cAAc,CAAC;IACvD,MAAM,UAAU,GACd,OAAO,CAAC,uBAAuB,IAAI,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrE,OAAO,+BAA+B,CAAC;QACrC,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,UAAU;QACnC,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;KAC1E,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -1,192 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Remove a non-authoritative fixture directory on a best-effort basis.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
export declare const SCRATCH_NAMESPACE = "lisa-scratch";
|
|
9
|
-
/** Prefix identifying a per-process run root inside the namespace. */
|
|
10
|
-
export declare const RUN_ROOT_PREFIX = "run-";
|
|
11
|
-
/**
|
|
12
|
-
* Backstop age after which a run root is reclaimed even though its recorded pid
|
|
13
|
-
* still resolves to a live process.
|
|
14
|
-
*
|
|
15
|
-
* Pids are recycled, so liveness alone can pin an abandoned root forever if an
|
|
16
|
-
* unrelated process happens to inherit its number. Six hours is longer than any
|
|
17
|
-
* legitimate suite and short enough that a recycled pid cannot hold a root
|
|
18
|
-
* across a working day.
|
|
19
|
-
*/
|
|
20
|
-
export declare const DEFAULT_RECLAIM_AGE_MS: number;
|
|
21
|
-
/** Environment variable that relocates the namespace away from the platform temp root. */
|
|
22
|
-
export declare const SCRATCH_ROOT_ENV = "LISA_TEST_SCRATCH_ROOT";
|
|
23
|
-
/**
|
|
24
|
-
* Resolves the directory the namespace lives in.
|
|
25
|
-
*
|
|
26
|
-
* Honours `LISA_TEST_SCRATCH_ROOT` first so an operator or CI job can point
|
|
27
|
-
* scratch space at a specific volume, then `os.tmpdir()` — which itself re-reads
|
|
28
|
-
* `TMPDIR` on every call, so the pre-existing `export TMPDIR=...` mitigation
|
|
29
|
-
* composes with this rather than being defeated by it.
|
|
30
|
-
* @returns Absolute path to the directory containing the scratch namespace.
|
|
31
|
-
*/
|
|
32
|
-
export declare const scratchBaseDir: () => string;
|
|
33
|
-
/**
|
|
34
|
-
* Absolute path of the shared namespace directory.
|
|
35
|
-
* @returns Path to `<base>/lisa-scratch`.
|
|
36
|
-
*/
|
|
37
|
-
export declare const scratchNamespaceDir: () => string;
|
|
38
|
-
/**
|
|
39
|
-
* Builds the name of a per-process run root.
|
|
40
|
-
*
|
|
41
|
-
* The pid is encoded in the name because it is the only durable record of who
|
|
42
|
-
* owns the directory: a killed process leaves no other trace, and the sweep has
|
|
43
|
-
* to tell an abandoned root from one a sibling run is using right now.
|
|
44
|
-
* @param pid - Owning process id
|
|
45
|
-
* @param startedAt - Epoch milliseconds the root was created
|
|
46
|
-
* @param suffix - Random suffix disambiguating two roots created in the same millisecond
|
|
47
|
-
* @returns The directory name, without any leading path.
|
|
48
|
-
*/
|
|
49
|
-
export declare const runRootName: (pid: number, startedAt: number, suffix: string) => string;
|
|
50
|
-
/** Ownership recorded in a run root's directory name. */
|
|
51
|
-
export interface RunRootOwner {
|
|
52
|
-
/** Process id that created the root */
|
|
53
|
-
readonly pid: number;
|
|
54
|
-
/** Epoch milliseconds the root was created */
|
|
55
|
-
readonly startedAt: number;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Recovers the pid and creation time encoded in a run root's name.
|
|
59
|
-
* @param name - A directory name from inside the namespace
|
|
60
|
-
* @returns The owner, or `undefined` when the name was not produced by {@link runRootName}.
|
|
61
|
-
*/
|
|
62
|
-
export declare const parseRunRootName: (name: string) => RunRootOwner | undefined;
|
|
63
|
-
/** Inputs deciding whether one namespace entry may be removed. */
|
|
64
|
-
export interface ReclaimDecisionInput {
|
|
65
|
-
/** Directory name inside the namespace */
|
|
66
|
-
readonly name: string;
|
|
67
|
-
/** Current epoch milliseconds */
|
|
68
|
-
readonly now: number;
|
|
69
|
-
/** Reports whether a pid still resolves to a running process */
|
|
70
|
-
readonly isProcessAlive: (pid: number) => boolean;
|
|
71
|
-
/** Age after which a live-pid root is reclaimed anyway */
|
|
72
|
-
readonly maxAgeMs?: number;
|
|
73
|
-
/** Name of the caller's own root, which is never reclaimed */
|
|
74
|
-
readonly selfName?: string;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Decides whether a namespace entry is abandoned residue.
|
|
78
|
-
*
|
|
79
|
-
* A name this module did not produce is not judged here — see
|
|
80
|
-
* {@link isStaleForeignEntry}, which reclaims it on age alone. Deleting another
|
|
81
|
-
* tool's fresh data is not this sweep's business; leaving it forever would let
|
|
82
|
-
* one stray directory wedge every future run.
|
|
83
|
-
* @param input - The entry and the facts needed to judge it
|
|
84
|
-
* @param input.name - Directory name inside the namespace
|
|
85
|
-
* @param input.now - Current epoch milliseconds
|
|
86
|
-
* @param input.isProcessAlive - Liveness probe for the recorded pid
|
|
87
|
-
* @param input.maxAgeMs - Age after which a live-pid root is reclaimed anyway
|
|
88
|
-
* @param input.selfName - Caller's own root name, never reclaimed
|
|
89
|
-
* @returns True when the entry may be removed.
|
|
90
|
-
*/
|
|
91
|
-
export declare const isReclaimable: ({ name, now, isProcessAlive, maxAgeMs, selfName, }: ReclaimDecisionInput) => boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Reports whether a pid resolves to a running process.
|
|
94
|
-
*
|
|
95
|
-
* `kill(pid, 0)` sends no signal; it only asks the kernel whether the target
|
|
96
|
-
* exists. `EPERM` means it exists and belongs to someone else, which still
|
|
97
|
-
* counts as alive.
|
|
98
|
-
* @param pid - Process id to probe
|
|
99
|
-
* @returns True when a process with that id exists.
|
|
100
|
-
*/
|
|
101
|
-
export declare const isProcessAlive: (pid: number) => boolean;
|
|
102
|
-
/**
|
|
103
|
-
* Decides whether an entry this module did not create may be removed.
|
|
104
|
-
*
|
|
105
|
-
* Age is the only signal available: a foreign name carries no owner, so there is
|
|
106
|
-
* nothing to ask the kernel about. Reclaiming on the same backstop the pid arm
|
|
107
|
-
* uses means the namespace self-heals from anything parked in it — an older
|
|
108
|
-
* release's naming scheme, a stray `mktemp` — without this sweep ever deleting
|
|
109
|
-
* something a live tool is still writing to.
|
|
110
|
-
*
|
|
111
|
-
* The alternative was to treat a foreign name as a hard failure. That was tried
|
|
112
|
-
* and rejected: it wedges permanently on one stray directory, and the message a
|
|
113
|
-
* downstream user gets — "run roots must be named by runRootName()" — names
|
|
114
|
-
* something they cannot act on.
|
|
115
|
-
* @param entryPath - Absolute path of the entry
|
|
116
|
-
* @param now - Current epoch milliseconds
|
|
117
|
-
* @param maxAgeMs - Age past which a foreign entry is reclaimed
|
|
118
|
-
* @returns True when the entry is old enough to remove.
|
|
119
|
-
*/
|
|
120
|
-
export declare const isStaleForeignEntry: (entryPath: string, now: number, maxAgeMs: number) => boolean;
|
|
121
|
-
/**
|
|
122
|
-
* Lists the namespace's direct children, treating an absent namespace as empty.
|
|
123
|
-
*
|
|
124
|
-
* A directory that cannot be read is not a directory that is empty — but for the
|
|
125
|
-
* one case that occurs here, the namespace not existing yet, the two are the
|
|
126
|
-
* same answer and every caller wants to continue rather than fail.
|
|
127
|
-
* @param dir - Namespace directory to list
|
|
128
|
-
* @returns The entry names, or an empty list.
|
|
129
|
-
*/
|
|
130
|
-
export declare const readNamespaceEntries: (dir: string) => readonly string[];
|
|
131
|
-
/** Outcome of one sweep of the namespace. */
|
|
132
|
-
export interface SweepResult {
|
|
133
|
-
/** Names removed as abandoned residue */
|
|
134
|
-
readonly removed: readonly string[];
|
|
135
|
-
/** Names left in place because a live owner still holds them */
|
|
136
|
-
readonly kept: readonly string[];
|
|
137
|
-
}
|
|
138
|
-
/** Options controlling one sweep. */
|
|
139
|
-
export interface SweepOptions {
|
|
140
|
-
/** Namespace directory to sweep (defaults to the resolved namespace) */
|
|
141
|
-
readonly dir?: string;
|
|
142
|
-
/** Current epoch milliseconds */
|
|
143
|
-
readonly now?: number;
|
|
144
|
-
/** Liveness probe, overridable for tests */
|
|
145
|
-
readonly isProcessAlive?: (pid: number) => boolean;
|
|
146
|
-
/** Age after which a live-pid root is reclaimed anyway */
|
|
147
|
-
readonly maxAgeMs?: number;
|
|
148
|
-
/** Caller's own root name, never reclaimed */
|
|
149
|
-
readonly selfName?: string;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Removes abandoned run roots from the namespace.
|
|
153
|
-
*
|
|
154
|
-
* This is the arm of the design that survives a kill. It runs at the START of a
|
|
155
|
-
* run precisely because the run that leaked cannot be the one to clean up.
|
|
156
|
-
* @param options - Sweep inputs
|
|
157
|
-
* @param options.dir - Namespace directory to sweep
|
|
158
|
-
* @param options.now - Current epoch milliseconds
|
|
159
|
-
* @param options.isProcessAlive - Liveness probe for recorded pids
|
|
160
|
-
* @param options.maxAgeMs - Age after which a live-pid root is reclaimed anyway
|
|
161
|
-
* @param options.selfName - Caller's own root name, never reclaimed
|
|
162
|
-
* @returns Which entries were removed and which were kept.
|
|
163
|
-
*/
|
|
164
|
-
export declare const sweepScratchNamespace: ({ dir, now, isProcessAlive: alive, maxAgeMs, selfName, }?: SweepOptions) => SweepResult;
|
|
165
|
-
/**
|
|
166
|
-
* Creates a run root owned by the current process and returns its path.
|
|
167
|
-
* @param options - Overrides for the namespace directory and clock
|
|
168
|
-
* @param options.dir - Namespace directory to create the root inside
|
|
169
|
-
* @param options.now - Epoch milliseconds recorded in the root's name
|
|
170
|
-
* @returns Absolute path of this process's run root.
|
|
171
|
-
*/
|
|
172
|
-
export declare const createRunRoot: ({ dir, now, }?: {
|
|
173
|
-
readonly dir?: string;
|
|
174
|
-
readonly now?: number;
|
|
175
|
-
}) => string;
|
|
176
|
-
/**
|
|
177
|
-
* Reclaims abandoned roots, then allocates one for this process.
|
|
178
|
-
*
|
|
179
|
-
* The order is the point, and it is why this is one function rather than two
|
|
180
|
-
* calls at the call site: reclaiming AFTER allocating leaves a window in which
|
|
181
|
-
* the namespace holds both, and reclaiming somewhere else entirely is how a
|
|
182
|
-
* caller ends up allocating without ever reclaiming at all.
|
|
183
|
-
* @param dir - Namespace directory
|
|
184
|
-
* @returns Absolute path of the newly created run root.
|
|
185
|
-
*/
|
|
186
|
-
export declare const reclaimAndCreateRunRoot: (dir: string) => string;
|
|
187
|
-
/**
|
|
188
|
-
* Removes a directory tree, ignoring an already-absent path.
|
|
189
|
-
* @param dir - Directory to remove
|
|
4
|
+
* Owned run-root teardown must use `removeOwnedScratchRunRoot`; this helper is
|
|
5
|
+
* retained only for nested fixture compatibility, where the next bounded sweep
|
|
6
|
+
* can recover residue.
|
|
7
|
+
* @param dir - Nested fixture directory to remove
|
|
190
8
|
*/
|
|
191
9
|
export declare const removeScratchDir: (dir: string) => void;
|
|
10
|
+
export { DEFAULT_RECLAIM_AGE_MS, RUN_ROOT_PREFIX, SCRATCH_NAMESPACE, parseRunRootName, runRootName, scratchBaseDir, scratchNamespaceDir, } from "./scratch-authority.js";
|
|
11
|
+
export { SCRATCH_PREFIXES_ENV, SCRATCH_SUITE_ENV, } from "./scratch-route-profile.js";
|
|
12
|
+
export { registeredScratchPrefixes, scratchSuiteLabel, } from "./scratch-route-profile.js";
|
|
13
|
+
export { isProcessAlive, isReclaimable, isStaleForeignEntry, liveOwnerBirthProbe, readNamespaceEntries, sweepScratchNamespace, } from "./scratch-sweep.js";
|
|
14
|
+
export type { ReclaimDecisionInput, SweepOptions, SweepResult, } from "./scratch-sweep.js";
|
|
15
|
+
export { materializeOwnedScratchRunRoot, openOwnedScratchRunRoot, prepareOwnedScratchRunRoot, } from "./scratch-run-root-intent.js";
|
|
16
|
+
export type { OwnedScratchRunRoot, ScratchRunRootIntentV1, } from "./scratch-run-root-intent.js";
|
|
17
|
+
export { adoptOwnedScratchRunRoot, createRunRoot, reclaimAndCreateOwnedRunRoot, reclaimAndCreateRunRoot, } from "./scratch-run-root-compatibility.js";
|
|
18
|
+
export { removeOwnedScratchRunRoot } from "./scratch-run-root-intent.js";
|
|
192
19
|
//# sourceMappingURL=scratch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scratch.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scratch.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch.ts"],"names":[],"mappings":"AAWA;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,IAM9C,CAAC;AAEF,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACV,oBAAoB,EACpB,YAAY,EACZ,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC"}
|