@orkestrel/probe 0.0.12 → 0.0.13

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.
@@ -1,20 +1,17 @@
1
- import { Case } from '@orkestrel/probe';
2
- import { Case as Case_2 } from '@orkestrel/probe';
3
- import { Check } from '@orkestrel/probe';
4
- import { Check as Check_2 } from '@orkestrel/probe';
5
- import { Claim } from '@orkestrel/probe';
6
- import { EmitterInterface } from '@orkestrel/emitter';
7
- import { EventEmitter } from 'node:events';
8
- import { ProbeEventMap } from '@orkestrel/probe';
9
- import { ProbeInterface } from '@orkestrel/probe';
10
- import { ProbeOptions } from '@orkestrel/probe';
11
- import { Project } from '@orkestrel/probe';
12
- import { Project as Project_2 } from '@orkestrel/probe';
13
- import { Stage } from '@orkestrel/probe';
14
- import { Stage as Stage_2 } from '@orkestrel/probe';
15
- import { Toolchain } from '@orkestrel/probe';
16
- import type * as TypeScript from 'typescript';
17
- import { Verdict } from '@orkestrel/probe';
1
+ import type { Case } from '@orkestrel/probe';
2
+ import type { Check } from '@orkestrel/probe';
3
+ import type { Claim } from '@orkestrel/probe';
4
+ import type { EmitterInterface } from '@orkestrel/emitter';
5
+ import type { EventEmitter } from 'node:events';
6
+ import type { Issue } from '@orkestrel/probe';
7
+ import type { LSPRange } from '@orkestrel/lsp';
8
+ import type { ProbeEventMap } from '@orkestrel/probe';
9
+ import type { ProbeInterface } from '@orkestrel/probe';
10
+ import type { ProbeOptions } from '@orkestrel/probe';
11
+ import type { Project } from '@orkestrel/probe';
12
+ import type { Stage } from '@orkestrel/probe';
13
+ import type { Toolchain } from '@orkestrel/probe';
14
+ import type { Verdict } from '@orkestrel/probe';
18
15
  import type * as VitestNode from 'vitest/node';
19
16
 
20
17
  /**
@@ -52,6 +49,27 @@ export declare function buildRevisionPath(workspace: string, path: string, revis
52
49
  */
53
50
  export declare function captureListeners(emitter: EventEmitter, events: readonly string[]): ListenerCapture;
54
51
 
52
+ /**
53
+ * Collects every regular file a target workspace holds, skipping the trees no inspection reads.
54
+ *
55
+ * @remarks
56
+ * Skips a directory named `.git`, `dist`, or `node_modules` at any depth, and the directory
57
+ * {@link TYPE_MIRROR} names, which would otherwise carry this package's own mirror into the walk. A
58
+ * directory this host cannot list is skipped rather than raised, so one unreadable directory in a
59
+ * consumer's tree is not a reason to refuse the whole walk. A symbolic link is not carried, whether
60
+ * it names a file or a directory, so a file reached only through one is absent from the result; a
61
+ * `Dirent` that is neither a directory nor a regular file is skipped the same way.
62
+ *
63
+ * @param workspace - The target workspace root
64
+ * @returns The absolute path of every regular file the walk found
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * collectWorkspaceFiles(process.cwd()).some((path) => path.endsWith('package.json')) // true
69
+ * ```
70
+ */
71
+ export declare function collectWorkspaceFiles(workspace: string): readonly string[];
72
+
55
73
  /**
56
74
  * Computes the canonical digest of one value as it stands in a target workspace.
57
75
  *
@@ -86,6 +104,34 @@ export declare function computeDigest(workspace: string, value: unknown): string
86
104
  */
87
105
  export declare function describeUnknown(value: unknown): string;
88
106
 
107
+ /**
108
+ * Carries one diagnostic line a compiler run reported, in this package's own coordinates.
109
+ *
110
+ * @remarks
111
+ * `path` and `range` are absent together, because a diagnostic the compiler reports about a
112
+ * project rather than about a file carries no location. `path` is spelled as the compiler printed
113
+ * it, relative to the directory the run started in. `range` holds the zero-based UTF-16 point the
114
+ * compiler reported: the plain-text output carries a start position and no extent, so `end` equals
115
+ * `start`. `message` is the diagnostic text, elaboration lines joined by newlines.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const diagnostic: Diagnostic = {
120
+ * path: 'src/core/greeting.ts',
121
+ * range: { start: { line: 0, character: 13 }, end: { line: 0, character: 13 } },
122
+ * message: "Type 'string' is not assignable to type 'number'.",
123
+ * }
124
+ * ```
125
+ */
126
+ export declare interface Diagnostic {
127
+ /** Names the file the compiler reported against, or is absent for a project diagnostic. */
128
+ readonly path?: string;
129
+ /** Holds the zero-based UTF-16 point the compiler reported, or is absent with `path`. */
130
+ readonly range?: LSPRange;
131
+ /** Holds the diagnostic text, with every elaboration line joined by a newline. */
132
+ readonly message: string;
133
+ }
134
+
89
135
  /**
90
136
  * Reports whether one path resolves outside the root it is read against.
91
137
  *
@@ -112,6 +158,48 @@ export declare function describeUnknown(value: unknown): string;
112
158
  */
113
159
  export declare function escapesRoot(root: string, target: string): boolean;
114
160
 
