@orkestrel/probe 0.0.3 → 0.0.5

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.
@@ -117,6 +117,21 @@ export declare function describeUnknown(value: unknown): string;
117
117
  */
118
118
  export declare function findRefusedPaths(value: unknown): readonly string[];
119
119
 
120
+ /**
121
+ * Guards one resident-stage operation with the stage failure contract.
122
+ *
123
+ * @param stage - The resident stage serving the operation
124
+ * @param operation - The operation to settle
125
+ * @returns The operation's fulfilled value
126
+ * @throws The original `ProbeError`, or an instrument-owned malformed failure retaining the cause
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * await guardStage('type', stage.inspect(subject))
131
+ * ```
132
+ */
133
+ export declare function guardStage<T>(stage: Stage, operation: Promise<T>): Promise<T>;
134
+
120
135
  /**
121
136
  * Selects the Language Server Protocol language identifier for a source path.
122
137
  *
@@ -482,6 +497,40 @@ export declare interface OverlayInterface {
482
497
  clear(): void;
483
498
  }
484
499
 
500
+ /**
501
+ * Overwrites a file that already exists, through a descriptor that refuses a symbolic link at the
502
+ * final component.
503
+ *
504
+ * @remarks
505
+ * A containment walk and the write that follows it are separate calls, so a process that swaps the
506
+ * final component between them decides where the bytes land. `writeFileSync` opens with the default
507
+ * `w` flag, which follows a symbolic link at that component and creates the file when it is absent,
508
+ * so the walk's reading does not bind the write. Opening `O_WRONLY | O_TRUNC | O_NOFOLLOW` moves
509
+ * that decision into the call that writes: a link at the final component refuses the open, and the
510
+ * omitted `O_CREAT` fails `ENOENT` for a target that has gone since the walk saw it. This is the
511
+ * mutating counterpart of the `wx` flag every create in this package uses. Which code the refusal
512
+ * carries is the host's; Linux with Node v22.22.2 reported `ELOOP` on 2026-08-24.
513
+ *
514
+ * A directory component stays open, because Node exposes no descriptor-relative call to walk and
515
+ * write through one set of descriptors. Where a host's Node build defines no `O_NOFOLLOW`, that flag
516
+ * contributes nothing to the flag set and the final component is open on that host too.
517
+ *
518
+ * @param file - The absolute path to overwrite
519
+ * @param text - The text to write
520
+ * @returns Nothing
521
+ * @throws The host's own fault when the final component is a symbolic link, when the file is
522
+ * absent, or when the host refuses the descriptor
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * const file = resolveWorkspaceFile('/srv/checkout', 'tmp/probe/arm-type.ts', true)
527
+ * writeFileSync(file, 'export type Signal = string\n', { encoding: 'utf8', flag: 'wx' })
528
+ * overwriteFile(file, 'export type Signal = number\n')
529
+ * readFileSync(file, 'utf8') // 'export type Signal = number\n'
530
+ * ```
531
+ */
532
+ export declare function overwriteFile(file: string, text: string): void;
533
+
485
534
  /**
486
535
  * Reads a JSON-RPC frame's declared byte length.
487
536
  *
@@ -649,6 +698,49 @@ export declare function readWorkspaceManifest(workspace: string, name: string):
649
698
  */
650
699
  export declare function relativeWorkspaceFile(workspace: string, file: string): string;
651
700
 
