@orkestrel/probe 0.0.25 → 0.0.26
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/dist/bin/main.js +1 -2
- package/dist/src/core/index.cjs +35 -33
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +9 -10
- package/dist/src/core/index.d.ts +9 -10
- package/dist/src/core/index.js +36 -34
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +172 -225
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +13 -6
- package/dist/src/server/index.d.ts +13 -6
- package/dist/src/server/index.js +173 -226
- package/dist/src/server/index.js.map +1 -1
- package/package.json +19 -18
|
@@ -7,6 +7,7 @@ let node_path = require("node:path");
|
|
|
7
7
|
let node_url = require("node:url");
|
|
8
8
|
let _orkestrel_contract = require("@orkestrel/contract");
|
|
9
9
|
let _src_core = require("../core/index.cjs");
|
|
10
|
+
let _orkestrel_process_server = require("@orkestrel/process/server");
|
|
10
11
|
let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
11
12
|
let _orkestrel_pool = require("@orkestrel/pool");
|
|
12
13
|
let _orkestrel_lsp = require("@orkestrel/lsp");
|
|
@@ -18,7 +19,7 @@ let node_async_hooks = require("node:async_hooks");
|
|
|
18
19
|
let node_child_process = require("node:child_process");
|
|
19
20
|
let _orkestrel_mcp_server = require("@orkestrel/mcp/server");
|
|
20
21
|
let _orkestrel_tool = require("@orkestrel/tool");
|
|
21
|
-
|
|
22
|
+
// src/server/errors.ts
|
|
22
23
|
/**
|
|
23
24
|
* Creates the protocol failure returned when the MCP handshake cannot arm the probe.
|
|
24
25
|
*
|
|
@@ -36,8 +37,7 @@ function createHandshakeError(error) {
|
|
|
36
37
|
code: error.code
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
-
//#region src/server/helpers.ts
|
|
40
|
+
// src/server/helpers.ts
|
|
41
41
|
/**
|
|
42
42
|
* Rewrites one path into the forward-slash spelling this package compares and reports paths in.
|
|
43
43
|
*
|
|
@@ -152,13 +152,13 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
|
|
|
152
152
|
if (!mutate) return file;
|
|
153
153
|
try {
|
|
154
154
|
const canonical = (0, node_fs.realpathSync)(root);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
descendant = (0, node_path.resolve)(
|
|
155
|
+
path.split(node_path.sep).reduce((parent, segment) => {
|
|
156
|
+
if (parent === void 0) return void 0;
|
|
157
|
+
const descendant = (0, node_path.resolve)(parent, segment);
|
|
158
158
|
const outcome = (0, _orkestrel_contract.attempt)(() => (0, node_fs.lstatSync)(descendant));
|
|
159
159
|
if (!outcome.success) {
|
|
160
160
|
const code = readFaultCode(outcome.error);
|
|
161
|
-
if (code === "ENOENT" || code === "ENOTDIR")
|
|
161
|
+
if (code === "ENOENT" || code === "ENOTDIR") return void 0;
|
|
162
162
|
throw outcome.error;
|
|
163
163
|
}
|
|
164
164
|
if (outcome.value.isSymbolicLink()) throw new _src_core.ProbeError(`Path crosses a symbolic link: ${target}`, {
|
|
@@ -166,12 +166,14 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
|
|
|
166
166
|
code: "refused",
|
|
167
167
|
context: { path: target }
|
|
168
168
|
});
|
|
169
|
-
|
|
169
|
+
const resolved = (0, node_fs.realpathSync)(descendant);
|
|
170
|
+
if (escapesRoot(canonical, resolved)) throw new _src_core.ProbeError(`Path escapes the workspace: ${target}`, {
|
|
170
171
|
origin: "claimant",
|
|
171
172
|
code: "refused",
|
|
172
173
|
context: { path: target }
|
|
173
174
|
});
|
|
174
|
-
|
|
175
|
+
return descendant;
|
|
176
|
+
}, root);
|
|
175
177
|
} catch (error) {
|
|
176
178
|
if ((0, _src_core.isProbeError)(error)) throw error;
|
|
177
179
|
const code = readFaultCode(error);
|
|
@@ -336,14 +338,11 @@ function relativeWorkspaceMessage(workspace, message) {
|
|
|
336
338
|
const root = (0, node_path.resolve)(workspace);
|
|
337
339
|
const forward = normalizePath(root);
|
|
338
340
|
const url = (0, node_url.pathToFileURL)(forward).href;
|
|
339
|
-
|
|
341
|
+
return [.../* @__PURE__ */ new Set([
|
|
340
342
|
url.endsWith("/") ? url : `${url}/`,
|
|
341
343
|
forward.endsWith("/") ? forward : `${forward}/`,
|
|
342
344
|
root.endsWith(node_path.sep) ? root : `${root}${node_path.sep}`
|
|
343
|
-
]);
|
|
344
|
-
let text = message;
|
|
345
|
-
for (const spelling of spellings) text = text.replaceAll(spelling, (match, offset, source) => /[^\s"'`()[\]{}<>,;:=|]/u.test(source.charAt(offset - 1)) ? match : "");
|
|
346
|
-
return text;
|
|
345
|
+
])].reduce((text, spelling) => text.replaceAll(spelling, (match, offset, source) => /[^\s"'`()[\]{}<>,;:=|]/u.test(source.charAt(offset - 1)) ? match : ""), message);
|
|
347
346
|
}
|
|
348
347
|
/**
|
|
349
348
|
* Scans the plain-text output of one compiler run into the diagnostics it reported.
|
|
@@ -372,37 +371,36 @@ function relativeWorkspaceMessage(workspace, message) {
|
|
|
372
371
|
* ```
|
|
373
372
|
*/
|
|
374
373
|
function scanDiagnostics(text) {
|
|
375
|
-
const
|
|
376
|
-
|
|
374
|
+
const lines = text.split(/\r\n|\n/);
|
|
375
|
+
const headers = lines.map((line, index) => {
|
|
377
376
|
const located = /^(.+)\((\d+),(\d+)\): (?:error|warning) TS\d+: (.*)$/u.exec(line);
|
|
378
377
|
if (located !== null) {
|
|
379
378
|
const point = {
|
|
380
379
|
line: Number.parseInt(located[2] ?? "1", 10) - 1,
|
|
381
380
|
character: Number.parseInt(located[3] ?? "1", 10) - 1
|
|
382
381
|
};
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
382
|
+
return {
|
|
383
|
+
index,
|
|
384
|
+
diagnostic: {
|
|
385
|
+
path: normalizePath(located[1] ?? ""),
|
|
386
|
+
range: {
|
|
387
|
+
start: point,
|
|
388
|
+
end: point
|
|
389
|
+
},
|
|
390
|
+
message: located[4] ?? ""
|
|
391
|
+
}
|
|
392
|
+
};
|
|
392
393
|
}
|
|
393
394
|
const bare = /^(?:error|warning) TS\d+: (.*)$/u.exec(line);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
const previous = diagnostics.at(-1);
|
|
399
|
-
if (previous === void 0 || !/^\s+\S/u.test(line)) continue;
|
|
400
|
-
diagnostics[diagnostics.length - 1] = {
|
|
401
|
-
...previous,
|
|
402
|
-
message: `${previous.message}\n${line}`
|
|
395
|
+
return bare === null ? void 0 : {
|
|
396
|
+
index,
|
|
397
|
+
diagnostic: { message: bare[1] ?? "" }
|
|
403
398
|
};
|
|
404
|
-
}
|
|
405
|
-
return
|
|
399
|
+
}).filter((header) => header !== void 0);
|
|
400
|
+
return headers.map((header, index) => ({
|
|
401
|
+
...header.diagnostic,
|
|
402
|
+
message: [header.diagnostic.message, ...lines.slice(header.index + 1, headers[index + 1]?.index).filter((line) => /^\s+\S/u.test(line))].join("\n")
|
|
403
|
+
}));
|
|
406
404
|
}
|
|
407
405
|
/**
|
|
408
406
|
* Resolves one installed module from the target workspace.
|
|
@@ -477,10 +475,9 @@ function loadWorkspaceVitest(workspace) {
|
|
|
477
475
|
* ```
|
|
478
476
|
*/
|
|
479
477
|
function readWorkspaceManifest(workspace, name) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
} catch (error) {
|
|
478
|
+
const resolved = (0, _orkestrel_contract.attempt)(() => resolveWorkspaceModule(workspace, `${name}/package.json`));
|
|
479
|
+
if (!resolved.success) {
|
|
480
|
+
const error = resolved.error;
|
|
484
481
|
if (!(0, _src_core.isProbeError)(error)) throw error;
|
|
485
482
|
throw new _src_core.ProbeError(`${name} does not publish a readable manifest`, {
|
|
486
483
|
origin: "workspace",
|
|
@@ -489,6 +486,7 @@ function readWorkspaceManifest(workspace, name) {
|
|
|
489
486
|
cause: error.cause
|
|
490
487
|
});
|
|
491
488
|
}
|
|
489
|
+
const path = resolved.value;
|
|
492
490
|
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.readFileSync)(path, "utf8"));
|
|
493
491
|
if (!reading.success) {
|
|
494
492
|
const error = reading.error;
|
|
@@ -504,17 +502,17 @@ function readWorkspaceManifest(workspace, name) {
|
|
|
504
502
|
cause: error
|
|
505
503
|
});
|
|
506
504
|
}
|
|
507
|
-
const
|
|
508
|
-
if (!
|
|
505
|
+
const parsed = (0, _orkestrel_contract.attempt)(() => JSON.parse(reading.value));
|
|
506
|
+
if (!parsed.success) throw new _src_core.ProbeError(`${name} does not publish a readable manifest`, {
|
|
509
507
|
origin: "workspace",
|
|
510
508
|
code: "malformed",
|
|
511
509
|
context: {
|
|
512
510
|
name,
|
|
513
511
|
path
|
|
514
512
|
},
|
|
515
|
-
cause:
|
|
513
|
+
cause: parsed.error
|
|
516
514
|
});
|
|
517
|
-
const contents =
|
|
515
|
+
const contents = parsed.value;
|
|
518
516
|
if (!(0, _orkestrel_contract.isRecord)(contents)) throw new _src_core.ProbeError(`${name} does not publish a readable manifest`, {
|
|
519
517
|
origin: "workspace",
|
|
520
518
|
code: "malformed",
|
|
@@ -685,20 +683,13 @@ function buildRevisionPath(workspace, path, revision) {
|
|
|
685
683
|
* ```
|
|
686
684
|
*/
|
|
687
685
|
function filterUniqueIssues(issues) {
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
issue.range
|
|
696
|
-
]);
|
|
697
|
-
if (seen.has(key)) continue;
|
|
698
|
-
seen.add(key);
|
|
699
|
-
distinct.push(issue);
|
|
700
|
-
}
|
|
701
|
-
return distinct;
|
|
686
|
+
const groups = Map.groupBy(issues, (issue) => JSON.stringify([
|
|
687
|
+
issue.origin,
|
|
688
|
+
issue.path,
|
|
689
|
+
issue.message,
|
|
690
|
+
issue.range
|
|
691
|
+
]));
|
|
692
|
+
return Iterator.from(groups.values()).flatMap((group) => group.slice(0, 1)).toArray();
|
|
702
693
|
}
|
|
703
694
|
/**
|
|
704
695
|
* Reports whether a path is a workspace module Vitest can cache.
|
|
@@ -716,13 +707,11 @@ function matchesWorkspaceModule(path) {
|
|
|
716
707
|
return /\.(?:[cm]?[jt]sx?|vue|json)$/.test(path);
|
|
717
708
|
}
|
|
718
709
|
/**
|
|
719
|
-
* Reports whether
|
|
710
|
+
* Reports whether a file's owning host is running or cannot be judged.
|
|
720
711
|
*
|
|
721
712
|
* @remarks
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
* than a process, so both answer true: the safe direction is to keep a file this package cannot
|
|
725
|
-
* account for.
|
|
713
|
+
* Keeps a file when its identity is invalid or `inspectProcess` reports existence, denied
|
|
714
|
+
* permission, or an indeterminate host failure. Only a confirmed absent process answers false.
|
|
726
715
|
*
|
|
727
716
|
* @param id - The process id a file's own revision names
|
|
728
717
|
* @returns True if that process is running or cannot be judged; false otherwise
|
|
@@ -735,12 +724,7 @@ function matchesWorkspaceModule(path) {
|
|
|
735
724
|
*/
|
|
736
725
|
function matchesLiveProcess(id) {
|
|
737
726
|
if (!(0, _orkestrel_contract.isNumber)(id) || !Number.isSafeInteger(id) || id <= 0) return true;
|
|
738
|
-
|
|
739
|
-
process.kill(id, 0);
|
|
740
|
-
return true;
|
|
741
|
-
} catch (error) {
|
|
742
|
-
return readFaultCode(error) === "EPERM";
|
|
743
|
-
}
|
|
727
|
+
return (0, _orkestrel_process_server.inspectProcess)(id) !== false;
|
|
744
728
|
}
|
|
745
729
|
/**
|
|
746
730
|
* Collects every regular file a target workspace holds, skipping the trees no inspection reads.
|
|
@@ -768,10 +752,8 @@ function collectWorkspaceFiles(workspace) {
|
|
|
768
752
|
while (directories.length > 0) {
|
|
769
753
|
const directory = directories.pop();
|
|
770
754
|
if (directory === void 0) break;
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
entries = (0, node_fs.readdirSync)(directory, { withFileTypes: true });
|
|
774
|
-
} catch {}
|
|
755
|
+
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.readdirSync)(directory, { withFileTypes: true }));
|
|
756
|
+
const entries = reading.success ? reading.value : [];
|
|
775
757
|
for (const entry of entries) {
|
|
776
758
|
if (entry.name === ".git" || entry.name === "dist" || entry.name === "node_modules") continue;
|
|
777
759
|
const path = (0, node_path.join)(directory, entry.name);
|
|
@@ -860,17 +842,10 @@ async function guardStage(stage, operation) {
|
|
|
860
842
|
*/
|
|
861
843
|
function findRefusedPaths(value) {
|
|
862
844
|
if (!(0, _orkestrel_contract.compileGuard)(_src_core.CLAIM_SHAPE)(value)) return [];
|
|
863
|
-
|
|
864
|
-
for (const phase of ["case", "control"]) {
|
|
845
|
+
return ["case", "control"].flatMap((phase) => {
|
|
865
846
|
const subject = value[phase];
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
for (const [member, draft] of drafts) {
|
|
869
|
-
if ((0, _src_core.isDraft)(draft)) continue;
|
|
870
|
-
members.push(`${member}.path`);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
return members;
|
|
847
|
+
return [[`${phase}.test`, subject.test], ...Iterator.from(subject.files.entries()).map(([index, file]) => [`${phase}.files.${index}`, file])].flatMap(([member, draft]) => (0, _src_core.isDraft)(draft) ? [] : [`${member}.path`]);
|
|
848
|
+
});
|
|
874
849
|
}
|
|
875
850
|
/**
|
|
876
851
|
* Rewrites every workspace-contained absolute path in a value to its workspace-relative form and
|
|
@@ -882,7 +857,8 @@ function findRefusedPaths(value) {
|
|
|
882
857
|
* its relative form keeps one commit checked out at two roots reading to one digest. A path that
|
|
883
858
|
* escapes the workspace is left as it stands, because rewriting it would name a file outside the
|
|
884
859
|
* tree by a relative spelling. Key order is the compiler's own and it differs between the supported
|
|
885
|
-
* majors, so sorting removes a difference that means nothing.
|
|
860
|
+
* majors, so sorting removes a difference that means nothing. A `__proto__` key is dropped, as
|
|
861
|
+
* the earlier assignment-built record dropped it, so a digest stays the same across releases.
|
|
886
862
|
*
|
|
887
863
|
* @param workspace - The target workspace root
|
|
888
864
|
* @param value - The value to rewrite
|
|
@@ -904,11 +880,7 @@ function normalizeValue(workspace, value) {
|
|
|
904
880
|
return normalizePath(path);
|
|
905
881
|
}
|
|
906
882
|
if ((0, _orkestrel_contract.isArray)(value)) return value.map((entry) => normalizeValue(workspace, entry));
|
|
907
|
-
if ((0, _orkestrel_contract.isRecord)(value))
|
|
908
|
-
const record = {};
|
|
909
|
-
for (const key of Object.keys(value).sort()) record[key] = normalizeValue(workspace, value[key]);
|
|
910
|
-
return record;
|
|
911
|
-
}
|
|
883
|
+
if ((0, _orkestrel_contract.isRecord)(value)) return Object.fromEntries(Object.keys(value).sort().filter((key) => key !== "__proto__").map((key) => [key, normalizeValue(workspace, value[key])]));
|
|
912
884
|
return value;
|
|
913
885
|
}
|
|
914
886
|
/**
|
|
@@ -945,9 +917,7 @@ function computeDigest(workspace, value) {
|
|
|
945
917
|
* ```
|
|
946
918
|
*/
|
|
947
919
|
function captureListeners(emitter, events) {
|
|
948
|
-
|
|
949
|
-
for (const event of events) capture.set(event, emitter.listeners(event));
|
|
950
|
-
return capture;
|
|
920
|
+
return new Map(Iterator.from(events).map((event) => [event, emitter.listeners(event)]));
|
|
951
921
|
}
|
|
952
922
|
/**
|
|
953
923
|
* Removes every listener one emitter gained for the captured events since its capture.
|
|
@@ -982,8 +952,7 @@ function releaseListeners(emitter, capture) {
|
|
|
982
952
|
emitter.removeListener(event, listener);
|
|
983
953
|
}
|
|
984
954
|
}
|
|
985
|
-
|
|
986
|
-
//#region src/server/parsers.ts
|
|
955
|
+
// src/server/parsers.ts
|
|
987
956
|
/**
|
|
988
957
|
* Parses the configuration one compiler run printed for a TypeScript project.
|
|
989
958
|
*
|
|
@@ -1038,8 +1007,7 @@ function parseRevisionOwner(revision) {
|
|
|
1038
1007
|
const owner = /^(\d+)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u.exec(revision)?.[1];
|
|
1039
1008
|
return owner === void 0 ? void 0 : Number.parseInt(owner, 10);
|
|
1040
1009
|
}
|
|
1041
|
-
|
|
1042
|
-
//#region src/server/Overlay.ts
|
|
1010
|
+
// src/server/Overlay.ts
|
|
1043
1011
|
/**
|
|
1044
1012
|
* Implements `OverlayInterface` over a private map from normalized absolute path to candidate text,
|
|
1045
1013
|
* minting at construction the `revision` a resident tool caches its answers against.
|
|
@@ -1125,15 +1093,13 @@ var Overlay = class {
|
|
|
1125
1093
|
this.#candidates.clear();
|
|
1126
1094
|
}
|
|
1127
1095
|
};
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
var version = "0.0.25";
|
|
1096
|
+
// package.json
|
|
1097
|
+
var version = "0.0.26";
|
|
1131
1098
|
var peerDependencies = {
|
|
1132
1099
|
"typescript": "^6.0.3 || ^7.0.2",
|
|
1133
1100
|
"vitest": "^4.1.12 || ^5.0.5"
|
|
1134
1101
|
};
|
|
1135
|
-
|
|
1136
|
-
//#region src/server/stages/LintStage.ts
|
|
1102
|
+
// src/server/stages/LintStage.ts
|
|
1137
1103
|
/**
|
|
1138
1104
|
* Inspects virtual documents through one resident Oxlint language server.
|
|
1139
1105
|
*
|
|
@@ -1346,8 +1312,7 @@ var LintStage = class {
|
|
|
1346
1312
|
}));
|
|
1347
1313
|
}
|
|
1348
1314
|
};
|
|
1349
|
-
|
|
1350
|
-
//#region src/server/stages/RuntimeStage.ts
|
|
1315
|
+
// src/server/stages/RuntimeStage.ts
|
|
1351
1316
|
/**
|
|
1352
1317
|
* Inspects tests through one resident Vitest service from the target workspace.
|
|
1353
1318
|
*
|
|
@@ -1510,14 +1475,13 @@ var RuntimeStage = class {
|
|
|
1510
1475
|
const file = node_path.sep === "\\" ? normalizePath(specification) : specification;
|
|
1511
1476
|
this.#specifications += 1;
|
|
1512
1477
|
this.#revisions.add(file);
|
|
1513
|
-
|
|
1514
|
-
let cleanup = [];
|
|
1478
|
+
const issues = [];
|
|
1515
1479
|
const task = project.createSpecification(file, void 0, "threads");
|
|
1516
1480
|
this.#progress += 1;
|
|
1517
1481
|
try {
|
|
1518
1482
|
try {
|
|
1519
1483
|
const result = await vitest.runTestSpecifications([task], false);
|
|
1520
|
-
issues
|
|
1484
|
+
issues.push(...this.#issues(result, file, subject.test.path), ...this.#misses(project, file));
|
|
1521
1485
|
} catch (error) {
|
|
1522
1486
|
if ((0, _src_core.isProbeError)(error)) throw error;
|
|
1523
1487
|
throw new _src_core.ProbeError(`The runtime stage could not run the generated specification (${describeUnknown(error)})`, {
|
|
@@ -1534,22 +1498,22 @@ var RuntimeStage = class {
|
|
|
1534
1498
|
this.#progress -= 1;
|
|
1535
1499
|
process.exitCode = exitCode;
|
|
1536
1500
|
this.#revisions.delete(file);
|
|
1537
|
-
|
|
1501
|
+
issues.push(...await this.#evict(vitest, file));
|
|
1538
1502
|
try {
|
|
1539
1503
|
const contained = resolveWorkspaceFile(this.#workspace, (0, node_path.relative)(this.#workspace, file), true);
|
|
1540
1504
|
if ((0, node_fs.existsSync)(contained)) (0, node_fs.unlinkSync)(contained);
|
|
1541
1505
|
} catch (error) {
|
|
1542
|
-
|
|
1506
|
+
issues.push({
|
|
1543
1507
|
origin: (0, _src_core.isProbeError)(error) && error.origin === "workspace" ? "workspace" : "instrument",
|
|
1544
1508
|
path: relativeWorkspaceFile(this.#workspace, file),
|
|
1545
1509
|
message: `The runtime stage could not delete the generated specification (${describeUnknown(error)})`
|
|
1546
|
-
}
|
|
1510
|
+
});
|
|
1547
1511
|
}
|
|
1548
1512
|
}
|
|
1549
1513
|
return {
|
|
1550
1514
|
stage: this.stage,
|
|
1551
1515
|
elapsed: Math.round(performance.now() - started),
|
|
1552
|
-
issues
|
|
1516
|
+
issues
|
|
1553
1517
|
};
|
|
1554
1518
|
} finally {
|
|
1555
1519
|
this.#loads.clear();
|
|
@@ -1676,47 +1640,52 @@ var RuntimeStage = class {
|
|
|
1676
1640
|
return text;
|
|
1677
1641
|
}
|
|
1678
1642
|
#specification(test) {
|
|
1679
|
-
|
|
1680
|
-
let generated;
|
|
1681
|
-
const outcome = (0, _orkestrel_contract.attempt)(() => {
|
|
1643
|
+
const location = (0, _orkestrel_contract.attempt)(() => {
|
|
1682
1644
|
const revision = `${process.pid}-${(0, node_crypto.randomUUID)()}`;
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1645
|
+
return {
|
|
1646
|
+
revision,
|
|
1647
|
+
file: buildRevisionPath(this.#workspace, test.path, revision)
|
|
1648
|
+
};
|
|
1649
|
+
});
|
|
1650
|
+
if (!location.success) return this.#specificationFault(test, location.error);
|
|
1651
|
+
const { revision, file } = location.value;
|
|
1652
|
+
const target = (0, node_path.relative)(this.#workspace, file);
|
|
1653
|
+
const containment = (0, _orkestrel_contract.attempt)(() => resolveWorkspaceFile(this.#workspace, target, true));
|
|
1654
|
+
if (!containment.success) return this.#specificationFault(test, containment.error, file);
|
|
1655
|
+
const directory = (0, _orkestrel_contract.attempt)(() => (0, node_fs.mkdirSync)((0, node_path.dirname)(file), { recursive: true }));
|
|
1656
|
+
if (!directory.success) return this.#specificationFault(test, directory.error, file, true);
|
|
1657
|
+
const written = (0, _orkestrel_contract.attempt)(() => {
|
|
1690
1658
|
resolveWorkspaceFile(this.#workspace, target, true);
|
|
1691
1659
|
(0, node_fs.writeFileSync)(file, (0, _src_core.formatSpecification)(test.text, revision), {
|
|
1692
1660
|
encoding: "utf8",
|
|
1693
1661
|
flag: "wx"
|
|
1694
1662
|
});
|
|
1695
|
-
return file;
|
|
1696
1663
|
});
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1664
|
+
return written.success ? file : this.#specificationFault(test, written.error, file);
|
|
1665
|
+
}
|
|
1666
|
+
#specificationFault(test, error, generated, directory = false) {
|
|
1667
|
+
if ((0, _src_core.isProbeError)(error) && error.origin === "claimant") throw new _src_core.ProbeError(error.message, {
|
|
1668
|
+
origin: error.origin,
|
|
1669
|
+
code: error.code,
|
|
1701
1670
|
context: {
|
|
1702
|
-
...
|
|
1671
|
+
...error.context,
|
|
1703
1672
|
path: test.path
|
|
1704
1673
|
},
|
|
1705
|
-
cause:
|
|
1674
|
+
cause: error.cause
|
|
1706
1675
|
});
|
|
1707
|
-
if (!
|
|
1676
|
+
if (!directory && generated !== void 0 && isRefusedName(generated, error)) throw new _src_core.ProbeError(`The runtime stage cannot write a specification beside a test path the host refuses (${describeUnknown(error)})`, {
|
|
1708
1677
|
origin: "claimant",
|
|
1709
1678
|
code: "refused",
|
|
1710
1679
|
context: {
|
|
1711
1680
|
stage: this.stage,
|
|
1712
1681
|
path: test.path
|
|
1713
1682
|
},
|
|
1714
|
-
cause:
|
|
1683
|
+
cause: error
|
|
1715
1684
|
});
|
|
1716
1685
|
return {
|
|
1717
|
-
origin: (0, _src_core.isProbeError)(
|
|
1686
|
+
origin: (0, _src_core.isProbeError)(error) && error.origin === "workspace" || directory ? "workspace" : "instrument",
|
|
1718
1687
|
path: test.path,
|
|
1719
|
-
message: `The runtime stage could not write the generated specification (${describeUnknown(
|
|
1688
|
+
message: `The runtime stage could not write the generated specification (${describeUnknown(error)})`
|
|
1720
1689
|
};
|
|
1721
1690
|
}
|
|
1722
1691
|
#project(vitest, path) {
|
|
@@ -1747,44 +1716,35 @@ var RuntimeStage = class {
|
|
|
1747
1716
|
}
|
|
1748
1717
|
#misses(project, specification) {
|
|
1749
1718
|
const file = normalizePath(specification);
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
let served = false;
|
|
1754
|
-
let loaded = false;
|
|
1755
|
-
for (const environment of Object.values(project.vite.environments)) {
|
|
1719
|
+
return this.#overlay.paths.flatMap((path) => {
|
|
1720
|
+
if (this.#reads.has(path)) return [];
|
|
1721
|
+
const served = Object.values(project.vite.environments).flatMap((environment) => {
|
|
1756
1722
|
const modules = environment.moduleGraph.getModulesByFile(path);
|
|
1757
|
-
if (modules === void 0)
|
|
1758
|
-
|
|
1723
|
+
if (modules === void 0) return [];
|
|
1724
|
+
return Iterator.from(modules).flatMap((module) => {
|
|
1759
1725
|
const pending = [...module.importers];
|
|
1760
1726
|
const seen = new Set(pending);
|
|
1761
|
-
let reaches = false;
|
|
1762
1727
|
while (pending.length > 0) {
|
|
1763
1728
|
const importer = pending.pop();
|
|
1764
1729
|
if (importer === void 0) break;
|
|
1765
|
-
if (importer.file !== null && normalizePath(importer.file) === file)
|
|
1766
|
-
reaches = true;
|
|
1767
|
-
break;
|
|
1768
|
-
}
|
|
1730
|
+
if (importer.file !== null && normalizePath(importer.file) === file) return [module.id !== null && this.#loads.has(normalizePath(module.id))];
|
|
1769
1731
|
for (const parent of importer.importers) {
|
|
1770
1732
|
if (seen.has(parent)) continue;
|
|
1771
1733
|
seen.add(parent);
|
|
1772
1734
|
pending.push(parent);
|
|
1773
1735
|
}
|
|
1774
1736
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
issues.push({
|
|
1737
|
+
return [];
|
|
1738
|
+
}).toArray();
|
|
1739
|
+
});
|
|
1740
|
+
if (served.length === 0) return [];
|
|
1741
|
+
const loaded = served.some((value) => value);
|
|
1742
|
+
return [{
|
|
1782
1743
|
origin: loaded ? "instrument" : "workspace",
|
|
1783
1744
|
path: relativeWorkspaceFile(this.#workspace, path),
|
|
1784
1745
|
message: loaded ? "The runtime overlay did not resolve this module" : "The workspace configuration served this module before the runtime overlay"
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
return issues;
|
|
1746
|
+
}];
|
|
1747
|
+
});
|
|
1788
1748
|
}
|
|
1789
1749
|
async #evict(vitest, file) {
|
|
1790
1750
|
try {
|
|
@@ -2026,8 +1986,7 @@ var RuntimeStage = class {
|
|
|
2026
1986
|
};
|
|
2027
1987
|
}
|
|
2028
1988
|
};
|
|
2029
|
-
|
|
2030
|
-
//#region src/server/stages/TypeStage.ts
|
|
1989
|
+
// src/server/stages/TypeStage.ts
|
|
2031
1990
|
/**
|
|
2032
1991
|
* Inspects TypeScript source by running the target workspace's own compiler over a mirror of it.
|
|
2033
1992
|
*
|
|
@@ -2136,6 +2095,14 @@ var TypeStage = class {
|
|
|
2136
2095
|
resolve(project) {
|
|
2137
2096
|
return guardStage(this.stage, this.#resolve(project));
|
|
2138
2097
|
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Tears down the compiler processes and releases the mirror without awaiting inspections.
|
|
2100
|
+
*
|
|
2101
|
+
* @remarks
|
|
2102
|
+
* On Windows, awaits each tree termination attempt before removing the mirror. Each attempt
|
|
2103
|
+
* uses `PROBE_DEADLINE`, the existing default inspection budget, as its utility timeout.
|
|
2104
|
+
* On POSIX, sends `SIGTERM` to each compiler. Warming settles before the mirror is removed.
|
|
2105
|
+
*/
|
|
2139
2106
|
destroy() {
|
|
2140
2107
|
if (this.#closing !== void 0) return this.#closing;
|
|
2141
2108
|
this.#closing = guardStage(this.stage, this.#destroy());
|
|
@@ -2198,7 +2165,7 @@ var TypeStage = class {
|
|
|
2198
2165
|
async #destroy() {
|
|
2199
2166
|
const children = [...this.#children];
|
|
2200
2167
|
this.#children.clear();
|
|
2201
|
-
|
|
2168
|
+
await Promise.all(children.map((child) => this.#terminate(child)));
|
|
2202
2169
|
await this.#warming?.catch(() => void 0);
|
|
2203
2170
|
try {
|
|
2204
2171
|
(0, node_fs.rmSync)(this.#mirror, {
|
|
@@ -2222,18 +2189,11 @@ var TypeStage = class {
|
|
|
2222
2189
|
await Promise.all(this.#projects().map((project) => this.#check(project, [])));
|
|
2223
2190
|
}
|
|
2224
2191
|
#projects() {
|
|
2225
|
-
|
|
2226
|
-
for (const axis of ["src", "app"]) {
|
|
2192
|
+
return ["tsconfig.json", ...["src", "app"].flatMap((axis) => {
|
|
2227
2193
|
const directory = resolveWorkspaceFile(this.#workspace, `configs/${axis}`);
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
} catch {
|
|
2232
|
-
continue;
|
|
2233
|
-
}
|
|
2234
|
-
for (const name of names.filter((entry) => /^tsconfig\.[^.]+\.json$/.test(entry)).sort()) projects.push(`configs/${axis}/${name}`);
|
|
2235
|
-
}
|
|
2236
|
-
return projects;
|
|
2194
|
+
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.readdirSync)(directory));
|
|
2195
|
+
return reading.success ? reading.value.filter((entry) => /^tsconfig\.[^.]+\.json$/.test(entry)).sort().map((name) => `configs/${axis}/${name}`) : [];
|
|
2196
|
+
})];
|
|
2237
2197
|
}
|
|
2238
2198
|
#refuseDestroyed() {
|
|
2239
2199
|
if (this.#closing !== void 0) throw (0, _src_core.createDestroyedError)("type stage");
|
|
@@ -2248,13 +2208,9 @@ var TypeStage = class {
|
|
|
2248
2208
|
}
|
|
2249
2209
|
#sweep() {
|
|
2250
2210
|
const root = resolveWorkspaceFile(this.#workspace, _src_core.TYPE_MIRROR);
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
} catch {
|
|
2255
|
-
return;
|
|
2256
|
-
}
|
|
2257
|
-
for (const entry of entries) {
|
|
2211
|
+
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.readdirSync)(root, { withFileTypes: true }));
|
|
2212
|
+
if (!reading.success) return;
|
|
2213
|
+
for (const entry of reading.value) {
|
|
2258
2214
|
if (!entry.isDirectory()) continue;
|
|
2259
2215
|
const owner = parseRevisionOwner(entry.name);
|
|
2260
2216
|
if (owner === void 0 || matchesLiveProcess(owner)) continue;
|
|
@@ -2308,12 +2264,7 @@ var TypeStage = class {
|
|
|
2308
2264
|
return contained;
|
|
2309
2265
|
}
|
|
2310
2266
|
#displace(target) {
|
|
2311
|
-
const ancestors =
|
|
2312
|
-
let directory = (0, node_path.dirname)(target);
|
|
2313
|
-
while (directory !== this.#mirror && directory !== (0, node_path.dirname)(directory)) {
|
|
2314
|
-
ancestors.unshift(directory);
|
|
2315
|
-
directory = (0, node_path.dirname)(directory);
|
|
2316
|
-
}
|
|
2267
|
+
const ancestors = (0, node_path.relative)(this.#mirror, target).split(node_path.sep).slice(0, -1).map((_segment, index, segments) => (0, node_path.join)(this.#mirror, ...segments.slice(0, index + 1)));
|
|
2317
2268
|
for (const ancestor of [...ancestors, target]) {
|
|
2318
2269
|
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.lstatSync)(ancestor));
|
|
2319
2270
|
if (!reading.success) continue;
|
|
@@ -2449,40 +2400,43 @@ var TypeStage = class {
|
|
|
2449
2400
|
}
|
|
2450
2401
|
});
|
|
2451
2402
|
}
|
|
2452
|
-
#spawn(args, cwd) {
|
|
2403
|
+
async #spawn(args, cwd) {
|
|
2453
2404
|
this.#refuseDestroyed();
|
|
2454
2405
|
const compiler = this.#compiler;
|
|
2455
2406
|
if (compiler === void 0) throw (0, _src_core.createDestroyedError)("type stage");
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
context: { stage: this.stage }
|
|
2473
|
-
}));
|
|
2474
|
-
return;
|
|
2407
|
+
const child = (0, node_child_process.spawn)(process.execPath, [compiler, ...args], {
|
|
2408
|
+
cwd,
|
|
2409
|
+
stdio: [
|
|
2410
|
+
"ignore",
|
|
2411
|
+
"pipe",
|
|
2412
|
+
"pipe"
|
|
2413
|
+
]
|
|
2414
|
+
});
|
|
2415
|
+
this.#children.add(child);
|
|
2416
|
+
const output = child.stdout;
|
|
2417
|
+
const errors = child.stderr;
|
|
2418
|
+
if (output === null || errors === null) {
|
|
2419
|
+
try {
|
|
2420
|
+
await this.#terminate(child);
|
|
2421
|
+
} finally {
|
|
2422
|
+
this.#children.delete(child);
|
|
2475
2423
|
}
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2424
|
+
throw new _src_core.ProbeError("The compiler was spawned without its own output streams", {
|
|
2425
|
+
origin: "instrument",
|
|
2426
|
+
code: "malformed",
|
|
2427
|
+
context: { stage: this.stage }
|
|
2428
|
+
});
|
|
2429
|
+
}
|
|
2430
|
+
return new Promise((settle, refuse) => {
|
|
2431
|
+
const stdout = [];
|
|
2432
|
+
const stderr = [];
|
|
2479
2433
|
output.setEncoding("utf8");
|
|
2480
2434
|
errors.setEncoding("utf8");
|
|
2481
2435
|
output.on("data", (chunk) => {
|
|
2482
|
-
stdout
|
|
2436
|
+
stdout.push(chunk);
|
|
2483
2437
|
});
|
|
2484
2438
|
errors.on("data", (chunk) => {
|
|
2485
|
-
stderr
|
|
2439
|
+
stderr.push(chunk);
|
|
2486
2440
|
});
|
|
2487
2441
|
child.on("error", (error) => {
|
|
2488
2442
|
this.#children.delete(child);
|
|
@@ -2500,28 +2454,22 @@ var TypeStage = class {
|
|
|
2500
2454
|
this.#children.delete(child);
|
|
2501
2455
|
settle({
|
|
2502
2456
|
...code === null ? {} : { status: code },
|
|
2503
|
-
stdout,
|
|
2504
|
-
stderr
|
|
2457
|
+
stdout: stdout.join(""),
|
|
2458
|
+
stderr: stderr.join("")
|
|
2505
2459
|
});
|
|
2506
2460
|
});
|
|
2507
2461
|
});
|
|
2508
2462
|
}
|
|
2509
|
-
#terminate(child) {
|
|
2463
|
+
async #terminate(child) {
|
|
2510
2464
|
const id = child.pid;
|
|
2511
2465
|
if (process.platform === "win32" && id !== void 0) {
|
|
2512
|
-
(0,
|
|
2513
|
-
"/pid",
|
|
2514
|
-
String(id),
|
|
2515
|
-
"/t",
|
|
2516
|
-
"/f"
|
|
2517
|
-
], { stdio: "ignore" });
|
|
2466
|
+
await (0, _orkestrel_process_server.killTree)(id, _src_core.PROBE_DEADLINE);
|
|
2518
2467
|
return;
|
|
2519
2468
|
}
|
|
2520
2469
|
child.kill("SIGTERM");
|
|
2521
2470
|
}
|
|
2522
2471
|
};
|
|
2523
|
-
|
|
2524
|
-
//#region src/server/Probe.ts
|
|
2472
|
+
// src/server/Probe.ts
|
|
2525
2473
|
/**
|
|
2526
2474
|
* Answers claims through its type, lint, and runtime stages.
|
|
2527
2475
|
*
|
|
@@ -2711,11 +2659,12 @@ var Probe = class {
|
|
|
2711
2659
|
return this.#starting;
|
|
2712
2660
|
}
|
|
2713
2661
|
async #start(filling, warming) {
|
|
2714
|
-
|
|
2715
|
-
try {
|
|
2662
|
+
const preparation = (0, _orkestrel_contract.attempt)(() => {
|
|
2716
2663
|
this.#support();
|
|
2717
|
-
|
|
2718
|
-
}
|
|
2664
|
+
return this.#arming === void 0 && this.#workbench();
|
|
2665
|
+
});
|
|
2666
|
+
if (!preparation.success) {
|
|
2667
|
+
const error = preparation.error;
|
|
2719
2668
|
this.#surface(error);
|
|
2720
2669
|
throw error;
|
|
2721
2670
|
}
|
|
@@ -2726,7 +2675,7 @@ var Probe = class {
|
|
|
2726
2675
|
throw this.#refuseArm(error);
|
|
2727
2676
|
}
|
|
2728
2677
|
if (this.#arming === void 0) {
|
|
2729
|
-
this.#arming = this.#arm(warming,
|
|
2678
|
+
this.#arming = this.#arm(warming, preparation.value);
|
|
2730
2679
|
this.#arming.catch(() => {
|
|
2731
2680
|
if (this.#refusal === void 0) this.#arming = void 0;
|
|
2732
2681
|
});
|
|
@@ -3064,8 +3013,7 @@ var Probe = class {
|
|
|
3064
3013
|
});
|
|
3065
3014
|
}
|
|
3066
3015
|
};
|
|
3067
|
-
|
|
3068
|
-
//#region src/server/ProbeServer.ts
|
|
3016
|
+
// src/server/ProbeServer.ts
|
|
3069
3017
|
/**
|
|
3070
3018
|
* Implements `ProbeServerInterface` over a `PassThrough` stream this server owns, binding the
|
|
3071
3019
|
* published `prove` tool and the dual-era dispatcher to this process's Model Context Protocol
|
|
@@ -3304,7 +3252,6 @@ var ProbeServer = class {
|
|
|
3304
3252
|
] : [rendered, minimal]).find((arm) => (0, _orkestrel_mcp.isBoundedJSON)(arm, bounds)) ?? minimal;
|
|
3305
3253
|
}
|
|
3306
3254
|
};
|
|
3307
|
-
//#endregion
|
|
3308
3255
|
exports.LintStage = LintStage;
|
|
3309
3256
|
exports.Overlay = Overlay;
|
|
3310
3257
|
exports.Probe = Probe;
|