161
+ /**
162
+ * Carries what one spawned workspace command reported when it closed.
163
+ *
164
+ * @remarks
165
+ * `stdout` and `stderr` mirror the POSIX stream names the child wrote to. `status` is the exit
166
+ * code, and it is absent when a signal ended the child, which is what teardown does to a run it
167
+ * abandons. A compiler's exit code is not its verdict — the majors this package supports disagree
168
+ * on it — so read the diagnostics the streams carry rather than this number.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * const execution: Execution = { status: 0, stdout: '{}\n', stderr: '' }
173
+ * ```
174
+ */
175
+ export declare interface Execution {
176
+ /** Holds the exit code, or is absent when a signal ended the child. */
177
+ readonly status?: number;
178
+ /** Holds everything the child wrote to its standard output. */
179
+ readonly stdout: string;
180
+ /** Holds everything the child wrote to its standard error. */
181
+ readonly stderr: string;
182
+ }
183
+
184
+ /**
185
+ * Filters one issue list to the distinct issues it carries, in the order they arrived.
186
+ *
187
+ * @remarks
188
+ * A stage that reads one candidate through more than one tool run reports that candidate's
189
+ * diagnostic once per run. Two issues are the same issue when their party, path, message, and range
190
+ * all agree, because those are every value an issue carries.
191
+ *
192
+ * @param issues - The issues to filter
193
+ * @returns The distinct issues, in the order the list carried them
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * const issue: Issue = { origin: 'claimant', path: 'src/core/greeting.ts', message: 'not assignable' }
198
+ * filterUniqueIssues([issue, { ...issue }]).length // 1
199
+ * ```
200
+ */
201
+ export declare function filterUniqueIssues(issues: readonly Issue[]): readonly Issue[];
202
+
115
203
  /**
116
204
  * Names every draft member of a claim-shaped value whose `path` this package's guard refuses.
117
205
  *
@@ -145,9 +233,9 @@ export declare function escapesRoot(root: string, target: string): boolean;
145
233
  export declare function findRefusedPaths(value: unknown): readonly string[];
146
234
 
147
235
  /**
148
- * Guards one resident-stage operation with the stage failure contract.
236
+ * Guards one stage operation with the stage failure contract.
149
237
  *
150
- * @param stage - The resident stage serving the operation
238
+ * @param stage - The stage serving the operation
151
239
  * @param operation - The operation to settle
152
240
  * @returns The operation's fulfilled value
153
241
  * @throws The original `ProbeError`, or an instrument-owned malformed failure retaining the cause
@@ -334,9 +422,9 @@ export declare class LintStage implements LintStageInterface {
334
422
  * @param workspace - The target workspace root. Default: the current working directory
335
423
  */
336
424
  constructor(workspace?: string);
337
- get stage(): Stage_2;
425
+ get stage(): Stage;
338
426
  get progress(): number;
339
- inspect(subject: Case_2, options?: InspectionOptions): Promise<Check_2>;
427
+ inspect(subject: Case, options?: InspectionOptions): Promise<Check>;
340
428
  destroy(): Promise<void>;
341
429
  }
342
430
 
@@ -368,6 +456,11 @@ export declare interface LintStageInterface extends StageInterface {
368
456
  * @returns One outcome for this stage
369
457
  * @throws When the caller supplies no bound, when the resident language server cannot start, or
370
458
  * when the stage has already been destroyed
459
+ *
460
+ * @example
461
+ * ```ts
462
+ * const check = await stage.inspect(subject, { signal: AbortSignal.timeout(30_000) })
463
+ * ```
371
464
  */
372
465
  inspect(subject: Case, options?: InspectionOptions): Promise<Check>;
373
466
  }
