@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.
Files changed (188) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +2 -1
  2. package/all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh +3 -2
  3. package/cdk/create-only/vitest.config.local.ts +5 -1
  4. package/cdk/package-lisa/package.lisa.json +14 -7
  5. package/dist/cli/lisa-test-run-bootstrap.d.ts +3 -0
  6. package/dist/cli/lisa-test-run-bootstrap.d.ts.map +1 -0
  7. package/dist/cli/lisa-test-run-bootstrap.js +201 -0
  8. package/dist/cli/lisa-test-run-bootstrap.js.map +1 -0
  9. package/dist/cli/lisa-test-run-ipc.d.ts +42 -0
  10. package/dist/cli/lisa-test-run-ipc.d.ts.map +1 -0
  11. package/dist/cli/lisa-test-run-ipc.js +200 -0
  12. package/dist/cli/lisa-test-run-ipc.js.map +1 -0
  13. package/dist/cli/lisa-test-run-process-group.d.ts +30 -0
  14. package/dist/cli/lisa-test-run-process-group.d.ts.map +1 -0
  15. package/dist/cli/lisa-test-run-process-group.js +123 -0
  16. package/dist/cli/lisa-test-run-process-group.js.map +1 -0
  17. package/dist/cli/lisa-test-run-reaper.d.ts +3 -0
  18. package/dist/cli/lisa-test-run-reaper.d.ts.map +1 -0
  19. package/dist/cli/lisa-test-run-reaper.js +195 -0
  20. package/dist/cli/lisa-test-run-reaper.js.map +1 -0
  21. package/dist/cli/lisa-test-run-runtime.d.ts +46 -0
  22. package/dist/cli/lisa-test-run-runtime.d.ts.map +1 -0
  23. package/dist/cli/lisa-test-run-runtime.js +123 -0
  24. package/dist/cli/lisa-test-run-runtime.js.map +1 -0
  25. package/dist/cli/lisa-test-run-supervisor.d.ts +13 -0
  26. package/dist/cli/lisa-test-run-supervisor.d.ts.map +1 -0
  27. package/dist/cli/lisa-test-run-supervisor.js +268 -0
  28. package/dist/cli/lisa-test-run-supervisor.js.map +1 -0
  29. package/dist/cli/lisa-test-run.d.ts +3 -0
  30. package/dist/cli/lisa-test-run.d.ts.map +1 -0
  31. package/dist/cli/lisa-test-run.js +66 -0
  32. package/dist/cli/lisa-test-run.js.map +1 -0
  33. package/dist/configs/vitest/base.d.ts.map +1 -1
  34. package/dist/configs/vitest/base.js +5 -2
  35. package/dist/configs/vitest/base.js.map +1 -1
  36. package/dist/configs/vitest/cdk.d.ts +4 -0
  37. package/dist/configs/vitest/cdk.d.ts.map +1 -1
  38. package/dist/configs/vitest/cdk.js +5 -0
  39. package/dist/configs/vitest/cdk.js.map +1 -1
  40. package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
  41. package/dist/configs/vitest/harper-fabric.js +1 -0
  42. package/dist/configs/vitest/harper-fabric.js.map +1 -1
  43. package/dist/configs/vitest/nestjs.d.ts.map +1 -1
  44. package/dist/configs/vitest/nestjs.js +1 -0
  45. package/dist/configs/vitest/nestjs.js.map +1 -1
  46. package/dist/configs/vitest/phaser.d.ts.map +1 -1
  47. package/dist/configs/vitest/phaser.js +1 -0
  48. package/dist/configs/vitest/phaser.js.map +1 -1
  49. package/dist/configs/vitest/scratch-authority.d.ts +25 -0
  50. package/dist/configs/vitest/scratch-authority.d.ts.map +1 -0
  51. package/dist/configs/vitest/scratch-authority.js +107 -0
  52. package/dist/configs/vitest/scratch-authority.js.map +1 -0
  53. package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts +5 -0
  54. package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts.map +1 -0
  55. package/dist/configs/vitest/scratch-bound-cleanup-programs.js +219 -0
  56. package/dist/configs/vitest/scratch-bound-cleanup-programs.js.map +1 -0
  57. package/dist/configs/vitest/scratch-bound-cleanup.d.ts +28 -0
  58. package/dist/configs/vitest/scratch-bound-cleanup.d.ts.map +1 -0
  59. package/dist/configs/vitest/scratch-bound-cleanup.js +155 -0
  60. package/dist/configs/vitest/scratch-bound-cleanup.js.map +1 -0
  61. package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts +17 -0
  62. package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts.map +1 -0
  63. package/dist/configs/vitest/scratch-bound-root-cleanup.js +149 -0
  64. package/dist/configs/vitest/scratch-bound-root-cleanup.js.map +1 -0
  65. package/dist/configs/vitest/scratch-global-setup.d.ts +17 -9
  66. package/dist/configs/vitest/scratch-global-setup.d.ts.map +1 -1
  67. package/dist/configs/vitest/scratch-global-setup.js +103 -35
  68. package/dist/configs/vitest/scratch-global-setup.js.map +1 -1
  69. package/dist/configs/vitest/scratch-leak-setup.d.ts +2 -0
  70. package/dist/configs/vitest/scratch-leak-setup.d.ts.map +1 -0
  71. package/dist/configs/vitest/scratch-leak-setup.js +108 -0
  72. package/dist/configs/vitest/scratch-leak-setup.js.map +1 -0
  73. package/dist/configs/vitest/scratch-namespace-authority.d.ts +80 -0
  74. package/dist/configs/vitest/scratch-namespace-authority.d.ts.map +1 -0
  75. package/dist/configs/vitest/scratch-namespace-authority.js +291 -0
  76. package/dist/configs/vitest/scratch-namespace-authority.js.map +1 -0
  77. package/dist/configs/vitest/scratch-owner.d.ts +131 -0
  78. package/dist/configs/vitest/scratch-owner.d.ts.map +1 -0
  79. package/dist/configs/vitest/scratch-owner.js +297 -0
  80. package/dist/configs/vitest/scratch-owner.js.map +1 -0
  81. package/dist/configs/vitest/scratch-route-profile.d.ts +36 -0
  82. package/dist/configs/vitest/scratch-route-profile.d.ts.map +1 -0
  83. package/dist/configs/vitest/scratch-route-profile.js +217 -0
  84. package/dist/configs/vitest/scratch-route-profile.js.map +1 -0
  85. package/dist/configs/vitest/scratch-run-root-compatibility.d.ts +33 -0
  86. package/dist/configs/vitest/scratch-run-root-compatibility.d.ts.map +1 -0
  87. package/dist/configs/vitest/scratch-run-root-compatibility.js +137 -0
  88. package/dist/configs/vitest/scratch-run-root-compatibility.js.map +1 -0
  89. package/dist/configs/vitest/scratch-run-root-intent.d.ts +66 -0
  90. package/dist/configs/vitest/scratch-run-root-intent.d.ts.map +1 -0
  91. package/dist/configs/vitest/scratch-run-root-intent.js +183 -0
  92. package/dist/configs/vitest/scratch-run-root-intent.js.map +1 -0
  93. package/dist/configs/vitest/scratch-setup.d.ts.map +1 -1
  94. package/dist/configs/vitest/scratch-setup.js +45 -15
  95. package/dist/configs/vitest/scratch-setup.js.map +1 -1
  96. package/dist/configs/vitest/scratch-supervision-intent.d.ts +8 -0
  97. package/dist/configs/vitest/scratch-supervision-intent.d.ts.map +1 -0
  98. package/dist/configs/vitest/scratch-supervision-intent.js +125 -0
  99. package/dist/configs/vitest/scratch-supervision-intent.js.map +1 -0
  100. package/dist/configs/vitest/scratch-supervision-protocol.d.ts +12 -0
  101. package/dist/configs/vitest/scratch-supervision-protocol.d.ts.map +1 -0
  102. package/dist/configs/vitest/scratch-supervision-protocol.js +230 -0
  103. package/dist/configs/vitest/scratch-supervision-protocol.js.map +1 -0
  104. package/dist/configs/vitest/scratch-supervision.d.ts +64 -0
  105. package/dist/configs/vitest/scratch-supervision.d.ts.map +1 -0
  106. package/dist/configs/vitest/scratch-supervision.js +209 -0
  107. package/dist/configs/vitest/scratch-supervision.js.map +1 -0
  108. package/dist/configs/vitest/scratch-sweep.d.ts +66 -0
  109. package/dist/configs/vitest/scratch-sweep.d.ts.map +1 -0
  110. package/dist/configs/vitest/scratch-sweep.js +82 -0
  111. package/dist/configs/vitest/scratch-sweep.js.map +1 -0
  112. package/dist/configs/vitest/scratch.d.ts +14 -187
  113. package/dist/configs/vitest/scratch.d.ts.map +1 -1
  114. package/dist/configs/vitest/scratch.js +18 -285
  115. package/dist/configs/vitest/scratch.js.map +1 -1
  116. package/dist/configs/vitest/typescript.d.ts.map +1 -1
  117. package/dist/configs/vitest/typescript.js +1 -0
  118. package/dist/configs/vitest/typescript.js.map +1 -1
  119. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  120. package/dist/core/lisa-owned-hash-ledger.js +4 -0
  121. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  122. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  123. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  124. package/dist/core/upstream-evidence-manifest.js +59 -13
  125. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  126. package/expo/package-lisa/package.lisa.json +14 -14
  127. package/harper-fabric/package-lisa/package.lisa.json +8 -3
  128. package/nestjs/create-only/vitest.config.local.ts +5 -1
  129. package/nestjs/package-lisa/package.lisa.json +14 -8
  130. package/npm-package/package-lisa/package.lisa.json +8 -0
  131. package/package.json +17 -13
  132. package/phaser/package-lisa/package.lisa.json +9 -3
  133. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  134. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  135. package/plugins/lisa/hooks/sonar-secrets.sh +3 -2
  136. package/plugins/lisa-agy/plugin.json +1 -1
  137. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  138. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  139. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  140. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  141. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  143. package/plugins/lisa-copilot/hooks/sonar-secrets.sh +3 -2
  144. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  145. package/plugins/lisa-cursor/hooks/sonar-secrets.sh +3 -2
  146. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  147. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  148. package/plugins/lisa-expo-agy/plugin.json +1 -1
  149. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  151. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  152. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  153. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  154. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  155. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  156. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  157. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  158. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  159. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  160. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  161. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  162. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  163. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  164. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  165. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  166. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  167. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  168. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  169. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  170. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  171. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  172. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  173. package/plugins/lisa-rails-agy/plugin.json +1 -1
  174. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  175. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  176. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  177. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  178. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  179. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  180. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  181. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  182. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  183. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  184. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  185. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  186. package/plugins/src/base/hooks/sonar-secrets.sh +3 -2
  187. package/typescript/create-only/vitest.config.local.ts +5 -1
  188. package/typescript/package-lisa/package.lisa.json +14 -8
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Filesystem authority for destructive scratch cleanup.
3
+ *
4
+ * Every removal is constrained to one direct child of a canonical, uid-owned,
5
+ * mode-0700 `lisa-scratch` namespace. The child is renamed to a random
6
+ * quarantine name inside that same namespace, then its inode and owner token
7
+ * are revalidated before a no-follow recursive unlink.
8
+ * @module configs/vitest/scratch-authority
9
+ */
10
+ import * as fs from "node:fs";
11
+ import * as path from "node:path";
12
+ import { classifyScratchOwner, readScratchOwnerRecord, } from "./scratch-owner.js";
13
+ import { SCRATCH_QUARANTINE_PREFIX, authorityForExistingScratchNamespace, readBoundedScratchNamespace, } from "./scratch-namespace-authority.js";
14
+ import { removeAuthorizedScratchRoot } from "./scratch-bound-root-cleanup.js";
15
+ /**
16
+ * Whether a marker is bound to the exact root and namespace inspected.
17
+ * @param authority - Pinned namespace authority
18
+ * @param root - Direct namespace child
19
+ * @param allowQuarantine - Whether the root canonical path may reflect quarantine
20
+ * @returns Parsed marker and whether every authority fact matches
21
+ */
22
+ function ownerMatchesPath(authority, root, allowQuarantine) {
23
+ const owner = readScratchOwnerRecord(root);
24
+ const stat = fs.lstatSync(root);
25
+ const matches = owner.namespace.canonicalPath === authority.namespace.canonicalPath &&
26
+ owner.namespace.dev === authority.namespace.dev &&
27
+ owner.namespace.ino === authority.namespace.ino &&
28
+ owner.root.dev === stat.dev &&
29
+ owner.root.ino === stat.ino &&
30
+ (allowQuarantine || owner.root.canonicalPath === fs.realpathSync(root));
31
+ return { owner, matches };
32
+ }
33
+ /**
34
+ * Remove a marked entry only when ownership says its process is gone or reused.
35
+ * @param authority - Pinned namespace authority
36
+ * @param name - Direct child basename
37
+ * @param root - Direct child path
38
+ * @param quarantine - Whether the child is an interrupted quarantine
39
+ * @param options - Process probes and self name
40
+ * @returns Removal disposition
41
+ */
42
+ function sweepMarkedEntry(authority, name, root, quarantine, options) {
43
+ const marked = ownerMatchesPath(authority, root, quarantine);
44
+ if (!marked.matches)
45
+ return "kept";
46
+ const disposition = classifyScratchOwner(marked.owner, options);
47
+ if (disposition === "preserve")
48
+ return "kept";
49
+ removeAuthorizedScratchRoot({
50
+ authority,
51
+ basename: name,
52
+ expectedToken: marked.owner.token,
53
+ });
54
+ return "removed";
55
+ }
56
+ /**
57
+ * Judge one direct namespace entry without ever following a symlink.
58
+ * @param authority - Pinned namespace authority
59
+ * @param name - Direct child basename
60
+ * @param options - Process probes and self name
61
+ * @returns Removal disposition
62
+ */
63
+ function sweepAuthorizedEntry(authority, name, options) {
64
+ if (name === options.selfName)
65
+ return "kept";
66
+ const root = path.join(authority.namespace.canonicalPath, name);
67
+ try {
68
+ const stat = fs.lstatSync(root);
69
+ if (stat.isSymbolicLink() || !stat.isDirectory())
70
+ return "kept";
71
+ }
72
+ catch (error) {
73
+ return error.code === "ENOENT"
74
+ ? "removed"
75
+ : "kept";
76
+ }
77
+ const quarantine = name.startsWith(SCRATCH_QUARANTINE_PREFIX);
78
+ const recognized = /^run-\d+-\d+-[^/\\]+$/u.test(name);
79
+ if (!recognized && !quarantine)
80
+ return "kept";
81
+ try {
82
+ return sweepMarkedEntry(authority, name, root, quarantine, options);
83
+ }
84
+ catch {
85
+ // A recognizable name is never deletion authority. Missing, malformed,
86
+ // unreadable, oversized, or identity-mismatched markers remain in place
87
+ // for the namespace audit to refuse with an actionable diagnosis.
88
+ return "kept";
89
+ }
90
+ }
91
+ /**
92
+ * Sweep one canonical namespace using durable owner/process-birth authority.
93
+ * @param options - Namespace and process probes
94
+ * @returns Names removed and preserved
95
+ */
96
+ export function sweepAuthorizedScratchNamespace(options) {
97
+ const authority = authorityForExistingScratchNamespace();
98
+ if (authority === undefined)
99
+ return { removed: [], kept: [] };
100
+ return readBoundedScratchNamespace(authority.namespace.canonicalPath).reduce((result, name) => sweepAuthorizedEntry(authority, name, options) === "removed"
101
+ ? { removed: [...result.removed, name], kept: result.kept }
102
+ : { removed: result.removed, kept: [...result.kept, name] }, { removed: [], kept: [] });
103
+ }
104
+ export { BOUND_DIRECTORY_CLEANUP_PROGRAM, removeAuthorizedScratchChild, removeAuthorizedScratchChildren, } from "./scratch-bound-cleanup.js";
105
+ export { removeAuthorizedScratchRoot };
106
+ export { DEFAULT_RECLAIM_AGE_MS, MAX_SCRATCH_NAMESPACE_NAME_BYTES, MAX_SCRATCH_NAMESPACE_SCAN_ENTRIES, RUN_ROOT_PREFIX, SCRATCH_DIRECT_ENTRY_LIMIT, SCRATCH_DIRECT_NAME_BYTES, SCRATCH_QUARANTINE_PREFIX, SCRATCH_NAMESPACE, assertScratchNamespaceAuthority, collectBoundedScratchNames, collectBoundedScratchNamespaceNames, createScratchNamespaceAuthority, parseRunRootName, readBoundedScratchNames, readBoundedScratchNamespace, runRootName, scratchBaseDir, scratchNamespaceDir, } from "./scratch-namespace-authority.js";
107
+ //# sourceMappingURL=scratch-authority.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-authority.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-authority.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Synchronous deletion program whose cwd is bound before traversal. */
2
+ export declare const BOUND_DIRECTORY_CLEANUP_PROGRAM: string;
3
+ /** Parent-cwd-bound program removing every preclassified direct child. */
4
+ export declare const BOUND_CHILDREN_CLEANUP_PROGRAM: string;
5
+ //# sourceMappingURL=scratch-bound-cleanup-programs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-cleanup-programs.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-cleanup-programs.ts"],"names":[],"mappings":"AAIA,wEAAwE;AACxE,eAAO,MAAM,+BAA+B,QAuE3C,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,8BAA8B,QA4I1C,CAAC"}
@@ -0,0 +1,219 @@
1
+ /** Inert CommonJS programs executed in kernel-bound scratch cwd handles. */
2
+ /** Exit status used when the child cwd does not match the expected inode. */
3
+ const BOUND_CLEANUP_IDENTITY_EXIT = 73;
4
+ /** Synchronous deletion program whose cwd is bound before traversal. */
5
+ export const BOUND_DIRECTORY_CLEANUP_PROGRAM = String.raw `
6
+ const fs = require("node:fs");
7
+ const path = require("node:path");
8
+ const expectedDev = process.argv[1];
9
+ const expectedIno = process.argv[2];
10
+ const root = fs.lstatSync(".");
11
+ if (!root.isDirectory() || root.isSymbolicLink() || String(root.dev) !== expectedDev || String(root.ino) !== expectedIno) {
12
+ process.stderr.write("scratch directory identity changed before bound cleanup\n");
13
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
14
+ }
15
+ const deadline = Date.now() + 30000;
16
+ let entries = 0;
17
+ const readChildren = candidate => {
18
+ const handle = fs.opendirSync(candidate);
19
+ const names = [];
20
+ try {
21
+ for (;;) {
22
+ const entry = handle.readSync();
23
+ if (entry === null) break;
24
+ if (Buffer.byteLength(entry.name, "utf8") > 1024) throw new Error("scratch cleanup basename exceeds 1024 bytes");
25
+ entries += 1;
26
+ if (entries > 100000) throw new Error("scratch cleanup entry bound exceeded");
27
+ names.push(entry.name);
28
+ }
29
+ } finally {
30
+ handle.closeSync();
31
+ }
32
+ return names;
33
+ };
34
+ const pending = [{ candidate: ".", depth: 0 }];
35
+ const scanned = [];
36
+ while (pending.length > 0) {
37
+ if (Date.now() > deadline) throw new Error("scratch cleanup time bound exceeded");
38
+ const item = pending.pop();
39
+ if (item.depth > 128) throw new Error("scratch cleanup depth bound exceeded");
40
+ let stat;
41
+ try {
42
+ stat = fs.lstatSync(item.candidate);
43
+ } catch (error) {
44
+ if (error.code === "ENOENT") continue;
45
+ throw error;
46
+ }
47
+ if (item.candidate !== ".") {
48
+ scanned.push({ candidate: item.candidate, depth: item.depth, dev: String(stat.dev), ino: String(stat.ino), directory: stat.isDirectory(), symlink: stat.isSymbolicLink() });
49
+ }
50
+ if (stat.isDirectory() && !stat.isSymbolicLink()) {
51
+ for (const child of readChildren(item.candidate)) {
52
+ pending.push({ candidate: path.join(item.candidate, child), depth: item.depth + 1 });
53
+ }
54
+ }
55
+ }
56
+ scanned.sort((left, right) => right.depth - left.depth);
57
+ for (const item of scanned) {
58
+ let stat;
59
+ try {
60
+ stat = fs.lstatSync(item.candidate);
61
+ } catch (error) {
62
+ if (error.code === "ENOENT") continue;
63
+ throw error;
64
+ }
65
+ if (String(stat.dev) !== item.dev || String(stat.ino) !== item.ino || stat.isDirectory() !== item.directory || stat.isSymbolicLink() !== item.symlink) {
66
+ process.stderr.write("scratch entry identity changed before bound cleanup: " + item.candidate + "\n");
67
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
68
+ }
69
+ try {
70
+ if (item.directory && !item.symlink) fs.rmdirSync(item.candidate);
71
+ else fs.unlinkSync(item.candidate);
72
+ } catch (error) {
73
+ if (error.code !== "ENOENT") throw error;
74
+ }
75
+ }
76
+ `;
77
+ /** Parent-cwd-bound program removing every preclassified direct child. */
78
+ export const BOUND_CHILDREN_CLEANUP_PROGRAM = String.raw `
79
+ const crypto = require("node:crypto");
80
+ const fs = require("node:fs");
81
+ const path = require("node:path");
82
+ const expectedDev = process.argv[1];
83
+ const expectedIno = process.argv[2];
84
+ const parent = fs.lstatSync(".");
85
+ if (!parent.isDirectory() || parent.isSymbolicLink() || String(parent.dev) !== expectedDev || String(parent.ino) !== expectedIno) {
86
+ process.stderr.write("scratch parent identity changed before bound cleanup\n");
87
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
88
+ }
89
+ const items = JSON.parse(fs.readFileSync(0, "utf8"));
90
+ const deadline = Date.now() + 30000;
91
+ let entries = 0;
92
+ const readChildren = candidate => {
93
+ const handle = fs.opendirSync(candidate);
94
+ const names = [];
95
+ try {
96
+ for (;;) {
97
+ const entry = handle.readSync();
98
+ if (entry === null) break;
99
+ if (Buffer.byteLength(entry.name, "utf8") > 1024) throw new Error("scratch cleanup basename exceeds 1024 bytes");
100
+ entries += 1;
101
+ if (entries > 100000) throw new Error("scratch cleanup entry bound exceeded");
102
+ names.push(entry.name);
103
+ }
104
+ } finally {
105
+ handle.closeSync();
106
+ }
107
+ return names;
108
+ };
109
+ const scanDirectory = rootName => {
110
+ const pending = [{ candidate: rootName, relative: "", depth: 0 }];
111
+ const scanned = [];
112
+ while (pending.length > 0) {
113
+ if (Date.now() > deadline) throw new Error("scratch cleanup time bound exceeded");
114
+ const item = pending.pop();
115
+ if (item.depth > 128) throw new Error("scratch cleanup depth bound exceeded");
116
+ let stat;
117
+ try {
118
+ stat = fs.lstatSync(item.candidate);
119
+ } catch (error) {
120
+ if (error.code === "ENOENT") continue;
121
+ throw error;
122
+ }
123
+ if (item.relative !== "") {
124
+ scanned.push({ relative: item.relative, depth: item.depth, dev: String(stat.dev), ino: String(stat.ino), directory: stat.isDirectory(), symlink: stat.isSymbolicLink() });
125
+ }
126
+ if (stat.isDirectory() && !stat.isSymbolicLink()) {
127
+ for (const child of readChildren(item.candidate)) {
128
+ pending.push({ candidate: path.join(item.candidate, child), relative: item.relative === "" ? child : path.join(item.relative, child), depth: item.depth + 1 });
129
+ }
130
+ }
131
+ }
132
+ return scanned;
133
+ };
134
+ const prepared = [];
135
+ for (const item of items) {
136
+ if (typeof item.basename !== "string" || item.basename === "" || path.basename(item.basename) !== item.basename || Buffer.byteLength(item.basename, "utf8") > 1024) {
137
+ throw new Error("scratch cleanup candidate must be a bounded direct basename");
138
+ }
139
+ let before;
140
+ try {
141
+ before = fs.lstatSync(item.basename);
142
+ } catch (error) {
143
+ if (error.code === "ENOENT") continue;
144
+ throw error;
145
+ }
146
+ if (String(before.dev) !== item.dev || String(before.ino) !== item.ino || before.isDirectory() !== item.directory || before.isSymbolicLink() !== item.symlink) {
147
+ process.stderr.write("scratch child identity changed before bound cleanup: " + item.basename + "\n");
148
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
149
+ }
150
+ prepared.push({ item });
151
+ }
152
+ for (const preparedItem of prepared) {
153
+ const item = preparedItem.item;
154
+ let before;
155
+ try {
156
+ before = fs.lstatSync(item.basename);
157
+ } catch (error) {
158
+ if (error.code === "ENOENT") continue;
159
+ throw error;
160
+ }
161
+ if (String(before.dev) !== item.dev || String(before.ino) !== item.ino || before.isDirectory() !== item.directory || before.isSymbolicLink() !== item.symlink) {
162
+ process.stderr.write("scratch child identity changed before quarantine: " + item.basename + "\n");
163
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
164
+ }
165
+ // Deterministic seam for the scan-after-quarantine control, in the shape the
166
+ // rest of this subsystem already uses for protocol faults. It writes only
167
+ // inside a child already authorized for deletion, and only under an explicit
168
+ // environment opt-in, so it cannot fire in a real run.
169
+ if (process.env.LISA_TEST_SCRATCH_CLEANUP_FAULT === "write-before-quarantine" && item.directory && !item.symlink) {
170
+ fs.writeFileSync(path.join(item.basename, "late-writer.txt"), "late");
171
+ }
172
+ const quarantine = ".lisa-child-quarantine-" + crypto.randomBytes(16).toString("hex");
173
+ fs.renameSync(item.basename, quarantine);
174
+ const quarantined = fs.lstatSync(quarantine);
175
+ if (String(quarantined.dev) !== item.dev || String(quarantined.ino) !== item.ino) {
176
+ process.stderr.write("scratch child identity changed during quarantine: " + item.basename + "\n");
177
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
178
+ }
179
+ if (item.directory && !item.symlink) {
180
+ // Scanned AFTER the quarantine rename, never before it. A pre-rename scan
181
+ // leaves a window in which a writer creates an entry that is therefore
182
+ // absent from the recorded list, never deleted, and left to fail the final
183
+ // rmdirSync with ENOTEMPTY -- so the cleanup that exists to remove a tree
184
+ // leaves the whole quarantined tree behind. After the rename the directory
185
+ // sits at an unguessable name no other writer holds a path to, so what is
186
+ // scanned here is what is there.
187
+ const scanned = scanDirectory(quarantine).sort((left, right) => right.depth - left.depth);
188
+ for (const child of scanned) {
189
+ const candidate = path.join(quarantine, child.relative);
190
+ let stat;
191
+ try {
192
+ stat = fs.lstatSync(candidate);
193
+ } catch (error) {
194
+ if (error.code === "ENOENT") continue;
195
+ throw error;
196
+ }
197
+ if (String(stat.dev) !== child.dev || String(stat.ino) !== child.ino || stat.isDirectory() !== child.directory || stat.isSymbolicLink() !== child.symlink) {
198
+ process.stderr.write("scratch descendant identity changed during bound cleanup: " + item.basename + "\n");
199
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
200
+ }
201
+ try {
202
+ if (child.directory && !child.symlink) fs.rmdirSync(candidate);
203
+ else fs.unlinkSync(candidate);
204
+ } catch (error) {
205
+ if (error.code !== "ENOENT") throw error;
206
+ }
207
+ }
208
+ const after = fs.lstatSync(quarantine);
209
+ if (String(after.dev) !== item.dev || String(after.ino) !== item.ino) {
210
+ process.stderr.write("scratch child identity changed during bound cleanup: " + item.basename + "\n");
211
+ process.exit(${String(BOUND_CLEANUP_IDENTITY_EXIT)});
212
+ }
213
+ fs.rmdirSync(quarantine);
214
+ } else {
215
+ fs.unlinkSync(quarantine);
216
+ }
217
+ }
218
+ `;
219
+ //# sourceMappingURL=scratch-bound-cleanup-programs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-cleanup-programs.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-cleanup-programs.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,6EAA6E;AAC7E,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAEvC,wEAAwE;AACxE,MAAM,CAAC,MAAM,+BAA+B,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;iBAQxC,MAAM,CAAC,2BAA2B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAsDjC,MAAM,CAAC,2BAA2B,CAAC;;;;;;;;;CASrD,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,8BAA8B,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;iBASvC,MAAM,CAAC,2BAA2B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA6DjC,MAAM,CAAC,2BAA2B,CAAC;;;;;;;;;;;;;;;mBAenC,MAAM,CAAC,2BAA2B,CAAC;;;;;;;;;;;;;;mBAcnC,MAAM,CAAC,2BAA2B,CAAC;;;;;;;;;;;;;;;;;;;;;;uBAsB/B,MAAM,CAAC,2BAA2B,CAAC;;;;;;;;;;;;qBAYrC,MAAM,CAAC,2BAA2B,CAAC;;;;;;;CAOvD,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { type ScratchPathIdentity } from "./scratch-owner.js";
2
+ export { BOUND_DIRECTORY_CLEANUP_PROGRAM } from "./scratch-bound-cleanup-programs.js";
3
+ /** Options for removing one direct child of an already-owned run root. */
4
+ export interface RemoveAuthorizedScratchChildOptions {
5
+ readonly parent: ScratchPathIdentity;
6
+ readonly basename: string;
7
+ readonly beforeIdentityCheck?: (candidate: string) => void;
8
+ readonly afterIdentityCheck?: (candidate: string) => void;
9
+ }
10
+ /** Options for one batched removal under an already-owned run root. */
11
+ export interface RemoveAuthorizedScratchChildrenOptions {
12
+ readonly parent: ScratchPathIdentity;
13
+ readonly basenames: readonly string[];
14
+ readonly beforeIdentityCheck?: (candidate: string) => void;
15
+ readonly afterIdentityCheck?: (candidate: string) => void;
16
+ readonly beforeBoundCleanup?: () => void;
17
+ }
18
+ /**
19
+ * Remove direct children through one process bound to the parent inode.
20
+ * @param options - Batched child-removal options
21
+ */
22
+ export declare function removeAuthorizedScratchChildren(options: RemoveAuthorizedScratchChildrenOptions): void;
23
+ /**
24
+ * Remove one direct child through the batched authority.
25
+ * @param options - Single child-removal options
26
+ */
27
+ export declare function removeAuthorizedScratchChild(options: RemoveAuthorizedScratchChildOptions): void;
28
+ //# sourceMappingURL=scratch-bound-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-cleanup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-cleanup.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,+BAA+B,EAAE,MAAM,qCAAqC,CAAC;AAEtF,0EAA0E;AAC1E,MAAM,WAAW,mCAAmC;IAClD,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAED,uEAAuE;AACvE,MAAM,WAAW,sCAAsC;IACrD,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1C;AA+HD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAwBN;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mCAAmC,GAC3C,IAAI,CAWN"}
@@ -0,0 +1,155 @@
1
+ /** Kernel-bound deletion programs for scratch roots and their direct children. */
2
+ import { spawnSync } from "node:child_process";
3
+ import * as fs from "node:fs";
4
+ import * as path from "node:path";
5
+ import { scratchPathIdentity, } from "./scratch-owner.js";
6
+ import { BOUND_CHILDREN_CLEANUP_PROGRAM } from "./scratch-bound-cleanup-programs.js";
7
+ export { BOUND_DIRECTORY_CLEANUP_PROGRAM } from "./scratch-bound-cleanup-programs.js";
8
+ /** Exit status used when the child cwd does not match the expected inode. */
9
+ const BOUND_CLEANUP_IDENTITY_EXIT = 73;
10
+ /**
11
+ * Demand one inert direct basename, never a path.
12
+ * @param basename - Candidate direct-child name
13
+ */
14
+ function validateBasename(basename) {
15
+ if (basename === "" ||
16
+ basename === "." ||
17
+ basename === ".." ||
18
+ path.basename(basename) !== basename ||
19
+ basename.includes("/") ||
20
+ basename.includes("\\")) {
21
+ throw new Error(`Scratch candidate must be a direct basename: ${basename}`);
22
+ }
23
+ if (Buffer.byteLength(basename, "utf8") > 1_024) {
24
+ throw new Error("Scratch candidate basename exceeds 1024 bytes");
25
+ }
26
+ }
27
+ /**
28
+ * Read one candidate without converting absence into an exception.
29
+ * @param candidate - Candidate path
30
+ * @returns Candidate metadata, or undefined when already absent
31
+ */
32
+ function lstatIfPresent(candidate) {
33
+ try {
34
+ return fs.lstatSync(candidate);
35
+ }
36
+ catch (error) {
37
+ if (error.code === "ENOENT")
38
+ return undefined;
39
+ throw error;
40
+ }
41
+ }
42
+ /**
43
+ * Assert that an already-owned parent still has its pinned identity.
44
+ * @param expected - Pinned parent identity
45
+ */
46
+ function assertScratchPathIdentity(expected) {
47
+ const current = scratchPathIdentity(expected.canonicalPath);
48
+ if (current.canonicalPath !== expected.canonicalPath ||
49
+ current.dev !== expected.dev ||
50
+ current.ino !== expected.ino) {
51
+ throw new Error("Scratch parent identity changed before child cleanup");
52
+ }
53
+ }
54
+ /**
55
+ * Inspect a direct child after its pinned parent passes revalidation.
56
+ * @param parent - Pinned parent identity
57
+ * @param basename - Direct-child name
58
+ * @param child - Candidate path
59
+ * @returns Candidate metadata, or undefined when already absent
60
+ */
61
+ function inspectAuthorizedScratchChild(parent, basename, child) {
62
+ validateBasename(basename);
63
+ assertScratchPathIdentity(parent);
64
+ return lstatIfPresent(child);
65
+ }
66
+ /**
67
+ * Capture every present candidate while the parent remains pinned.
68
+ * @param options - Batched child-removal options
69
+ * @returns Bound cleanup descriptions for present children
70
+ */
71
+ function collectAuthorizedChildren(options) {
72
+ assertScratchPathIdentity(options.parent);
73
+ return options.basenames.flatMap(basename => {
74
+ const child = path.join(options.parent.canonicalPath, basename);
75
+ options.beforeIdentityCheck?.(child);
76
+ const stat = inspectAuthorizedScratchChild(options.parent, basename, child);
77
+ if (stat === undefined)
78
+ return [];
79
+ options.afterIdentityCheck?.(child);
80
+ return [
81
+ {
82
+ basename,
83
+ dev: String(stat.dev),
84
+ ino: String(stat.ino),
85
+ directory: stat.isDirectory(),
86
+ symlink: stat.isSymbolicLink(),
87
+ },
88
+ ];
89
+ });
90
+ }
91
+ /**
92
+ * Execute the inode-bound child-cleanup worker.
93
+ * @param options - Batched child-removal options
94
+ * @param input - Validated serialized child descriptions
95
+ * @returns Worker result
96
+ */
97
+ function executeBoundChildrenCleanup(options, input) {
98
+ options.beforeBoundCleanup?.();
99
+ return spawnSync(process.execPath, [
100
+ "--input-type=commonjs",
101
+ "--eval",
102
+ BOUND_CHILDREN_CLEANUP_PROGRAM,
103
+ String(options.parent.dev),
104
+ String(options.parent.ino),
105
+ ], {
106
+ cwd: options.parent.canonicalPath,
107
+ encoding: "utf8",
108
+ input,
109
+ maxBuffer: 64 * 1024,
110
+ });
111
+ }
112
+ /**
113
+ * Remove direct children through one process bound to the parent inode.
114
+ * @param options - Batched child-removal options
115
+ */
116
+ export function removeAuthorizedScratchChildren(options) {
117
+ if (new Set(options.basenames).size !== options.basenames.length) {
118
+ throw new Error("Scratch child cleanup refuses duplicate basenames");
119
+ }
120
+ const items = collectAuthorizedChildren(options);
121
+ if (items.length === 0)
122
+ return;
123
+ const input = JSON.stringify(items);
124
+ if (Buffer.byteLength(input, "utf8") > 32 * 1024 * 1024) {
125
+ throw new Error("Scratch child cleanup batch exceeds 33554432 bytes");
126
+ }
127
+ const result = executeBoundChildrenCleanup(options, input);
128
+ if (result.error !== undefined)
129
+ throw result.error;
130
+ if (result.status === BOUND_CLEANUP_IDENTITY_EXIT) {
131
+ throw new Error(result.stderr.trim() ||
132
+ "Scratch child identity changed before bound cleanup");
133
+ }
134
+ if (result.status !== 0 || result.signal !== null) {
135
+ throw new Error(`Scratch bound cleanup failed: ${result.stderr.trim() || result.signal || String(result.status)}`);
136
+ }
137
+ assertScratchPathIdentity(options.parent);
138
+ }
139
+ /**
140
+ * Remove one direct child through the batched authority.
141
+ * @param options - Single child-removal options
142
+ */
143
+ export function removeAuthorizedScratchChild(options) {
144
+ removeAuthorizedScratchChildren({
145
+ parent: options.parent,
146
+ basenames: [options.basename],
147
+ ...(options.beforeIdentityCheck === undefined
148
+ ? {}
149
+ : { beforeIdentityCheck: options.beforeIdentityCheck }),
150
+ ...(options.afterIdentityCheck === undefined
151
+ ? {}
152
+ : { afterIdentityCheck: options.afterIdentityCheck }),
153
+ });
154
+ }
155
+ //# sourceMappingURL=scratch-bound-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-cleanup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-cleanup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type ScratchNamespaceAuthority } from "./scratch-namespace-authority.js";
2
+ import { type ScratchPathIdentity } from "./scratch-owner.js";
3
+ /** Options for an authorised root removal. */
4
+ export interface RemoveAuthorizedScratchRootOptions {
5
+ readonly authority: ScratchNamespaceAuthority;
6
+ readonly basename: string;
7
+ readonly expectedToken?: string;
8
+ readonly expectedIdentity?: ScratchPathIdentity;
9
+ readonly afterIdentityCheck?: (candidate: string) => void;
10
+ }
11
+ /**
12
+ * Quarantine and delete one direct owned root.
13
+ * @param options - Authorized root-removal options
14
+ * @returns Whether an existing root was removed
15
+ */
16
+ export declare function removeAuthorizedScratchRoot(options: RemoveAuthorizedScratchRootOptions): boolean;
17
+ //# sourceMappingURL=scratch-bound-root-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-root-cleanup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-root-cleanup.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B,8CAA8C;AAC9C,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,SAAS,EAAE,yBAAyB,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IAChD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAsJD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,OAAO,CA8BT"}