@orkestrel/probe 0.0.24 → 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 +245 -260
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +31 -7
- package/dist/src/server/index.d.ts +31 -7
- package/dist/src/server/index.js +247 -262
- package/dist/src/server/index.js.map +1 -1
- package/package.json +23 -22
|
@@ -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");
|
|
@@ -14,10 +15,11 @@ let node_events = require("node:events");
|
|
|
14
15
|
let _orkestrel_timeout = require("@orkestrel/timeout");
|
|
15
16
|
let _orkestrel_lsp_server = require("@orkestrel/lsp/server");
|
|
16
17
|
let node_stream = require("node:stream");
|
|
18
|
+
let node_async_hooks = require("node:async_hooks");
|
|
17
19
|
let node_child_process = require("node:child_process");
|
|
18
20
|
let _orkestrel_mcp_server = require("@orkestrel/mcp/server");
|
|
19
21
|
let _orkestrel_tool = require("@orkestrel/tool");
|
|
20
|
-
|
|
22
|
+
// src/server/errors.ts
|
|
21
23
|
/**
|
|
22
24
|
* Creates the protocol failure returned when the MCP handshake cannot arm the probe.
|
|
23
25
|
*
|
|
@@ -35,8 +37,7 @@ function createHandshakeError(error) {
|
|
|
35
37
|
code: error.code
|
|
36
38
|
});
|
|
37
39
|
}
|
|
38
|
-
|
|
39
|
-
//#region src/server/helpers.ts
|
|
40
|
+
// src/server/helpers.ts
|
|
40
41
|
/**
|
|
41
42
|
* Rewrites one path into the forward-slash spelling this package compares and reports paths in.
|
|
42
43
|
*
|
|
@@ -151,13 +152,13 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
|
|
|
151
152
|
if (!mutate) return file;
|
|
152
153
|
try {
|
|
153
154
|
const canonical = (0, node_fs.realpathSync)(root);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
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);
|
|
157
158
|
const outcome = (0, _orkestrel_contract.attempt)(() => (0, node_fs.lstatSync)(descendant));
|
|
158
159
|
if (!outcome.success) {
|
|
159
160
|
const code = readFaultCode(outcome.error);
|
|
160
|
-
if (code === "ENOENT" || code === "ENOTDIR")
|
|
161
|
+
if (code === "ENOENT" || code === "ENOTDIR") return void 0;
|
|
161
162
|
throw outcome.error;
|
|
162
163
|
}
|
|
163
164
|
if (outcome.value.isSymbolicLink()) throw new _src_core.ProbeError(`Path crosses a symbolic link: ${target}`, {
|
|
@@ -165,12 +166,14 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
|
|
|
165
166
|
code: "refused",
|
|
166
167
|
context: { path: target }
|
|
167
168
|
});
|
|
168
|
-
|
|
169
|
+
const resolved = (0, node_fs.realpathSync)(descendant);
|
|
170
|
+
if (escapesRoot(canonical, resolved)) throw new _src_core.ProbeError(`Path escapes the workspace: ${target}`, {
|
|
169
171
|
origin: "claimant",
|
|
170
172
|
code: "refused",
|
|
171
173
|
context: { path: target }
|
|
172
174
|
});
|
|
173
|
-
|
|
175
|
+
return descendant;
|
|
176
|
+
}, root);
|
|
174
177
|
} catch (error) {
|
|
175
178
|
if ((0, _src_core.isProbeError)(error)) throw error;
|
|
176
179
|
const code = readFaultCode(error);
|
|
@@ -335,14 +338,11 @@ function relativeWorkspaceMessage(workspace, message) {
|
|
|
335
338
|
const root = (0, node_path.resolve)(workspace);
|
|
336
339
|
const forward = normalizePath(root);
|
|
337
340
|
const url = (0, node_url.pathToFileURL)(forward).href;
|
|
338
|
-
|
|
341
|
+
return [.../* @__PURE__ */ new Set([
|
|
339
342
|
url.endsWith("/") ? url : `${url}/`,
|
|
340
343
|
forward.endsWith("/") ? forward : `${forward}/`,
|
|
341
344
|
root.endsWith(node_path.sep) ? root : `${root}${node_path.sep}`
|
|
342
|
-
]);
|
|
343
|
-
let text = message;
|
|
344
|
-
for (const spelling of spellings) text = text.replaceAll(spelling, (match, offset, source) => /[^\s"'`()[\]{}<>,;:=|]/u.test(source.charAt(offset - 1)) ? match : "");
|
|
345
|
-
return text;
|
|
345
|
+
])].reduce((text, spelling) => text.replaceAll(spelling, (match, offset, source) => /[^\s"'`()[\]{}<>,;:=|]/u.test(source.charAt(offset - 1)) ? match : ""), message);
|
|
346
346
|
}
|
|
347
347
|
/**
|
|
348
348
|
* Scans the plain-text output of one compiler run into the diagnostics it reported.
|
|
@@ -371,37 +371,36 @@ function relativeWorkspaceMessage(workspace, message) {
|
|
|
371
371
|
* ```
|
|
372
372
|
*/
|
|
373
373
|
function scanDiagnostics(text) {
|
|
374
|
-
const
|
|
375
|
-
|
|
374
|
+
const lines = text.split(/\r\n|\n/);
|
|
375
|
+
const headers = lines.map((line, index) => {
|
|
376
376
|
const located = /^(.+)\((\d+),(\d+)\): (?:error|warning) TS\d+: (.*)$/u.exec(line);
|
|
377
377
|
if (located !== null) {
|
|
378
378
|
const point = {
|
|
379
379
|
line: Number.parseInt(located[2] ?? "1", 10) - 1,
|
|
380
380
|
character: Number.parseInt(located[3] ?? "1", 10) - 1
|
|
381
381
|
};
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
+
};
|
|
391
393
|
}
|
|
392
394
|
const bare = /^(?:error|warning) TS\d+: (.*)$/u.exec(line);
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
const previous = diagnostics.at(-1);
|
|
398
|
-
if (previous === void 0 || !/^\s+\S/u.test(line)) continue;
|
|
399
|
-
diagnostics[diagnostics.length - 1] = {
|
|
400
|
-
...previous,
|
|
401
|
-
message: `${previous.message}\n${line}`
|
|
395
|
+
return bare === null ? void 0 : {
|
|
396
|
+
index,
|
|
397
|
+
diagnostic: { message: bare[1] ?? "" }
|
|
402
398
|
};
|
|
403
|
-
}
|
|
404
|
-
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
|
+
}));
|
|
405
404
|
}
|
|
406
405
|
/**
|
|
407
406
|
* Resolves one installed module from the target workspace.
|
|
@@ -476,10 +475,9 @@ function loadWorkspaceVitest(workspace) {
|
|
|
476
475
|
* ```
|
|
477
476
|
*/
|
|
478
477
|
function readWorkspaceManifest(workspace, name) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
} catch (error) {
|
|
478
|
+
const resolved = (0, _orkestrel_contract.attempt)(() => resolveWorkspaceModule(workspace, `${name}/package.json`));
|
|
479
|
+
if (!resolved.success) {
|
|
480
|
+
const error = resolved.error;
|
|
483
481
|
if (!(0, _src_core.isProbeError)(error)) throw error;
|
|
484
482
|
throw new _src_core.ProbeError(`${name} does not publish a readable manifest`, {
|
|
485
483
|
origin: "workspace",
|
|
@@ -488,6 +486,7 @@ function readWorkspaceManifest(workspace, name) {
|
|
|
488
486
|
cause: error.cause
|
|
489
487
|
});
|
|
490
488
|
}
|
|
489
|
+
const path = resolved.value;
|
|
491
490
|
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.readFileSync)(path, "utf8"));
|
|
492
491
|
if (!reading.success) {
|
|
493
492
|
const error = reading.error;
|
|
@@ -503,17 +502,17 @@ function readWorkspaceManifest(workspace, name) {
|
|
|
503
502
|
cause: error
|
|
504
503
|
});
|
|
505
504
|
}
|
|
506
|
-
const
|
|
507
|
-
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`, {
|
|
508
507
|
origin: "workspace",
|
|
509
508
|
code: "malformed",
|
|
510
509
|
context: {
|
|
511
510
|
name,
|
|
512
511
|
path
|
|
513
512
|
},
|
|
514
|
-
cause:
|
|
513
|
+
cause: parsed.error
|
|
515
514
|
});
|
|
516
|
-
const contents =
|
|
515
|
+
const contents = parsed.value;
|
|
517
516
|
if (!(0, _orkestrel_contract.isRecord)(contents)) throw new _src_core.ProbeError(`${name} does not publish a readable manifest`, {
|
|
518
517
|
origin: "workspace",
|
|
519
518
|
code: "malformed",
|
|
@@ -533,6 +532,8 @@ function readWorkspaceManifest(workspace, name) {
|
|
|
533
532
|
* @remarks
|
|
534
533
|
* A package publishes its binaries under keys of its own choosing, and that key is often not the
|
|
535
534
|
* package name: `typescript` publishes `tsc`. Name the key as `command` wherever the two differ.
|
|
535
|
+
* Resolution reads `package.json` and its `bin` field, so TypeScript 6 and 7 need no exported
|
|
536
|
+
* `typescript/bin/tsc` subpath.
|
|
536
537
|
* Default: the package name.
|
|
537
538
|
*
|
|
538
539
|
* @param workspace - The target workspace root
|
|
@@ -682,20 +683,13 @@ function buildRevisionPath(workspace, path, revision) {
|
|
|
682
683
|
* ```
|
|
683
684
|
*/
|
|
684
685
|
function filterUniqueIssues(issues) {
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
issue.range
|
|
693
|
-
]);
|
|
694
|
-
if (seen.has(key)) continue;
|
|
695
|
-
seen.add(key);
|
|
696
|
-
distinct.push(issue);
|
|
697
|
-
}
|
|
698
|
-
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();
|
|
699
693
|
}
|
|
700
694
|
/**
|
|
701
695
|
* Reports whether a path is a workspace module Vitest can cache.
|
|
@@ -713,13 +707,11 @@ function matchesWorkspaceModule(path) {
|
|
|
713
707
|
return /\.(?:[cm]?[jt]sx?|vue|json)$/.test(path);
|
|
714
708
|
}
|
|
715
709
|
/**
|
|
716
|
-
* Reports whether
|
|
710
|
+
* Reports whether a file's owning host is running or cannot be judged.
|
|
717
711
|
*
|
|
718
712
|
* @remarks
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
* than a process, so both answer true: the safe direction is to keep a file this package cannot
|
|
722
|
-
* 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.
|
|
723
715
|
*
|
|
724
716
|
* @param id - The process id a file's own revision names
|
|
725
717
|
* @returns True if that process is running or cannot be judged; false otherwise
|
|
@@ -732,12 +724,7 @@ function matchesWorkspaceModule(path) {
|
|
|
732
724
|
*/
|
|
733
725
|
function matchesLiveProcess(id) {
|
|
734
726
|
if (!(0, _orkestrel_contract.isNumber)(id) || !Number.isSafeInteger(id) || id <= 0) return true;
|
|
735
|
-
|
|
736
|
-
process.kill(id, 0);
|
|
737
|
-
return true;
|
|
738
|
-
} catch (error) {
|
|
739
|
-
return readFaultCode(error) === "EPERM";
|
|
740
|
-
}
|
|
727
|
+
return (0, _orkestrel_process_server.inspectProcess)(id) !== false;
|
|
741
728
|
}
|
|
742
729
|
/**
|
|
743
730
|
* Collects every regular file a target workspace holds, skipping the trees no inspection reads.
|
|
@@ -765,10 +752,8 @@ function collectWorkspaceFiles(workspace) {
|
|
|
765
752
|
while (directories.length > 0) {
|
|
766
753
|
const directory = directories.pop();
|
|
767
754
|
if (directory === void 0) break;
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
entries = (0, node_fs.readdirSync)(directory, { withFileTypes: true });
|
|
771
|
-
} catch {}
|
|
755
|
+
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.readdirSync)(directory, { withFileTypes: true }));
|
|
756
|
+
const entries = reading.success ? reading.value : [];
|
|
772
757
|
for (const entry of entries) {
|
|
773
758
|
if (entry.name === ".git" || entry.name === "dist" || entry.name === "node_modules") continue;
|
|
774
759
|
const path = (0, node_path.join)(directory, entry.name);
|
|
@@ -857,17 +842,10 @@ async function guardStage(stage, operation) {
|
|
|
857
842
|
*/
|
|
858
843
|
function findRefusedPaths(value) {
|
|
859
844
|
if (!(0, _orkestrel_contract.compileGuard)(_src_core.CLAIM_SHAPE)(value)) return [];
|
|
860
|
-
|
|
861
|
-
for (const phase of ["case", "control"]) {
|
|
845
|
+
return ["case", "control"].flatMap((phase) => {
|
|
862
846
|
const subject = value[phase];
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
for (const [member, draft] of drafts) {
|
|
866
|
-
if ((0, _src_core.isDraft)(draft)) continue;
|
|
867
|
-
members.push(`${member}.path`);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
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
|
+
});
|
|
871
849
|
}
|
|
872
850
|
/**
|
|
873
851
|
* Rewrites every workspace-contained absolute path in a value to its workspace-relative form and
|
|
@@ -879,7 +857,8 @@ function findRefusedPaths(value) {
|
|
|
879
857
|
* its relative form keeps one commit checked out at two roots reading to one digest. A path that
|
|
880
858
|
* escapes the workspace is left as it stands, because rewriting it would name a file outside the
|
|
881
859
|
* tree by a relative spelling. Key order is the compiler's own and it differs between the supported
|
|
882
|
-
* 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.
|
|
883
862
|
*
|
|
884
863
|
* @param workspace - The target workspace root
|
|
885
864
|
* @param value - The value to rewrite
|
|
@@ -901,11 +880,7 @@ function normalizeValue(workspace, value) {
|
|
|
901
880
|
return normalizePath(path);
|
|
902
881
|
}
|
|
903
882
|
if ((0, _orkestrel_contract.isArray)(value)) return value.map((entry) => normalizeValue(workspace, entry));
|
|
904
|
-
if ((0, _orkestrel_contract.isRecord)(value))
|
|
905
|
-
const record = {};
|
|
906
|
-
for (const key of Object.keys(value).sort()) record[key] = normalizeValue(workspace, value[key]);
|
|
907
|
-
return record;
|
|
908
|
-
}
|
|
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])]));
|
|
909
884
|
return value;
|
|
910
885
|
}
|
|
911
886
|
/**
|
|
@@ -942,9 +917,7 @@ function computeDigest(workspace, value) {
|
|
|
942
917
|
* ```
|
|
943
918
|
*/
|
|
944
919
|
function captureListeners(emitter, events) {
|
|
945
|
-
|
|
946
|
-
for (const event of events) capture.set(event, emitter.listeners(event));
|
|
947
|
-
return capture;
|
|
920
|
+
return new Map(Iterator.from(events).map((event) => [event, emitter.listeners(event)]));
|
|
948
921
|
}
|
|
949
922
|
/**
|
|
950
923
|
* Removes every listener one emitter gained for the captured events since its capture.
|
|
@@ -975,12 +948,11 @@ function captureListeners(emitter, events) {
|
|
|
975
948
|
*/
|
|
976
949
|
function releaseListeners(emitter, capture) {
|
|
977
950
|
for (const [event, captured] of capture) for (const listener of emitter.listeners(event)) {
|
|
978
|
-
if (captured.includes(listener)) continue;
|
|
951
|
+
if (captured.includes(listener) || !(0, _orkestrel_contract.isFunction)(listener)) continue;
|
|
979
952
|
emitter.removeListener(event, listener);
|
|
980
953
|
}
|
|
981
954
|
}
|
|
982
|
-
|
|
983
|
-
//#region src/server/parsers.ts
|
|
955
|
+
// src/server/parsers.ts
|
|
984
956
|
/**
|
|
985
957
|
* Parses the configuration one compiler run printed for a TypeScript project.
|
|
986
958
|
*
|
|
@@ -1035,8 +1007,7 @@ function parseRevisionOwner(revision) {
|
|
|
1035
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];
|
|
1036
1008
|
return owner === void 0 ? void 0 : Number.parseInt(owner, 10);
|
|
1037
1009
|
}
|
|
1038
|
-
|
|
1039
|
-
//#region src/server/Overlay.ts
|
|
1010
|
+
// src/server/Overlay.ts
|
|
1040
1011
|
/**
|
|
1041
1012
|
* Implements `OverlayInterface` over a private map from normalized absolute path to candidate text,
|
|
1042
1013
|
* minting at construction the `revision` a resident tool caches its answers against.
|
|
@@ -1122,15 +1093,13 @@ var Overlay = class {
|
|
|
1122
1093
|
this.#candidates.clear();
|
|
1123
1094
|
}
|
|
1124
1095
|
};
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
var version = "0.0.24";
|
|
1096
|
+
// package.json
|
|
1097
|
+
var version = "0.0.26";
|
|
1128
1098
|
var peerDependencies = {
|
|
1129
|
-
"typescript": "^6.0.3",
|
|
1130
|
-
"vitest": "^4.1.12"
|
|
1099
|
+
"typescript": "^6.0.3 || ^7.0.2",
|
|
1100
|
+
"vitest": "^4.1.12 || ^5.0.5"
|
|
1131
1101
|
};
|
|
1132
|
-
|
|
1133
|
-
//#region src/server/stages/LintStage.ts
|
|
1102
|
+
// src/server/stages/LintStage.ts
|
|
1134
1103
|
/**
|
|
1135
1104
|
* Inspects virtual documents through one resident Oxlint language server.
|
|
1136
1105
|
*
|
|
@@ -1343,12 +1312,16 @@ var LintStage = class {
|
|
|
1343
1312
|
}));
|
|
1344
1313
|
}
|
|
1345
1314
|
};
|
|
1346
|
-
|
|
1347
|
-
//#region src/server/stages/RuntimeStage.ts
|
|
1315
|
+
// src/server/stages/RuntimeStage.ts
|
|
1348
1316
|
/**
|
|
1349
1317
|
* Inspects tests through one resident Vitest service from the target workspace.
|
|
1350
1318
|
*
|
|
1351
1319
|
* @remarks
|
|
1320
|
+
* Vitest 4.1.12 and 5.0.5 share the mode-first `createVitest` overload used by this stage.
|
|
1321
|
+
* Result eviction removes one file from Vitest 4's results cache. Vitest 5 exposes only a full
|
|
1322
|
+
* cache clear, so eviction clears its results, dependency, and filesystem module caches.
|
|
1323
|
+
* Warming removes termination handlers registered within the factory's async context before
|
|
1324
|
+
* signal delivery, while retaining handlers the host registers outside that context.
|
|
1352
1325
|
* Starting launches Vitest with the threads pool and instruments each inline or function-declared
|
|
1353
1326
|
* project with a Vite plugin that reads the active inspection's candidate overlay. A selected
|
|
1354
1327
|
* string-declared project reports a workspace issue because its configuration carries no runtime
|
|
@@ -1502,14 +1475,13 @@ var RuntimeStage = class {
|
|
|
1502
1475
|
const file = node_path.sep === "\\" ? normalizePath(specification) : specification;
|
|
1503
1476
|
this.#specifications += 1;
|
|
1504
1477
|
this.#revisions.add(file);
|
|
1505
|
-
|
|
1506
|
-
let cleanup = [];
|
|
1478
|
+
const issues = [];
|
|
1507
1479
|
const task = project.createSpecification(file, void 0, "threads");
|
|
1508
1480
|
this.#progress += 1;
|
|
1509
1481
|
try {
|
|
1510
1482
|
try {
|
|
1511
1483
|
const result = await vitest.runTestSpecifications([task], false);
|
|
1512
|
-
issues
|
|
1484
|
+
issues.push(...this.#issues(result, file, subject.test.path), ...this.#misses(project, file));
|
|
1513
1485
|
} catch (error) {
|
|
1514
1486
|
if ((0, _src_core.isProbeError)(error)) throw error;
|
|
1515
1487
|
throw new _src_core.ProbeError(`The runtime stage could not run the generated specification (${describeUnknown(error)})`, {
|
|
@@ -1526,22 +1498,22 @@ var RuntimeStage = class {
|
|
|
1526
1498
|
this.#progress -= 1;
|
|
1527
1499
|
process.exitCode = exitCode;
|
|
1528
1500
|
this.#revisions.delete(file);
|
|
1529
|
-
|
|
1501
|
+
issues.push(...await this.#evict(vitest, file));
|
|
1530
1502
|
try {
|
|
1531
1503
|
const contained = resolveWorkspaceFile(this.#workspace, (0, node_path.relative)(this.#workspace, file), true);
|
|
1532
1504
|
if ((0, node_fs.existsSync)(contained)) (0, node_fs.unlinkSync)(contained);
|
|
1533
1505
|
} catch (error) {
|
|
1534
|
-
|
|
1506
|
+
issues.push({
|
|
1535
1507
|
origin: (0, _src_core.isProbeError)(error) && error.origin === "workspace" ? "workspace" : "instrument",
|
|
1536
1508
|
path: relativeWorkspaceFile(this.#workspace, file),
|
|
1537
1509
|
message: `The runtime stage could not delete the generated specification (${describeUnknown(error)})`
|
|
1538
|
-
}
|
|
1510
|
+
});
|
|
1539
1511
|
}
|
|
1540
1512
|
}
|
|
1541
1513
|
return {
|
|
1542
1514
|
stage: this.stage,
|
|
1543
1515
|
elapsed: Math.round(performance.now() - started),
|
|
1544
|
-
issues
|
|
1516
|
+
issues
|
|
1545
1517
|
};
|
|
1546
1518
|
} finally {
|
|
1547
1519
|
this.#loads.clear();
|
|
@@ -1573,38 +1545,46 @@ var RuntimeStage = class {
|
|
|
1573
1545
|
this.#sweep();
|
|
1574
1546
|
const output = new node_stream.PassThrough();
|
|
1575
1547
|
output.resume();
|
|
1548
|
+
const scope = new node_async_hooks.AsyncLocalStorage();
|
|
1549
|
+
const listener = this.#captureSignal.bind(this, scope);
|
|
1576
1550
|
const signals = captureListeners(process, ["SIGINT", "SIGTERM"]);
|
|
1577
|
-
|
|
1551
|
+
process.on("newListener", listener);
|
|
1578
1552
|
try {
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1553
|
+
return await scope.run(true, () => {
|
|
1554
|
+
try {
|
|
1555
|
+
return factory("test", {
|
|
1556
|
+
root: this.#workspace,
|
|
1557
|
+
config: resolveWorkspaceFile(this.#workspace, "vite.config.ts"),
|
|
1558
|
+
watch: false,
|
|
1559
|
+
run: true,
|
|
1560
|
+
pool: "threads",
|
|
1561
|
+
reporters: [{
|
|
1562
|
+
onInit() {},
|
|
1563
|
+
onTestRunEnd() {}
|
|
1564
|
+
}]
|
|
1565
|
+
}, { plugins: [{
|
|
1566
|
+
name: "orkestrel-project-instrumentation",
|
|
1567
|
+
enforce: "post",
|
|
1568
|
+
config: this.#configure.bind(this)
|
|
1569
|
+
}] }, {
|
|
1570
|
+
stdout: output,
|
|
1571
|
+
stderr: process.stderr
|
|
1572
|
+
});
|
|
1573
|
+
} finally {
|
|
1574
|
+
releaseListeners(process, signals);
|
|
1575
|
+
}
|
|
1596
1576
|
});
|
|
1597
1577
|
} catch (error) {
|
|
1598
1578
|
throw this.#configuration(error);
|
|
1599
1579
|
} finally {
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
try {
|
|
1603
|
-
return await warming;
|
|
1604
|
-
} catch (error) {
|
|
1605
|
-
throw this.#configuration(error);
|
|
1580
|
+
process.off("newListener", listener);
|
|
1581
|
+
scope.disable();
|
|
1606
1582
|
}
|
|
1607
1583
|
}
|
|
1584
|
+
#captureSignal(scope, event, listener) {
|
|
1585
|
+
if (scope.getStore() !== true || event !== "SIGINT" && event !== "SIGTERM") return;
|
|
1586
|
+
queueMicrotask(() => process.off(event, listener));
|
|
1587
|
+
}
|
|
1608
1588
|
#configure(config) {
|
|
1609
1589
|
const test = config.test;
|
|
1610
1590
|
if (test?.projects === void 0) return;
|
|
@@ -1660,47 +1640,52 @@ var RuntimeStage = class {
|
|
|
1660
1640
|
return text;
|
|
1661
1641
|
}
|
|
1662
1642
|
#specification(test) {
|
|
1663
|
-
|
|
1664
|
-
let generated;
|
|
1665
|
-
const outcome = (0, _orkestrel_contract.attempt)(() => {
|
|
1643
|
+
const location = (0, _orkestrel_contract.attempt)(() => {
|
|
1666
1644
|
const revision = `${process.pid}-${(0, node_crypto.randomUUID)()}`;
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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)(() => {
|
|
1674
1658
|
resolveWorkspaceFile(this.#workspace, target, true);
|
|
1675
1659
|
(0, node_fs.writeFileSync)(file, (0, _src_core.formatSpecification)(test.text, revision), {
|
|
1676
1660
|
encoding: "utf8",
|
|
1677
1661
|
flag: "wx"
|
|
1678
1662
|
});
|
|
1679
|
-
return file;
|
|
1680
1663
|
});
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
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,
|
|
1685
1670
|
context: {
|
|
1686
|
-
...
|
|
1671
|
+
...error.context,
|
|
1687
1672
|
path: test.path
|
|
1688
1673
|
},
|
|
1689
|
-
cause:
|
|
1674
|
+
cause: error.cause
|
|
1690
1675
|
});
|
|
1691
|
-
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)})`, {
|
|
1692
1677
|
origin: "claimant",
|
|
1693
1678
|
code: "refused",
|
|
1694
1679
|
context: {
|
|
1695
1680
|
stage: this.stage,
|
|
1696
1681
|
path: test.path
|
|
1697
1682
|
},
|
|
1698
|
-
cause:
|
|
1683
|
+
cause: error
|
|
1699
1684
|
});
|
|
1700
1685
|
return {
|
|
1701
|
-
origin: (0, _src_core.isProbeError)(
|
|
1686
|
+
origin: (0, _src_core.isProbeError)(error) && error.origin === "workspace" || directory ? "workspace" : "instrument",
|
|
1702
1687
|
path: test.path,
|
|
1703
|
-
message: `The runtime stage could not write the generated specification (${describeUnknown(
|
|
1688
|
+
message: `The runtime stage could not write the generated specification (${describeUnknown(error)})`
|
|
1704
1689
|
};
|
|
1705
1690
|
}
|
|
1706
1691
|
#project(vitest, path) {
|
|
@@ -1731,44 +1716,35 @@ var RuntimeStage = class {
|
|
|
1731
1716
|
}
|
|
1732
1717
|
#misses(project, specification) {
|
|
1733
1718
|
const file = normalizePath(specification);
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
let served = false;
|
|
1738
|
-
let loaded = false;
|
|
1739
|
-
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) => {
|
|
1740
1722
|
const modules = environment.moduleGraph.getModulesByFile(path);
|
|
1741
|
-
if (modules === void 0)
|
|
1742
|
-
|
|
1723
|
+
if (modules === void 0) return [];
|
|
1724
|
+
return Iterator.from(modules).flatMap((module) => {
|
|
1743
1725
|
const pending = [...module.importers];
|
|
1744
1726
|
const seen = new Set(pending);
|
|
1745
|
-
let reaches = false;
|
|
1746
1727
|
while (pending.length > 0) {
|
|
1747
1728
|
const importer = pending.pop();
|
|
1748
1729
|
if (importer === void 0) break;
|
|
1749
|
-
if (importer.file !== null && normalizePath(importer.file) === file)
|
|
1750
|
-
reaches = true;
|
|
1751
|
-
break;
|
|
1752
|
-
}
|
|
1730
|
+
if (importer.file !== null && normalizePath(importer.file) === file) return [module.id !== null && this.#loads.has(normalizePath(module.id))];
|
|
1753
1731
|
for (const parent of importer.importers) {
|
|
1754
1732
|
if (seen.has(parent)) continue;
|
|
1755
1733
|
seen.add(parent);
|
|
1756
1734
|
pending.push(parent);
|
|
1757
1735
|
}
|
|
1758
1736
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
issues.push({
|
|
1737
|
+
return [];
|
|
1738
|
+
}).toArray();
|
|
1739
|
+
});
|
|
1740
|
+
if (served.length === 0) return [];
|
|
1741
|
+
const loaded = served.some((value) => value);
|
|
1742
|
+
return [{
|
|
1766
1743
|
origin: loaded ? "instrument" : "workspace",
|
|
1767
1744
|
path: relativeWorkspaceFile(this.#workspace, path),
|
|
1768
1745
|
message: loaded ? "The runtime overlay did not resolve this module" : "The workspace configuration served this module before the runtime overlay"
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
return issues;
|
|
1746
|
+
}];
|
|
1747
|
+
});
|
|
1772
1748
|
}
|
|
1773
1749
|
async #evict(vitest, file) {
|
|
1774
1750
|
try {
|
|
@@ -1798,8 +1774,16 @@ var RuntimeStage = class {
|
|
|
1798
1774
|
}
|
|
1799
1775
|
vitest.watcher.onFileDelete(file);
|
|
1800
1776
|
vitest.watcher.invalidates.delete(file);
|
|
1801
|
-
vitest.cache.results
|
|
1802
|
-
|
|
1777
|
+
const results = "results" in vitest.cache ? vitest.cache.results : void 0;
|
|
1778
|
+
if ((0, _orkestrel_contract.isObject)(results) && "removeFromCache" in results && typeof results.removeFromCache === "function" && "writeToCache" in results && typeof results.writeToCache === "function") {
|
|
1779
|
+
results.removeFromCache(normalizePath((0, node_path.relative)(this.#workspace, file)));
|
|
1780
|
+
await results.writeToCache();
|
|
1781
|
+
} else if ("clearCache" in vitest && typeof vitest.clearCache === "function") await vitest.clearCache();
|
|
1782
|
+
else throw new _src_core.ProbeError("Vitest exposes no cache eviction API", {
|
|
1783
|
+
origin: "instrument",
|
|
1784
|
+
code: "malformed",
|
|
1785
|
+
context: { stage: this.stage }
|
|
1786
|
+
});
|
|
1803
1787
|
return [];
|
|
1804
1788
|
} catch (error) {
|
|
1805
1789
|
return [{
|
|
@@ -2002,12 +1986,13 @@ var RuntimeStage = class {
|
|
|
2002
1986
|
};
|
|
2003
1987
|
}
|
|
2004
1988
|
};
|
|
2005
|
-
|
|
2006
|
-
//#region src/server/stages/TypeStage.ts
|
|
1989
|
+
// src/server/stages/TypeStage.ts
|
|
2007
1990
|
/**
|
|
2008
1991
|
* Inspects TypeScript source by running the target workspace's own compiler over a mirror of it.
|
|
2009
1992
|
*
|
|
2010
1993
|
* @remarks
|
|
1994
|
+
* TypeScript 6 and 7 run through the `tsc` entry in their package manifest; neither a compiler API
|
|
1995
|
+
* nor an exported binary subpath is required.
|
|
2011
1996
|
* The compiler is a process this stage spawns rather than a module it loads, so nothing here holds
|
|
2012
1997
|
* a resident program and the host's event loop is free for the whole check. Starting sweeps a
|
|
2013
1998
|
* mirror an earlier host left behind, copies the workspace into a fresh one under
|
|
@@ -2045,6 +2030,7 @@ var TypeStage = class {
|
|
|
2045
2030
|
#revision = `${process.pid}-${(0, node_crypto.randomUUID)()}`;
|
|
2046
2031
|
#mirror;
|
|
2047
2032
|
#configs = /* @__PURE__ */ new Map();
|
|
2033
|
+
#validated = /* @__PURE__ */ new Set();
|
|
2048
2034
|
#mirrored = /* @__PURE__ */ new Map();
|
|
2049
2035
|
#drafts = /* @__PURE__ */ new Set();
|
|
2050
2036
|
#children = /* @__PURE__ */ new Set();
|
|
@@ -2098,6 +2084,8 @@ var TypeStage = class {
|
|
|
2098
2084
|
* refreshed from the workspace first on a cache miss, so the digest names the configuration the
|
|
2099
2085
|
* check applies. The reading is cached per project for the life of the stage, so a draft written
|
|
2100
2086
|
* afterward cannot move it.
|
|
2087
|
+
* Before returning the identity, validates the project with `--noEmit` unless an unoverlaid check
|
|
2088
|
+
* already validated it. TypeScript 7's `--showConfig` can succeed for a malformed project.
|
|
2101
2089
|
*
|
|
2102
2090
|
* @param project - The workspace-relative TypeScript project to resolve
|
|
2103
2091
|
* @returns The resolved workspace-relative path and the digest of its compiler options
|
|
@@ -2107,6 +2095,14 @@ var TypeStage = class {
|
|
|
2107
2095
|
resolve(project) {
|
|
2108
2096
|
return guardStage(this.stage, this.#resolve(project));
|
|
2109
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
|
+
*/
|
|
2110
2106
|
destroy() {
|
|
2111
2107
|
if (this.#closing !== void 0) return this.#closing;
|
|
2112
2108
|
this.#closing = guardStage(this.stage, this.#destroy());
|
|
@@ -2157,6 +2153,10 @@ var TypeStage = class {
|
|
|
2157
2153
|
const contained = this.#contain(project);
|
|
2158
2154
|
this.#progress += 1;
|
|
2159
2155
|
const config = await this.#configure(contained);
|
|
2156
|
+
if (!this.#validated.has(contained)) {
|
|
2157
|
+
this.#refresh();
|
|
2158
|
+
await this.#check(contained, []);
|
|
2159
|
+
}
|
|
2160
2160
|
return {
|
|
2161
2161
|
path: contained,
|
|
2162
2162
|
digest: computeDigest(this.#workspace, config.compilerOptions)
|
|
@@ -2165,7 +2165,7 @@ var TypeStage = class {
|
|
|
2165
2165
|
async #destroy() {
|
|
2166
2166
|
const children = [...this.#children];
|
|
2167
2167
|
this.#children.clear();
|
|
2168
|
-
|
|
2168
|
+
await Promise.all(children.map((child) => this.#terminate(child)));
|
|
2169
2169
|
await this.#warming?.catch(() => void 0);
|
|
2170
2170
|
try {
|
|
2171
2171
|
(0, node_fs.rmSync)(this.#mirror, {
|
|
@@ -2176,6 +2176,7 @@ var TypeStage = class {
|
|
|
2176
2176
|
});
|
|
2177
2177
|
} catch {}
|
|
2178
2178
|
this.#configs.clear();
|
|
2179
|
+
this.#validated.clear();
|
|
2179
2180
|
this.#mirrored.clear();
|
|
2180
2181
|
this.#drafts.clear();
|
|
2181
2182
|
}
|
|
@@ -2188,18 +2189,11 @@ var TypeStage = class {
|
|
|
2188
2189
|
await Promise.all(this.#projects().map((project) => this.#check(project, [])));
|
|
2189
2190
|
}
|
|
2190
2191
|
#projects() {
|
|
2191
|
-
|
|
2192
|
-
for (const axis of ["src", "app"]) {
|
|
2192
|
+
return ["tsconfig.json", ...["src", "app"].flatMap((axis) => {
|
|
2193
2193
|
const directory = resolveWorkspaceFile(this.#workspace, `configs/${axis}`);
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
} catch {
|
|
2198
|
-
continue;
|
|
2199
|
-
}
|
|
2200
|
-
for (const name of names.filter((entry) => /^tsconfig\.[^.]+\.json$/.test(entry)).sort()) projects.push(`configs/${axis}/${name}`);
|
|
2201
|
-
}
|
|
2202
|
-
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
|
+
})];
|
|
2203
2197
|
}
|
|
2204
2198
|
#refuseDestroyed() {
|
|
2205
2199
|
if (this.#closing !== void 0) throw (0, _src_core.createDestroyedError)("type stage");
|
|
@@ -2214,13 +2208,9 @@ var TypeStage = class {
|
|
|
2214
2208
|
}
|
|
2215
2209
|
#sweep() {
|
|
2216
2210
|
const root = resolveWorkspaceFile(this.#workspace, _src_core.TYPE_MIRROR);
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
} catch {
|
|
2221
|
-
return;
|
|
2222
|
-
}
|
|
2223
|
-
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) {
|
|
2224
2214
|
if (!entry.isDirectory()) continue;
|
|
2225
2215
|
const owner = parseRevisionOwner(entry.name);
|
|
2226
2216
|
if (owner === void 0 || matchesLiveProcess(owner)) continue;
|
|
@@ -2274,12 +2264,7 @@ var TypeStage = class {
|
|
|
2274
2264
|
return contained;
|
|
2275
2265
|
}
|
|
2276
2266
|
#displace(target) {
|
|
2277
|
-
const ancestors =
|
|
2278
|
-
let directory = (0, node_path.dirname)(target);
|
|
2279
|
-
while (directory !== this.#mirror && directory !== (0, node_path.dirname)(directory)) {
|
|
2280
|
-
ancestors.unshift(directory);
|
|
2281
|
-
directory = (0, node_path.dirname)(directory);
|
|
2282
|
-
}
|
|
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)));
|
|
2283
2268
|
for (const ancestor of [...ancestors, target]) {
|
|
2284
2269
|
const reading = (0, _orkestrel_contract.attempt)(() => (0, node_fs.lstatSync)(ancestor));
|
|
2285
2270
|
if (!reading.success) continue;
|
|
@@ -2349,7 +2334,9 @@ var TypeStage = class {
|
|
|
2349
2334
|
}
|
|
2350
2335
|
});
|
|
2351
2336
|
}
|
|
2352
|
-
|
|
2337
|
+
const issues = this.#issues(diagnostics, project, scratch);
|
|
2338
|
+
if (this.#drafts.size === 0) this.#validated.add(this.#contain(project));
|
|
2339
|
+
return issues;
|
|
2353
2340
|
}
|
|
2354
2341
|
#scratch(project, config, drafts) {
|
|
2355
2342
|
const directory = (0, node_path.dirname)(project);
|
|
@@ -2374,13 +2361,13 @@ var TypeStage = class {
|
|
|
2374
2361
|
(0, node_fs.writeFileSync)(target, `${JSON.stringify(body, void 0, " ")}\n`, "utf8");
|
|
2375
2362
|
return contained;
|
|
2376
2363
|
}
|
|
2377
|
-
#issues(diagnostics, project) {
|
|
2364
|
+
#issues(diagnostics, project, scratch) {
|
|
2378
2365
|
const issues = [];
|
|
2379
2366
|
for (const diagnostic of diagnostics) {
|
|
2380
2367
|
const message = this.#translate(diagnostic.message);
|
|
2381
2368
|
const resolved = diagnostic.path === void 0 ? void 0 : (0, node_path.resolve)(this.#mirror, diagnostic.path);
|
|
2382
2369
|
const drafted = resolved !== void 0 && this.#drafts.has(escapesRoot(this.#mirror, resolved) ? relativeWorkspaceFile(this.#workspace, resolved) : relativeWorkspaceFile(this.#mirror, resolved));
|
|
2383
|
-
if (resolved === void 0 || !drafted && (0, node_path.extname)(resolved) === ".json") throw new _src_core.ProbeError(message, {
|
|
2370
|
+
if (resolved === void 0 || !drafted && (0, node_path.extname)(resolved) === ".json") throw new _src_core.ProbeError(message.replaceAll(normalizePath(scratch), normalizePath(project)), {
|
|
2384
2371
|
origin: "workspace",
|
|
2385
2372
|
code: "malformed",
|
|
2386
2373
|
context: {
|
|
@@ -2413,40 +2400,43 @@ var TypeStage = class {
|
|
|
2413
2400
|
}
|
|
2414
2401
|
});
|
|
2415
2402
|
}
|
|
2416
|
-
#spawn(args, cwd) {
|
|
2403
|
+
async #spawn(args, cwd) {
|
|
2417
2404
|
this.#refuseDestroyed();
|
|
2418
2405
|
const compiler = this.#compiler;
|
|
2419
2406
|
if (compiler === void 0) throw (0, _src_core.createDestroyedError)("type stage");
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
context: { stage: this.stage }
|
|
2437
|
-
}));
|
|
2438
|
-
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);
|
|
2439
2423
|
}
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
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 = [];
|
|
2443
2433
|
output.setEncoding("utf8");
|
|
2444
2434
|
errors.setEncoding("utf8");
|
|
2445
2435
|
output.on("data", (chunk) => {
|
|
2446
|
-
stdout
|
|
2436
|
+
stdout.push(chunk);
|
|
2447
2437
|
});
|
|
2448
2438
|
errors.on("data", (chunk) => {
|
|
2449
|
-
stderr
|
|
2439
|
+
stderr.push(chunk);
|
|
2450
2440
|
});
|
|
2451
2441
|
child.on("error", (error) => {
|
|
2452
2442
|
this.#children.delete(child);
|
|
@@ -2464,32 +2454,28 @@ var TypeStage = class {
|
|
|
2464
2454
|
this.#children.delete(child);
|
|
2465
2455
|
settle({
|
|
2466
2456
|
...code === null ? {} : { status: code },
|
|
2467
|
-
stdout,
|
|
2468
|
-
stderr
|
|
2457
|
+
stdout: stdout.join(""),
|
|
2458
|
+
stderr: stderr.join("")
|
|
2469
2459
|
});
|
|
2470
2460
|
});
|
|
2471
2461
|
});
|
|
2472
2462
|
}
|
|
2473
|
-
#terminate(child) {
|
|
2463
|
+
async #terminate(child) {
|
|
2474
2464
|
const id = child.pid;
|
|
2475
2465
|
if (process.platform === "win32" && id !== void 0) {
|
|
2476
|
-
(0,
|
|
2477
|
-
"/pid",
|
|
2478
|
-
String(id),
|
|
2479
|
-
"/t",
|
|
2480
|
-
"/f"
|
|
2481
|
-
], { stdio: "ignore" });
|
|
2466
|
+
await (0, _orkestrel_process_server.killTree)(id, _src_core.PROBE_DEADLINE);
|
|
2482
2467
|
return;
|
|
2483
2468
|
}
|
|
2484
2469
|
child.kill("SIGTERM");
|
|
2485
2470
|
}
|
|
2486
2471
|
};
|
|
2487
|
-
|
|
2488
|
-
//#region src/server/Probe.ts
|
|
2472
|
+
// src/server/Probe.ts
|
|
2489
2473
|
/**
|
|
2490
2474
|
* Answers claims through its type, lint, and runtime stages.
|
|
2491
2475
|
*
|
|
2492
2476
|
* @remarks
|
|
2477
|
+
* The workspace peers are TypeScript `^6.0.3 || ^7.0.2` and Vitest `^4.1.12 || ^5.0.5`.
|
|
2478
|
+
* Starting refuses TypeScript majors outside 6 and 7 before a stage starts.
|
|
2493
2479
|
* Construction resolves the target workspace's toolchain. Starting warms every stage and waits
|
|
2494
2480
|
* for lint and runtime; type warming and the boot controls continue behind that gate. The boot
|
|
2495
2481
|
* controls mutate imported dependencies and refuse service unless the type and runtime stages
|
|
@@ -2538,7 +2524,7 @@ var TypeStage = class {
|
|
|
2538
2524
|
* const probe = new Probe({ workspace: process.cwd() })
|
|
2539
2525
|
* const verdict = await probe.prove(claim)
|
|
2540
2526
|
* verdict.digest // 'bdf03e5dfd6bd413ead671c7a2940fcf'
|
|
2541
|
-
* verdict.receipt //
|
|
2527
|
+
* verdict.receipt?.startsWith(`probe:${verdict.digest}:type:`) // true
|
|
2542
2528
|
* await probe.destroy()
|
|
2543
2529
|
* ```
|
|
2544
2530
|
*/
|
|
@@ -2673,11 +2659,12 @@ var Probe = class {
|
|
|
2673
2659
|
return this.#starting;
|
|
2674
2660
|
}
|
|
2675
2661
|
async #start(filling, warming) {
|
|
2676
|
-
|
|
2677
|
-
try {
|
|
2662
|
+
const preparation = (0, _orkestrel_contract.attempt)(() => {
|
|
2678
2663
|
this.#support();
|
|
2679
|
-
|
|
2680
|
-
}
|
|
2664
|
+
return this.#arming === void 0 && this.#workbench();
|
|
2665
|
+
});
|
|
2666
|
+
if (!preparation.success) {
|
|
2667
|
+
const error = preparation.error;
|
|
2681
2668
|
this.#surface(error);
|
|
2682
2669
|
throw error;
|
|
2683
2670
|
}
|
|
@@ -2688,7 +2675,7 @@ var Probe = class {
|
|
|
2688
2675
|
throw this.#refuseArm(error);
|
|
2689
2676
|
}
|
|
2690
2677
|
if (this.#arming === void 0) {
|
|
2691
|
-
this.#arming = this.#arm(warming,
|
|
2678
|
+
this.#arming = this.#arm(warming, preparation.value);
|
|
2692
2679
|
this.#arming.catch(() => {
|
|
2693
2680
|
if (this.#refusal === void 0) this.#arming = void 0;
|
|
2694
2681
|
});
|
|
@@ -3014,9 +3001,9 @@ var Probe = class {
|
|
|
3014
3001
|
#support() {
|
|
3015
3002
|
const version = this.#toolchain.typescript;
|
|
3016
3003
|
const range = peerDependencies.typescript;
|
|
3017
|
-
const supported = /^\^(\d+)\./u.exec(range)?.[1];
|
|
3018
3004
|
const found = /^(\d+)\./u.exec(version)?.[1];
|
|
3019
|
-
|
|
3005
|
+
const supported = range.split(" || ").some((entry) => /^\^(\d+)\./u.exec(entry)?.[1] === found);
|
|
3006
|
+
if (found === void 0 || !supported) throw new _src_core.ProbeError(`The supported TypeScript range is ${range}; found ${version}`, {
|
|
3020
3007
|
origin: "workspace",
|
|
3021
3008
|
code: "malformed",
|
|
3022
3009
|
context: {
|
|
@@ -3026,8 +3013,7 @@ var Probe = class {
|
|
|
3026
3013
|
});
|
|
3027
3014
|
}
|
|
3028
3015
|
};
|
|
3029
|
-
|
|
3030
|
-
//#region src/server/ProbeServer.ts
|
|
3016
|
+
// src/server/ProbeServer.ts
|
|
3031
3017
|
/**
|
|
3032
3018
|
* Implements `ProbeServerInterface` over a `PassThrough` stream this server owns, binding the
|
|
3033
3019
|
* published `prove` tool and the dual-era dispatcher to this process's Model Context Protocol
|
|
@@ -3266,7 +3252,6 @@ var ProbeServer = class {
|
|
|
3266
3252
|
] : [rendered, minimal]).find((arm) => (0, _orkestrel_mcp.isBoundedJSON)(arm, bounds)) ?? minimal;
|
|
3267
3253
|
}
|
|
3268
3254
|
};
|
|
3269
|
-
//#endregion
|
|
3270
3255
|
exports.LintStage = LintStage;
|
|
3271
3256
|
exports.Overlay = Overlay;
|
|
3272
3257
|
exports.Probe = Probe;
|