@orkestrel/probe 0.0.5 → 0.0.8

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.
@@ -9,7 +9,8 @@ let _src_core = require("../core/index.cjs");
9
9
  let _orkestrel_emitter = require("@orkestrel/emitter");
10
10
  let _orkestrel_queue = require("@orkestrel/queue");
11
11
  let _orkestrel_timeout = require("@orkestrel/timeout");
12
- let node_child_process = require("node:child_process");
12
+ let _orkestrel_lsp = require("@orkestrel/lsp");
13
+ let _orkestrel_lsp_server = require("@orkestrel/lsp/server");
13
14
  let node_stream = require("node:stream");
14
15
  let node_timers_promises = require("node:timers/promises");
15
16
  let _orkestrel_mcp = require("@orkestrel/mcp");
@@ -114,12 +115,18 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
114
115
  * A containment walk and the write that follows it are separate calls, so a process that swaps the
115
116
  * final component between them decides where the bytes land. `writeFileSync` opens with the default
116
117
  * `w` flag, which follows a symbolic link at that component and creates the file when it is absent,
117
- * so the walk's reading does not bind the write. Opening `O_WRONLY | O_TRUNC | O_NOFOLLOW` moves
118
- * that decision into the call that writes: a link at the final component refuses the open, and the
118
+ * so the walk's reading does not bind the write. Opening `O_WRONLY | O_NOFOLLOW` moves that
119
+ * decision into the call that writes: a link at the final component refuses the open, and the
119
120
  * omitted `O_CREAT` fails `ENOENT` for a target that has gone since the walk saw it. This is the
120
121
  * mutating counterpart of the `wx` flag every create in this package uses. Which code the refusal
121
122
  * carries is the host's; Linux with Node v22.22.2 reported `ELOOP` on 2026-08-24.
122
123
  *
124
+ * Truncation runs through the held descriptor rather than through an `O_TRUNC` flag, so it reaches
125
+ * the file the open bound. A Windows host refuses the numeric `O_TRUNC` without `O_CREAT` outright:
126
+ * measured on 2026-08-26 on Windows 11 with Node v24.19.0 over NTFS, `openSync` reported `EINVAL`
127
+ * for `O_WRONLY | O_TRUNC` and for `O_RDWR | O_TRUNC` while `O_WRONLY` alone opened, and adding
128
+ * `O_CREAT` would trade that refusal for the create this function must never perform.
129
+ *
123
130
  * A directory component stays open, because Node exposes no descriptor-relative call to walk and
124
131
  * write through one set of descriptors. Where a host's Node build defines no `O_NOFOLLOW`, that flag
125
132
  * contributes nothing to the flag set and the final component is open on that host too.