701
+ /**
702
+ * Projects the paths one tool named in a message into the forms this package's issues expose.
703
+ *
704
+ * @remarks
705
+ * An issue's `path` is workspace-relative, and its message is prose a tool wrote about the same
706
+ * tree. Left alone that prose names a file by the host's own layout, so one issue reports a path two
707
+ * ways and a reader on another checkout is sent to a directory that is not theirs. Each spelling of
708
+ * the root is removed: the absolute path, the backslash spelling a Windows tool writes, and the
709
+ * `file:` URL a runtime names a module by. A root the host itself names ends in a separator, and the
710
+ * paths beneath it continue straight from it rather than from one more separator. A path outside the
711
+ * workspace stands as it is, because a relative spelling of it would name a file the tree does not
712
+ * hold.
713
+ *
714
+ * A spelling of the root counts only where a path begins: at the start of the message, or after a
715
+ * character no path carries. A directory whose own name ends in this root's text holds a different
716
+ * tree, so the root inside a longer path names a file this workspace does not hold and that path
717
+ * survives whole.
718
+ *
719
+ * Only the root is removed. A name is otherwise the tool's, including a `.probe-` name that reads
720
+ * like a specification this package generates: the stage that wrote one knows the exact name it
721
+ * wrote and renames that name itself, which is the only reading that separates its own file from a
722
+ * file the target tree owns.
723
+ *
724
+ * Everything else the tool wrote survives verbatim, separators included. A message carries text
725
+ * that is not a path — a runtime escapes a newline as a backslash when it prints a string inline,
726
+ * and a compiler quotes a string literal with its escapes intact — so rewriting every separator
727
+ * would corrupt the evidence the message exists to carry. The cost is that the path left behind
728
+ * keeps the separator the tool chose, which on Windows is not the one `Issue.path` uses.
729
+ *
730
+ * @param workspace - The target workspace root
731
+ * @param message - The message a tool reported
732
+ * @returns The message with the host's own layout removed from every path that begins with it
733
+ *
734
+ * @example
735
+ * ```ts
736
+ * relativeWorkspaceMessage('/srv/checkout', 'Cannot read /srv/checkout/tsconfig.json')
737
+ * // 'Cannot read tsconfig.json'
738
+ * relativeWorkspaceMessage('/srv/checkout', 'Cannot read /mirror/srv/checkout/tsconfig.json')
739
+ * // 'Cannot read /mirror/srv/checkout/tsconfig.json'
740
+ * ```
741
+ */
742
+ export declare function relativeWorkspaceMessage(workspace: string, message: string): string;
743
+
652
744
  /**
653
745
  * Removes every listener one emitter gained for the captured events since its capture.
654
746
  *
@@ -749,6 +841,13 @@ export declare function resolveWorkspaceModule(workspace: string, specifier: str
749
841
  * workspace module whose disk content or candidate revision changed, runs that specification,
750
842
  * evicts its result, and deletes the file. Clearing the overlay makes the next snapshot differ from
751
843
  * the candidate revision, so the next inspection invalidates that module and reads disk again.
844
+ * After the run, the stage checks each covered module reachable from the generated specification
845
+ * against the overlay paths its loader served. A target-config loader that served first produces a
846
+ * workspace issue; a module id probe's loader received but failed to match produces an instrument
847
+ * issue.
848
+ * The runtime overlay leaves bare specifier resolution to Vite: it replaces a bare import only
849
+ * after Vite resolves that import to a covered file. The type stage overlays the declared test
850
+ * path, while this stage executes a generated sibling and overlays only the candidate files.
752
851
  *
753
852
  * Vite retains one unresolved URL for every specification path, so the stage replaces its whole
754
853
  * Vitest service after 64 specifications rather than deleting from each map that service owns. Any
@@ -772,9 +871,12 @@ export declare function resolveWorkspaceModule(workspace: string, specifier: str
772
871
  * test path the host refuses to create is a claimant failure with `code: 'refused'`. Each is thrown
773
872
  * rather than reported, because a test that never ran is no evidence about the candidate.
774
873
  *
775
- * The write path's physical-containment guarantee covers the claim inputs and the target tree as
776
- * inspected. A concurrent process that mutates a path component between the final inspection and
777
- * the write is outside that guarantee.
874
+ * Physical containment covers the claim inputs, the target tree as the walk inspected it, and the
875
+ * final component at the moment this stage writes or unlinks it: a specification is created with the
876
+ * `wx` flag, which fails rather than following a symbolic link that appeared after the walk, and an
877
+ * unlink names the final component itself rather than what it points at. A directory component
878
+ * swapped for a symbolic link between the walk and the write stays open, because Node exposes no
879
+ * descriptor-relative call to pin the walk and the write to one set of descriptors.
778
880
  *
779
881
  * @example
780
882
  * ```ts
@@ -117,6 +117,21 @@ export declare function describeUnknown(value: unknown): string;
117
117
  */
