@orkestrel/probe 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/bin/main.js +1 -1
- package/dist/bin/main.js.map +1 -1
- package/dist/src/core/index.cjs +43 -3
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +69 -11
- package/dist/src/core/index.d.ts +69 -11
- package/dist/src/core/index.js +41 -4
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +345 -191
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +75 -37
- package/dist/src/server/index.d.ts +75 -37
- package/dist/src/server/index.js +346 -193
- package/dist/src/server/index.js.map +1 -1
- package/package.json +8 -8
package/dist/src/server/index.js
CHANGED
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { JSONRPC_SERVER_ERROR, MCPError, createMCPLegacy, createMCPServer, isBoundedJSON } from "@orkestrel/mcp";
|
|
2
3
|
import { closeSync, constants, existsSync, ftruncateSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
5
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
7
|
import { attempt, compileGuard, compileSchema, isArray, isError, isNumber, isObject, isRecord, isString, parseJSON, schemaToParameters } from "@orkestrel/contract";
|
|
7
|
-
import { CLAIM_SHAPE, LINT_DEADLINE, PROBE_DEADLINE, PROBE_KEYS, PROBE_SPECIFICATIONS, ProbeError, RUNTIME_PLUGIN, TYPE_MIRROR, computeReceipt, createDestroyedError, formatCheck, formatReceipt, formatSpecification, formatVerdict, isClaim, isDraft, isProbeError, isVerdict, matchesSpecification } from "../core/index.js";
|
|
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";
|
|
8
9
|
import { Emitter } from "@orkestrel/emitter";
|
|
9
|
-
import {
|
|
10
|
+
import { createPool, isPoolError } from "@orkestrel/pool";
|
|
11
|
+
import { createLSPClient, isLSPError } from "@orkestrel/lsp";
|
|
12
|
+
import { addAbortListener } from "node:events";
|
|
10
13
|
import { createTimeout } from "@orkestrel/timeout";
|
|
11
|
-
import { createLSPClient } from "@orkestrel/lsp";
|
|
12
14
|
import { createStdioClientTransport } from "@orkestrel/lsp/server";
|
|
13
15
|
import { PassThrough } from "node:stream";
|
|
14
16
|
import { spawn, spawnSync } from "node:child_process";
|
|
15
|
-
import { createMCPLegacy, createMCPServer, isBoundedJSON } from "@orkestrel/mcp";
|
|
16
17
|
import { createStdioServer } from "@orkestrel/mcp/server";
|
|
17
18
|
import { createTool, createToolManager } from "@orkestrel/tool";
|
|
19
|
+
//#region src/server/errors.ts
|
|
20
|
+
/**
|
|
21
|
+
* Creates the protocol failure returned when the MCP handshake cannot arm the probe.
|
|
22
|
+
*
|
|
23
|
+
* @param error - The classified probe failure that ended initialization
|
|
24
|
+
* @returns An MCP server error carrying the probe failure's origin and code as protocol data
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* createHandshakeError(createDestroyedError('probe server')).code // -32000
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function createHandshakeError(error) {
|
|
32
|
+
return new MCPError(`[${error.origin}] ${error.code}: ${error.message}`, JSONRPC_SERVER_ERROR, {
|
|
33
|
+
origin: error.origin,
|
|
34
|
+
code: error.code
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
18
38
|
//#region src/server/helpers.ts
|
|
19
39
|
/**
|
|
20
40
|
* Rewrites one path into the forward-slash spelling this package compares and reports paths in.
|
|
@@ -1103,7 +1123,7 @@ var Overlay = class {
|
|
|
1103
1123
|
};
|
|
1104
1124
|
//#endregion
|
|
1105
1125
|
//#region package.json
|
|
1106
|
-
var version = "0.0.
|
|
1126
|
+
var version = "0.0.22";
|
|
1107
1127
|
var peerDependencies = {
|
|
1108
1128
|
"typescript": "^6.0.3",
|
|
1109
1129
|
"vitest": "^4.1.11"
|
|
@@ -1114,7 +1134,7 @@ var peerDependencies = {
|
|
|
1114
1134
|
* Inspects virtual documents through one resident Oxlint language server.
|
|
1115
1135
|
*
|
|
1116
1136
|
* @remarks
|
|
1117
|
-
*
|
|
1137
|
+
* Starting launches the target workspace's Oxlint binary with its Language Server Protocol mode
|
|
1118
1138
|
* and hands the conversation to `@orkestrel/lsp`: `createStdioClientTransport` owns the child
|
|
1119
1139
|
* process and its bytes, and `createLSPClient` owns the framing, the request correlation, the
|
|
1120
1140
|
* capabilities this stage advertises, and the choice between pulled and pushed diagnostics. This
|
|
@@ -1150,21 +1170,22 @@ var peerDependencies = {
|
|
|
1150
1170
|
var LintStage = class {
|
|
1151
1171
|
#workspace;
|
|
1152
1172
|
#documents = /* @__PURE__ */ new Set();
|
|
1153
|
-
#
|
|
1173
|
+
#warming;
|
|
1174
|
+
#exited = Promise.withResolvers();
|
|
1154
1175
|
#client;
|
|
1176
|
+
#transport;
|
|
1155
1177
|
#ending;
|
|
1156
1178
|
#closing;
|
|
1157
1179
|
#progress = 0;
|
|
1158
1180
|
#revision = 0;
|
|
1159
1181
|
/**
|
|
1160
|
-
*
|
|
1182
|
+
* Records the target workspace without starting its language server.
|
|
1161
1183
|
*
|
|
1162
1184
|
* @param workspace - The target workspace root. Default: the current working directory
|
|
1163
1185
|
*/
|
|
1164
1186
|
constructor(workspace = process.cwd()) {
|
|
1165
1187
|
this.#workspace = workspace;
|
|
1166
|
-
this.#
|
|
1167
|
-
this.#warmth.catch(() => {});
|
|
1188
|
+
this.#exited.promise.catch(() => {});
|
|
1168
1189
|
}
|
|
1169
1190
|
get stage() {
|
|
1170
1191
|
return "lint";
|
|
@@ -1172,6 +1193,15 @@ var LintStage = class {
|
|
|
1172
1193
|
get progress() {
|
|
1173
1194
|
return this.#progress;
|
|
1174
1195
|
}
|
|
1196
|
+
get exit() {
|
|
1197
|
+
return this.#exited.promise;
|
|
1198
|
+
}
|
|
1199
|
+
start() {
|
|
1200
|
+
if (this.#closing !== void 0) return Promise.reject(createDestroyedError("lint stage"));
|
|
1201
|
+
this.#warming ??= guardStage(this.stage, this.#warm());
|
|
1202
|
+
this.#warming.catch(() => {});
|
|
1203
|
+
return this.#warming;
|
|
1204
|
+
}
|
|
1175
1205
|
inspect(subject, options) {
|
|
1176
1206
|
return guardStage(this.stage, this.#inspect(subject, options));
|
|
1177
1207
|
}
|
|
@@ -1188,8 +1218,10 @@ var LintStage = class {
|
|
|
1188
1218
|
context: { stage: this.stage }
|
|
1189
1219
|
});
|
|
1190
1220
|
const started = performance.now();
|
|
1191
|
-
|
|
1221
|
+
await this.start();
|
|
1192
1222
|
if (this.#closing !== void 0) throw createDestroyedError("lint stage");
|
|
1223
|
+
const client = this.#client;
|
|
1224
|
+
if (client === void 0) throw this.#fault("The Oxlint language server did not initialize");
|
|
1193
1225
|
const issues = [];
|
|
1194
1226
|
for (const draft of [...subject.files, subject.test]) issues.push(...await this.#document(client, draft, options.signal));
|
|
1195
1227
|
return {
|
|
@@ -1200,38 +1232,39 @@ var LintStage = class {
|
|
|
1200
1232
|
}
|
|
1201
1233
|
async #destroy() {
|
|
1202
1234
|
await this.#client?.destroy();
|
|
1235
|
+
await this.#transport?.close();
|
|
1203
1236
|
}
|
|
1204
1237
|
async #warm() {
|
|
1205
1238
|
const binary = resolveWorkspaceBinary(this.#workspace, "oxlint");
|
|
1239
|
+
const transport = createStdioClientTransport({
|
|
1240
|
+
server: {
|
|
1241
|
+
command: [
|
|
1242
|
+
process.execPath,
|
|
1243
|
+
binary,
|
|
1244
|
+
"--lsp"
|
|
1245
|
+
],
|
|
1246
|
+
directory: this.#workspace
|
|
1247
|
+
},
|
|
1248
|
+
grace: LINT_DEADLINE / 2
|
|
1249
|
+
});
|
|
1250
|
+
this.#transport = transport;
|
|
1206
1251
|
const client = createLSPClient({
|
|
1207
|
-
transport
|
|
1208
|
-
server: {
|
|
1209
|
-
command: [
|
|
1210
|
-
process.execPath,
|
|
1211
|
-
binary,
|
|
1212
|
-
"--lsp"
|
|
1213
|
-
],
|
|
1214
|
-
directory: this.#workspace
|
|
1215
|
-
},
|
|
1216
|
-
grace: LINT_DEADLINE / 2
|
|
1217
|
-
}),
|
|
1252
|
+
transport,
|
|
1218
1253
|
workspace: pathToFileURL(this.#workspace).href,
|
|
1219
1254
|
timeout: LINT_DEADLINE
|
|
1220
1255
|
});
|
|
1221
1256
|
this.#client = client;
|
|
1222
1257
|
client.emitter.on("exit", (exit) => this.#retire(exit));
|
|
1223
|
-
await client.start();
|
|
1224
|
-
return client;
|
|
1225
|
-
}
|
|
1226
|
-
async #warmed() {
|
|
1227
1258
|
try {
|
|
1228
|
-
|
|
1259
|
+
await client.start();
|
|
1229
1260
|
} catch (error) {
|
|
1230
1261
|
throw this.#translate(error);
|
|
1231
1262
|
}
|
|
1232
1263
|
}
|
|
1233
1264
|
#retire(exit) {
|
|
1265
|
+
if (this.#closing !== void 0) return;
|
|
1234
1266
|
this.#ending = exit.signal === null ? `code ${exit.code}` : `signal ${exit.signal}`;
|
|
1267
|
+
this.#exited.reject(this.#fault(`The Oxlint language server exited with ${this.#ending}`));
|
|
1235
1268
|
}
|
|
1236
1269
|
async #document(client, draft, signal) {
|
|
1237
1270
|
const uri = pathToFileURL(resolveWorkspaceFile(this.#workspace, draft.path)).href;
|
|
@@ -1274,9 +1307,9 @@ var LintStage = class {
|
|
|
1274
1307
|
});
|
|
1275
1308
|
}
|
|
1276
1309
|
#translate(error, path) {
|
|
1277
|
-
if (this.#closing !== void 0) return createDestroyedError("lint stage");
|
|
1278
1310
|
const ending = this.#ending;
|
|
1279
1311
|
if (ending !== void 0) return this.#fault(`The Oxlint language server exited with ${ending}`, error, path);
|
|
1312
|
+
if (this.#closing !== void 0) return createDestroyedError("lint stage");
|
|
1280
1313
|
if (isError(error)) return error;
|
|
1281
1314
|
return this.#fault(describeUnknown(error), error, path);
|
|
1282
1315
|
}
|
|
@@ -1315,7 +1348,7 @@ var LintStage = class {
|
|
|
1315
1348
|
* Inspects tests through one resident Vitest service from the target workspace.
|
|
1316
1349
|
*
|
|
1317
1350
|
* @remarks
|
|
1318
|
-
*
|
|
1351
|
+
* Starting launches Vitest with the threads pool and instruments each inline or function-declared
|
|
1319
1352
|
* project with a Vite plugin that reads the active inspection's candidate overlay. A selected
|
|
1320
1353
|
* string-declared project reports a workspace issue because its configuration carries no runtime
|
|
1321
1354
|
* overlay plugin. Every inspection writes one fresh sibling specification, invalidates each
|
|
@@ -1374,6 +1407,7 @@ var LintStage = class {
|
|
|
1374
1407
|
var RuntimeStage = class {
|
|
1375
1408
|
#workspace;
|
|
1376
1409
|
#vitest;
|
|
1410
|
+
#warming;
|
|
1377
1411
|
#overlay = new Overlay();
|
|
1378
1412
|
#loads = /* @__PURE__ */ new Set();
|
|
1379
1413
|
#reads = /* @__PURE__ */ new Set();
|
|
@@ -1383,16 +1417,12 @@ var RuntimeStage = class {
|
|
|
1383
1417
|
#closing;
|
|
1384
1418
|
#progress = 0;
|
|
1385
1419
|
/**
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
* @remarks Loading starts during this call. A loader refusal rejects the owned warm operation
|
|
1389
|
-
* rather than construction, so `inspect` reports it and `destroy` remains available.
|
|
1420
|
+
* Records the target workspace without loading its Vitest service.
|
|
1390
1421
|
*
|
|
1391
1422
|
* @param workspace - The target workspace root. Default: the current working directory
|
|
1392
1423
|
*/
|
|
1393
1424
|
constructor(workspace = process.cwd()) {
|
|
1394
1425
|
this.#workspace = workspace;
|
|
1395
|
-
this.#store(this.#warm());
|
|
1396
1426
|
}
|
|
1397
1427
|
get stage() {
|
|
1398
1428
|
return "runtime";
|
|
@@ -1401,6 +1431,20 @@ var RuntimeStage = class {
|
|
|
1401
1431
|
return this.#progress;
|
|
1402
1432
|
}
|
|
1403
1433
|
/**
|
|
1434
|
+
* Begins the warm or joins the warm already begun.
|
|
1435
|
+
*
|
|
1436
|
+
* @returns A promise that resolves after the resident Vitest service is ready
|
|
1437
|
+
* @throws When warming fails or teardown has begun
|
|
1438
|
+
*/
|
|
1439
|
+
start() {
|
|
1440
|
+
if (this.#closing !== void 0) return Promise.reject(createDestroyedError("runtime stage"));
|
|
1441
|
+
this.#warming ??= guardStage(this.stage, this.#store(this.#warm()).then(() => {}));
|
|
1442
|
+
this.#warming.catch(() => {
|
|
1443
|
+
this.#warming = void 0;
|
|
1444
|
+
});
|
|
1445
|
+
return this.#warming;
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1404
1448
|
* Inspects one case.
|
|
1405
1449
|
*
|
|
1406
1450
|
* @param subject - The candidate drafts and test to inspect
|
|
@@ -1431,6 +1475,7 @@ var RuntimeStage = class {
|
|
|
1431
1475
|
async #inspect(subject) {
|
|
1432
1476
|
if (this.#closing !== void 0) throw createDestroyedError("runtime stage");
|
|
1433
1477
|
const started = performance.now();
|
|
1478
|
+
await this.start();
|
|
1434
1479
|
const exitCode = process.exitCode;
|
|
1435
1480
|
const vitest = await this.#runner();
|
|
1436
1481
|
if (this.#closing !== void 0) throw createDestroyedError("runtime stage");
|
|
@@ -1963,7 +2008,7 @@ var RuntimeStage = class {
|
|
|
1963
2008
|
*
|
|
1964
2009
|
* @remarks
|
|
1965
2010
|
* The compiler is a process this stage spawns rather than a module it loads, so nothing here holds
|
|
1966
|
-
* a resident program and the host's event loop is free for the whole check.
|
|
2011
|
+
* a resident program and the host's event loop is free for the whole check. Starting sweeps a
|
|
1967
2012
|
* mirror an earlier host left behind, copies the workspace into a fresh one under
|
|
1968
2013
|
* {@link TYPE_MIRROR}, and builds each declared project's incremental state there, so the first
|
|
1969
2014
|
* inspection is warm.
|
|
@@ -2006,16 +2051,13 @@ var TypeStage = class {
|
|
|
2006
2051
|
#closing;
|
|
2007
2052
|
#progress = 0;
|
|
2008
2053
|
/**
|
|
2009
|
-
*
|
|
2054
|
+
* Records the workspace and the mirror path without starting its compiler.
|
|
2010
2055
|
*
|
|
2011
2056
|
* @param workspace - The target workspace root. Default: the current working directory
|
|
2012
2057
|
*/
|
|
2013
2058
|
constructor(workspace = process.cwd()) {
|
|
2014
2059
|
this.#workspace = workspace;
|
|
2015
|
-
this.#
|
|
2016
|
-
this.#mirror = resolveWorkspaceFile(workspace, `${TYPE_MIRROR}/${this.#revision}`);
|
|
2017
|
-
this.#warming = this.#warm();
|
|
2018
|
-
this.#warming.catch(() => {});
|
|
2060
|
+
this.#mirror = resolve(workspace, TYPE_MIRROR, this.#revision);
|
|
2019
2061
|
}
|
|
2020
2062
|
get stage() {
|
|
2021
2063
|
return "type";
|
|
@@ -2023,6 +2065,12 @@ var TypeStage = class {
|
|
|
2023
2065
|
get progress() {
|
|
2024
2066
|
return this.#progress;
|
|
2025
2067
|
}
|
|
2068
|
+
start() {
|
|
2069
|
+
if (this.#closing !== void 0) return Promise.reject(createDestroyedError("type stage"));
|
|
2070
|
+
this.#warming ??= guardStage(this.stage, this.#warm());
|
|
2071
|
+
this.#warming.catch(() => {});
|
|
2072
|
+
return this.#warming;
|
|
2073
|
+
}
|
|
2026
2074
|
/**
|
|
2027
2075
|
* Inspects one case, against a caller-named project where the caller names one.
|
|
2028
2076
|
*
|
|
@@ -2066,7 +2114,7 @@ var TypeStage = class {
|
|
|
2066
2114
|
async #inspect(subject, project) {
|
|
2067
2115
|
this.#refuseDestroyed();
|
|
2068
2116
|
const started = performance.now();
|
|
2069
|
-
await this
|
|
2117
|
+
await this.start();
|
|
2070
2118
|
this.#refuseDestroyed();
|
|
2071
2119
|
const test = resolveWorkspaceFile(this.#workspace, subject.test.path);
|
|
2072
2120
|
const resolved = subject.files.map((draft) => ({
|
|
@@ -2102,6 +2150,8 @@ var TypeStage = class {
|
|
|
2102
2150
|
}
|
|
2103
2151
|
}
|
|
2104
2152
|
async #resolve(project) {
|
|
2153
|
+
this.#refuseDestroyed();
|
|
2154
|
+
await this.start();
|
|
2105
2155
|
this.#refuseDestroyed();
|
|
2106
2156
|
const contained = this.#contain(project);
|
|
2107
2157
|
this.#progress += 1;
|
|
@@ -2115,7 +2165,7 @@ var TypeStage = class {
|
|
|
2115
2165
|
const children = [...this.#children];
|
|
2116
2166
|
this.#children.clear();
|
|
2117
2167
|
for (const child of children) this.#terminate(child);
|
|
2118
|
-
await this.#warming
|
|
2168
|
+
await this.#warming?.catch(() => void 0);
|
|
2119
2169
|
try {
|
|
2120
2170
|
rmSync(this.#mirror, {
|
|
2121
2171
|
recursive: true,
|
|
@@ -2129,6 +2179,7 @@ var TypeStage = class {
|
|
|
2129
2179
|
this.#drafts.clear();
|
|
2130
2180
|
}
|
|
2131
2181
|
async #warm() {
|
|
2182
|
+
this.#compiler = resolveWorkspaceBinary(this.#workspace, "typescript", "tsc");
|
|
2132
2183
|
this.#sweep();
|
|
2133
2184
|
this.#createMirror();
|
|
2134
2185
|
this.#refresh();
|
|
@@ -2363,8 +2414,10 @@ var TypeStage = class {
|
|
|
2363
2414
|
}
|
|
2364
2415
|
#spawn(args, cwd) {
|
|
2365
2416
|
this.#refuseDestroyed();
|
|
2417
|
+
const compiler = this.#compiler;
|
|
2418
|
+
if (compiler === void 0) throw createDestroyedError("type stage");
|
|
2366
2419
|
return new Promise((settle, refuse) => {
|
|
2367
|
-
const child = spawn(process.execPath, [
|
|
2420
|
+
const child = spawn(process.execPath, [compiler, ...args], {
|
|
2368
2421
|
cwd,
|
|
2369
2422
|
stdio: [
|
|
2370
2423
|
"ignore",
|
|
@@ -2401,7 +2454,7 @@ var TypeStage = class {
|
|
|
2401
2454
|
code: "malformed",
|
|
2402
2455
|
context: {
|
|
2403
2456
|
stage: this.stage,
|
|
2404
|
-
path:
|
|
2457
|
+
path: compiler
|
|
2405
2458
|
},
|
|
2406
2459
|
cause: error
|
|
2407
2460
|
}));
|
|
@@ -2436,9 +2489,10 @@ var TypeStage = class {
|
|
|
2436
2489
|
* Answers claims through its type, lint, and runtime stages.
|
|
2437
2490
|
*
|
|
2438
2491
|
* @remarks
|
|
2439
|
-
* Construction resolves the target workspace's toolchain
|
|
2492
|
+
* Construction resolves the target workspace's toolchain. Starting warms every stage and waits
|
|
2493
|
+
* for lint and runtime; type warming and the boot controls continue behind that gate. The boot
|
|
2440
2494
|
* controls mutate imported dependencies and refuse service unless the type and runtime stages
|
|
2441
|
-
* report their respective changes. One
|
|
2495
|
+
* report their respective changes. One pool per stage admits inspections in arrival order, one at
|
|
2442
2496
|
* a time, so a stage never serves two claims at once and the deadline covers active work rather
|
|
2443
2497
|
* than queue wait. Each active stage inspection has a coordinator-owned deadline. An expiry at any
|
|
2444
2498
|
* stage abandons that stage and replaces it before the next queued inspection begins, so one slow
|
|
@@ -2483,34 +2537,36 @@ var TypeStage = class {
|
|
|
2483
2537
|
* const probe = new Probe({ workspace: process.cwd() })
|
|
2484
2538
|
* const verdict = await probe.prove(claim)
|
|
2485
2539
|
* verdict.digest // 'bdf03e5dfd6bd413ead671c7a2940fcf'
|
|
2486
|
-
* verdict.receipt // 'probe:bdf03e5dfd6bd413ead671c7a2940fcf:type:typescript@6.0.3:oxlint@1.
|
|
2540
|
+
* verdict.receipt // 'probe:bdf03e5dfd6bd413ead671c7a2940fcf:type:typescript@6.0.3:oxlint@1.87.0:vitest@4.1.11:configs/src/tsconfig.core.json@434f59254d58cf2683d453a26bd0d837'
|
|
2487
2541
|
* await probe.destroy()
|
|
2488
2542
|
* ```
|
|
2489
2543
|
*/
|
|
2490
2544
|
var Probe = class {
|
|
2491
2545
|
#workspace;
|
|
2492
2546
|
#deadline;
|
|
2547
|
+
#warmth;
|
|
2493
2548
|
#emitter;
|
|
2494
2549
|
#toolchain;
|
|
2495
2550
|
#type;
|
|
2496
2551
|
#lint;
|
|
2497
2552
|
#runtime;
|
|
2498
|
-
#typeQueue;
|
|
2499
|
-
#lintQueue;
|
|
2500
|
-
#runtimeQueue;
|
|
2501
2553
|
#deadlines = /* @__PURE__ */ new WeakSet();
|
|
2502
2554
|
#surfaced = /* @__PURE__ */ new WeakSet();
|
|
2503
|
-
#
|
|
2555
|
+
#survivors = /* @__PURE__ */ new Map();
|
|
2556
|
+
#abort = new AbortController();
|
|
2504
2557
|
#arming;
|
|
2558
|
+
#refusal;
|
|
2559
|
+
#starting;
|
|
2505
2560
|
#closing;
|
|
2506
2561
|
/**
|
|
2507
|
-
* Resolves the target toolchain and
|
|
2562
|
+
* Resolves the target toolchain and constructs the stage pools without warming them.
|
|
2508
2563
|
*
|
|
2509
|
-
* @param options - Workspace,
|
|
2564
|
+
* @param options - Workspace, inspection and warm bounds, and initial observation hooks
|
|
2510
2565
|
*/
|
|
2511
2566
|
constructor(options) {
|
|
2512
2567
|
this.#workspace = options?.workspace ?? process.cwd();
|
|
2513
2568
|
this.#deadline = createTimeout({ ms: options?.deadline ?? PROBE_DEADLINE }).ms;
|
|
2569
|
+
this.#warmth = createTimeout({ ms: options?.warm ?? PROBE_WARM }).ms;
|
|
2514
2570
|
this.#emitter = new Emitter({
|
|
2515
2571
|
...options?.on === void 0 ? {} : { on: options.on },
|
|
2516
2572
|
...options?.error === void 0 ? {} : { error: options.error }
|
|
@@ -2520,26 +2576,28 @@ var Probe = class {
|
|
|
2520
2576
|
oxlint: this.#version("oxlint"),
|
|
2521
2577
|
vitest: this.#version("vitest")
|
|
2522
2578
|
});
|
|
2523
|
-
this.#type =
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
handler: this.#inspectType.bind(this)
|
|
2579
|
+
this.#type = createPool({
|
|
2580
|
+
create: () => this.#warm(new TypeStage(this.#workspace)),
|
|
2581
|
+
destroy: this.#dispose.bind(this),
|
|
2582
|
+
error: this.#surface.bind(this),
|
|
2583
|
+
min: 1,
|
|
2584
|
+
restarts: PROBE_RESTARTS
|
|
2530
2585
|
});
|
|
2531
|
-
this.#
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2586
|
+
this.#lint = createPool({
|
|
2587
|
+
create: this.#createLint.bind(this),
|
|
2588
|
+
destroy: this.#dispose.bind(this),
|
|
2589
|
+
watch: (stage) => stage.exit,
|
|
2590
|
+
error: this.#surface.bind(this),
|
|
2591
|
+
min: 1,
|
|
2592
|
+
restarts: PROBE_RESTARTS
|
|
2535
2593
|
});
|
|
2536
|
-
this.#
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2594
|
+
this.#runtime = createPool({
|
|
2595
|
+
create: () => this.#warm(new RuntimeStage(this.#workspace)),
|
|
2596
|
+
destroy: this.#dispose.bind(this),
|
|
2597
|
+
error: this.#surface.bind(this),
|
|
2598
|
+
min: 1,
|
|
2599
|
+
restarts: PROBE_RESTARTS
|
|
2540
2600
|
});
|
|
2541
|
-
this.#arming = this.#arm();
|
|
2542
|
-
this.#arming.catch(() => {});
|
|
2543
2601
|
}
|
|
2544
2602
|
get emitter() {
|
|
2545
2603
|
return this.#emitter;
|
|
@@ -2551,7 +2609,16 @@ var Probe = class {
|
|
|
2551
2609
|
try {
|
|
2552
2610
|
this.#support();
|
|
2553
2611
|
this.#admit(claim);
|
|
2554
|
-
await this
|
|
2612
|
+
await this.start();
|
|
2613
|
+
const arming = this.#arming;
|
|
2614
|
+
try {
|
|
2615
|
+
await arming;
|
|
2616
|
+
} catch (error) {
|
|
2617
|
+
if (this.#arming === arming) this.#arming = void 0;
|
|
2618
|
+
const refusal = this.#refusal;
|
|
2619
|
+
this.#refusal = void 0;
|
|
2620
|
+
throw refusal === void 0 ? error : refusal.cause;
|
|
2621
|
+
}
|
|
2555
2622
|
if (this.#closing !== void 0) throw createDestroyedError("probe");
|
|
2556
2623
|
const started = performance.now();
|
|
2557
2624
|
const id = randomUUID();
|
|
@@ -2586,45 +2653,65 @@ var Probe = class {
|
|
|
2586
2653
|
}
|
|
2587
2654
|
destroy() {
|
|
2588
2655
|
if (this.#closing !== void 0) return this.#closing;
|
|
2589
|
-
this.#closing = this.#destroy();
|
|
2656
|
+
this.#closing = Promise.resolve().then(this.#destroy.bind(this));
|
|
2590
2657
|
return this.#closing;
|
|
2591
2658
|
}
|
|
2592
|
-
|
|
2593
|
-
|
|
2659
|
+
start() {
|
|
2660
|
+
if (this.#closing !== void 0) return Promise.reject(createDestroyedError("probe"));
|
|
2661
|
+
const warming = this.#type.start();
|
|
2662
|
+
warming.catch(() => {});
|
|
2663
|
+
const filling = Promise.all([this.#lint.start(), this.#runtime.start()]);
|
|
2664
|
+
filling.catch(() => {});
|
|
2665
|
+
if (this.#starting !== void 0) return this.#starting;
|
|
2666
|
+
this.#starting = this.#start(filling, warming);
|
|
2667
|
+
this.#starting.then(() => {
|
|
2668
|
+
this.#starting = void 0;
|
|
2669
|
+
}, () => {
|
|
2670
|
+
this.#starting = void 0;
|
|
2671
|
+
});
|
|
2672
|
+
return this.#starting;
|
|
2673
|
+
}
|
|
2674
|
+
async #start(filling, warming) {
|
|
2675
|
+
let created = false;
|
|
2594
2676
|
try {
|
|
2595
|
-
|
|
2596
|
-
|
|
2677
|
+
this.#support();
|
|
2678
|
+
if (this.#arming === void 0) created = this.#workbench();
|
|
2679
|
+
} catch (error) {
|
|
2680
|
+
this.#surface(error);
|
|
2681
|
+
throw error;
|
|
2682
|
+
}
|
|
2683
|
+
try {
|
|
2684
|
+
await filling;
|
|
2685
|
+
if (this.#closing !== void 0) throw createDestroyedError("probe");
|
|
2597
2686
|
} catch (error) {
|
|
2598
|
-
|
|
2687
|
+
throw this.#refuseArm(error);
|
|
2599
2688
|
}
|
|
2600
|
-
if (this.#arming ===
|
|
2601
|
-
this.#arming = this.#arm();
|
|
2602
|
-
this.#arming.catch(() => {
|
|
2689
|
+
if (this.#arming === void 0) {
|
|
2690
|
+
this.#arming = this.#arm(warming, created);
|
|
2691
|
+
this.#arming.catch(() => {
|
|
2692
|
+
if (this.#refusal === void 0) this.#arming = void 0;
|
|
2693
|
+
});
|
|
2603
2694
|
}
|
|
2604
|
-
await this.#arming;
|
|
2605
2695
|
}
|
|
2606
|
-
async #arm() {
|
|
2607
|
-
let created;
|
|
2696
|
+
async #arm(warming, created) {
|
|
2608
2697
|
try {
|
|
2609
|
-
|
|
2698
|
+
await warming;
|
|
2610
2699
|
} catch (error) {
|
|
2611
|
-
this.#
|
|
2612
|
-
|
|
2700
|
+
if (this.#closing !== void 0) throw createDestroyedError("probe");
|
|
2701
|
+
const cause = this.#refusal?.cause ?? (isPoolError(error) ? error.cause : error);
|
|
2702
|
+
this.#surface(cause);
|
|
2703
|
+
throw cause;
|
|
2613
2704
|
}
|
|
2614
2705
|
try {
|
|
2706
|
+
if (this.#closing !== void 0) throw createDestroyedError("probe");
|
|
2615
2707
|
await this.#boot(created);
|
|
2616
2708
|
} catch (error) {
|
|
2617
|
-
|
|
2618
|
-
origin: "instrument",
|
|
2619
|
-
code: "malformed",
|
|
2620
|
-
cause: error
|
|
2621
|
-
});
|
|
2622
|
-
this.#surface(failure);
|
|
2623
|
-
throw failure;
|
|
2709
|
+
throw this.#refuseArm(error);
|
|
2624
2710
|
}
|
|
2625
2711
|
this.#emitter.emit("arm", this.#toolchain);
|
|
2626
2712
|
}
|
|
2627
2713
|
#surface(error) {
|
|
2714
|
+
if (this.#closing !== void 0) return;
|
|
2628
2715
|
if (isProbeError(error)) this.#surfaced.add(error);
|
|
2629
2716
|
this.#emitter.emit("error", error);
|
|
2630
2717
|
}
|
|
@@ -2740,102 +2827,130 @@ var Probe = class {
|
|
|
2740
2827
|
subject,
|
|
2741
2828
|
claim
|
|
2742
2829
|
};
|
|
2743
|
-
|
|
2744
|
-
this.#
|
|
2745
|
-
this.#
|
|
2746
|
-
this.#
|
|
2747
|
-
];
|
|
2748
|
-
for (const pending of admitted) pending.catch(() => {});
|
|
2749
|
-
return Promise.all(admitted);
|
|
2830
|
+
return Promise.all([
|
|
2831
|
+
this.#inspectType(inspection),
|
|
2832
|
+
this.#inspectLint(inspection),
|
|
2833
|
+
this.#inspectRuntime(inspection)
|
|
2834
|
+
]);
|
|
2750
2835
|
}
|
|
2751
2836
|
async #inspectType(inspection) {
|
|
2752
|
-
const
|
|
2753
|
-
|
|
2754
|
-
const stage = this.#type;
|
|
2755
|
-
return await this.#inspectStage(stage, stage.progress, () => stage.inspect(inspection.subject, inspection.claim.project), inspection.claim);
|
|
2756
|
-
} finally {
|
|
2757
|
-
release();
|
|
2758
|
-
}
|
|
2837
|
+
const token = await this.#lease(this.#type);
|
|
2838
|
+
return this.#inspectStage(token, () => token.value.inspect(inspection.subject, inspection.claim.project), inspection.claim);
|
|
2759
2839
|
}
|
|
2760
|
-
#inspectLint(inspection) {
|
|
2761
|
-
const
|
|
2762
|
-
return this.#inspectStage(
|
|
2840
|
+
async #inspectLint(inspection) {
|
|
2841
|
+
const token = await this.#lease(this.#lint);
|
|
2842
|
+
return this.#inspectStage(token, (signal) => token.value.inspect(inspection.subject, { signal }), inspection.claim);
|
|
2763
2843
|
}
|
|
2764
|
-
#inspectRuntime(inspection) {
|
|
2765
|
-
const
|
|
2766
|
-
return this.#inspectStage(
|
|
2844
|
+
async #inspectRuntime(inspection) {
|
|
2845
|
+
const token = await this.#lease(this.#runtime);
|
|
2846
|
+
return this.#inspectStage(token, () => token.value.inspect(inspection.subject), inspection.claim);
|
|
2767
2847
|
}
|
|
2768
|
-
async #inspectStage(
|
|
2848
|
+
async #inspectStage(token, operation, claim, message = `The ${token.value.stage} stage exceeded ${this.#deadline} ms`) {
|
|
2849
|
+
const stage = token.value;
|
|
2769
2850
|
try {
|
|
2770
|
-
return await this.#bound(operation,
|
|
2851
|
+
return await this.#bound(operation, message, stage, stage.progress);
|
|
2771
2852
|
} catch (error) {
|
|
2772
|
-
this.#
|
|
2853
|
+
if (isProbeError(error) && this.#deadlines.has(error)) {
|
|
2854
|
+
await token.destroy().catch(() => {});
|
|
2855
|
+
if (this.#closing === void 0) this.#emitter.emit("expire", claim);
|
|
2856
|
+
}
|
|
2773
2857
|
throw error;
|
|
2858
|
+
} finally {
|
|
2859
|
+
token.release();
|
|
2774
2860
|
}
|
|
2775
2861
|
}
|
|
2776
|
-
async #bound(operation, message, stage, progress) {
|
|
2777
|
-
const timeout = createTimeout({ ms
|
|
2862
|
+
async #bound(operation, message, stage, progress, ms = this.#deadline) {
|
|
2863
|
+
const timeout = createTimeout({ ms });
|
|
2778
2864
|
timeout.start();
|
|
2779
2865
|
const expiry = this.#expiry(timeout, message, stage, progress);
|
|
2780
2866
|
const refusal = expiry.catch((error) => error);
|
|
2781
2867
|
try {
|
|
2782
2868
|
return await Promise.race([operation(timeout.signal), expiry]);
|
|
2783
2869
|
} catch (error) {
|
|
2784
|
-
if (
|
|
2785
|
-
|
|
2786
|
-
throw await refusal;
|
|
2870
|
+
if (timeout.expired) throw await refusal;
|
|
2871
|
+
throw error;
|
|
2787
2872
|
} finally {
|
|
2788
2873
|
timeout.clear();
|
|
2789
2874
|
}
|
|
2790
2875
|
}
|
|
2791
|
-
async #
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
timeout.clear();
|
|
2799
|
-
}
|
|
2800
|
-
if (this.#closing !== void 0) return false;
|
|
2801
|
-
if (stage === this.#type) {
|
|
2802
|
-
this.#type = new TypeStage(this.#workspace);
|
|
2803
|
-
return true;
|
|
2804
|
-
}
|
|
2805
|
-
if (stage === this.#lint) {
|
|
2806
|
-
this.#lint = new LintStage(this.#workspace);
|
|
2807
|
-
return true;
|
|
2876
|
+
async #resolve(claim) {
|
|
2877
|
+
const token = await this.#lease(this.#type);
|
|
2878
|
+
if (this.#refusal !== void 0) {
|
|
2879
|
+
const refusal = this.#refusal;
|
|
2880
|
+
this.#refusal = void 0;
|
|
2881
|
+
token.release();
|
|
2882
|
+
throw refusal.cause;
|
|
2808
2883
|
}
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2884
|
+
const stage = token.value;
|
|
2885
|
+
return this.#inspectStage(token, () => stage.resolve(claim.project), claim, `The type stage project resolution exceeded ${this.#deadline} ms`);
|
|
2886
|
+
}
|
|
2887
|
+
async #lease(pool) {
|
|
2888
|
+
try {
|
|
2889
|
+
return await pool.acquire();
|
|
2890
|
+
} catch (error) {
|
|
2891
|
+
if (isPoolError(error)) {
|
|
2892
|
+
if (error.code === "destroyed") throw createDestroyedError("probe");
|
|
2893
|
+
if (error.code === "create" && pool === this.#type) {
|
|
2894
|
+
const refusal = this.#refusal;
|
|
2895
|
+
this.#refusal = void 0;
|
|
2896
|
+
throw refusal === void 0 ? error.cause : refusal.cause;
|
|
2897
|
+
}
|
|
2898
|
+
if (error.code === "create" || error.code === "cleanup") throw error.cause;
|
|
2899
|
+
}
|
|
2900
|
+
throw error;
|
|
2812
2901
|
}
|
|
2813
|
-
return false;
|
|
2814
2902
|
}
|
|
2815
|
-
|
|
2816
|
-
const
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2903
|
+
#createLint() {
|
|
2904
|
+
for (const cause of this.#survivors.values()) throw new ProbeError("The Oxlint language server survived cleanup; restart the probe server", {
|
|
2905
|
+
origin: "instrument",
|
|
2906
|
+
code: "malformed",
|
|
2907
|
+
context: { stage: "lint" },
|
|
2908
|
+
cause
|
|
2909
|
+
});
|
|
2910
|
+
return this.#warm(new LintStage(this.#workspace));
|
|
2821
2911
|
}
|
|
2822
|
-
async #
|
|
2823
|
-
const
|
|
2912
|
+
async #warm(stage) {
|
|
2913
|
+
const bound = stage.stage === "type" ? this.#warmth : this.#deadline;
|
|
2914
|
+
const aborted = Promise.withResolvers();
|
|
2915
|
+
aborted.promise.catch(() => {});
|
|
2916
|
+
const subscription = addAbortListener(this.#abort.signal, () => aborted.reject(createDestroyedError("probe")));
|
|
2824
2917
|
try {
|
|
2825
|
-
|
|
2918
|
+
if (this.#closing !== void 0) throw createDestroyedError("probe");
|
|
2919
|
+
await this.#bound(() => Promise.race([stage.start(), aborted.promise]), `The ${stage.stage} stage warm exceeded ${bound} ms`, stage, stage.progress, bound);
|
|
2920
|
+
return stage;
|
|
2921
|
+
} catch (error) {
|
|
2922
|
+
if (stage.stage === "type" && this.#closing === void 0) this.#refusal ??= { cause: error };
|
|
2826
2923
|
try {
|
|
2827
|
-
|
|
2828
|
-
} catch (
|
|
2829
|
-
this.#
|
|
2830
|
-
throw
|
|
2924
|
+
await this.#dispose(stage);
|
|
2925
|
+
} catch (failure) {
|
|
2926
|
+
this.#survivors.set(stage, failure);
|
|
2927
|
+
throw failure;
|
|
2831
2928
|
}
|
|
2929
|
+
throw error;
|
|
2832
2930
|
} finally {
|
|
2833
|
-
|
|
2931
|
+
subscription[Symbol.dispose]();
|
|
2834
2932
|
}
|
|
2835
2933
|
}
|
|
2836
|
-
#
|
|
2837
|
-
|
|
2838
|
-
|
|
2934
|
+
async #dispose(stage) {
|
|
2935
|
+
const bound = stage.stage === "lint" ? Math.max(this.#deadline, LINT_TEARDOWN) : this.#deadline;
|
|
2936
|
+
try {
|
|
2937
|
+
await this.#bound(() => stage.destroy(), `The ${stage.stage} stage teardown exceeded ${bound} ms`, stage, stage.progress, bound);
|
|
2938
|
+
} catch (error) {
|
|
2939
|
+
if (isProbeError(error) && this.#deadlines.has(error)) return;
|
|
2940
|
+
if (this.#closing !== void 0 || stage.stage === "lint" && isProbeError(error) && isLSPError(error.cause) && error.cause.code === "timeout") throw error;
|
|
2941
|
+
this.#surface(error);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
#refuseArm(error) {
|
|
2945
|
+
if (this.#closing !== void 0) return createDestroyedError("probe");
|
|
2946
|
+
const cause = isPoolError(error) ? error.cause : error;
|
|
2947
|
+
const failure = new ProbeError(`The probe could not arm: ${describeUnknown(cause)}`, {
|
|
2948
|
+
origin: "instrument",
|
|
2949
|
+
code: "malformed",
|
|
2950
|
+
cause
|
|
2951
|
+
});
|
|
2952
|
+
this.#surface(failure);
|
|
2953
|
+
return failure;
|
|
2839
2954
|
}
|
|
2840
2955
|
#expiry(timeout, message, stage, progress) {
|
|
2841
2956
|
return new Promise((_resolve, reject) => {
|
|
@@ -2845,7 +2960,7 @@ var Probe = class {
|
|
|
2845
2960
|
code: "deadline",
|
|
2846
2961
|
context: {
|
|
2847
2962
|
stage: stage.stage,
|
|
2848
|
-
deadline:
|
|
2963
|
+
deadline: timeout.ms
|
|
2849
2964
|
}
|
|
2850
2965
|
});
|
|
2851
2966
|
this.#deadlines.add(error);
|
|
@@ -2854,26 +2969,23 @@ var Probe = class {
|
|
|
2854
2969
|
});
|
|
2855
2970
|
}
|
|
2856
2971
|
async #destroy() {
|
|
2972
|
+
this.#abort.abort();
|
|
2973
|
+
const barriers = Promise.allSettled([
|
|
2974
|
+
this.#type.destroy(),
|
|
2975
|
+
this.#lint.destroy(),
|
|
2976
|
+
this.#runtime.destroy()
|
|
2977
|
+
]);
|
|
2857
2978
|
try {
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
await Promise.
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
this.#destroyStage(this.#runtime)
|
|
2865
|
-
]);
|
|
2979
|
+
await this.#starting?.catch(() => {});
|
|
2980
|
+
await this.#arming?.catch(() => {});
|
|
2981
|
+
const results = await barriers;
|
|
2982
|
+
const survivors = await Promise.allSettled([...this.#survivors.keys()].map((stage) => this.#dispose(stage)));
|
|
2983
|
+
this.#survivors.clear();
|
|
2984
|
+
for (const result of [...results, ...survivors]) if (result.status === "rejected") throw isPoolError(result.reason) ? result.reason.cause : result.reason;
|
|
2866
2985
|
} finally {
|
|
2867
2986
|
this.#emitter.destroy();
|
|
2868
2987
|
}
|
|
2869
2988
|
}
|
|
2870
|
-
async #destroyStage(stage) {
|
|
2871
|
-
try {
|
|
2872
|
-
await this.#bound(() => stage.destroy(), `The ${stage.stage} stage teardown exceeded ${this.#deadline} ms`, stage, stage.progress);
|
|
2873
|
-
} catch (error) {
|
|
2874
|
-
if (!isProbeError(error) || !this.#deadlines.has(error)) throw error;
|
|
2875
|
-
}
|
|
2876
|
-
}
|
|
2877
2989
|
#version(name) {
|
|
2878
2990
|
const version = readWorkspaceManifest(this.#workspace, name).contents.version;
|
|
2879
2991
|
if (!isString(version)) throw new ProbeError(`${name} publishes no readable version`, {
|
|
@@ -2918,12 +3030,13 @@ var Probe = class {
|
|
|
2918
3030
|
/**
|
|
2919
3031
|
* Implements `ProbeServerInterface` over a `PassThrough` stream this server owns, binding the
|
|
2920
3032
|
* published `prove` tool and the dual-era dispatcher to this process's Model Context Protocol
|
|
2921
|
-
* stdio transport.
|
|
3033
|
+
* stdio transport. Starting creates the probe and awaits its lint and runtime onset; type warming
|
|
3034
|
+
* and the boot controls continue behind it.
|
|
2922
3035
|
*
|
|
2923
3036
|
* @remarks
|
|
2924
3037
|
* Construction snapshots the probe options, publishes the `prove` tool, and binds the dual-era
|
|
2925
|
-
* dispatcher to the stdio transport.
|
|
2926
|
-
*
|
|
3038
|
+
* dispatcher to the stdio transport.
|
|
3039
|
+
* Discovery remains available while the legacy handshake awaits that onset. Construction is held
|
|
2927
3040
|
* before it begins, so teardown entered through a construction callback waits for the resulting
|
|
2928
3041
|
* probe and releases it. A constructor refusal clears that held operation for a later retry.
|
|
2929
3042
|
*
|
|
@@ -2933,7 +3046,8 @@ var Probe = class {
|
|
|
2933
3046
|
* serving is still attached and still fires afterwards. The transport reads a stream this server
|
|
2934
3047
|
* owns rather than this process's standard input, which is what makes that possible: the
|
|
2935
3048
|
* transport's own listeners land on that stream, and the only listeners this server ever puts on
|
|
2936
|
-
* `process.stdin` are the `data`, `close`, and `error` forwarders
|
|
3049
|
+
* `process.stdin` are the `data`, `close`, and `error` forwarders and the input-end teardown.
|
|
3050
|
+
* `destroy` pauses the
|
|
2937
3051
|
* stream only when this server is what set it flowing and nothing else is reading it, so a process
|
|
2938
3052
|
* that outlives this server reads its own standard input again and receives its own signals.
|
|
2939
3053
|
* Teardown removes the signal handlers first: it takes seconds when a boot is in flight, and a
|
|
@@ -2942,7 +3056,7 @@ var Probe = class {
|
|
|
2942
3056
|
* @example
|
|
2943
3057
|
* ```ts
|
|
2944
3058
|
* const server = new ProbeServer({ workspace: process.cwd() })
|
|
2945
|
-
* server.start()
|
|
3059
|
+
* await server.start()
|
|
2946
3060
|
* await server.destroy()
|
|
2947
3061
|
* ```
|
|
2948
3062
|
*/
|
|
@@ -2954,24 +3068,28 @@ var ProbeServer = class {
|
|
|
2954
3068
|
#close;
|
|
2955
3069
|
#error;
|
|
2956
3070
|
#signal;
|
|
3071
|
+
#end;
|
|
3072
|
+
#starting;
|
|
2957
3073
|
#server;
|
|
2958
3074
|
#owns;
|
|
2959
3075
|
#construction;
|
|
2960
3076
|
#closing;
|
|
2961
3077
|
/**
|
|
2962
3078
|
* Snapshots the probe options this server will use and binds the published schema to this
|
|
2963
|
-
* process's stdio transport
|
|
3079
|
+
* process's stdio transport without warming the workspace.
|
|
2964
3080
|
*
|
|
2965
|
-
* @param options - Workspace, deadline, and initial observation hooks for the
|
|
3081
|
+
* @param options - Workspace, deadline, and initial observation hooks for the probe
|
|
2966
3082
|
*/
|
|
2967
3083
|
constructor(options) {
|
|
2968
3084
|
const workspace = options?.workspace;
|
|
2969
3085
|
const deadline = options?.deadline;
|
|
3086
|
+
const warm = options?.warm;
|
|
2970
3087
|
const on = options?.on;
|
|
2971
3088
|
const error = options?.error;
|
|
2972
3089
|
this.#options = Object.freeze({
|
|
2973
3090
|
workspace: resolve(workspace ?? process.cwd()),
|
|
2974
3091
|
...deadline === void 0 ? {} : { deadline },
|
|
3092
|
+
...warm === void 0 ? {} : { warm },
|
|
2975
3093
|
...on === void 0 ? {} : { on: Object.freeze({ ...on }) },
|
|
2976
3094
|
...error === void 0 ? {} : { error }
|
|
2977
3095
|
});
|
|
@@ -2982,22 +3100,52 @@ var ProbeServer = class {
|
|
|
2982
3100
|
this.#close = this.#finish.bind(this);
|
|
2983
3101
|
this.#error = this.#fail.bind(this);
|
|
2984
3102
|
this.#signal = this.#release.bind(this);
|
|
3103
|
+
this.#end = this.#release.bind(this);
|
|
2985
3104
|
}
|
|
2986
3105
|
start() {
|
|
2987
|
-
if (this.#closing !== void 0)
|
|
2988
|
-
|
|
3106
|
+
if (this.#closing !== void 0) return Promise.reject(createDestroyedError("probe server"));
|
|
3107
|
+
this.#starting ??= this.#setup();
|
|
3108
|
+
this.#starting.catch(() => {});
|
|
3109
|
+
return this.#starting;
|
|
3110
|
+
}
|
|
3111
|
+
destroy() {
|
|
3112
|
+
if (this.#closing !== void 0) return this.#closing;
|
|
3113
|
+
this.#closing = this.#destroy();
|
|
3114
|
+
return this.#closing;
|
|
3115
|
+
}
|
|
3116
|
+
async #setup() {
|
|
2989
3117
|
this.#owns = process.stdin.readableFlowing !== true;
|
|
2990
3118
|
this.#transport.start();
|
|
2991
3119
|
process.stdin.on("data", this.#data);
|
|
2992
3120
|
process.stdin.on("close", this.#close);
|
|
2993
3121
|
process.stdin.on("error", this.#error);
|
|
3122
|
+
process.stdin.on("end", this.#end);
|
|
2994
3123
|
process.on("SIGINT", this.#signal);
|
|
2995
3124
|
process.on("SIGTERM", this.#signal);
|
|
3125
|
+
try {
|
|
3126
|
+
const probe = await this.#resolveProbe();
|
|
3127
|
+
if (this.#closing !== void 0) return;
|
|
3128
|
+
await probe.start();
|
|
3129
|
+
if (this.#closing !== void 0) return;
|
|
3130
|
+
} catch (error) {
|
|
3131
|
+
if (this.#closing !== void 0) return;
|
|
3132
|
+
throw error;
|
|
3133
|
+
}
|
|
2996
3134
|
}
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3135
|
+
async #handshake(options) {
|
|
3136
|
+
options.signal.throwIfAborted();
|
|
3137
|
+
const aborted = Promise.withResolvers();
|
|
3138
|
+
const listener = addAbortListener(options.signal, () => aborted.reject(options.signal.reason));
|
|
3139
|
+
try {
|
|
3140
|
+
await Promise.race([this.#starting, aborted.promise]);
|
|
3141
|
+
if (this.#closing !== void 0) throw createDestroyedError("probe server");
|
|
3142
|
+
} catch (error) {
|
|
3143
|
+
options.signal.throwIfAborted();
|
|
3144
|
+
if (isProbeError(error)) throw createHandshakeError(error);
|
|
3145
|
+
throw error;
|
|
3146
|
+
} finally {
|
|
3147
|
+
listener[Symbol.dispose]();
|
|
3148
|
+
}
|
|
3001
3149
|
}
|
|
3002
3150
|
async #destroy() {
|
|
3003
3151
|
process.removeListener("SIGINT", this.#signal);
|
|
@@ -3006,13 +3154,17 @@ var ProbeServer = class {
|
|
|
3006
3154
|
process.stdin.removeListener("data", this.#data);
|
|
3007
3155
|
process.stdin.removeListener("close", this.#close);
|
|
3008
3156
|
process.stdin.removeListener("error", this.#error);
|
|
3157
|
+
process.stdin.removeListener("end", this.#end);
|
|
3009
3158
|
if (this.#owns === true && process.stdin.listenerCount("data") === 0) process.stdin.pause();
|
|
3010
3159
|
this.#owns = void 0;
|
|
3011
3160
|
this.#stream.destroy();
|
|
3012
3161
|
const construction = this.#construction;
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3162
|
+
try {
|
|
3163
|
+
const probe = await construction?.catch(() => void 0);
|
|
3164
|
+
if (probe !== void 0) await probe.destroy();
|
|
3165
|
+
} finally {
|
|
3166
|
+
await this.#starting?.catch(() => {});
|
|
3167
|
+
}
|
|
3016
3168
|
}
|
|
3017
3169
|
#receive(chunk) {
|
|
3018
3170
|
this.#stream.write(chunk);
|
|
@@ -3046,7 +3198,8 @@ var ProbeServer = class {
|
|
|
3046
3198
|
},
|
|
3047
3199
|
tools,
|
|
3048
3200
|
limit: { keys: PROBE_KEYS },
|
|
3049
|
-
execution: this.#execute.bind(this)
|
|
3201
|
+
execution: this.#execute.bind(this),
|
|
3202
|
+
handshake: this.#handshake.bind(this)
|
|
3050
3203
|
});
|
|
3051
3204
|
}
|
|
3052
3205
|
async #prove(input) {
|
|
@@ -3113,6 +3266,6 @@ var ProbeServer = class {
|
|
|
3113
3266
|
}
|
|
3114
3267
|
};
|
|
3115
3268
|
//#endregion
|
|
3116
|
-
export { LintStage, Overlay, Probe, ProbeServer, RuntimeStage, TypeStage, buildRevisionPath, captureListeners, collectWorkspaceFiles, computeDigest, 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 };
|
|
3269
|
+
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 };
|
|
3117
3270
|
|
|
3118
3271
|
//# sourceMappingURL=index.js.map
|