@@ -139,8 +146,9 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
139
146
  * ```
140
147
  */
141
148
  function overwriteFile(file, text) {
142
- const descriptor = (0, node_fs.openSync)(file, node_fs.constants.O_WRONLY | node_fs.constants.O_TRUNC | node_fs.constants.O_NOFOLLOW);
149
+ const descriptor = (0, node_fs.openSync)(file, node_fs.constants.O_WRONLY | node_fs.constants.O_NOFOLLOW);
143
150
  try {
151
+ (0, node_fs.ftruncateSync)(descriptor, 0);
144
152
  (0, node_fs.writeFileSync)(descriptor, text, "utf8");
145
153
  } finally {
146
154
  (0, node_fs.closeSync)(descriptor);
@@ -515,24 +523,6 @@ function matchesWorkspaceModule(path) {
515
523
  return /\.(?:[cm]?[jt]sx?|vue|json)$/.test(path);
516
524
  }
517
525
  /**
518
- * Reads a JSON-RPC frame's declared byte length.
519
- *
520
- * @param header - The frame header text without its terminating empty line
521
- * @returns The non-negative content length, or `undefined` for an invalid header
522
- *
523
- * @example
524
- * ```ts
525
- * parseContentLength('Content-Length: 128') // 128
526
- * parseContentLength('Content-Type: application/json') // undefined
527
- * ```
528
- */
529
- function parseContentLength(header) {
530
- const text = /(?:^|\r\n)Content-Length:\s*(\d+)(?:\r\n|$)/i.exec(header)?.[1];
531
- if (text === void 0) return void 0;
532
- const length = Number.parseInt(text, 10);
533
- return Number.isSafeInteger(length) && length >= 0 ? length : void 0;
534
- }
535
- /**
536
526
  * Normalizes a caught or foreign error into readable text.
537
527
  *
538
528
  * @param value - The value to describe
@@ -806,7 +796,7 @@ var Overlay = class {
806
796
  };
807
797
  //#endregion
808
798
  //#region package.json
809
- var version = "0.0.5";
799
+ var version = "0.0.8";
810
800
  var peerDependencies = {
811
801
  "oxlint": "^1.77.0",
812
802
  "typescript": "^6.0.0",
@@ -818,13 +808,22 @@ var peerDependencies = {
818
808
  * Inspects virtual documents through one resident Oxlint language server.
819
809
  *
820
810
  * @remarks
821
- * Construction starts the target workspace's Oxlint binary with its Language Server Protocol
822
- * mode. Each inspection opens the supplied text by URI, waits for published diagnostics, and
823
- * closes the document without writing it to disk.
811
+ * Construction starts the target workspace's Oxlint binary with its Language Server Protocol mode
812
+ * and hands the conversation to `@orkestrel/lsp`: `createStdioTransport` owns the child process and
813
+ * its bytes, and `createLSPClient` owns the framing, the request correlation, the capabilities this
814
+ * stage advertises, and the choice between pulled and pushed diagnostics. This stage owns the
815
+ * workspace, the candidate identity, and the projection from a diagnostic to an `Issue`.
816
+ *
817
+ * Each inspection opens the supplied text by URI, waits for the client's diagnostics, and closes the
818
+ * document without writing it to disk. The caller's own signal is what bounds that wait, so a server
819
+ * that admits a document and publishes nothing refuses the inspection when the caller's bound
820
+ * expires rather than holding it for the life of the process. This stage mints no bound of its own
821
+ * for the wait: a second one would race the caller's, and the answer would depend on scheduling.
824
822
  *
825
823
  * Teardown is bounded at both ends. Warming waits for the server's `initialize` response and the
826
- * ending waits for its `shutdown` reply, and the protocol leaves both answers to the server, so
827
- * `destroy` bounds each exchange and takes the ending back with a signal when the bound passes.
824
+ * ending waits for its `shutdown` reply, and the protocol leaves both answers to the server, so the
825
+ * client's deadline bounds each exchange and the transport's cooperative window takes the ending
826
+ * back with a signal when that window passes.
828
827
  *
829
828
  * The URI is the path the candidate declared, so the candidate receives exactly the rule set the
830
829
  * workspace's own lint gate applies to that path, including an override anchored to one directory,
@@ -837,24 +836,20 @@ var peerDependencies = {
837
836
  * @example
838
837
  * ```ts
839
838
  * const stage = new LintStage('/srv/checkout')
840
- * const check = await stage.inspect(subject)
839
+ * const check = await stage.inspect(subject, { signal: AbortSignal.timeout(30_000) })
841
840
  * await stage.destroy()