@@ -392,22 +485,44 @@ export declare interface LintStageInterface extends StageInterface {
392
485
  export declare type ListenerCapture = ReadonlyMap<string, readonly Function[]>;
393
486
 
394
487
  /**
395
- * Loads one installed tool module from a target workspace.
488
+ * Loads the installed `vitest/node` module from a target workspace.
489
+ *
490
+ * @remarks
491
+ * The only tool this package drives in process. The compiler is a process this package spawns
492
+ * rather than a module it loads, so `typescript` is resolved through {@link resolveWorkspaceBinary}
493
+ * instead.
396
494
  *
397
495
  * @param workspace - The target workspace root
398
- * @param specifier - The module specifier to load
399
- * @returns The installed module
400
- * @throws When the workspace cannot load the module
496
+ * @returns The installed `vitest/node` module
497
+ * @throws When the workspace cannot load `vitest/node`
401
498
  *
402
499
  * @example
403
500
  * ```ts
404
- * const typescript = loadWorkspaceModule(process.cwd(), 'typescript')
405
- * console.log(typescript.version)
501
+ * const vitest = loadWorkspaceVitest(process.cwd())
502
+ * typeof vitest.createVitest === 'function' // true
406
503
  * ```
407
504
  */
408
- export declare function loadWorkspaceModule(workspace: string, specifier: 'typescript'): typeof TypeScript;
505
+ export declare function loadWorkspaceVitest(workspace: string): typeof VitestNode;
409
506
 
410
- export declare function loadWorkspaceModule(workspace: string, specifier: 'vitest/node'): typeof VitestNode;
507
+ /**
508
+ * Reports whether the host that wrote one file is still running.
509
+ *
510
+ * @remarks
511
+ * Signal 0 delivers nothing and reports reachability alone. A process this one may not signal
512
+ * reports `EPERM` and reads as running, and a non-positive identity names a process group rather
513
+ * than a process, so both answer true: the safe direction is to keep a file this package cannot
514
+ * account for.
515
+ *
516
+ * @param id - The process id a file's own revision names
517
+ * @returns True if that process is running or cannot be judged; false otherwise
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * matchesLiveProcess(process.pid) // true
522
+ * matchesLiveProcess(0) // true
523
+ * ```
524
+ */
525
+ export declare function matchesLiveProcess(id: number): boolean;
411
526
 
412
527
  /**
413
528
  * Reports whether a path is a workspace module Vitest can cache.
@@ -447,12 +562,12 @@ export declare function normalizePath(path: string): string;
447
562
  * sorts every record's keys.
448
563
  *
449
564
  * @remarks
450
- * A parsed TypeScript project carries the absolute root it was parsed at, so the same commit
451
- * checked out at two paths produces two values that describe one configuration. Rewriting those
452
- * members makes the value portable and stops a host path leaking into anything derived from it. A
453
- * path that escapes the workspace is left as it stands, because rewriting it would name a file
454
- * outside the tree by a relative spelling. Key order is the parser's, not the configuration's, so
455
- * sorting removes a difference that means nothing.
565
+ * `tsc --showConfig` spells a path relative to the project file, so a printed record carries an
566
+ * absolute path only where a project itself declares one. Rewriting a contained absolute path to
567
+ * its relative form keeps one commit checked out at two roots reading to one digest. A path that
568
+ * escapes the workspace is left as it stands, because rewriting it would name a file outside the
569
+ * tree by a relative spelling. Key order is the compiler's own and it differs between the supported
570
+ * majors, so sorting removes a difference that means nothing.
456
571
  *
457
572
  * @param workspace - The target workspace root
458
573
  * @param value - The value to rewrite
@@ -467,7 +582,8 @@ export declare function normalizePath(path: string): string;
467
582
  export declare function normalizeValue(workspace: string, value: unknown): unknown;
468
583
 
469
584
  /**
470
- * Holds the candidate drafts one inspection substitutes for the files a tool would read from disk.
585
+ * Implements `OverlayInterface` over a private map from normalized absolute path to candidate text,
586
+ * minting at construction the `revision` a resident tool caches its answers against.
471
587
  *
472
588
  * @remarks
473
589
  * One overlay belongs to one inspection: the inspection creates it, records every candidate it
@@ -476,39 +592,32 @@ export declare function normalizeValue(workspace: string, value: unknown): unkno
476
592
  * is what makes a resident tool re-read a path this overlay holds, so two inspections that supply
477
593
  * different text for one path never share a cached answer.
478
594
  *
479
- * This overlay matches a lookup key the way the host its stage declares to its tool matches file
480
- * names. A host that resolves two spellings of one file name to one file leaves the tool holding
481
- * whichever spelling it met first — the disk one, where the file is on disk — and the tool asks
482
- * under that, so an overlay matching keys exactly answers nothing and the tool reads the committed
483
- * file instead. Mint the overlay with the sensitivity the stage declares, and one reading decides
484
- * both. The recorded spelling is what `paths` reports and what `covers` matches a directory
485
- * against, so a folded key never reaches the paths a tool is handed and containment stays an exact
486
- * comparison.
595
+ * A lookup key matches a recorded path exactly, after both pass through `normalizePath`, so a tool
596
+ * that reports its own paths with backslashes still reaches a candidate recorded with forward
597
+ * slashes. Case is never folded: the recorded spelling is what `paths` reports, what `covers`
598
+ * compares a directory against, and what `text` answers under. A stage whose tool resolves two
599
+ * spellings of one file name to one file therefore reports the path its tool served rather than
600
+ * matching the two spellings here.
487
601
  *
488
602
  * @example
489
603
  * ```ts
490
604
  * const overlay = new Overlay()
491
- * overlay.set('/srv/checkout/src/core/greeting.ts', "export const GREETING = 'hi'\n")
492
- * console.log(overlay.text('/srv/checkout/src/core/greeting.ts'))
605
+ * const path = '/srv/checkout/src/core/factories.ts'
606
+ * overlay.set(path, "export function createGreeting(): string {\n\treturn 'hi'\n}\n")
607
+ * console.log(overlay.text(path))
493
608
  * overlay.clear()
494
609
  * ```
495
610
  */
496
611
  export declare class Overlay implements OverlayInterface {
497
612
  #private;
498
- /**
499
- * Creates an empty candidate set under a fresh identity.
500
- *
501
- * @param options - Construction options
502
- */
503
- constructor(options?: OverlayOptions);
504
613
  get revision(): string;
505
614
  get paths(): readonly string[];
506
615
  /**
507
616
  * Records one candidate's text against the absolute path it stands in for.
508
617
  *
509
618
  * @remarks
510
- * Recording a candidate under a path this overlay's own matching reads as one already recorded
511
- * replaces that candidate, the way one file holds one text.
619
+ * Recording a candidate under a path this overlay already holds replaces that candidate, the way
620
+ * one file holds one text.
512
621
  *
513
622
  * @param path - The absolute path the candidate replaces
514
623
  * @param text - The candidate's full contents
@@ -529,9 +638,7 @@ export declare class Overlay implements OverlayInterface {
529
638
  * The answer is derived from the paths the overlay holds rather than stored, so it stops being
530
639
  * true exactly when the inspection that declared those candidates clears them. Both sides pass
531
640
  * through `normalizePath` first, because a tool that normalizes its own paths asks about a
532
- * directory in a spelling the recorded path may not share. Case is never folded here: this is a
533
- * containment comparison, and it reads the recorded spelling whatever an overlay minted for a
534
- * case-folding host matches its lookup keys by.
641
+ * directory in a spelling the recorded path may not share.
535
642
  *
536
643
  * @param directory - The absolute directory path to check
537
644
  * @returns True if a candidate path sits beneath the directory; false otherwise
@@ -550,10 +657,10 @@ export declare class Overlay implements OverlayInterface {
550
657
  *
551
658
  * @remarks
552
659
  * A stage records every candidate the inspection carries before it reads any of them, and clears
553
- * the set when the inspection ends, whatever ended it. The entity names no tool: each stage adapts
554
- * one overlay to the host its own tool expects, so a language service, a document protocol, and a
555
- * module resolver read one candidate set through their own adapters rather than through one shared
556
- * filesystem. Paths are absolute and the stage resolves them, because only the stage knows the
660
+ * the set when the inspection ends, whatever ended it. The runtime stage's module resolver reads
661
+ * one candidate set through its adapter and is the stage that holds an overlay; the type stage
662
+ * writes each draft into its mirror and the lint stage opens each draft as a document, so neither
663
+ * holds one. Paths are absolute and the stage resolves them, because only the stage knows the
557
664
  * workspace a candidate's declared path is relative to. `revision` identifies the set, so a
558
665
  * resident tool that caches by version reads fresh text for a path this overlay holds and reads
559
666
  * disk again after `clear`.
@@ -565,7 +672,8 @@ export declare class Overlay implements OverlayInterface {
565
672
  * @example
566
673
  * ```ts
567
674
  * const overlay: OverlayInterface = new Overlay()
568
- * overlay.set('/srv/checkout/src/core/greeting.ts', "export const GREETING = 'hi'\n")
675
+ * const path = '/srv/checkout/src/core/factories.ts'
676
+ * overlay.set(path, "export function createGreeting(): string {\n\treturn 'hi'\n}\n")
569
677
  * overlay.covers('/srv/checkout/src/core') // true
570
678
  * overlay.clear()
571
679
  * ```
@@ -581,6 +689,12 @@ export declare interface OverlayInterface {
581
689
  * @param path - The absolute path the candidate replaces
582
690
  * @param text - The candidate's full contents
583
691
  * @returns Nothing
692
+ *
693
+ * @example
694
+ * ```ts
695
+ * const path = '/srv/checkout/src/core/factories.ts'
696
+ * overlay.set(path, "export function createGreeting(): string {\n\treturn 'hi'\n}\n")
697
+ * ```
584
698
  */
585
699
  set(path: string, text: string): void;
586
700
  /**
@@ -600,32 +714,29 @@ export declare interface OverlayInterface {
600
714
  *
601
715
  * @param directory - The absolute directory path to check
602
716
  * @returns True if a candidate path sits beneath the directory; false otherwise
717
+ *
718
+ * @example
719
+ * ```ts
720
+ * const path = '/srv/checkout/src/core/factories.ts'
721
+ * overlay.set(path, "export function createGreeting(): string {\n\treturn 'hi'\n}\n")
722
+ * overlay.covers('/srv/checkout/src/core') // true
723
+ * ```
603
724
  */
604
725
  covers(directory: string): boolean;
605
726
  /**
606
727
  * Releases every candidate.
607
728
  *
608
729
  * @returns Nothing
730
+ *
731
+ * @example
732
+ * ```ts
733
+ * overlay.clear()
734
+ * overlay.paths // []
735
+ * ```
609
736
  */
610
737
  clear(): void;
611
738
  }
612
739
 
613
- /**
614
- * Carries the construction options `Overlay` accepts.
615
- *
616
- * @example
617
- * ```ts
618
- * const options: OverlayOptions = { sensitive: false }
619
- * ```
620
- */
621
- export declare interface OverlayOptions {
622
- /**
623
- * If `true`, a lookup key matches a recorded path exactly; if `false`, it matches with file-name
624
- * case folded. Default: `true`
625
- */
626
- readonly sensitive?: boolean;
627
- }
628
-
629
740
  /**
630
741
  * Overwrites a file that already exists, through a descriptor that refuses a symbolic link at the
631
742
  * final component.
@@ -667,7 +778,50 @@ export declare interface OverlayOptions {
667
778
  export declare function overwriteFile(file: string, text: string): void;
668
779
 
669
780
  /**
670
- * Answers claims through resident TypeScript, Oxlint, and Vitest stages.
781
+ * Parses the configuration one compiler run printed for a TypeScript project.
782
+ *
783
+ * @remarks
784
+ * Reads what `tsc --showConfig` writes: a JSON record carrying the resolved `compilerOptions`
785
+ * beside the project's own file selection. `compilerOptions` is carried unvalidated, because a
786
+ * caller digests it rather than reading a member out of it. `files` and `include` are read only
787
+ * where the printed record carries one whose entries are all strings, so a member the compiler
788
+ * printed in another shape reads as absent rather than as an empty selection. Text that is not a
789
+ * JSON record, including the diagnostic the compiler prints for a project it refuses, yields
790
+ * `undefined`.
791
+ *
792
+ * @param text - The compiler's printed configuration
793
+ * @returns The project's resolved configuration, or `undefined` when the text carries no record
794
+ *
795
+ * @example
796
+ * ```ts
797
+ * parseProjectConfig('{"compilerOptions":{"strict":true}}')?.compilerOptions // { strict: true }
798
+ * parseProjectConfig("error TS5058: The specified path does not exist.") // undefined
799
+ * ```
800
+ */
801
+ export declare function parseProjectConfig(text: string): ProjectConfig | undefined;
802
+
803
+ /**
804
+ * Parses the process id one revision identity names.
805
+ *
806
+ * @remarks
807
+ * Every file and directory this package writes into a target carries the writing host's process id
808
+ * and a fresh UUID, joined by `-`. A sweep reads that id to decide whether the host that wrote the
809
+ * name is gone. The whole identity must match, so a name carrying anything else yields `undefined`
810
+ * and the sweep leaves what it found alone.
811
+ *
812
+ * @param revision - The revision identity a name carries
813
+ * @returns The process id it names, or `undefined` when the text is not a revision identity
814
+ *
815
+ * @example
816
+ * ```ts
817
+ * parseRevisionOwner('4821-1b4e28ba-2fa1-11d2-883f-0016d3cca427') // 4821
818
+ * parseRevisionOwner('4821-9f0c') // undefined
819
+ * ```
820
+ */
821
+ export declare function parseRevisionOwner(revision: string): number | undefined;
822
+
823
+ /**
824
+ * Answers claims through its type, lint, and runtime stages.
671
825
  *
672
826
  * @remarks
673
827
  * Construction resolves the target workspace's toolchain and begins warming every stage. The boot
@@ -679,17 +833,52 @@ export declare function overwriteFile(file: string, text: string): void;
679
833
  * claim costs that claim rather than the process. A failed boot is replaced the same way: the next
680
834
  * claim runs the controls again rather than inheriting a refusal.
681
835
  *
682
- * @example
836
+ * @example The claim that earns a receipt
683
837
  * ```ts
684
- * const probe = new Probe({ workspace: '/srv/checkout' })
838
+ * import type { Claim } from '@orkestrel/probe'
839
+ * import { Probe } from '@orkestrel/probe/server'
840
+ *
841
+ * const claim: Claim = {
842
+ * project: 'configs/src/tsconfig.core.json',
843
+ * case: {
844
+ * files: [
845
+ * {
846
+ * path: 'src/core/factories.ts',
847
+ * text: "export function createGreeting(): string {\n\treturn 'hi'\n}\n",
848
+ * },
849
+ * ],
850
+ * test: {
851
+ * path: 'tmp/probe/greeting.test.ts',
852
+ * text: "import { expect, test } from 'vitest'\nimport { createGreeting } from '../../src/core/factories.js'\ntest('greets', () => expect(createGreeting()).toBe('hi'))\n",
853
+ * },
854
+ * },
855
+ * control: {
856
+ * files: [
857
+ * {
858
+ * path: 'src/core/factories.ts',
859
+ * text: "export function createGreeting(): number {\n\treturn 'hi'\n}\n",
860
+ * },
861
+ * ],
862
+ * test: {
863
+ * path: 'tmp/probe/greeting.test.ts',
864
+ * text: "import { expect, test } from 'vitest'\nimport { createGreeting } from '../../src/core/factories.js'\ntest('greets', () => expect(createGreeting()).toBe('hi'))\n",
865
+ * },
866
+ * stage: 'type',
867
+ * reason: 'a string returned as a number must not compile',
868
+ * },
869
+ * }
870
+ *
871
+ * const probe = new Probe({ workspace: process.cwd() })
685
872
  * const verdict = await probe.prove(claim)
873
+ * verdict.digest // 'fcb88a2dee987b8673c1fc7107979470'
874
+ * verdict.receipt // 'probe:fcb88a2dee987b8673c1fc7107979470:type:typescript@6.0.3:oxlint@1.82.0:vitest@4.1.11:configs/src/tsconfig.core.json@434f59254d58cf2683d453a26bd0d837'
686
875
  * await probe.destroy()
687
876
  * ```
688
877
  */
689
878
  export declare class Probe implements ProbeInterface {
690
879
  #private;
691
880
  /**
692
- * Resolves the target toolchain and starts warming the resident stages.
881
+ * Resolves the target toolchain and starts warming every stage.
693
882
  *
694
883
  * @param options - Workspace, deadline, and initial observation hooks
695
884
  */
@@ -701,7 +890,9 @@ export declare class Probe implements ProbeInterface {
701
890
  }
702
891
 
703
892
  /**
704
- * Serves one probe over this process's Model Context Protocol stdio transport.
893
+ * Implements `ProbeServerInterface` over a `PassThrough` stream this server owns, binding the
894
+ * published `prove` tool and the dual-era dispatcher to this process's Model Context Protocol
895
+ * stdio transport.
705
896
  *
706
897
  * @remarks
707
898
  * Construction creates the probe, publishes the `prove` tool, and binds the dual-era dispatcher to
@@ -746,8 +937,8 @@ export declare class ProbeServer implements ProbeServerInterface {
746
937
  * The server owns the process it runs in. `start` seizes standard input and standard output for
747
938
  * the transport and registers the termination handlers a harness signals, so a host that starts one
748
939
  * has already given the process to it. `destroy` reverses all of that and tears the probe down with
749
- * it, which is why there is no verb that stops serving and leaves the resident engines running: a
750
- * probe nothing is reading from holds its resident tools for nobody.
940
+ * it, which is why there is no verb that stops serving and leaves the stages standing: a probe
941
+ * nothing is reading from holds its tools and its mirror for nobody.
751
942
  *
752
943
  * @example
753
944
  * ```ts
@@ -767,6 +958,12 @@ export declare interface ProbeServerInterface {
767
958
  *
768
959
  * @returns Nothing
769
960
  * @throws When this call comes after teardown begins
961
+ *
962
+ * @example
963
+ * ```ts
964
+ * const server = new ProbeServer({ workspace: process.cwd() })
965
+ * server.start()
966
+ * ```
770
967
  */
771
968
  start(): void;
772
969
  /**
@@ -783,11 +980,40 @@ export declare interface ProbeServerInterface {
783
980
  * `start` and when something else is reading it at release, and a stream nothing had read yet
784
981
  * and nothing else reads is paused.
785
982
  *
786
- * @returns A promise that settles after the probe releases its resident engines
983
+ * @returns A promise that settles after the probe releases every stage's tool and mirror
787
984
  */
788
985
  destroy(): Promise<void>;
789
986
  }
790
987
 
988
+ /**
989
+ * Carries what one TypeScript project resolved to, as the compiler itself printed it.
990
+ *
991
+ * @remarks
992
+ * The type stage reads this from `tsc --showConfig`, which prints the resolved options beside the
993
+ * project's own file selection. `compilerOptions` is the compiler's record and is carried
994
+ * unvalidated, because this package digests it rather than reading a member out of it. `files` is
995
+ * the selection the project resolved to, spelled relative to the project file, and `include` is
996
+ * present only where the project or a project it extends declares one. Every path is the
997
+ * compiler's own spelling, so a copy of the project file placed beside the original reads them
998
+ * unchanged.
999
+ *
1000
+ * @example
1001
+ * ```ts
1002
+ * const config: ProjectConfig = {
1003
+ * compilerOptions: { strict: true, rootDir: '../../src/core' },
1004
+ * files: ['../../src/core/index.ts'],
1005
+ * }
1006
+ * ```
1007
+ */
1008
+ export declare interface ProjectConfig {
1009
+ /** Holds the compiler options the project resolved to, unvalidated. */
1010
+ readonly compilerOptions: unknown;
1011
+ /** Holds the files the project resolved to, or is absent when the compiler printed none. */
1012
+ readonly files?: readonly string[];
1013
+ /** Holds the file patterns the project declares, or is absent when it declares none. */
1014
+ readonly include?: readonly string[];
1015
+ }
1016
+
791
1017
  /**
792
1018
  * Reads the condition code a native fault carries.
793
1019
  *
@@ -919,20 +1145,26 @@ export declare function releaseListeners(emitter: EventEmitter, capture: Listene
919
1145
  /**
920
1146
  * Resolves a package's portable JavaScript binary from the target workspace.
921
1147
  *
1148
+ * @remarks
1149
+ * A package publishes its binaries under keys of its own choosing, and that key is often not the
1150
+ * package name: `typescript` publishes `tsc`. Name the key as `command` wherever the two differ.
1151
+ * Default: the package name.
1152
+ *
922
1153
  * @param workspace - The target workspace root
923
- * @param name - The installed package name and binary key
1154
+ * @param name - The installed package name
1155
+ * @param command - The `bin` key to read. Default: `name`
924
1156
  * @returns The absolute JavaScript entry named by the package's `bin` field
925
- * @throws When the package does not publish a binary under the requested name
1157
+ * @throws When the package does not publish a binary under the requested key
926
1158
  *
927
1159
  * @example
928
1160
  * ```ts
929
1161
  * relativeWorkspaceFile(process.cwd(), resolveWorkspaceBinary(process.cwd(), 'oxlint'))
930
1162
  * // 'node_modules/oxlint/bin/oxlint'
931
- * resolveWorkspaceBinary(process.cwd(), 'typescript')
932
- * // throws: typescript does not publish the typescript binary
1163
+ * relativeWorkspaceFile(process.cwd(), resolveWorkspaceBinary(process.cwd(), 'typescript', 'tsc'))
1164
+ * // 'node_modules/typescript/bin/tsc'
933
1165
  * ```
934
1166
  */
935
- export declare function resolveWorkspaceBinary(workspace: string, name: string): string;
1167
+ export declare function resolveWorkspaceBinary(workspace: string, name: string, command?: string): string;
936
1168
 
937
1169
  /**
938
1170
  * Resolves a path inside a target workspace and rejects traversal outside it.
@@ -1044,24 +1276,72 @@ export declare class RuntimeStage implements StageInterface {
1044
1276
  * @param workspace - The target workspace root. Default: the current working directory
1045
1277
  */
1046
1278
  constructor(workspace?: string);
1047
- get stage(): Stage_2;
1279
+ get stage(): Stage;
1048
1280
  get progress(): number;
1049
- inspect(subject: Case_2): Promise<Check_2>;
1281
+ /**
1282
+ * Inspects one case.
1283
+ *
1284
+ * @param subject - The candidate drafts and test to inspect
1285
+ * @returns One outcome for this stage
1286
+ * @throws When the workspace's own tool cannot start or has already been destroyed
1287
+ */
1288
+ inspect(subject: Case): Promise<Check>;
1289
+ /**
1290
+ * Tears down the resident tool or the mirror and releases its resources.
1291
+ *
1292
+ * @remarks
1293
+ * A stage abandons every inspection it holds rather than waiting behind one, so teardown never
1294
+ * waits for an inspection to return. An abandoned inspection rejects, either at the stage's own
1295
+ * guard or as the owned tool closes. Teardown is bounded whatever the stage's own tool does: a
1296
+ * tool that answers neither its warming exchange nor its ending is signalled and released at the
1297
+ * stage's own deadline. A coordinator replaces a stage whose worker no longer returns because
1298
+ * teardown neither waits for an inspection nor waits past that deadline.
1299
+ *
1300
+ * @returns A promise that settles after the resident tool or the mirror releases its resources
1301
+ */
1050
1302
  destroy(): Promise<void>;
1051
1303
  }