118
118
  export declare function findRefusedPaths(value: unknown): readonly string[];
119
119
 
120
+ /**
121
+ * Guards one resident-stage operation with the stage failure contract.
122
+ *
123
+ * @param stage - The resident stage serving the operation
124
+ * @param operation - The operation to settle
125
+ * @returns The operation's fulfilled value
126
+ * @throws The original `ProbeError`, or an instrument-owned malformed failure retaining the cause
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * await guardStage('type', stage.inspect(subject))
131
+ * ```
132
+ */
133
+ export declare function guardStage<T>(stage: Stage, operation: Promise<T>): Promise<T>;
134
+
120
135
  /**
121
136
  * Selects the Language Server Protocol language identifier for a source path.
122
137
  *
@@ -482,6 +497,40 @@ export declare interface OverlayInterface {
482
497
  clear(): void;
483
498
  }
484
499
 
500
+ /**
501
+ * Overwrites a file that already exists, through a descriptor that refuses a symbolic link at the
502
+ * final component.
503
+ *
504
+ * @remarks
505
+ * A containment walk and the write that follows it are separate calls, so a process that swaps the
506
+ * final component between them decides where the bytes land. `writeFileSync` opens with the default
507
+ * `w` flag, which follows a symbolic link at that component and creates the file when it is absent,
508
+ * so the walk's reading does not bind the write. Opening `O_WRONLY | O_TRUNC | O_NOFOLLOW` moves
509
+ * that decision into the call that writes: a link at the final component refuses the open, and the
510
+ * omitted `O_CREAT` fails `ENOENT` for a target that has gone since the walk saw it. This is the
511
+ * mutating counterpart of the `wx` flag every create in this package uses. Which code the refusal
512
+ * carries is the host's; Linux with Node v22.22.2 reported `ELOOP` on 2026-08-24.
513
+ *
514
+ * A directory component stays open, because Node exposes no descriptor-relative call to walk and
515
+ * write through one set of descriptors. Where a host's Node build defines no `O_NOFOLLOW`, that flag
516
+ * contributes nothing to the flag set and the final component is open on that host too.
517
+ *
518
+ * @param file - The absolute path to overwrite
519
+ * @param text - The text to write
520
+ * @returns Nothing
521
+ * @throws The host's own fault when the final component is a symbolic link, when the file is
522
+ * absent, or when the host refuses the descriptor
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * const file = resolveWorkspaceFile('/srv/checkout', 'tmp/probe/arm-type.ts', true)
527
+ * writeFileSync(file, 'export type Signal = string\n', { encoding: 'utf8', flag: 'wx' })
528
+ * overwriteFile(file, 'export type Signal = number\n')
529
+ * readFileSync(file, 'utf8') // 'export type Signal = number\n'
530
+ * ```
531
+ */
532
+ export declare function overwriteFile(file: string, text: string): void;
533
+
485
534
  /**
486
535
  * Reads a JSON-RPC frame's declared byte length.
487
536
  *
@@ -649,6 +698,49 @@ export declare function readWorkspaceManifest(workspace: string, name: string):
649
698
  */
650
699
  export declare function relativeWorkspaceFile(workspace: string, file: string): string;
651
700
 