842
841
  * ```
843
842
  */
844
843
  var LintStage = class {
845
844
  #workspace;
846
845
  #deadline = 2e3;
846
+ #documents = /* @__PURE__ */ new Set();
847
847
  #warmth;
848
- #responses = /* @__PURE__ */ new Map();
849
- #failures = /* @__PURE__ */ new Map();
850
- #documents = /* @__PURE__ */ new Map();
851
- #publishes = /* @__PURE__ */ new Map();
852
- #refusals = /* @__PURE__ */ new Map();
853
- #child;
854
- #buffer = Buffer.alloc(0);
855
- #sequence = 0;
848
+ #client;
849
+ #ending;
856
850
  #closing;
857
851
  #progress = 0;
852
+ #revision = 0;
858
853
  #destroyed = false;
859
854
  /**
860
855
  * Starts warming the target workspace's Oxlint language server.
@@ -872,8 +867,8 @@ var LintStage = class {
872
867
  get progress() {
873
868
  return this.#progress;
874
869
  }
875
- inspect(subject) {
876
- return guardStage(this.stage, this.#inspect(subject));
870
+ inspect(subject, options) {
871
+ return guardStage(this.stage, this.#inspect(subject, options));
877
872
  }
878
873
  destroy() {
879
874
  if (this.#closing !== void 0) return this.#closing;
@@ -881,13 +876,18 @@ var LintStage = class {
881
876
  this.#closing = guardStage(this.stage, this.#destroy());
882
877
  return this.#closing;
883
878
  }
884
- async #inspect(subject) {
879
+ async #inspect(subject, options) {
885
880
  if (this.#destroyed) throw (0, _src_core.createDestroyedError)("lint stage");
881
+ if (options === void 0) throw new _src_core.ProbeError("The lint stage inspects only under a bound its caller supplies", {
882
+ origin: "claimant",
883
+ code: "refused",
884
+ context: { stage: this.stage }
885
+ });
886
886
  const started = performance.now();
887
- await this.#warmth;
887
+ const client = await this.#warmed();
888
888
  if (this.#destroyed) throw (0, _src_core.createDestroyedError)("lint stage");
889
889
  const issues = [];
890
- for (const draft of [...subject.files, subject.test]) issues.push(...await this.#document(draft));
890
+ for (const draft of [...subject.files, subject.test]) issues.push(...await this.#document(client, draft, options.signal));
891
891
  return {
892
892
  stage: this.stage,
893
893
  elapsed: Math.round(performance.now() - started),
@@ -895,118 +895,86 @@ var LintStage = class {
895
895
  };
896
896
  }
897
897
  async #destroy() {
898
- const child = await this.#warmed();
899
- this.#fail((0, _src_core.createDestroyedError)("lint stage"));
900
- if (child === void 0 || this.#ending !== void 0) return;
901
- const released = new Promise((resolve) => {
902
- child.once("exit", () => resolve());
903
- child.once("close", () => resolve());
898
+ await this.#client?.destroy();
899
+ }
900
+ async #warm() {
901
+ const binary = resolveWorkspaceBinary(this.#workspace, "oxlint");
902
+ const client = (0, _orkestrel_lsp.createLSPClient)({
903
+ transport: (0, _orkestrel_lsp_server.createStdioTransport)({
904
+ server: {
905
+ command: [
906
+ process.execPath,
907
+ binary,
908
+ "--lsp"
909
+ ],
910
+ directory: this.#workspace
911
+ },
912
+ grace: this.#deadline / 2
913
+ }),
914
+ workspace: (0, node_url.pathToFileURL)(this.#workspace).href,
915
+ timeout: this.#deadline
904
916
  });
905
- await this.#release(child, released);
917
+ this.#client = client;
918
+ client.emitter.on("exit", (exit) => this.#retire(exit));
919
+ await client.start();
920
+ return client;
906
921
  }
907
922
  async #warmed() {
908
- const timeout = (0, _orkestrel_timeout.createTimeout)({ ms: this.#deadline });
909
- timeout.start();
910
- try {
911
- return await Promise.race([this.#warmth, this.#expiry(timeout)]);
912
- } catch {
913
- return this.#child;
914
- } finally {
915
- timeout.clear();
916
- }
917
- }
918
- async #release(child, released) {
919
- const timeout = (0, _orkestrel_timeout.createTimeout)({ ms: this.#deadline });
920
- timeout.start();
921
- try {
922
- await Promise.race([this.#retire(child).then(() => released), this.#expiry(timeout)]);
923
- } catch {
924
- child.kill("SIGKILL");
925
- await released;
926
- } finally {
927
- timeout.clear();
928
- }
929
- }
930
- async #retire(child) {
931
923
  try {
932
- await this.#request("shutdown", void 0);
933
- this.#notify("exit", void 0);
934
- } catch {
935
- child.kill("SIGKILL");
924
+ return await this.#warmth;
925
+ } catch (error) {
926
+ throw this.#translate(error);
936
927
  }
937
928
  }
938
- #expiry(timeout) {
939
- return new Promise((_resolve, reject) => {
940
- timeout.signal.addEventListener("abort", () => reject(new _src_core.ProbeError(`The Oxlint language server did not exit within ${timeout.ms} ms`, {
941
- origin: "instrument",
942
- code: "deadline",
943
- context: {
944
- stage: this.stage,
945
- deadline: timeout.ms
946
- }
947
- })), { once: true });
948
- });
949
- }
950
- async #warm() {
951
- const binary = resolveWorkspaceBinary(this.#workspace, "oxlint");
952
- const child = (0, node_child_process.spawn)(process.execPath, [binary, "--lsp"], {
953
- cwd: this.#workspace,
954
- stdio: "pipe"
955
- });
956
- this.#child = child;
957
- child.stderr.resume();
958
- child.stdout.on("data", (chunk) => this.#read(chunk));
959
- child.stdin.on("error", (error) => this.#fail(error));
960
- child.on("error", (error) => this.#fail(error));
961
- child.on("exit", (code, signal) => this.#exit(code, signal));
962
- await this.#request("initialize", {
963
- processId: process.pid,
964
- rootUri: (0, node_url.pathToFileURL)(this.#workspace).href,
965
- capabilities: {},
966
- workspaceFolders: [{
967
- uri: (0, node_url.pathToFileURL)(this.#workspace).href,
968
- name: "workspace"
969
- }]
970
- });
971
- this.#notify("initialized", {});
972
- return child;
929
+ #retire(exit) {
930
+ this.#ending = exit.signal === null ? `code ${exit.code}` : `signal ${exit.signal}`;
973
931
  }
974
- #document(draft) {
932
+ async #document(client, draft, signal) {
975
933
  const uri = (0, node_url.pathToFileURL)(resolveWorkspaceFile(this.#workspace, draft.path)).href;
976
- if (this.#publishes.has(uri)) return Promise.reject(new _src_core.ProbeError(`The lint stage is already inspecting ${draft.path}`, {
934
+ if (this.#documents.has(uri)) throw new _src_core.ProbeError(`The lint stage is already inspecting ${draft.path}`, {
977
935
  origin: "claimant",
978
936
  code: "refused",
979
937
  context: {
980
938
  stage: this.stage,
981
939
  path: draft.path
982
940
  }
983
- }));
984
- const inspected = new Promise((resolve, reject) => {
985
- this.#documents.set(uri, normalizePath(draft.path));
986
- this.#publishes.set(uri, resolve);
987
- this.#refusals.set(uri, reject);
988
- }).finally(() => this.#close(uri));
941
+ });
942
+ this.#documents.add(uri);
943
+ this.#progress += 1;
944
+ this.#revision += 1;
989
945
  try {
990
- this.#notify("textDocument/didOpen", { textDocument: {
946
+ const diagnostics = await client.open({
991
947
  uri,
992
948
  languageId: inferDocumentLanguage(draft.path),
993
- version: this.#sequence,
949
+ version: this.#revision,
994
950
  text: draft.text
995
- } });
996
- this.#progress += 1;
951
+ }, { signal });
952
+ return this.#issues(normalizePath(draft.path), diagnostics);
997
953
  } catch (error) {
998
- this.#refusals.get(uri)?.(this.#fault(describeUnknown(error), error, draft.path));
954
+ if (signal.aborted) throw this.#abandoned(draft.path);
955
+ throw this.#translate(error, draft.path);
956
+ } finally {
957
+ this.#progress -= 1;
958
+ this.#documents.delete(uri);
959
+ await client.close(uri).catch(() => void 0);
999
960
  }
1000
- return inspected;
1001
961
  }
1002
- get #ending() {
1003
- const child = this.#child;
1004
- if (child === void 0) return void 0;
1005
- if (child.exitCode === null && child.signalCode === null) return void 0;
1006
- return this.#describe(child.exitCode, child.signalCode);
962
+ #abandoned(path) {
963
+ return new _src_core.ProbeError("The lint stage inspection stopped at the bound its caller supplied", {
964
+ origin: "claimant",
965
+ code: "deadline",
966
+ context: {
967
+ stage: this.stage,
968
+ path
969
+ }
970
+ });
1007
971
  }
1008
- #describe(code, signal) {
1009
- return signal === null ? `code ${code}` : `signal ${signal}`;
972
+ #translate(error, path) {
973
+ if (this.#destroyed) return (0, _src_core.createDestroyedError)("lint stage");
974
+ const ending = this.#ending;
975
+ if (ending !== void 0) return this.#fault(`The Oxlint language server exited with ${ending}`, error, path);
976
+ if (error instanceof Error) return error;
977
+ return this.#fault(describeUnknown(error), error, path);
1010
978
  }
1011
979
  #fault(message, cause, path) {
1012
980
  return new _src_core.ProbeError(message, {
@@ -1019,153 +987,22 @@ var LintStage = class {
1019
987
  ...cause === void 0 ? {} : { cause }
1020
988
  });
1021
989
  }
1022
- get #reachable() {
1023
- return this.#ending === void 0 && this.#child?.stdin.writable === true;
1024
- }
1025
- #close(uri) {
1026
- this.#documents.delete(uri);
1027
- this.#publishes.delete(uri);
1028
- this.#refusals.delete(uri);
1029
- if (this.#destroyed || !this.#reachable) return;
1030
- this.#notify("textDocument/didClose", { textDocument: { uri } });
1031
- }
1032
- #request(method, params) {
1033
- this.#sequence += 1;
1034
- const id = this.#sequence;
1035
- const response = new Promise((resolve, reject) => {
1036
- this.#responses.set(id, resolve);
1037
- this.#failures.set(id, reject);
1038
- });
1039
- try {
1040
- this.#send({
1041
- jsonrpc: "2.0",
1042
- id,
1043
- method,
1044
- params
1045
- });
1046
- } catch (error) {
1047
- const reject = this.#failures.get(id);
1048
- this.#responses.delete(id);
1049
- this.#failures.delete(id);
1050
- reject?.(this.#fault(describeUnknown(error), error));
1051
- }
1052
- return response;
1053
- }
1054
- #notify(method, params) {
1055
- this.#sequence += 1;
1056
- this.#send({
1057
- jsonrpc: "2.0",
1058
- method,
1059
- params
1060
- });
1061
- }
1062
- #send(message) {
1063
- const child = this.#child;
1064
- if (this.#ending !== void 0) throw this.#fault(`The Oxlint language server exited with ${this.#ending}`);
1065
- if (child === void 0) throw this.#fault("The Oxlint language server is not running");
1066
- if (!child.stdin.writable) throw this.#fault("The Oxlint language server closed its input");
1067
- const content = JSON.stringify(message);
1068
- const header = `Content-Length: ${Buffer.byteLength(content)}\r\n\r\n`;
1069
- child.stdin.write(header + content);
1070
- }
1071
- #read(chunk) {
1072
- this.#buffer = Buffer.concat([this.#buffer, chunk]);
1073
- while (this.#frame());
1074
- }
1075
- #frame() {
1076
- const boundary = this.#buffer.indexOf("\r\n\r\n");
1077
- if (boundary < 0) return false;
1078
- const length = parseContentLength(this.#buffer.subarray(0, boundary).toString("ascii"));
1079
- if (length === void 0) {
1080
- this.#fail(this.#fault("Oxlint sent an invalid JSON-RPC frame header"));
1081
- return false;
1082
- }
1083
- const start = boundary + 4;
1084
- const end = start + length;
1085
- if (this.#buffer.length < end) return false;
1086
- const content = this.#buffer.subarray(start, end).toString("utf8");
1087
- this.#buffer = this.#buffer.subarray(end);
1088
- try {
1089
- const message = JSON.parse(content);
1090
- this.#receive(message);
1091
- } catch (error) {
1092
- this.#fail(this.#fault(`Oxlint sent invalid JSON: ${describeUnknown(error)}`, error));
1093
- }
1094
- return true;
1095
- }
1096
- #receive(message) {
1097
- if (typeof message !== "object" || message === null) return;
1098
- if ("id" in message && typeof message.id === "number") {
1099
- const id = message.id;
1100
- const resolve = this.#responses.get(id);
1101
- const reject = this.#failures.get(id);
1102
- this.#responses.delete(id);
1103
- this.#failures.delete(id);
1104
- if ("error" in message && message.error !== void 0) reject?.(this.#fault(describeUnknown(message.error)));
1105
- else resolve?.("result" in message ? message.result : void 0);
1106
- return;
1107
- }
1108
- if (!("method" in message) || message.method !== "textDocument/publishDiagnostics") return;
1109
- if (!("params" in message) || typeof message.params !== "object" || message.params === null) return;
1110
- const params = message.params;
1111
- if (!("uri" in params) || typeof params.uri !== "string") return;
1112
- if (!("diagnostics" in params) || !Array.isArray(params.diagnostics)) return;
1113
- const publish = this.#publishes.get(params.uri);
1114
- if (publish === void 0) return;
1115
- publish(this.#issues(params.uri, params.diagnostics));
1116
- }
1117
- #issues(uri, diagnostics) {
1118
- const path = this.#documents.get(uri);
1119
- if (path === void 0) return [];
1120
- const issues = [];
1121
- for (const diagnostic of diagnostics) {
1122
- if (typeof diagnostic !== "object" || diagnostic === null) continue;
1123
- if (!("message" in diagnostic) || typeof diagnostic.message !== "string") continue;
1124
- if (!("range" in diagnostic) || typeof diagnostic.range !== "object" || diagnostic.range === null) {
1125
- issues.push({
1126
- origin: "claimant",
1127
- path,
1128
- message: diagnostic.message
1129
- });
1130
- continue;
1131
- }
1132
- const range = diagnostic.range;
1133
- if (!("start" in range) || typeof range.start !== "object" || range.start === null) {
1134
- issues.push({
1135
- origin: "claimant",
1136
- path,
1137
- message: diagnostic.message
1138
- });
1139
- continue;
1140
- }
1141
- const start = range.start;
1142
- if (!("line" in start) || typeof start.line !== "number") {
1143
- issues.push({
1144
- origin: "claimant",
1145
- path,
1146
- message: diagnostic.message
1147
- });
1148
- continue;
990
+ #issues(path, diagnostics) {
991
+ return diagnostics.map((diagnostic) => ({
992
+ origin: "claimant",
993
+ path,
994
+ message: diagnostic.message,
995
+ range: {
996
+ start: {
997
+ line: diagnostic.range.start.line,
998
+ character: diagnostic.range.start.character
999
+ },
1000
+ end: {
1001
+ line: diagnostic.range.end.line,
1002
+ character: diagnostic.range.end.character
1003
+ }
1149
1004
  }
1150
- issues.push({
1151
- origin: "claimant",
1152
- path,
1153
- message: diagnostic.message,
1154
- line: start.line + 1
1155
- });
1156
- }
1157
- return issues;
1158
- }
1159
- #fail(error) {
1160
- for (const reject of this.#failures.values()) reject(error);
1161
- for (const reject of this.#refusals.values()) reject(error);
1162
- this.#responses.clear();
1163
- this.#failures.clear();
1164
- this.#publishes.clear();
1165
- this.#refusals.clear();
1166
- }
1167
- #exit(code, signal) {
1168
- this.#fail(this.#fault(`The Oxlint language server exited with ${this.#describe(code, signal)}`));
1005
+ }));
1169
1006
  }
1170
1007
  };
1171
1008
  //#endregion
@@ -1796,11 +1633,19 @@ var RuntimeStage = class {
1796
1633
  path,
1797
1634
  message
1798
1635
  };
1636
+ const character = "column" in stack && typeof stack.column === "number" ? stack.column - 1 : 0;
1637
+ const position = {
1638
+ line: stack.line - 1,
1639
+ character
1640
+ };
1799
1641
  return {
1800
1642
  origin: "claimant",
1801
1643
  path,
1802
1644
  message,
1803
- line: stack.line
1645
+ range: {
1646
+ start: position,
1647
+ end: position
1648
+ }
1804
1649
  };
1805
1650
  }