1052
1304
 
1053
1305
  /**
1054
- * Inspects one case with a resident workspace tool.
1306
+ * Scans the plain-text output of one compiler run into the diagnostics it reported.
1055
1307
  *
1056
1308
  * @remarks
1057
- * Warming begins at construction. The `inspect` method awaits that one warm operation and reuses
1058
- * the resulting tool across calls. A stage serves one inspection at a time and admits none itself.
1059
- * Await an inspection before starting the next one, or admit through one queue per stage the way
1060
- * `Probe` does: a second concurrent call reaches the same resident tool and the same overlay,
1061
- * document, and specification state the first is still using. A stage never holds a later
1062
- * inspection behind an earlier one, so a caller that abandons an inspection at its own deadline
1063
- * can still use the stage. The `destroy` method permanently tears the stage down and releases
1064
- * every resource it owns.
1309
+ * Reads the shape `tsc --pretty false` prints: `path(line,column): error TSnnnn: message` for a
1310
+ * diagnostic about a file, and `error TSnnnn: message` for one about a project. A line that carries
1311
+ * neither prefix and begins with whitespace is an elaboration of the diagnostic above it and joins
1312
+ * that message with a newline; any other line is skipped, so a runtime's own failure text yields no
1313
+ * diagnostic and a caller reads that as the fault it is. The compiler counts lines and columns from
1314
+ * one and columns in UTF-16 code units, so each location is lowered to the zero-based UTF-16 point
1315
+ * `Diagnostic.range` fixes and `end` resolves to `start`, which is the extent this output carries.
1316
+ * A warning reads as a diagnostic, because the workspace's own gate reports it the same way. The
1317
+ * `TS\d+` token that follows `error` or `warning` is what recognizes the line; nothing is captured
1318
+ * from it, because `Diagnostic` carries no field for it.
1319
+ *
1320
+ * @param text - The compiler's output
1321
+ * @returns One record per reported diagnostic, in the compiler's own order
1322
+ *
1323
+ * @example
1324
+ * ```ts
1325
+ * scanDiagnostics("src/a.ts(1,14): error TS2322: Type 'string' is not assignable to type 'number'.")
1326
+ * // [{ path: 'src/a.ts', range: { start: { line: 0, character: 13 }, end: { line: 0, character: 13 } }, message: "Type 'string' is not assignable to type 'number'." }]
1327
+ * scanDiagnostics('error TS5058: The specified path does not exist.')
1328
+ * // [{ message: 'The specified path does not exist.' }]
1329
+ * ```
1330
+ */
1331
+ export declare function scanDiagnostics(text: string): readonly Diagnostic[];
1332
+
1333
+ /**
1334
+ * Inspects one case with the workspace's own tool.
1335
+ *
1336
+ * @remarks
1337
+ * Warming begins at construction. The `inspect` method awaits that one warm operation, which
1338
+ * builds the resident tool or the mirror it reuses across calls. A stage serves one inspection at
1339
+ * a time and admits none itself. Await an inspection before starting the next one, or admit
1340
+ * through one queue per stage the way `Probe` does: a second concurrent call reaches the same
1341
+ * resident tool or mirror and the same overlay, document, and specification state the first is
1342
+ * still using. A stage never holds a later inspection behind an earlier one, so a caller that
1343
+ * abandons an inspection at its own deadline can still use the stage. The `destroy` method
1344
+ * permanently tears the stage down and releases every resource it owns.
1065
1345
  *
1066
1346
  * @example
1067
1347
  * ```ts
@@ -1071,7 +1351,7 @@ export declare class RuntimeStage implements StageInterface {
1071
1351
  * ```
1072
1352
  */
