@orkestrel/probe 0.0.1 → 0.0.2

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.
@@ -107,6 +107,53 @@ function resolveWorkspaceFile(workspace, target, mutate = false) {
107
107
  return file;
108
108
  }
109
109
  /**
110
+ * Reports whether a fault means the host refuses the name a caller supplied for a file to create.
111
+ *
112
+ * @remarks
113
+ * A host reports one refusal with a code of its own and several unrelated conditions with one
114
+ * shared code, so the code alone does not separate a name the host will not accept from a file
115
+ * that is merely absent. `ENAMETOOLONG` names the refusal outright. `ERR_INVALID_ARG_VALUE` names
116
+ * every argument Node rejects, so it names the refusal only for a path carrying the NUL byte Node
117
+ * refuses; the same code arrives for a bad `flag` and a bad encoding, which say nothing about the
118
+ * name. An `ENOENT` raised while the parent stats as a directory names the refusal too, because an
119
+ * ordinary absent file beneath an existing directory would have been created instead; a Windows
120
+ * host reports an overlong component and a character its filesystem reserves that way. Measured on
121
+ * 2026-08-21 on Windows 11 with Node v24.18.1 over NTFS, `writeFileSync` reported `ENOENT` for a
122
+ * 300-character final component and never reported `ENAMETOOLONG`.
123
+ *
124
+ * This reads the fault the host reported rather than applying a length or character policy of its
125
+ * own, so a host that accepts a name another host refuses is not second-guessed and no platform
126
+ * branch decides the answer. Every read of the fault is guarded, so it is total over every value a
127
+ * published caller can supply: a proxy whose `has` trap throws, a value whose `code` getter throws,
128
+ * a fault carrying no code, and a parent this host cannot stat each report false.
129
+ *
130
+ * @param file - The path whose creation failed
131
+ * @param error - The fault the failed operation raised
132
+ * @returns True when the fault means the host refuses this name; false otherwise
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * try {
137
+ * writeFileSync('tmp/probe/greeting\0.test.ts', '')
138
+ * } catch (error) {
139
+ * isRefusedName('tmp/probe/greeting\0.test.ts', error) // true
140
+ * }
141
+ * isRefusedName('tmp/probe/greeting.test.ts', new Error('the runtime stage was destroyed')) // false
142
+ * ```
143
+ */
144
+ function isRefusedName(file, error) {
145
+ if (typeof error !== "object" || error === null) return false;
146
+ const fault = error;
147
+ const reading = (0, _orkestrel_contract.attempt)(() => "code" in fault ? fault.code : void 0);
148
+ if (!reading.success) return false;
149
+ const code = reading.value;
150
+ if (code === "ENAMETOOLONG") return true;
151
+ if (code === "ERR_INVALID_ARG_VALUE") return file.includes("\0");
152
+ if (code !== "ENOENT") return false;
153
+ const parent = (0, _orkestrel_contract.attempt)(() => (0, node_fs.statSync)((0, node_path.dirname)(file), { throwIfNoEntry: false }));
154
+ return parent.success && parent.value !== void 0 && parent.value.isDirectory();
155
+ }
156
+ /**
110
157
  * Projects an absolute tool path into the workspace-relative form issues expose.
111
158
  *
112
159
  * @param workspace - The target workspace root
@@ -639,7 +686,7 @@ var Overlay = class {
639
686
  };
640
687
  //#endregion
641
688
  //#region package.json
642
- var version = "0.0.1";
689
+ var version = "0.0.2";
643
690
  var peerDependencies = {
644
691
  "oxlint": "^1.77.0",
645
692
  "typescript": "^6.0.0",
@@ -1027,11 +1074,12 @@ var LintStage = class {
1027
1074
  * as a path string, into which the stage can install no overlay, and a specification path that
1028
1075
  * crosses a symbolic link, whose existing components this host cannot inspect, or whose directory
1029
1076
  * the target tree blocks probe from creating. An `origin: 'instrument'` issue names this package's
1030
- * own machinery: a specification it could not write after its directory exists, or one it could
1031
- * not run, evict, or delete, in each case for a reason the target tree does not own, and a run
1032
- * that reported no test. An unmapped caller test and a test whose named project is absent are claimant failures with
1033
- * `code: 'missing'`, thrown rather than reported, because a test that never ran is no evidence
1034
- * about the candidate.
1077
+ * own machinery: a specification it could not write after its directory exists and the host did not
1078
+ * refuse the caller's own name, or one it could not run, evict, or delete, in each case for a
1079
+ * reason the target tree does not own, and a run that reported no test. An unmapped caller test and
1080
+ * a test whose named project is absent are claimant failures with `code: 'missing'`, and a declared
1081
+ * test path the host refuses to create is a claimant failure with `code: 'refused'`. Each is thrown
1082
+ * rather than reported, because a test that never ran is no evidence about the candidate.
1035
1083
  *
1036
1084
  * The write path's physical-containment guarantee covers the claim inputs and the target tree as
1037
1085
  * inspected. A concurrent process that mutates a path component between the final inspection and
@@ -1094,7 +1142,7 @@ var RuntimeStage = class {
1094
1142
  elapsed: Math.round(performance.now() - started),
1095
1143
  issues: [specification]
1096
1144
  };
1097
- const file = specification;
1145
+ const file = node_path.sep === "\\" ? normalizePath(specification) : specification;
1098
1146
  this.#specifications += 1;
1099
1147
  this.#revisions.add(file);
1100
1148
  let issues = [];
@@ -1244,9 +1292,11 @@ var RuntimeStage = class {
1244
1292
  }
1245
1293
  #specification(test) {
1246
1294
  let creating = false;
1295
+ let generated;
1247
1296
  const outcome = (0, _orkestrel_contract.attempt)(() => {
1248
1297
  const revision = `${process.pid}-${(0, node_crypto.randomUUID)()}`;
1249
1298
  const file = createRevisionFile(this.#workspace, test.path, revision);
1299
+ generated = file;
1250
1300
  const target = (0, node_path.relative)(this.#workspace, file);
1251
1301
  resolveWorkspaceFile(this.#workspace, target, true);
1252
1302
  creating = true;
@@ -1269,6 +1319,15 @@ var RuntimeStage = class {
1269
1319
  },
1270
1320
  cause: outcome.error.cause
1271
1321
  });
1322
+ if (!creating && generated !== void 0 && isRefusedName(generated, outcome.error)) throw new _src_core.ProbeError(`The runtime stage cannot write a specification beside a test path the host refuses (${describeUnknown(outcome.error)})`, {
1323
+ origin: "claimant",
1324
+ code: "refused",
1325
+ context: {
1326
+ stage: this.stage,
1327
+ path: test.path
1328
+ },
1329
+ cause: outcome.error
1330
+ });
1272
1331
  return {
1273
1332
  origin: outcome.error instanceof _src_core.ProbeError && outcome.error.origin === "workspace" || creating ? "workspace" : "instrument",
1274
1333
  path: test.path,
@@ -1367,8 +1426,9 @@ var RuntimeStage = class {
1367
1426
  for (const [path, digest] of modules) this.#modules.set(path, digest);
1368
1427
  }
1369
1428
  #invalidate(vitest, path) {
1370
- vitest.invalidateFile(path);
1371
- vitest.watcher.invalidates.add(path);
1429
+ const id = node_path.sep === "\\" ? normalizePath(path) : path;
1430
+ vitest.invalidateFile(id);
1431
+ vitest.watcher.invalidates.add(id);
1372
1432
  }
1373
1433
  #snapshot() {
1374
1434
  const modules = /* @__PURE__ */ new Map();