1806
1651
  return {
@@ -2142,7 +1987,10 @@ var TypeStage = class {
2142
1987
  origin: "claimant",
2143
1988
  path,
2144
1989
  message,
2145
- line: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1
1990
+ range: {
1991
+ start: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start),
1992
+ end: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start + (diagnostic.length ?? 0))
1993
+ }
2146
1994
  };
2147
1995
  }
2148
1996
  };
@@ -2180,6 +2028,7 @@ var Probe = class {
2180
2028
  #lintQueue;
2181
2029
  #runtimeQueue;
2182
2030
  #deadlines = /* @__PURE__ */ new WeakSet();
2031
+ #surfaced = /* @__PURE__ */ new WeakSet();
2183
2032
  #typeTail = Promise.resolve();
2184
2033
  #arming;
2185
2034
  #closing;
@@ -2261,7 +2110,7 @@ var Probe = class {
2261
2110
  this.#emitter.emit("prove", verdict);
2262
2111
  return verdict;
2263
2112
  } catch (error) {
2264
- this.#emitter.emit("error", error);
2113
+ if (!(error instanceof _src_core.ProbeError) || !this.#surfaced.has(error)) this.#emitter.emit("error", error);
2265
2114
  throw error;
2266
2115
  }
2267
2116
  }
@@ -2286,18 +2135,30 @@ var Probe = class {
2286
2135
  await this.#arming;
2287
2136
  }
2288
2137
  async #arm() {
2289
- const created = this.#workbench();
2138
+ let created;
2139
+ try {
2140
+ created = this.#workbench();
2141
+ } catch (error) {
2142
+ this.#surface(error);
2143
+ throw error;
2144
+ }
2290
2145
  try {
2291
2146
  await this.#boot(created);
2292
2147
  } catch (error) {
2293
- throw new _src_core.ProbeError(`The probe could not arm: ${describeUnknown(error)}`, {
2148
+ const failure = new _src_core.ProbeError(`The probe could not arm: ${describeUnknown(error)}`, {
2294
2149
  origin: "instrument",
2295
2150
  code: "malformed",
2296
2151
  cause: error
2297
2152
  });
2153
+ this.#surface(failure);
2154
+ throw failure;
2298
2155
  }
2299
2156
  this.#emitter.emit("arm", this.#toolchain);
2300
2157
  }
2158
+ #surface(error) {
2159
+ if (error instanceof _src_core.ProbeError) this.#surfaced.add(error);
2160
+ this.#emitter.emit("error", error);
2161
+ }
2301
2162
  #workbench() {
2302
2163
  const path = "tmp/probe";
2303
2164
  const directory = resolveWorkspaceFile(this.#workspace, path);
@@ -2422,16 +2283,18 @@ var Probe = class {
2422
2283
  const release = await this.#admitType();
2423
2284
  try {
2424
2285
  const stage = this.#type;
2425
- return await this.#inspectStage(stage, stage.progress, stage.inspect(inspection.subject, inspection.claim.project), inspection.claim);
2286
+ return await this.#inspectStage(stage, stage.progress, () => stage.inspect(inspection.subject, inspection.claim.project), inspection.claim);
2426
2287
  } finally {
2427
2288
  release();
2428
2289
  }
2429
2290
  }
2430
2291
  #inspectLint(inspection) {
2431
- return this.#inspectStage(this.#lint, this.#lint.progress, this.#lint.inspect(inspection.subject), inspection.claim);
2292
+ const stage = this.#lint;
2293
+ return this.#inspectStage(stage, stage.progress, (signal) => stage.inspect(inspection.subject, { signal }), inspection.claim);
2432
2294
  }
2433
2295
  #inspectRuntime(inspection) {
2434
- return this.#inspectStage(this.#runtime, this.#runtime.progress, this.#runtime.inspect(inspection.subject), inspection.claim);
2296
+ const stage = this.#runtime;
2297
+ return this.#inspectStage(stage, stage.progress, () => stage.inspect(inspection.subject), inspection.claim);
2435
2298
  }