1073
1353
  export declare interface StageInterface {
1074
- /** Names the inspection this resident stage performs. */
1354
+ /** Names the inspection this stage performs. */
1075
1355
  readonly stage: Stage;
1076
1356
  /**
1077
1357
  * Reports claimant-owned progress the coordinator compares with its inspection snapshot.
@@ -1089,41 +1369,58 @@ export declare interface StageInterface {
1089
1369
  *
1090
1370
  * @param subject - The candidate drafts and test to inspect
1091
1371
  * @returns One outcome for this stage
1092
- * @throws When the resident tool cannot start or has already been destroyed
1372
+ * @throws When the workspace's own tool cannot start or has already been destroyed
1373
+ *
1374
+ * @example
1375
+ * ```ts
1376
+ * const check = await stage.inspect(subject)
1377
+ * console.log(check.stage, check.elapsed, check.issues.length)
1378
+ * ```
1093
1379
  */
1094
1380
  inspect(subject: Case): Promise<Check>;
1095
1381
  /**
1096
- * Tears down the resident tool and releases its resources.
1382
+ * Tears down the resident tool or the mirror and releases its resources.
1097
1383
  *
1098
1384
  * @remarks
1099
1385
  * A stage abandons every inspection it holds rather than waiting behind one, so teardown never
1100
1386
  * waits for an inspection to return. An abandoned inspection rejects, either at the stage's own
1101
- * guard or as the owned tool closes. Teardown is bounded whatever the resident tool does: a tool
1102
- * that answers neither its warming exchange nor its ending is signalled and released at the
1387
+ * guard or as the owned tool closes. Teardown is bounded whatever the stage's own tool does: a
1388
+ * tool that answers neither its warming exchange nor its ending is signalled and released at the
1103
1389
  * stage's own deadline. A coordinator replaces a stage whose worker no longer returns because
1104
1390
  * teardown neither waits for an inspection nor waits past that deadline.
1105
1391
  *
1106
- * @returns A promise that settles after the resident tool releases its resources
1392
+ * @returns A promise that settles after the resident tool or the mirror releases its resources
1107
1393
  */
1108
1394
  destroy(): Promise<void>;
1109
1395
  }