701
+ /**
702
+ * Projects the paths one tool named in a message into the forms this package's issues expose.
703
+ *
704
+ * @remarks
705
+ * An issue's `path` is workspace-relative, and its message is prose a tool wrote about the same
706
+ * tree. Left alone that prose names a file by the host's own layout, so one issue reports a path two
707
+ * ways and a reader on another checkout is sent to a directory that is not theirs. Each spelling of
708
+ * the root is removed: the absolute path, the backslash spelling a Windows tool writes, and the
709
+ * `file:` URL a runtime names a module by. A root the host itself names ends in a separator, and the
710
+ * paths beneath it continue straight from it rather than from one more separator. A path outside the
711
+ * workspace stands as it is, because a relative spelling of it would name a file the tree does not
712
+ * hold.
713
+ *
714
+ * A spelling of the root counts only where a path begins: at the start of the message, or after a
715
+ * character no path carries. A directory whose own name ends in this root's text holds a different
716
+ * tree, so the root inside a longer path names a file this workspace does not hold and that path
717
+ * survives whole.
718
+ *
719
+ * Only the root is removed. A name is otherwise the tool's, including a `.probe-` name that reads
720
+ * like a specification this package generates: the stage that wrote one knows the exact name it
721
+ * wrote and renames that name itself, which is the only reading that separates its own file from a
722
+ * file the target tree owns.
723
+ *
724
+ * Everything else the tool wrote survives verbatim, separators included. A message carries text
725
+ * that is not a path — a runtime escapes a newline as a backslash when it prints a string inline,
726
+ * and a compiler quotes a string literal with its escapes intact — so rewriting every separator
727
+ * would corrupt the evidence the message exists to carry. The cost is that the path left behind
728
+ * keeps the separator the tool chose, which on Windows is not the one `Issue.path` uses.
729
+ *
730
+ * @param workspace - The target workspace root
731
+ * @param message - The message a tool reported
732
+ * @returns The message with the host's own layout removed from every path that begins with it
733
+ *
734
+ * @example
735
+ * ```ts
736
+ * relativeWorkspaceMessage('/srv/checkout', 'Cannot read /srv/checkout/tsconfig.json')
737
+ * // 'Cannot read tsconfig.json'
738
+ * relativeWorkspaceMessage('/srv/checkout', 'Cannot read /mirror/srv/checkout/tsconfig.json')
739
+ * // 'Cannot read /mirror/srv/checkout/tsconfig.json'
740
+ * ```
741
+ */
742
+ export declare function relativeWorkspaceMessage(workspace: string, message: string): string;
743
+
652
744
  /**
653
745
  * Removes every listener one emitter gained for the captured events since its capture.
654
746
  *
@@ -749,6 +841,13 @@ export declare function resolveWorkspaceModule(workspace: string, specifier: str
749
841
  * workspace module whose disk content or candidate revision changed, runs that specification,
750
842
  * evicts its result, and deletes the file. Clearing the overlay makes the next snapshot differ from
751
843
  * the candidate revision, so the next inspection invalidates that module and reads disk again.
844
+ * After the run, the stage checks each covered module reachable from the generated specification
845
+ * against the overlay paths its loader served. A target-config loader that served first produces a
846
+ * workspace issue; a module id probe's loader received but failed to match produces an instrument
847
+ * issue.
848
+ * The runtime overlay leaves bare specifier resolution to Vite: it replaces a bare import only
849
+ * after Vite resolves that import to a covered file. The type stage overlays the declared test
850
+ * path, while this stage executes a generated sibling and overlays only the candidate files.
752
851
  *
753
852
  * Vite retains one unresolved URL for every specification path, so the stage replaces its whole
754
853
  * Vitest service after 64 specifications rather than deleting from each map that service owns. Any
@@ -772,9 +871,12 @@ export declare function resolveWorkspaceModule(workspace: string, specifier: str
772
871
  * test path the host refuses to create is a claimant failure with `code: 'refused'`. Each is thrown
773
872
  * rather than reported, because a test that never ran is no evidence about the candidate.
774
873
  *
775
- * The write path's physical-containment guarantee covers the claim inputs and the target tree as
776
- * inspected. A concurrent process that mutates a path component between the final inspection and
777
- * the write is outside that guarantee.
874
+ * Physical containment covers the claim inputs, the target tree as the walk inspected it, and the
875
+ * final component at the moment this stage writes or unlinks it: a specification is created with the
876
+ * `wx` flag, which fails rather than following a symbolic link that appeared after the walk, and an
877
+ * unlink names the final component itself rather than what it points at. A directory component
878
+ * swapped for a symbolic link between the walk and the write stays open, because Node exposes no
879
+ * descriptor-relative call to pin the walk and the write to one set of descriptors.
778
880
  *
779
881
  * @example
780
882
  * ```ts