2436
2299
  async #inspectStage(stage, progress, operation, claim) {
2437
2300
  try {
@@ -2444,12 +2307,14 @@ var Probe = class {
2444
2307
  async #bound(operation, message, stage, progress) {
2445
2308
  const timeout = (0, _orkestrel_timeout.createTimeout)({ ms: this.#deadline });
2446
2309
  timeout.start();
2310
+ const expiry = this.#expiry(timeout, message, stage, progress);
2311
+ const refusal = expiry.catch((error) => error);
2447
2312
  try {
2448
- return await Promise.race([operation, this.#expiry(timeout, message, stage, progress)]);
2313
+ return await Promise.race([operation(timeout.signal), expiry]);
2449
2314
  } catch (error) {
2450
2315
  if (!timeout.expired) throw error;
2451
2316
  await this.#recycle(stage);
2452
- throw error;
2317
+ throw await refusal;
2453
2318
  } finally {
2454
2319
  timeout.clear();
2455
2320
  }
@@ -2490,7 +2355,7 @@ var Probe = class {
2490
2355
  try {
2491
2356
  const stage = this.#type;
2492
2357
  try {
2493
- return await this.#bound(stage.resolve(claim.project), `The type stage project resolution exceeded ${this.#deadline} ms`, stage, stage.progress);
2358
+ return await this.#bound(() => stage.resolve(claim.project), `The type stage project resolution exceeded ${this.#deadline} ms`, stage, stage.progress);
2494
2359
  } catch (error) {
2495
2360
  this.#emitExpiry(stage, claim);
2496
2361
  throw error;
@@ -2531,7 +2396,7 @@ var Probe = class {
2531
2396
  }
2532
2397
  async #destroyStage(stage) {
2533
2398
  try {
2534
- await this.#bound(stage.destroy(), `The ${stage.stage} stage teardown exceeded ${this.#deadline} ms`, stage, stage.progress);
2399
+ await this.#bound(() => stage.destroy(), `The ${stage.stage} stage teardown exceeded ${this.#deadline} ms`, stage, stage.progress);
2535
2400
  } catch (error) {
2536
2401
  if (!(error instanceof _src_core.ProbeError) || !this.#deadlines.has(error)) throw error;
2537
2402
  }
@@ -2740,7 +2605,6 @@ exports.matchesWorkspaceModule = matchesWorkspaceModule;
2740
2605
  exports.normalizePath = normalizePath;
2741
2606
  exports.normalizeValue = normalizeValue;
2742
2607
  exports.overwriteFile = overwriteFile;
2743
- exports.parseContentLength = parseContentLength;
2744
2608
  exports.readWorkspaceManifest = readWorkspaceManifest;
2745
2609
  exports.relativeWorkspaceFile = relativeWorkspaceFile;
2746
2610
  exports.relativeWorkspaceMessage = relativeWorkspaceMessage;