@codyswann/lisa 4.28.4 → 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/README.md +3 -1
- package/all/copy-overwrite/scripts/lisa-gates.mjs +2 -1
- package/all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh +3 -2
- package/all/copy-overwrite/scripts/lisa-postinstall.mjs +6 -2
- package/cdk/create-only/vitest.config.local.ts +5 -1
- package/cdk/package-lisa/package.lisa.json +14 -7
- package/dist/cli/apply.d.ts +20 -0
- package/dist/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +70 -3
- package/dist/cli/apply.js.map +1 -1
- package/dist/cli/doctor-apply-freshness.d.ts.map +1 -1
- package/dist/cli/doctor-apply-freshness.js +10 -4
- package/dist/cli/doctor-apply-freshness.js.map +1 -1
- 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/cli/shared-options.d.ts +11 -4
- package/dist/cli/shared-options.d.ts.map +1 -1
- package/dist/cli/shared-options.js +8 -8
- package/dist/cli/shared-options.js.map +1 -1
- 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/apply-mode.d.ts +80 -29
- package/dist/core/apply-mode.d.ts.map +1 -1
- package/dist/core/apply-mode.js +23 -2
- package/dist/core/apply-mode.js.map +1 -1
- package/dist/core/apply-receipt.d.ts +5 -1
- package/dist/core/apply-receipt.d.ts.map +1 -1
- package/dist/core/apply-receipt.js.map +1 -1
- package/dist/core/config.d.ts +24 -12
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +8 -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 +63 -15
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.d.ts +1 -1
- package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.js +28 -5
- package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
- package/dist/utils/postinstall-trampoline-source.js +10 -1
- package/dist/utils/postinstall-trampoline-source.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/scripts/install-claude-plugins.sh +7 -2
- package/typescript/create-only/vitest.config.local.ts +5 -1
- package/typescript/package-lisa/package.lisa.json +14 -8
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/** Immutable wrapper-authoritative scratch registries for every managed route. */
|
|
2
|
+
import { env } from "node:process";
|
|
3
|
+
/** JSON-array environment contract for operator-added direct-child prefixes. */
|
|
4
|
+
export const SCRATCH_PREFIXES_ENV = "LISA_TEST_SCRATCH_PREFIXES";
|
|
5
|
+
/** Environment assertion for the immutable route suite label. */
|
|
6
|
+
export const SCRATCH_SUITE_ENV = "LISA_TEST_SCRATCH_SUITE";
|
|
7
|
+
/** Maximum number of registered prefixes accepted from configuration. */
|
|
8
|
+
const MAX_REGISTERED_PREFIXES = 64;
|
|
9
|
+
/** Maximum bytes in one registered prefix or suite label. */
|
|
10
|
+
const MAX_LABEL_BYTES = 128;
|
|
11
|
+
/** Wrapper-owned base registry; operator additions may only widen prefixes. */
|
|
12
|
+
const ROUTE_PROFILES = Object.freeze({
|
|
13
|
+
lisa: Object.freeze({
|
|
14
|
+
suiteLabel: "lisa",
|
|
15
|
+
registeredPrefixes: Object.freeze([
|
|
16
|
+
"changelog-",
|
|
17
|
+
"derived-",
|
|
18
|
+
"e2e-",
|
|
19
|
+
"failure-signatures-",
|
|
20
|
+
"invoked-",
|
|
21
|
+
"lisa-",
|
|
22
|
+
"maestro-",
|
|
23
|
+
"node-",
|
|
24
|
+
"review-",
|
|
25
|
+
"skipreq-",
|
|
26
|
+
"state-",
|
|
27
|
+
"vacuity-",
|
|
28
|
+
"wiki-",
|
|
29
|
+
]),
|
|
30
|
+
}),
|
|
31
|
+
typescript: Object.freeze({
|
|
32
|
+
suiteLabel: "typescript",
|
|
33
|
+
registeredPrefixes: [],
|
|
34
|
+
}),
|
|
35
|
+
"npm-package": Object.freeze({
|
|
36
|
+
suiteLabel: "npm-package",
|
|
37
|
+
registeredPrefixes: [],
|
|
38
|
+
}),
|
|
39
|
+
node: Object.freeze({
|
|
40
|
+
suiteLabel: "node",
|
|
41
|
+
registeredPrefixes: Object.freeze(["node-"]),
|
|
42
|
+
}),
|
|
43
|
+
expo: Object.freeze({
|
|
44
|
+
suiteLabel: "expo",
|
|
45
|
+
registeredPrefixes: [],
|
|
46
|
+
}),
|
|
47
|
+
nestjs: Object.freeze({ suiteLabel: "nestjs", registeredPrefixes: [] }),
|
|
48
|
+
cdk: Object.freeze({
|
|
49
|
+
suiteLabel: "cdk",
|
|
50
|
+
registeredPrefixes: Object.freeze(["cdk.out"]),
|
|
51
|
+
}),
|
|
52
|
+
"harper-fabric": Object.freeze({
|
|
53
|
+
suiteLabel: "harper-fabric",
|
|
54
|
+
registeredPrefixes: [],
|
|
55
|
+
}),
|
|
56
|
+
phaser: Object.freeze({
|
|
57
|
+
suiteLabel: "phaser",
|
|
58
|
+
registeredPrefixes: [],
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* Whether a label contains a control code unsafe for markers/diagnostics.
|
|
63
|
+
* @param label - Candidate label
|
|
64
|
+
* @returns True when a control code is present
|
|
65
|
+
*/
|
|
66
|
+
function containsControlCode(label) {
|
|
67
|
+
return [...label].some(character => {
|
|
68
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
69
|
+
return codePoint <= 0x1f || codePoint === 0x7f;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parse one inherited registry while preserving its public diagnostic.
|
|
74
|
+
* @param raw - Serialized prefix registry
|
|
75
|
+
* @returns Decoded unvalidated value
|
|
76
|
+
*/
|
|
77
|
+
function parsePrefixRegistry(raw) {
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(raw);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
throw new Error(`${SCRATCH_PREFIXES_ENV} must be a JSON array of prefixes`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read the validated bounded prefix registry inherited by scratch owners.
|
|
87
|
+
* @returns Sorted unique direct-child prefixes
|
|
88
|
+
*/
|
|
89
|
+
export function registeredScratchPrefixes() {
|
|
90
|
+
const raw = env[SCRATCH_PREFIXES_ENV];
|
|
91
|
+
if (raw === undefined || raw === "")
|
|
92
|
+
return [];
|
|
93
|
+
const parsed = parsePrefixRegistry(raw);
|
|
94
|
+
if (!Array.isArray(parsed) || parsed.length > MAX_REGISTERED_PREFIXES) {
|
|
95
|
+
throw new Error(`${SCRATCH_PREFIXES_ENV} must contain at most ${String(MAX_REGISTERED_PREFIXES)} prefixes`);
|
|
96
|
+
}
|
|
97
|
+
const prefixes = parsed.map(value => {
|
|
98
|
+
if (typeof value !== "string" ||
|
|
99
|
+
value === "" ||
|
|
100
|
+
Buffer.byteLength(value, "utf8") > MAX_LABEL_BYTES ||
|
|
101
|
+
value.includes("/") ||
|
|
102
|
+
value.includes("\\") ||
|
|
103
|
+
value === "." ||
|
|
104
|
+
value === "..") {
|
|
105
|
+
throw new Error(`${SCRATCH_PREFIXES_ENV} contains an invalid prefix`);
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
});
|
|
109
|
+
return [...new Set(prefixes)].sort((left, right) => left.localeCompare(right));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Read the opaque bounded suite label used for owner attribution.
|
|
113
|
+
* @returns Valid suite label
|
|
114
|
+
*/
|
|
115
|
+
export function scratchSuiteLabel() {
|
|
116
|
+
const label = env[SCRATCH_SUITE_ENV] ?? "vitest";
|
|
117
|
+
if (label === "" ||
|
|
118
|
+
Buffer.byteLength(label, "utf8") > MAX_LABEL_BYTES ||
|
|
119
|
+
containsControlCode(label)) {
|
|
120
|
+
throw new Error(`${SCRATCH_SUITE_ENV} contains an invalid suite label`);
|
|
121
|
+
}
|
|
122
|
+
return label;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Demand one bounded direct basename.
|
|
126
|
+
* @param value - Candidate value
|
|
127
|
+
* @param context - Public diagnostic context
|
|
128
|
+
* @returns Validated direct label
|
|
129
|
+
*/
|
|
130
|
+
function validateLabel(value, context) {
|
|
131
|
+
if (typeof value !== "string" ||
|
|
132
|
+
value === "" ||
|
|
133
|
+
Buffer.byteLength(value, "utf8") > MAX_LABEL_BYTES ||
|
|
134
|
+
containsControlCode(value) ||
|
|
135
|
+
value.includes("/") ||
|
|
136
|
+
value.includes("\\") ||
|
|
137
|
+
value === "." ||
|
|
138
|
+
value === "..") {
|
|
139
|
+
throw new Error(`${context} contains an invalid direct label`);
|
|
140
|
+
}
|
|
141
|
+
return value;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Decode one operator prefix registry.
|
|
145
|
+
* @param raw - Serialized registry
|
|
146
|
+
* @returns Unvalidated decoded value
|
|
147
|
+
*/
|
|
148
|
+
function parseOperatorPrefixes(raw) {
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(raw);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
throw new Error(`${SCRATCH_PREFIXES_ENV} must be a JSON array of prefixes`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Parse and canonicalize operator prefix additions exactly once.
|
|
158
|
+
* @param environment - Inherited operator configuration
|
|
159
|
+
* @returns Validated operator-added prefixes
|
|
160
|
+
*/
|
|
161
|
+
function operatorPrefixes(environment) {
|
|
162
|
+
const raw = environment[SCRATCH_PREFIXES_ENV];
|
|
163
|
+
if (raw === undefined || raw === "")
|
|
164
|
+
return [];
|
|
165
|
+
const parsed = parseOperatorPrefixes(raw);
|
|
166
|
+
if (!Array.isArray(parsed) || parsed.length > MAX_REGISTERED_PREFIXES) {
|
|
167
|
+
throw new Error(`${SCRATCH_PREFIXES_ENV} must contain at most ${String(MAX_REGISTERED_PREFIXES)} prefixes`);
|
|
168
|
+
}
|
|
169
|
+
return parsed.map(value => validateLabel(value, SCRATCH_PREFIXES_ENV));
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Resolve a generated route plus bounded operator additions before allocation.
|
|
173
|
+
* @param name - Exact managed route name
|
|
174
|
+
* @param environment - Inherited operator configuration
|
|
175
|
+
* @returns Deeply frozen immutable registry
|
|
176
|
+
*/
|
|
177
|
+
export function resolveScratchRouteProfile(name, environment = env) {
|
|
178
|
+
if (!Object.hasOwn(ROUTE_PROFILES, name)) {
|
|
179
|
+
throw new Error(`Unknown lisa-test-run scratch profile: ${name || "<missing>"}`);
|
|
180
|
+
}
|
|
181
|
+
const routeName = name;
|
|
182
|
+
const base = ROUTE_PROFILES[routeName];
|
|
183
|
+
const assertedSuite = environment[SCRATCH_SUITE_ENV];
|
|
184
|
+
if (assertedSuite !== undefined &&
|
|
185
|
+
validateLabel(assertedSuite, SCRATCH_SUITE_ENV) !== base.suiteLabel) {
|
|
186
|
+
throw new Error(`${SCRATCH_SUITE_ENV} conflicts with wrapper profile ${routeName}`);
|
|
187
|
+
}
|
|
188
|
+
const prefixes = [
|
|
189
|
+
...new Set([...base.registeredPrefixes, ...operatorPrefixes(environment)]),
|
|
190
|
+
].sort((left, right) => (left === right ? 0 : left < right ? -1 : 1));
|
|
191
|
+
if (prefixes.length > MAX_REGISTERED_PREFIXES) {
|
|
192
|
+
throw new Error(`${SCRATCH_PREFIXES_ENV} exceeds the ${String(MAX_REGISTERED_PREFIXES)} prefix limit after composition`);
|
|
193
|
+
}
|
|
194
|
+
return Object.freeze({
|
|
195
|
+
name: routeName,
|
|
196
|
+
suiteLabel: base.suiteLabel,
|
|
197
|
+
registeredPrefixes: Object.freeze(prefixes),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Assert dynamic Vitest env agrees with the frozen wrapper lease exactly.
|
|
202
|
+
* @param profile - Frozen wrapper-authoritative profile
|
|
203
|
+
* @param environment - Dynamic Vitest environment to compare
|
|
204
|
+
*/
|
|
205
|
+
export function assertScratchRouteProfile(profile, environment = env) {
|
|
206
|
+
const observedSuite = validateLabel(environment[SCRATCH_SUITE_ENV], SCRATCH_SUITE_ENV);
|
|
207
|
+
const observedPrefixes = operatorPrefixes(environment);
|
|
208
|
+
const canonical = [...new Set(observedPrefixes)].sort((left, right) => left === right ? 0 : left < right ? -1 : 1);
|
|
209
|
+
if (observedSuite !== profile.suiteLabel ||
|
|
210
|
+
canonical.length !== profile.registeredPrefixes.length ||
|
|
211
|
+
canonical.some((prefix, index) => prefix !== profile.registeredPrefixes[index])) {
|
|
212
|
+
throw new Error(`Vitest scratch configuration conflicts with the immutable wrapper profile: ` +
|
|
213
|
+
`expected ${profile.suiteLabel}/${JSON.stringify(profile.registeredPrefixes)}, ` +
|
|
214
|
+
`received ${observedSuite}/${JSON.stringify(canonical)}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=scratch-route-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-route-profile.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-route-profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { writeScratchOwnerRecord } from "./scratch-owner.js";
|
|
2
|
+
import type { OwnedScratchRunRoot } from "./scratch-run-root-intent.js";
|
|
3
|
+
/** Optional seams for creating one transactional run root. */
|
|
4
|
+
interface CreateRunRootOptions {
|
|
5
|
+
readonly now?: number;
|
|
6
|
+
readonly writeOwnerRecord?: typeof writeScratchOwnerRecord;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create a legacy-compatible owned root with transactional marker rollback.
|
|
10
|
+
* @param options - Clock and marker-writer seams
|
|
11
|
+
* @param options.now - Epoch milliseconds recorded in the basename
|
|
12
|
+
* @param options.writeOwnerRecord - Injectable transactional marker writer
|
|
13
|
+
* @returns Absolute run-root path
|
|
14
|
+
*/
|
|
15
|
+
export declare const createRunRoot: ({ now, writeOwnerRecord, }?: CreateRunRootOptions) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Reclaim residue, then allocate a durable owned-root handle.
|
|
18
|
+
* @returns Owned current-process root
|
|
19
|
+
*/
|
|
20
|
+
export declare function reclaimAndCreateOwnedRunRoot(): OwnedScratchRunRoot;
|
|
21
|
+
/**
|
|
22
|
+
* Upgrade a source/dist-compatible path memo created by an older setup copy.
|
|
23
|
+
* @param root - Existing memoized run-root path
|
|
24
|
+
* @returns Durable handle for the existing root
|
|
25
|
+
*/
|
|
26
|
+
export declare function adoptOwnedScratchRunRoot(root: string): OwnedScratchRunRoot;
|
|
27
|
+
/**
|
|
28
|
+
* Reclaim residue, then return one current-process root path.
|
|
29
|
+
* @returns Newly allocated root path
|
|
30
|
+
*/
|
|
31
|
+
export declare const reclaimAndCreateRunRoot: () => string;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=scratch-run-root-compatibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-run-root-compatibility.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-run-root-compatibility.ts"],"names":[],"mappings":"AAgBA,OAAO,EAIL,uBAAuB,EAExB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAQxE,8DAA8D;AAC9D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,uBAAuB,CAAC;CAC5D;AAyBD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,6BAG3B,oBAAyB,KAAG,MAsC9B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,mBAAmB,CAYlE;AA8BD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAoB1E;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAAO,MACN,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** Compatibility allocation/adoption atop the owned run-root lifecycle. */
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { createScratchNamespaceAuthority, parseRunRootName, removeAuthorizedScratchRoot, runRootName, } from "./scratch-authority.js";
|
|
6
|
+
import { registeredScratchPrefixes, scratchSuiteLabel, } from "./scratch-route-profile.js";
|
|
7
|
+
import { createScratchOwnerRecord, readScratchOwnerRecord, scratchPathIdentity, writeScratchOwnerRecord, } from "./scratch-owner.js";
|
|
8
|
+
import { sweepScratchNamespace } from "./scratch-sweep.js";
|
|
9
|
+
/**
|
|
10
|
+
* Create the immutable owner marker for a newly materialized root.
|
|
11
|
+
* @param authority - Pinned namespace authority
|
|
12
|
+
* @param root - Newly materialized direct root
|
|
13
|
+
* @param now - Creation epoch milliseconds
|
|
14
|
+
* @param configuration - Validated owner attribution
|
|
15
|
+
* @returns Immutable owner marker
|
|
16
|
+
*/
|
|
17
|
+
function createOwnerForNewRoot(authority, root, now, configuration) {
|
|
18
|
+
return createScratchOwnerRecord({
|
|
19
|
+
authority,
|
|
20
|
+
root,
|
|
21
|
+
suiteLabel: configuration.suiteLabel,
|
|
22
|
+
registeredPrefixes: configuration.registeredPrefixes,
|
|
23
|
+
now: new Date(now),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a legacy-compatible owned root with transactional marker rollback.
|
|
28
|
+
* @param options - Clock and marker-writer seams
|
|
29
|
+
* @param options.now - Epoch milliseconds recorded in the basename
|
|
30
|
+
* @param options.writeOwnerRecord - Injectable transactional marker writer
|
|
31
|
+
* @returns Absolute run-root path
|
|
32
|
+
*/
|
|
33
|
+
export const createRunRoot = ({ now = Date.now(), writeOwnerRecord = writeScratchOwnerRecord, } = {}) => {
|
|
34
|
+
if (!Number.isSafeInteger(now) || now < 0) {
|
|
35
|
+
throw new Error("Scratch run-root timestamp must be a non-negative integer");
|
|
36
|
+
}
|
|
37
|
+
const configuration = {
|
|
38
|
+
suiteLabel: scratchSuiteLabel(),
|
|
39
|
+
registeredPrefixes: registeredScratchPrefixes(),
|
|
40
|
+
};
|
|
41
|
+
const authority = createScratchNamespaceAuthority();
|
|
42
|
+
const root = path.join(authority.namespace.canonicalPath, runRootName(process.pid, now, randomBytes(4).toString("hex")));
|
|
43
|
+
const expectedIdentity = (() => {
|
|
44
|
+
fs.mkdirSync(root, { mode: 0o700 });
|
|
45
|
+
return scratchPathIdentity(root);
|
|
46
|
+
})();
|
|
47
|
+
try {
|
|
48
|
+
const owner = createOwnerForNewRoot(authority, root, now, configuration);
|
|
49
|
+
writeOwnerRecord(root, owner);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
try {
|
|
53
|
+
removeAuthorizedScratchRoot({
|
|
54
|
+
authority,
|
|
55
|
+
basename: path.basename(root),
|
|
56
|
+
expectedIdentity,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (cleanupError) {
|
|
60
|
+
throw new AggregateError([error, cleanupError], "Scratch root allocation failed and rollback could not reclaim it");
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
return root;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Reclaim residue, then allocate a durable owned-root handle.
|
|
68
|
+
* @returns Owned current-process root
|
|
69
|
+
*/
|
|
70
|
+
export function reclaimAndCreateOwnedRunRoot() {
|
|
71
|
+
const authority = createScratchNamespaceAuthority();
|
|
72
|
+
const root = (() => {
|
|
73
|
+
sweepScratchNamespace();
|
|
74
|
+
return createRunRoot();
|
|
75
|
+
})();
|
|
76
|
+
return {
|
|
77
|
+
path: root,
|
|
78
|
+
basename: path.basename(root),
|
|
79
|
+
authority,
|
|
80
|
+
owner: readScratchOwnerRecord(root),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read an existing marker or atomically upgrade a legacy current root.
|
|
85
|
+
* @param authority - Pinned namespace authority
|
|
86
|
+
* @param root - Existing current-process root
|
|
87
|
+
* @param startedAt - Timestamp encoded in the recognized basename
|
|
88
|
+
* @returns Existing or newly written marker
|
|
89
|
+
*/
|
|
90
|
+
function readOrCreateAdoptedOwner(authority, root, startedAt) {
|
|
91
|
+
try {
|
|
92
|
+
return readScratchOwnerRecord(root);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (error.code !== "ENOENT")
|
|
96
|
+
throw error;
|
|
97
|
+
const owner = createScratchOwnerRecord({
|
|
98
|
+
authority,
|
|
99
|
+
root,
|
|
100
|
+
suiteLabel: scratchSuiteLabel(),
|
|
101
|
+
registeredPrefixes: registeredScratchPrefixes(),
|
|
102
|
+
now: new Date(startedAt),
|
|
103
|
+
});
|
|
104
|
+
writeScratchOwnerRecord(root, owner);
|
|
105
|
+
return owner;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Upgrade a source/dist-compatible path memo created by an older setup copy.
|
|
110
|
+
* @param root - Existing memoized run-root path
|
|
111
|
+
* @returns Durable handle for the existing root
|
|
112
|
+
*/
|
|
113
|
+
export function adoptOwnedScratchRunRoot(root) {
|
|
114
|
+
const authority = createScratchNamespaceAuthority();
|
|
115
|
+
const canonicalRoot = fs.realpathSync(root);
|
|
116
|
+
if (path.dirname(canonicalRoot) !== authority.namespace.canonicalPath ||
|
|
117
|
+
path.basename(root) !== path.basename(canonicalRoot)) {
|
|
118
|
+
throw new Error("Memoized scratch root is outside canonical authority");
|
|
119
|
+
}
|
|
120
|
+
const parsed = parseRunRootName(path.basename(root));
|
|
121
|
+
if (parsed?.pid !== process.pid) {
|
|
122
|
+
throw new Error("Memoized scratch root is not owned by this process");
|
|
123
|
+
}
|
|
124
|
+
const owner = readOrCreateAdoptedOwner(authority, root, parsed.startedAt);
|
|
125
|
+
return {
|
|
126
|
+
path: root,
|
|
127
|
+
basename: path.basename(root),
|
|
128
|
+
authority,
|
|
129
|
+
owner,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Reclaim residue, then return one current-process root path.
|
|
134
|
+
* @returns Newly allocated root path
|
|
135
|
+
*/
|
|
136
|
+
export const reclaimAndCreateRunRoot = () => reclaimAndCreateOwnedRunRoot().path;
|
|
137
|
+
//# sourceMappingURL=scratch-run-root-compatibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-run-root-compatibility.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-run-root-compatibility.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type ScratchNamespaceAuthority } from "./scratch-authority.js";
|
|
2
|
+
import { writeScratchOwnerRecord, type ScratchOwnerRecordV1, type ScratchPathIdentity } from "./scratch-owner.js";
|
|
3
|
+
/** Durable handle for one run root owned by this process. */
|
|
4
|
+
export interface OwnedScratchRunRoot {
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly basename: string;
|
|
7
|
+
readonly authority: ScratchNamespaceAuthority;
|
|
8
|
+
readonly owner: ScratchOwnerRecordV1;
|
|
9
|
+
}
|
|
10
|
+
/** Precommitted immutable intent for one exact direct run root. */
|
|
11
|
+
export interface ScratchRunRootIntentV1 {
|
|
12
|
+
readonly schema: 1;
|
|
13
|
+
readonly rootPath: string;
|
|
14
|
+
readonly basename: string;
|
|
15
|
+
readonly authority: ScratchNamespaceAuthority;
|
|
16
|
+
readonly pid: number;
|
|
17
|
+
readonly processBirthFingerprint: string;
|
|
18
|
+
readonly createdAt: string;
|
|
19
|
+
readonly token: string;
|
|
20
|
+
readonly suiteLabel: string;
|
|
21
|
+
readonly registeredPrefixes: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
/** Deterministic seams for precommitting process-birth authority. */
|
|
24
|
+
interface PrepareRunRootOptions {
|
|
25
|
+
readonly platform?: NodeJS.Platform;
|
|
26
|
+
readonly processBirthFingerprint?: (pid: number) => string | undefined;
|
|
27
|
+
readonly suiteLabel?: string;
|
|
28
|
+
readonly registeredPrefixes?: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
/** Deterministic seams for transactional root materialization. */
|
|
31
|
+
interface MaterializeRunRootOptions {
|
|
32
|
+
readonly writeOwnerRecord?: typeof writeScratchOwnerRecord;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Prepare an exact root identity without creating that root.
|
|
36
|
+
* @param options - Deterministic authority seams
|
|
37
|
+
* @returns Immutable precommitted root intent
|
|
38
|
+
*/
|
|
39
|
+
export declare function prepareOwnedScratchRunRoot(options?: PrepareRunRootOptions): ScratchRunRootIntentV1;
|
|
40
|
+
/**
|
|
41
|
+
* Materialize one armed root and persist its immutable marker.
|
|
42
|
+
* @param intent - Precommitted root intent
|
|
43
|
+
* @param options - Transactional marker-writer seams
|
|
44
|
+
* @returns Durable owned-root handle
|
|
45
|
+
*/
|
|
46
|
+
export declare function materializeOwnedScratchRunRoot(intent: ScratchRunRootIntentV1, options?: MaterializeRunRootOptions): OwnedScratchRunRoot;
|
|
47
|
+
/**
|
|
48
|
+
* Validate an opened marker against the complete armed intent.
|
|
49
|
+
* @param intent - Precommitted root facts
|
|
50
|
+
* @param owner - Persisted owner marker
|
|
51
|
+
* @param root - Currently opened root identity
|
|
52
|
+
*/
|
|
53
|
+
declare function assertIntentOwner(intent: ScratchRunRootIntentV1, owner: ScratchOwnerRecordV1, root: ScratchPathIdentity): void;
|
|
54
|
+
/**
|
|
55
|
+
* Open one exact materialized root without following a replacement symlink.
|
|
56
|
+
* @param intent - Armed root intent
|
|
57
|
+
* @returns Owned handle, or undefined when not yet materialized
|
|
58
|
+
*/
|
|
59
|
+
export declare function openOwnedScratchRunRoot(intent: ScratchRunRootIntentV1): OwnedScratchRunRoot | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Remove a run root using only the authority captured when it was made.
|
|
62
|
+
* @param owned - Durable handle or precommitted intent
|
|
63
|
+
*/
|
|
64
|
+
export declare function removeOwnedScratchRunRoot(owned: OwnedScratchRunRoot | ScratchRunRootIntentV1): void;
|
|
65
|
+
export { assertIntentOwner };
|
|
66
|
+
//# sourceMappingURL=scratch-run-root-intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch-run-root-intent.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-run-root-intent.ts"],"names":[],"mappings":"AAKA,OAAO,EAML,KAAK,yBAAyB,EAC/B,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAKL,uBAAuB,EACvB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,yBAAyB,CAAC;IAC9C,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;CACtC;AAED,mEAAmE;AACnE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,yBAAyB,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;CAChD;AAED,qEAAqE;AACrE,UAAU,qBAAqB;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjD;AAED,kEAAkE;AAClE,UAAU,yBAAyB;IACjC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,uBAAuB,CAAC;CAC5D;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,GAAE,qBAA0B,GAClC,sBAAsB,CA+BxB;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,sBAAsB,EAC9B,OAAO,GAAE,yBAA8B,GACtC,mBAAmB,CAiDrB;AAED;;;;;GAKG;AACH,iBAAS,iBAAiB,CACxB,MAAM,EAAE,sBAAsB,EAC9B,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,mBAAmB,GACxB,IAAI,CAcN;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAC7B,mBAAmB,GAAG,SAAS,CAiBjC;AAiCD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,mBAAmB,GAAG,sBAAsB,GAClD,IAAI,CAuBN;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/** Pre-root intent, materialization, and reopening for one owned scratch root. */
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { SCRATCH_QUARANTINE_PREFIX, createScratchNamespaceAuthority, readBoundedScratchNamespace, removeAuthorizedScratchRoot, runRootName, } from "./scratch-authority.js";
|
|
6
|
+
import { registeredScratchPrefixes, scratchSuiteLabel, } from "./scratch-route-profile.js";
|
|
7
|
+
import { createScratchOwnerRecord, processBirthFingerprint, readScratchOwnerRecord, scratchPathIdentity, writeScratchOwnerRecord, } from "./scratch-owner.js";
|
|
8
|
+
/**
|
|
9
|
+
* Prepare an exact root identity without creating that root.
|
|
10
|
+
* @param options - Deterministic authority seams
|
|
11
|
+
* @returns Immutable precommitted root intent
|
|
12
|
+
*/
|
|
13
|
+
export function prepareOwnedScratchRunRoot(options = {}) {
|
|
14
|
+
const suiteLabel = options.suiteLabel ?? scratchSuiteLabel();
|
|
15
|
+
const prefixes = options.registeredPrefixes ?? registeredScratchPrefixes();
|
|
16
|
+
const authority = createScratchNamespaceAuthority();
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const basename = runRootName(process.pid, now, randomBytes(4).toString("hex"));
|
|
19
|
+
const platform = options.platform ?? process.platform;
|
|
20
|
+
const birth = (options.processBirthFingerprint ?? processBirthFingerprint)(process.pid);
|
|
21
|
+
if (birth === undefined && (platform === "darwin" || platform === "linux")) {
|
|
22
|
+
throw new Error(`Scratch process-birth authority is unavailable on ${platform}`);
|
|
23
|
+
}
|
|
24
|
+
return Object.freeze({
|
|
25
|
+
schema: 1,
|
|
26
|
+
rootPath: path.join(authority.namespace.canonicalPath, basename),
|
|
27
|
+
basename,
|
|
28
|
+
authority,
|
|
29
|
+
pid: process.pid,
|
|
30
|
+
processBirthFingerprint: birth ?? `unsupported:${String(process.pid)}`,
|
|
31
|
+
createdAt: new Date(now).toISOString(),
|
|
32
|
+
token: randomBytes(16).toString("hex"),
|
|
33
|
+
suiteLabel,
|
|
34
|
+
registeredPrefixes: prefixes,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Materialize one armed root and persist its immutable marker.
|
|
39
|
+
* @param intent - Precommitted root intent
|
|
40
|
+
* @param options - Transactional marker-writer seams
|
|
41
|
+
* @returns Durable owned-root handle
|
|
42
|
+
*/
|
|
43
|
+
export function materializeOwnedScratchRunRoot(intent, options = {}) {
|
|
44
|
+
if (intent.schema !== 1)
|
|
45
|
+
throw new Error("Invalid scratch root intent schema");
|
|
46
|
+
if (path.join(intent.authority.namespace.canonicalPath, intent.basename) !==
|
|
47
|
+
intent.rootPath) {
|
|
48
|
+
throw new Error("Scratch root intent path does not match its authority");
|
|
49
|
+
}
|
|
50
|
+
const expectedIdentity = (() => {
|
|
51
|
+
fs.mkdirSync(intent.rootPath, { mode: 0o700 });
|
|
52
|
+
return scratchPathIdentity(intent.rootPath);
|
|
53
|
+
})();
|
|
54
|
+
try {
|
|
55
|
+
const owner = createScratchOwnerRecord({
|
|
56
|
+
authority: intent.authority,
|
|
57
|
+
root: intent.rootPath,
|
|
58
|
+
pid: intent.pid,
|
|
59
|
+
processBirthFingerprint: intent.processBirthFingerprint,
|
|
60
|
+
suiteLabel: intent.suiteLabel,
|
|
61
|
+
registeredPrefixes: intent.registeredPrefixes,
|
|
62
|
+
token: intent.token,
|
|
63
|
+
now: new Date(intent.createdAt),
|
|
64
|
+
});
|
|
65
|
+
(options.writeOwnerRecord ?? writeScratchOwnerRecord)(intent.rootPath, owner);
|
|
66
|
+
return {
|
|
67
|
+
path: intent.rootPath,
|
|
68
|
+
basename: intent.basename,
|
|
69
|
+
authority: intent.authority,
|
|
70
|
+
owner,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
try {
|
|
75
|
+
removeAuthorizedScratchRoot({
|
|
76
|
+
authority: intent.authority,
|
|
77
|
+
basename: intent.basename,
|
|
78
|
+
expectedIdentity,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (cleanupError) {
|
|
82
|
+
throw new AggregateError([error, cleanupError], "Scratch root materialization failed and rollback could not reclaim it");
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate an opened marker against the complete armed intent.
|
|
89
|
+
* @param intent - Precommitted root facts
|
|
90
|
+
* @param owner - Persisted owner marker
|
|
91
|
+
* @param root - Currently opened root identity
|
|
92
|
+
*/
|
|
93
|
+
function assertIntentOwner(intent, owner, root) {
|
|
94
|
+
if (owner.token !== intent.token ||
|
|
95
|
+
owner.pid !== intent.pid ||
|
|
96
|
+
owner.processBirthFingerprint !== intent.processBirthFingerprint ||
|
|
97
|
+
owner.namespace.dev !== intent.authority.namespace.dev ||
|
|
98
|
+
owner.namespace.ino !== intent.authority.namespace.ino ||
|
|
99
|
+
owner.root.dev !== root.dev ||
|
|
100
|
+
owner.root.ino !== root.ino) {
|
|
101
|
+
throw new Error("Scratch owner token or armed identity does not match intent");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Open one exact materialized root without following a replacement symlink.
|
|
106
|
+
* @param intent - Armed root intent
|
|
107
|
+
* @returns Owned handle, or undefined when not yet materialized
|
|
108
|
+
*/
|
|
109
|
+
export function openOwnedScratchRunRoot(intent) {
|
|
110
|
+
// eslint-disable-next-line functional/no-let -- path-level ENOENT must be separated from marker-level ENOENT
|
|
111
|
+
let root;
|
|
112
|
+
try {
|
|
113
|
+
root = scratchPathIdentity(intent.rootPath);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error.code === "ENOENT")
|
|
117
|
+
return undefined;
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
const owner = readScratchOwnerRecord(intent.rootPath);
|
|
121
|
+
assertIntentOwner(intent, owner, root);
|
|
122
|
+
return {
|
|
123
|
+
path: intent.rootPath,
|
|
124
|
+
basename: intent.basename,
|
|
125
|
+
authority: intent.authority,
|
|
126
|
+
owner,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Find an interrupted quarantine bound to the intent token and root inode.
|
|
131
|
+
* @param intent - Precommitted root facts
|
|
132
|
+
* @returns Matching quarantine identity, or undefined
|
|
133
|
+
*/
|
|
134
|
+
function interruptedQuarantine(intent) {
|
|
135
|
+
for (const basename of readBoundedScratchNamespace(intent.authority.namespace.canonicalPath)) {
|
|
136
|
+
if (!basename.startsWith(SCRATCH_QUARANTINE_PREFIX))
|
|
137
|
+
continue;
|
|
138
|
+
const candidate = path.join(intent.authority.namespace.canonicalPath, basename);
|
|
139
|
+
try {
|
|
140
|
+
const owner = readScratchOwnerRecord(candidate);
|
|
141
|
+
if (owner.token !== intent.token)
|
|
142
|
+
continue;
|
|
143
|
+
const identity = scratchPathIdentity(candidate);
|
|
144
|
+
assertIntentOwner(intent, owner, identity);
|
|
145
|
+
return { basename, identity };
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Foreign and malformed quarantines never authorize this intent.
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Remove a run root using only the authority captured when it was made.
|
|
155
|
+
* @param owned - Durable handle or precommitted intent
|
|
156
|
+
*/
|
|
157
|
+
export function removeOwnedScratchRunRoot(owned) {
|
|
158
|
+
if ("rootPath" in owned) {
|
|
159
|
+
const opened = openOwnedScratchRunRoot(owned);
|
|
160
|
+
if (opened !== undefined) {
|
|
161
|
+
removeOwnedScratchRunRoot(opened);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const quarantine = interruptedQuarantine(owned);
|
|
165
|
+
if (quarantine === undefined)
|
|
166
|
+
return;
|
|
167
|
+
removeAuthorizedScratchRoot({
|
|
168
|
+
authority: owned.authority,
|
|
169
|
+
basename: quarantine.basename,
|
|
170
|
+
expectedToken: owned.token,
|
|
171
|
+
expectedIdentity: quarantine.identity,
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
removeAuthorizedScratchRoot({
|
|
176
|
+
authority: owned.authority,
|
|
177
|
+
basename: owned.basename,
|
|
178
|
+
expectedToken: owned.owner.token,
|
|
179
|
+
expectedIdentity: owned.owner.root,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
export { assertIntentOwner };
|
|
183
|
+
//# sourceMappingURL=scratch-run-root-intent.js.map
|