1110
1396
 
1111
1397
  /**
1112
- * Inspects TypeScript source through resident language services from the target workspace.
1398
+ * Inspects TypeScript source by running the target workspace's own compiler over a mirror of it.
1113
1399
  *
1114
1400
  * @remarks
1115
- * Construction starts loading the workspace's compiler and warming one service per project the
1116
- * workspace declares. A candidate draft is checked against the project a call names, or
1117
- * against its own scoped environment project when a call names none, while the test uses the root
1118
- * project. Disk snapshots use their modification time as the service version so dependency edits
1119
- * cannot leave a warm answer stale. Candidate text lives in an overlay the inspection owns, and
1120
- * the language-service host answers existence, reads, and versions from it, so a candidate that
1121
- * exists only as text is importable and a candidate that shadows a disk file is checked as the
1122
- * text the case supplied. A project outside the declared set holds one recycled slot, so a caller
1123
- * varying the project cannot grow the resident set. A language service checks one candidate
1124
- * synchronously, so the stage hands the host's event loop back at each candidate boundary: a
1125
- * caller's deadline is answered within one candidate's check rather than after the whole
1126
- * inspection, and an inspection abandoned at that deadline stops at the next boundary.
1401
+ * The compiler is a process this stage spawns rather than a module it loads, so nothing here holds
1402
+ * a resident program and the host's event loop is free for the whole check. Construction sweeps a
1403
+ * mirror an earlier host left behind, copies the workspace into a fresh one under
1404
+ * {@link TYPE_MIRROR}, and builds each declared project's incremental state there, so the first
1405
+ * inspection is warm.
1406
+ *
1407
+ * Every inspection refreshes the mirror by content digest, writes each candidate draft and the test
1408
+ * at its mirrored declared path, and runs `tsc --noEmit --pretty false` once per distinct selected
1409
+ * project. A draft therefore shadows the file it replaces: a consumer of that path is checked
1410
+ * against the draft's text, and a draft importing a sibling draft resolves to the sibling draft.
1411
+ * Nothing is written outside `tmp/`, and the workspace's own copy of a drafted file never moves.
1412
+ *
1413
+ * Each run reads a scratch project this stage writes beside the mirrored project it extends, so
1414
+ * every relative path the project declares — its own `rootDir`, `include`, `paths`, and the
1415
+ * projects it extends — resolves inside the mirror without being rewritten. The scratch adds the
1416
+ * drafts as `files`, which the project's own `exclude` does not reach, and its incremental state
1417
+ * lives in the mirror so reuse survives the refresh.
1418
+ *
1419
+ * The diagnostics decide the outcome, never the exit code, which the supported compiler majors
1420
+ * disagree on. A diagnostic naming a project file, and one naming no file at all, is the target
1421
+ * tree's own configuration fault and raises rather than reporting, unless the `.json` file is one
1422
+ * the claim itself drafted; every other diagnostic is a claimant issue at the point the compiler
1423
+ * reported, which is the extent the plain-text output carries.
1127
1424
  *
1128
1425
  * @example
1129
1426
  * ```ts
@@ -1135,12 +1432,12 @@ export declare interface StageInterface {
1135
1432
  export declare class TypeStage implements TypeStageInterface {
1136
1433
  #private;
1137
1434
  /**
1138
- * Starts warming the target workspace's TypeScript service.
1435
+ * Resolves the target workspace's compiler and starts warming its mirror.
1139
1436
  *
1140
1437
  * @param workspace - The target workspace root. Default: the current working directory
1141
1438
  */
