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