@@ -1718,8 +1778,9 @@ var TypeStage = class {
1718
1778
  const path = resolveWorkspaceFile(this.#workspace, project);
1719
1779
  const existing = this.#services.get(path);
1720
1780
  if (existing !== void 0) return existing;
1721
- const config = typescript.readConfigFile(path, typescript.sys.readFile);
1722
- if (config.error !== void 0) throw new _src_core.ProbeError(typescript.flattenDiagnosticMessageText(config.error.messageText, "\n"), {
1781
+ const spelling = normalizePath(path);
1782
+ const config = typescript.readConfigFile(spelling, typescript.sys.readFile);
1783
+ if (config.error !== void 0) throw new _src_core.ProbeError(this.#translate(typescript, config.error.messageText, path), {
1723
1784
  origin: "workspace",
1724
1785
  code: "malformed",
1725
1786
  context: {
@@ -1727,8 +1788,8 @@ var TypeStage = class {
1727
1788
  project
1728
1789
  }
1729
1790
  });
1730
- const parsed = typescript.parseJsonConfigFileContent(config.config, typescript.sys, (0, node_path.dirname)(path), void 0, path);
1731
- if (parsed.errors.length > 0) throw new _src_core.ProbeError(typescript.flattenDiagnosticMessageText(parsed.errors[0]?.messageText ?? "", "\n"), {
1791
+ const parsed = typescript.parseJsonConfigFileContent(config.config, typescript.sys, (0, node_path.dirname)(spelling), void 0, spelling);
1792
+ if (parsed.errors.length > 0) throw new _src_core.ProbeError(this.#translate(typescript, parsed.errors[0]?.messageText, path), {
1732
1793
  origin: "workspace",
1733
1794
  code: "malformed",
1734
1795
  context: {
@@ -1757,6 +1818,9 @@ var TypeStage = class {
1757
1818
  if (!this.#resident.has(path)) this.#recycle(path);
1758
1819
  return service;
1759
1820
  }
1821
+ #translate(typescript, message, path) {
1822
+ return normalizePath(typescript.flattenDiagnosticMessageText(message, "\n")).replaceAll(normalizePath(path), relativeWorkspaceFile(this.#workspace, path));
1823
+ }
1760
1824
  #configure(service, project) {
1761
1825
  const path = resolveWorkspaceFile(this.#workspace, project);
1762
1826
  if (this.#diagnostics.has(path)) return;
@@ -2277,7 +2341,7 @@ var ProbeServer = class {
2277
2341
  const tools = (0, _orkestrel_tool.createToolManager)();
2278
2342
  tools.add((0, _orkestrel_tool.createTool)({
2279
2343
  name: "prove",
2280
- description: "Proves a claim with type, lint, and runtime evidence.",
2344
+ description: "Answers whether a TypeScript edit compiles, lints, and passes its test in this workspace — call it before relying on such a claim, instead of reasoning or writing a throwaway probe. Supply the edit you believe is correct and an edit that must break with the stage it breaks at; the closing line is a receipt only when the case ran clean and the control broke exactly at its declared stage, and no receipt otherwise. For a performance claim, measure before proving: write a guarded bench block — if (import.meta.env.MODE === 'benchmark') — beside the probe test, run the workspace bench script, and record only magnitudes; a settled ratio then proves here as an ordinary runtime claim.",
2281
2345
  parameters,
2282
2346
  execute: async (input) => {
2283
2347
  if (!(0, _src_core.isClaim)(input)) {
@@ -2331,6 +2395,7 @@ exports.findRefusedPaths = findRefusedPaths;
2331
2395
  exports.inferDocumentLanguage = inferDocumentLanguage;
2332
2396
  exports.inferTestProject = inferTestProject;
2333
2397
  exports.inferTypeProject = inferTypeProject;
2398
+ exports.isRefusedName = isRefusedName;
2334
2399
  exports.loadWorkspaceModule = loadWorkspaceModule;
2335
2400
  exports.matchesWorkspaceModule = matchesWorkspaceModule;
2336
2401
  exports.normalizePath = normalizePath;