1142
1439
  constructor(workspace?: string);
1143
- get stage(): Stage_2;
1440
+ get stage(): Stage;
1144
1441
  get progress(): number;
1145
1442
  /**
1146
1443
  * Inspects one case, against a caller-named project where the caller names one.
@@ -1154,24 +1451,25 @@ export declare class TypeStage implements TypeStageInterface {
1154
1451
  * @param project - The workspace-relative TypeScript project the candidate drafts are checked
1155
1452
  * against. Default: the scoped project each candidate path infers
1156
1453
  * @returns One outcome for this stage
1157
- * @throws When the resident compiler cannot start or the stage has already been destroyed
1454
+ * @throws When the workspace refuses the mirror, when a project the run reads is malformed, or
1455
+ * when the stage has already been destroyed
1158
1456
  */
1159
- inspect(subject: Case_2, project?: string): Promise<Check_2>;
1457
+ inspect(subject: Case, project?: string): Promise<Check>;
1160
1458
  /**
1161
1459
  * Resolves one project to the path and digest this stage applies for it.
1162
1460
  *
1163
1461
  * @remarks
1164
- * Reads the parse this stage itself applies, filling its cache when the project is not already
1165
- * resident, so the reported digest is the configuration the inspection is judged under rather
1166
- * than a second parse a caller ran. The returned record is a value copy, so a later eviction
1167
- * does not move it.
1462
+ * Reads the configuration the compiler itself prints for the project as the mirror holds it,
1463
+ * refreshed from the workspace first on a cache miss, so the digest names the configuration the
1464
+ * check applies. The reading is cached per project for the life of the stage, so a draft written
1465
+ * afterward cannot move it.
1168
1466
  *
1169
1467
  * @param project - The workspace-relative TypeScript project to resolve
1170
1468
  * @returns The resolved workspace-relative path and the digest of its compiler options
1171
- * @throws When the project escapes the workspace, cannot be parsed, or the stage has already
1172
- * been destroyed
1469
+ * @throws When the project escapes the workspace, when the compiler refuses it, or when the
1470
+ * stage has already been destroyed
1173
1471
  */
