@orkestrel/probe 0.0.23 → 0.0.25

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.
@@ -14,6 +14,7 @@ let node_events = require("node:events");
14
14
  let _orkestrel_timeout = require("@orkestrel/timeout");
15
15
  let _orkestrel_lsp_server = require("@orkestrel/lsp/server");
16
16
  let node_stream = require("node:stream");
17
+ let node_async_hooks = require("node:async_hooks");
17
18
  let node_child_process = require("node:child_process");
18
19
  let _orkestrel_mcp_server = require("@orkestrel/mcp/server");
19
20
  let _orkestrel_tool = require("@orkestrel/tool");
@@ -533,6 +534,8 @@ function readWorkspaceManifest(workspace, name) {
533
534
  * @remarks
534
535
  * A package publishes its binaries under keys of its own choosing, and that key is often not the
535
536
  * package name: `typescript` publishes `tsc`. Name the key as `command` wherever the two differ.
537
+ * Resolution reads `package.json` and its `bin` field, so TypeScript 6 and 7 need no exported
538
+ * `typescript/bin/tsc` subpath.
536
539
  * Default: the package name.
537
540
  *
538
541
  * @param workspace - The target workspace root
@@ -975,7 +978,7 @@ function captureListeners(emitter, events) {
975
978
  */
976
979
  function releaseListeners(emitter, capture) {
977
980
  for (const [event, captured] of capture) for (const listener of emitter.listeners(event)) {
978
- if (captured.includes(listener)) continue;
981
+ if (captured.includes(listener) || !(0, _orkestrel_contract.isFunction)(listener)) continue;
979
982
  emitter.removeListener(event, listener);
980
983
  }
981
984
  }
@@ -1124,10 +1127,10 @@ var Overlay = class {
1124
1127
  };
1125
1128
  //#endregion
1126
1129
  //#region package.json
1127
- var version = "0.0.23";
1130
+ var version = "0.0.25";
1128
1131
  var peerDependencies = {
1129
- "typescript": "^6.0.3",
1130
- "vitest": "^4.1.11"
1132
+ "typescript": "^6.0.3 || ^7.0.2",
1133
+ "vitest": "^4.1.12 || ^5.0.5"
1131
1134
  };
1132
1135
  //#endregion
1133
1136
  //#region src/server/stages/LintStage.ts
@@ -1349,6 +1352,11 @@ var LintStage = class {
1349
1352
  * Inspects tests through one resident Vitest service from the target workspace.
1350
1353
  *
1351
1354
  * @remarks
1355
+ * Vitest 4.1.12 and 5.0.5 share the mode-first `createVitest` overload used by this stage.
1356
+ * Result eviction removes one file from Vitest 4's results cache. Vitest 5 exposes only a full
1357
+ * cache clear, so eviction clears its results, dependency, and filesystem module caches.
1358
+ * Warming removes termination handlers registered within the factory's async context before
1359
+ * signal delivery, while retaining handlers the host registers outside that context.
1352
1360
  * Starting launches Vitest with the threads pool and instruments each inline or function-declared
1353
1361
  * project with a Vite plugin that reads the active inspection's candidate overlay. A selected
1354
1362
  * string-declared project reports a workspace issue because its configuration carries no runtime
@@ -1573,38 +1581,46 @@ var RuntimeStage = class {
1573
1581
  this.#sweep();
1574
1582
  const output = new node_stream.PassThrough();
1575
1583
  output.resume();
1584
+ const scope = new node_async_hooks.AsyncLocalStorage();
1585
+ const listener = this.#captureSignal.bind(this, scope);
1576
1586
  const signals = captureListeners(process, ["SIGINT", "SIGTERM"]);
1577
- let warming;
1587
+ process.on("newListener", listener);
1578
1588
  try {
1579
- warming = factory("test", {
1580
- root: this.#workspace,
1581
- config: resolveWorkspaceFile(this.#workspace, "vite.config.ts"),
1582
- watch: false,
1583
- run: true,
1584
- pool: "threads",
1585
- reporters: [{
1586
- onInit() {},
1587
- onTestRunEnd() {}
1588
- }]
1589
- }, { plugins: [{
1590
- name: "orkestrel-project-instrumentation",
1591
- enforce: "post",
1592
- config: this.#configure.bind(this)
1593
- }] }, {
1594
- stdout: output,
1595
- stderr: process.stderr
1589
+ return await scope.run(true, () => {
1590
+ try {
1591
+ return factory("test", {
1592
+ root: this.#workspace,
1593
+ config: resolveWorkspaceFile(this.#workspace, "vite.config.ts"),
1594
+ watch: false,
1595
+ run: true,
1596
+ pool: "threads",
1597
+ reporters: [{
1598
+ onInit() {},
1599
+ onTestRunEnd() {}
1600
+ }]
1601
+ }, { plugins: [{
1602
+ name: "orkestrel-project-instrumentation",
1603
+ enforce: "post",
1604
+ config: this.#configure.bind(this)
1605
+ }] }, {
1606
+ stdout: output,
1607
+ stderr: process.stderr
1608
+ });
1609
+ } finally {
1610
+ releaseListeners(process, signals);
1611
+ }
1596
1612
  });
1597
1613
  } catch (error) {
1598
1614
  throw this.#configuration(error);
1599
1615
  } finally {
1600
- releaseListeners(process, signals);
1601
- }
1602
- try {
1603
- return await warming;
1604
- } catch (error) {
1605
- throw this.#configuration(error);
1616
+ process.off("newListener", listener);
1617
+ scope.disable();
1606
1618
  }
1607
1619
  }
1620
+ #captureSignal(scope, event, listener) {
1621
+ if (scope.getStore() !== true || event !== "SIGINT" && event !== "SIGTERM") return;
1622
+ queueMicrotask(() => process.off(event, listener));
1623
+ }
1608
1624
  #configure(config) {
1609
1625
  const test = config.test;
1610
1626
  if (test?.projects === void 0) return;
@@ -1798,8 +1814,16 @@ var RuntimeStage = class {
1798
1814
  }
1799
1815
  vitest.watcher.onFileDelete(file);
1800
1816
  vitest.watcher.invalidates.delete(file);
1801
- vitest.cache.results.removeFromCache(normalizePath((0, node_path.relative)(this.#workspace, file)));
1802
- await vitest.cache.results.writeToCache();
1817
+ const results = "results" in vitest.cache ? vitest.cache.results : void 0;
1818
+ if ((0, _orkestrel_contract.isObject)(results) && "removeFromCache" in results && typeof results.removeFromCache === "function" && "writeToCache" in results && typeof results.writeToCache === "function") {
1819
+ results.removeFromCache(normalizePath((0, node_path.relative)(this.#workspace, file)));
1820
+ await results.writeToCache();
1821
+ } else if ("clearCache" in vitest && typeof vitest.clearCache === "function") await vitest.clearCache();
1822
+ else throw new _src_core.ProbeError("Vitest exposes no cache eviction API", {
1823
+ origin: "instrument",
1824
+ code: "malformed",
1825
+ context: { stage: this.stage }
1826
+ });
1803
1827
  return [];
1804
1828
  } catch (error) {
1805
1829
  return [{
@@ -2008,6 +2032,8 @@ var RuntimeStage = class {
2008
2032
  * Inspects TypeScript source by running the target workspace's own compiler over a mirror of it.
2009
2033
  *
2010
2034
  * @remarks
2035
+ * TypeScript 6 and 7 run through the `tsc` entry in their package manifest; neither a compiler API
2036
+ * nor an exported binary subpath is required.
2011
2037
  * The compiler is a process this stage spawns rather than a module it loads, so nothing here holds
2012
2038
  * a resident program and the host's event loop is free for the whole check. Starting sweeps a
2013
2039
  * mirror an earlier host left behind, copies the workspace into a fresh one under
@@ -2045,6 +2071,7 @@ var TypeStage = class {
2045
2071
  #revision = `${process.pid}-${(0, node_crypto.randomUUID)()}`;
2046
2072
  #mirror;
2047
2073
  #configs = /* @__PURE__ */ new Map();
2074
+ #validated = /* @__PURE__ */ new Set();
2048
2075
  #mirrored = /* @__PURE__ */ new Map();
2049
2076
  #drafts = /* @__PURE__ */ new Set();
2050
2077
  #children = /* @__PURE__ */ new Set();
@@ -2098,6 +2125,8 @@ var TypeStage = class {
2098
2125
  * refreshed from the workspace first on a cache miss, so the digest names the configuration the
2099
2126
  * check applies. The reading is cached per project for the life of the stage, so a draft written
2100
2127
  * afterward cannot move it.
2128
+ * Before returning the identity, validates the project with `--noEmit` unless an unoverlaid check
2129
+ * already validated it. TypeScript 7's `--showConfig` can succeed for a malformed project.
2101
2130
  *
2102
2131
  * @param project - The workspace-relative TypeScript project to resolve
2103
2132
  * @returns The resolved workspace-relative path and the digest of its compiler options
@@ -2157,6 +2186,10 @@ var TypeStage = class {
2157
2186
  const contained = this.#contain(project);
2158
2187
  this.#progress += 1;
2159
2188
  const config = await this.#configure(contained);
2189
+ if (!this.#validated.has(contained)) {
2190
+ this.#refresh();
2191
+ await this.#check(contained, []);
2192
+ }
2160
2193
  return {
2161
2194
  path: contained,
2162
2195
  digest: computeDigest(this.#workspace, config.compilerOptions)
@@ -2176,6 +2209,7 @@ var TypeStage = class {
2176
2209
  });
2177
2210
  } catch {}
2178
2211
  this.#configs.clear();
2212
+ this.#validated.clear();
2179
2213
  this.#mirrored.clear();
2180
2214
  this.#drafts.clear();
2181
2215
  }
@@ -2349,7 +2383,9 @@ var TypeStage = class {
2349
2383
  }
2350
2384
  });
2351
2385
  }
2352
- return this.#issues(diagnostics, project);
2386
+ const issues = this.#issues(diagnostics, project, scratch);
2387
+ if (this.#drafts.size === 0) this.#validated.add(this.#contain(project));
2388
+ return issues;
2353
2389
  }
2354
2390
  #scratch(project, config, drafts) {
2355
2391
  const directory = (0, node_path.dirname)(project);
@@ -2374,13 +2410,13 @@ var TypeStage = class {
2374
2410
  (0, node_fs.writeFileSync)(target, `${JSON.stringify(body, void 0, " ")}\n`, "utf8");
2375
2411
  return contained;
2376
2412
  }
2377
- #issues(diagnostics, project) {
2413
+ #issues(diagnostics, project, scratch) {
2378
2414
  const issues = [];
2379
2415
  for (const diagnostic of diagnostics) {
2380
2416
  const message = this.#translate(diagnostic.message);
2381
2417
  const resolved = diagnostic.path === void 0 ? void 0 : (0, node_path.resolve)(this.#mirror, diagnostic.path);
2382
2418
  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, {
2419
+ if (resolved === void 0 || !drafted && (0, node_path.extname)(resolved) === ".json") throw new _src_core.ProbeError(message.replaceAll(normalizePath(scratch), normalizePath(project)), {
2384
2420
  origin: "workspace",
2385
2421
  code: "malformed",
2386
2422
  context: {
@@ -2490,6 +2526,8 @@ var TypeStage = class {
2490
2526
  * Answers claims through its type, lint, and runtime stages.
2491
2527
  *
2492
2528
  * @remarks
2529
+ * The workspace peers are TypeScript `^6.0.3 || ^7.0.2` and Vitest `^4.1.12 || ^5.0.5`.
2530
+ * Starting refuses TypeScript majors outside 6 and 7 before a stage starts.
2493
2531
  * Construction resolves the target workspace's toolchain. Starting warms every stage and waits
2494
2532
  * for lint and runtime; type warming and the boot controls continue behind that gate. The boot
2495
2533
  * controls mutate imported dependencies and refuse service unless the type and runtime stages
@@ -2538,7 +2576,7 @@ var TypeStage = class {
2538
2576
  * const probe = new Probe({ workspace: process.cwd() })
2539
2577
  * const verdict = await probe.prove(claim)
2540
2578
  * verdict.digest // 'bdf03e5dfd6bd413ead671c7a2940fcf'
2541
- * verdict.receipt // 'probe:bdf03e5dfd6bd413ead671c7a2940fcf:type:typescript@6.0.3:oxlint@1.87.0:vitest@4.1.11:configs/src/tsconfig.core.json@434f59254d58cf2683d453a26bd0d837'
2579
+ * verdict.receipt?.startsWith(`probe:${verdict.digest}:type:`) // true
2542
2580
  * await probe.destroy()
2543
2581
  * ```
2544
2582
  */
@@ -3014,9 +3052,9 @@ var Probe = class {
3014
3052
  #support() {
3015
3053
  const version = this.#toolchain.typescript;
3016
3054
  const range = peerDependencies.typescript;
3017
- const supported = /^\^(\d+)\./u.exec(range)?.[1];
3018
3055
  const found = /^(\d+)\./u.exec(version)?.[1];
3019
- if (supported === void 0 || found !== supported) throw new _src_core.ProbeError(`The supported TypeScript range is ${range}; found ${version}`, {
3056
+ const supported = range.split(" || ").some((entry) => /^\^(\d+)\./u.exec(entry)?.[1] === found);
3057
+ if (found === void 0 || !supported) throw new _src_core.ProbeError(`The supported TypeScript range is ${range}; found ${version}`, {
3020
3058
  origin: "workspace",
3021
3059
  code: "malformed",
3022
3060
  context: {