1174
- resolve(project: string): Promise<Project_2>;
1472
+ resolve(project: string): Promise<Project>;
1175
1473
  destroy(): Promise<void>;
1176
1474
  }
1177
1475
 
@@ -1181,7 +1479,8 @@ export declare class TypeStage implements TypeStageInterface {
1181
1479
  * @remarks
1182
1480
  * The type stage carries members the shared stage contract cannot: the lint and runtime stages
1183
1481
  * read no project, so a project parameter and a project lookup belong here rather than on
1184
- * `StageInterface`.
1482
+ * `StageInterface`. `resolve` reads the compiler's own printed configuration for the workspace's
1483
+ * copy of the project, so a claim's drafts never move the digest it reports.
1185
1484
  *
1186
1485
  * @example
1187
1486
  * ```ts
@@ -1197,7 +1496,13 @@ export declare interface TypeStageInterface extends StageInterface {
1197
1496
  * @param project - The workspace-relative TypeScript project the candidate drafts are checked
1198
1497
  * against. Default: the scoped project each candidate path infers
1199
1498
  * @returns One outcome for this stage
1200
- * @throws When the resident compiler cannot start or the stage has already been destroyed
1499
+ * @throws When the workspace refuses the mirror, when a project the run reads is malformed, or
1500
+ * when the stage has already been destroyed
1501
+ *
1502
+ * @example
1503
+ * ```ts
1504
+ * const check = await stage.inspect(subject, 'configs/src/tsconfig.core.json')
1505
+ * ```
1201
1506
  */
1202
1507
  inspect(subject: Case, project?: string): Promise<Check>;
1203
1508
  /**
@@ -1205,8 +1510,14 @@ export declare interface TypeStageInterface extends StageInterface {
1205
1510
  *
1206
1511
  * @param project - The workspace-relative TypeScript project to resolve
1207
1512
  * @returns The resolved workspace-relative path and the digest of its compiler options
1208
- * @throws When the project escapes the workspace, cannot be parsed, or the stage has already
1209
- * been destroyed
1513
+ * @throws When the project escapes the workspace, when the compiler refuses it, or when the
1514
+ * stage has already been destroyed
1515
+ *
1516
+ * @example
1517
+ * ```ts
1518
+ * const project = await stage.resolve('configs/src/tsconfig.core.json')
1519
+ * console.log(project.path, project.digest)
1520
+ * ```
1210
1521
  */
1211
1522
  resolve(project: string): Promise<Project>;
1212
1523
  }