@orkestrel/console 0.0.11 → 0.0.12

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.
@@ -5,14 +5,14 @@ import { LogLevel } from '@orkestrel/console';
5
5
  import { SinkInterface } from '@orkestrel/console';
6
6
 
7
7
  /**
8
- * One intercepted process-stream write — the immutable, serializable record a
8
+ * Represents one intercepted process-stream write — the immutable, serializable record a
9
9
  * {@link ProcessCaptureInterface} buffers and emits, the server analogue of the core
10
10
  * `CapturedMessage`.
11
11
  *
12
12
  * @remarks
13
13
  * - `level` — the {@link StreamLevel} naming which stream (`stdout` / `stderr`) was written.
14
- * - `text` — the chunk decoded to a string (via {@link import('./helpers.js').decodeChunk} —
15
- * total, never throws), VERBATIM: no trailing-newline trimming and no ANSI stripping, so the
14
+ * - `text` — the chunk decoded to a string (through {@link import('./helpers.js').decodeChunk} —
15
+ * total, never throws), verbatim: no trailing-newline trimming and no ANSI stripping, so the
16
16
  * captured text is exactly the bytes the program emitted.
17
17
  * - `time` — the capture instant as epoch milliseconds (`Date.now()`); a plain number so the record
18
18
  * stays serializable and orderable.
@@ -25,52 +25,7 @@ export declare interface CapturedChunk {
25
25
  }
26
26
 
27
27
  /**
28
- * The width in character cells of a stream target its live `columns` when it is a TTY, else the
29
- * non-interactive {@link DEFAULT_COLUMNS} fallback. The basis a {@link import('./types.js').ServerSinkInterface}
30
- * reports through `columns` so a `Reporter` / `Progress` can size its layout to the terminal.
31
- *
32
- * @remarks
33
- * Reads `target.columns` ON EACH CALL (so a getter-backed real stream reflects a live resize) and
34
- * accepts it only when it is a positive finite number; a missing / `0` / non-finite `columns` (a
35
- * piped, non-TTY stream) falls back to {@link DEFAULT_COLUMNS}. Total — never throws.
36
- *
37
- * @param target - The stream whose width to probe
38
- * @returns The terminal column count, or {@link DEFAULT_COLUMNS} when not a TTY
39
- */
40
- export declare function columnsOf(target: StreamTargetInterface): number;
41
-
42
- /**
43
- * Create an observable {@link ProcessCaptureInterface} — the server "own ALL output" capture. It
44
- * intercepts the RAW `process.stdout.write` / `process.stderr.write` (not just `console.*`, which is
45
- * the core `Capture`), so it catches direct `process` writes, library output, and child-process
46
- * pipes. Each intercepted write becomes a frozen {@link import('./types.js').CapturedChunk},
47
- * buffered (bounded, per-stream) and emitted on `capture`; per options it is mirrored back to the
48
- * real stream and/or forwarded to a {@link import('@src/core').SinkInterface}.
49
- *
50
- * @param options - See {@link ProcessCaptureOptions}
51
- * @returns A {@link ProcessCaptureInterface}
52
- *
53
- * @remarks
54
- * - **The wrapper never throws and passes backpressure through** — a throw in `process.stdout.write`
55
- * would crash the host, so chunks are decoded totally and the original's `boolean` is returned.
56
- * - **Snapshot-at-start + non-reentrant + process-global** — `start()` snapshots and swaps the
57
- * pristine `write`; `stop()` restores the EXACT original. At most ONE may be active at a time.
58
- * Create any server sink BEFORE installing a capture so the mirror's replay is not re-captured.
59
- *
60
- * @example
61
- * ```ts
62
- * import { createProcessCapture } from '@src/server'
63
- *
64
- * const capture = createProcessCapture({ levels: ['stderr'], mirror: true })
65
- * capture.start()
66
- * process.stderr.write('a library diagnostic\n') // captured AND still shown
67
- * capture.stop()
68
- * ```
69
- */
70
- export declare function createProcessCapture(options?: ProcessCaptureOptions): ProcessCaptureInterface;
71
-
72
- /**
73
- * Create the server TTY {@link ServerSinkInterface} — the C-g server output backend, the
28
+ * Creates the server TTY {@link ServerSinkInterface}the server output backend, the
74
29
  * env-symmetric sibling of `createBrowserSink` / core's `createConsoleSink`. `write(text, level?)`
75
30
  * routes by level to the process streams and uses construction-time styled facts: it sends ANSI
76
31
  * straight to a styled target (with a leading `\r` overwriting a terminal line natively) but
@@ -82,39 +37,41 @@ export declare function createProcessCapture(options?: ProcessCaptureOptions): P
82
37
  *
83
38
  * @remarks
84
39
  * - **Routes by level.** `error` / `warn` → the error stream (`process.stderr` by default), every
85
- * other level (and an omitted level) → the out stream (`process.stdout`) — the SAME routing as
86
- * core's `createConsoleSink`, so a logger's `error` reaches `stderr`.
40
+ * other level (and an omitted level) → the `stdout` stream (`process.stdout`) — the same routing
41
+ * as core's `createConsoleSink`, so a logger's `error` reaches `stderr`. Both call the one
42
+ * {@link import('@src/core').selectWriter} leaf, which is what keeps them identical.
87
43
  * - **Per-target styled facts.** At construction, each target uses `options.styled` when supplied;
88
44
  * otherwise {@link inferStyled} applies the injected `environment` (default `process.env`) and
89
45
  * then that target's `isTTY`.
90
- * Writes use those stored facts, so `styled` and the out target's strip decision never disagree;
91
- * the err target keeps its own fact internally.
92
- * - **Width.** `columns` reflects the live `out.columns` (so it tracks a terminal resize), falling
93
- * back to {@link import('./constants.js').DEFAULT_COLUMNS} when the out stream is not a TTY — or a
94
- * fixed value when `options.columns` is supplied. Feed it to a `Reporter` / `Progress` `width`.
95
- * - **Injectable + guard-narrowed.** `options.out` / `options.err` default to `process.stdout` /
96
- * `process.stderr` but accept ANY {@link import('./types.js').StreamTargetInterface}, resolved
97
- * through {@link isStreamTarget} (AGENTS §14 — narrow the boundary, never `as`), so a test drives
46
+ * Writes use those stored facts, so `styled` and the `stdout` target's strip decision never
47
+ * disagree; the `stderr` target keeps its own fact internally.
48
+ * - **Width.** `columns` reflects the live `stdout.columns` (so it tracks a terminal resize),
49
+ * falling back to {@link import('./constants.js').DEFAULT_COLUMNS} when the `stdout` stream is not
50
+ * a TTY — or a fixed value when `options.columns` is supplied. Feed it to a `Reporter` /
51
+ * `Progress` `width`.
52
+ * - **Injectable + guard-narrowed.** `options.stdout` / `options.stderr` default to `process.stdout`
53
+ * / `process.stderr` but accept any {@link import('./types.js').StreamTargetInterface}, resolved
54
+ * through {@link isStreamTarget} (narrow the boundary, never `as`), so a test drives
98
55
  * the sink (and the isTTY-strip path) with a fake stream that never touches the real process
99
56
  * streams.
100
57
  *
101
58
  * @example
102
59
  * ```ts
103
- * import { createLogger, createReporter, createStyler } from '@src/core'
104
- * import { createServerSink } from '@src/server'
60
+ * import { createStyler, Logger, Reporter } from '@orkestrel/console'
61
+ * import { createServerSink } from '@orkestrel/console/server'
105
62
  *
106
63
  * const sink = createServerSink()
107
64
  * const styler = createStyler({ enabled: sink.styled })
108
- * const logger = createLogger({ name: 'app', sink, styler })
65
+ * const logger = new Logger({ name: 'app', sink, styler })
109
66
  * logger.error('boom') // → process.stderr, ANSI rendered on a TTY / stripped to a pipe
110
- * const reporter = createReporter({ sink, width: sink.columns })
67
+ * const reporter = new Reporter({ sink, width: sink.columns })
111
68
  * ```
112
69
  */
113
70
  export declare function createServerSink(options?: ServerSinkOptions): ServerSinkInterface;
114
71
 
115
72
  /**
116
- * Decode one `process.stdout.write` / `process.stderr.write` chunk to a string — TOTAL, never
117
- * throws (AGENTS §14). The process write signature accepts `string | Uint8Array` plus an optional
73
+ * Decodes one `process.stdout.write` / `process.stderr.write` chunk to a string — total, never
74
+ * throws. The process write signature accepts `string | Uint8Array` plus an optional
118
75
  * encoding; the capture wrapper reuses this so intercepting a raw stream write can never crash the
119
76
  * host (a throw inside `process.stdout.write` would take the program down).
120
77
  *
@@ -123,12 +80,12 @@ export declare function createServerSink(options?: ServerSinkOptions): ServerSin
123
80
  * and `process.stdout.write('text')` all pass a string).
124
81
  * - A `Buffer` chunk is decoded with the supplied `encoding` when it is a recognized
125
82
  * {@link BufferEncoding} (`process` write supports `'utf8'` / `'hex'` / `'base64'` / …), defaulting
126
- * to `'utf8'`; a bare `Uint8Array` is decoded via `TextDecoder` (always utf-8 — the `encoding`
127
- * argument applies ONLY to a `Buffer`, never a plain `Uint8Array`).
83
+ * to `'utf8'`; a bare `Uint8Array` is decoded through `TextDecoder` (always utf-8 — the `encoding`
84
+ * argument applies only to a `Buffer`, never a plain `Uint8Array`).
128
85
  * - Anything else is coerced with `String(chunk)` (a number / object / bigint / symbol a misbehaving
129
86
  * writer hands the stream). The coercion is itself guarded: a value whose `toString` /
130
87
  * `Symbol.toPrimitive` throws yields the stable `'[unprintable]'` placeholder. So the helper is
131
- * TOTAL on every input — it always yields SOME string, never an exception (a throw here would
88
+ * total on every input — it always yields some string, never an exception (a throw here would
132
89
  * escape into `process.*.write` and crash the host).
133
90
  *
134
91
  * @param chunk - The chunk passed to the stream's `write`
@@ -145,30 +102,38 @@ export declare function createServerSink(options?: ServerSinkOptions): ServerSin
145
102
  export declare function decodeChunk(chunk: unknown, encoding?: unknown): string;
146
103
 
147
104
  /**
148
- * The default set of {@link StreamLevel}s a process capture patches when `options.levels` is omitted
149
- * — BOTH streams ({@link STREAM_LEVELS}). A consumer narrows it (e.g. just `['stderr']`) via
150
- * `options.levels`.
105
+ * Sets the terminal width {@link import('./factories.js').createServerSink} reports through
106
+ * {@link import('./types.js').ServerSinkInterface.columns} when the `stdout` stream is not a TTY (so
107
+ * `.columns` is `undefined`) and no explicit `options.columns` was supplied — the conventional
108
+ * 80-column default a non-interactive context (a pipe, a CI log) assumes.
151
109
  */
152
- export declare const DEFAULT_CAPTURE_LEVELS: readonly StreamLevel[];
110
+ export declare const DEFAULT_COLUMNS = 80;
153
111
 
154
112
  /**
155
- * The default bounded-buffer cap for a {@link import('./types.js').ProcessCaptureInterface} — at
113
+ * Sets the default bounded-buffer cap for a {@link import('./types.js').ProcessCaptureInterface} — at
156
114
  * most this many recent {@link import('./types.js').CapturedChunk}s are retained per buffer (the
157
- * total buffer AND each per-stream bucket; oldest dropped first). Mirrors the core `Capture`'s
158
- * `DEFAULT_CAPTURE_LIMIT`; a consumer overrides it via `options.limit`.
115
+ * total buffer and each per-stream bucket; oldest dropped first). Mirrors the core `Capture`'s
116
+ * `DEFAULT_CAPTURE_LIMIT`; a consumer overrides it through `options.limit`.
159
117
  */
160
- export declare const DEFAULT_CAPTURE_LIMIT = 1000;
118
+ export declare const DEFAULT_STREAM_LIMIT = 1000;
161
119
 
162
120
  /**
163
- * The terminal width {@link import('./factories.js').createServerSink} reports through
164
- * {@link import('./types.js').ServerSinkInterface.columns} when the out stream is NOT a TTY (so
165
- * `.columns` is `undefined`) and no explicit `options.columns` was supplied the conventional
166
- * 80-column default a non-interactive context (a pipe, a CI log) assumes.
121
+ * Infers the width in character cells of a stream target — its live `columns` when it is a TTY, else
122
+ * the non-interactive {@link DEFAULT_COLUMNS} fallback. The basis a {@link import('./types.js').ServerSinkInterface}
123
+ * reports through `columns` so a `Reporter` / `Progress` can size its layout to the terminal.
124
+ *
125
+ * @remarks
126
+ * Reads `target.columns` on each call (so a getter-backed real stream reflects a live resize) and
127
+ * accepts it only when it is a positive finite number; a missing / `0` / non-finite `columns` (a
128
+ * piped, non-TTY stream) falls back to {@link DEFAULT_COLUMNS}. Total — never throws.
129
+ *
130
+ * @param target - The stream whose width to probe
131
+ * @returns The terminal column count, or {@link DEFAULT_COLUMNS} when not a TTY
167
132
  */
168
- export declare const DEFAULT_COLUMNS = 80;
133
+ export declare function inferColumns(target: StreamTargetInterface): number;
169
134
 
170
135
  /**
171
- * Infer whether one stream target should receive styled output. The result is a construction-time
136
+ * Infers whether one stream target receives styled output. The result is a construction-time
172
137
  * target fact for {@link import('./factories.js').createServerSink}; this helper is pure and never
173
138
  * reads process globals itself.
174
139
  *
@@ -179,7 +144,7 @@ export declare const DEFAULT_COLUMNS = 80;
179
144
  *
180
145
  * @param target - The stream target whose terminal capability is the fallback
181
146
  * @param environment - The environment record supplying `FORCE_COLOR` and `NO_COLOR`
182
- * @returns Whether output for the target should retain styling and control sequences
147
+ * @returns True if output for the target retains styling and control sequences; false otherwise
183
148
  *
184
149
  * @example
185
150
  * ```ts
@@ -190,28 +155,28 @@ export declare const DEFAULT_COLUMNS = 80;
190
155
  export declare function inferStyled(target: StreamTargetInterface, environment: Readonly<Record<string, string | undefined>>): boolean;
191
156
 
192
157
  /**
193
- * Whether `encoding` is a {@link BufferEncoding} accepted by `Buffer.prototype.toString` — a total
194
- * guard used by {@link decodeChunk} to honor a process-write `encoding` argument only when it is a
195
- * real Node encoding (otherwise utf-8 is assumed).
158
+ * Checks whether `encoding` is a {@link BufferEncoding} accepted by `Buffer.prototype.toString` — a total
159
+ * guard used by {@link import('./helpers.js').decodeChunk} to honor a process-write `encoding`
160
+ * argument only when it is a real Node encoding (otherwise utf-8 is assumed).
196
161
  *
197
162
  * @param encoding - The candidate encoding (the second `write` argument, possibly a callback)
198
- * @returns `true` when `encoding` names a supported buffer encoding
163
+ * @returns True if `encoding` names a supported buffer encoding; false otherwise
199
164
  */
200
165
  export declare function isBufferEncoding(encoding: unknown): encoding is BufferEncoding;
201
166
 
202
167
  /**
203
- * Whether `value` is a usable {@link StreamTargetInterface} — a record with a callable `write`. A
204
- * total type guard (AGENTS §14): it NEVER throws and returns `false` for anything off-shape, so it
168
+ * Checks whether `value` is a usable {@link StreamTargetInterface} — a record with a callable `write`. A
169
+ * total type guard: it never throws and returns `false` for anything off-shape, so it
205
170
  * narrows the one unavoidable boundary (the real `process.stdout` / `process.stderr`, or a fake
206
171
  * stream a test injects) to the exact slice the sink + capture touch — no `as`.
207
172
  *
208
173
  * @remarks
209
174
  * Only `write` is required (the irreducible output method); `isTTY` and `columns` are optional on
210
175
  * {@link StreamTargetInterface}, so their absence does not disqualify a target — a piped stream
211
- * (no `isTTY`) is still a valid write target, just a non-terminal one.
176
+ * (no `isTTY`) is still a valid write target, only a non-terminal one.
212
177
  *
213
178
  * @param value - Any value crossing the boundary (a process stream, an injected fake, `unknown`)
214
- * @returns `true` when `value` has a callable `write`
179
+ * @returns True if `value` has a callable `write`; false otherwise
215
180
  *
216
181
  * @example
217
182
  * ```ts
@@ -223,28 +188,28 @@ export declare function isBufferEncoding(encoding: unknown): encoding is BufferE
223
188
  export declare function isStreamTarget(value: unknown): value is StreamTargetInterface;
224
189
 
225
190
  /**
226
- * An observable interceptor of the RAW process output streams (AGENTS §13) — it takes control of
227
- * `process.stdout.write` / `process.stderr.write` on the WRITE side. While `active`, every write to
191
+ * Implements an observable interceptor of the raw process output streams — it takes control of
192
+ * `process.stdout.write` / `process.stderr.write` on the write side. While `active`, every write to
228
193
  * a configured {@link StreamLevel} is captured as a frozen {@link CapturedChunk}, buffered (total +
229
- * per-stream, bounded), emitted on `capture`, and — per options — mirrored to the real stream and/or
230
- * forwarded to a {@link SinkInterface}.
194
+ * per-stream, bounded), emitted on `capture`, and — per options — mirrored to the real stream,
195
+ * forwarded to a {@link SinkInterface}, or both.
231
196
  *
232
197
  * @remarks
233
198
  * Where the core `Capture` patches `console.*` (the high-level read side), this patches the
234
- * low-level stream `write`, so it owns ALL server output: a direct `process.stdout.write`, a
199
+ * low-level stream `write`, so it owns all server output: a direct `process.stdout.write`, a
235
200
  * third-party library's writes, a child-process pipe — not only `console.*`.
236
201
  *
237
- * - **Snapshot-at-start (the no-capture-loop principle).** `start()` snapshots the CURRENT
202
+ * - **Snapshot-at-start (the no-capture-loop principle).** `start()` snapshots the current
238
203
  * `process[stream].write` for each configured level, then installs the wrappers. The mirror
239
204
  * replays through that snapshot (bound to its stream) — so a server sink created from the same
240
- * streams BEFORE the capture is never re-captured: this catches OTHER writers, not the mirror's
205
+ * streams before the capture is never re-captured: this catches other writers, not the mirror's
241
206
  * own replay. Create your sinks before installing a capture.
242
- * - **Idempotent + PROCESS-GLOBAL + NON-REENTRANT.** `start()` while `active` is a no-op (never
207
+ * - **Idempotent + process-global + non-reentrant.** `start()` while `active` is a no-op (never
243
208
  * double-patches — that would snapshot the wrapper as the "original" and break restore); `stop()`
244
- * while inactive is a no-op. It patches the ONE global `process`, so at most ONE process capture
209
+ * while inactive is a no-op. It patches the one global `process`, so at most one process capture
245
210
  * may be active at a time — two concurrently would interleave buffers and clobber each other's
246
211
  * restore.
247
- * - **The wrapper NEVER throws and passes backpressure through.** A throw inside
212
+ * - **The wrapper never throws and passes backpressure through.** A throw inside
248
213
  * `process.stdout.write` would crash the host, so the wrapper decodes each chunk totally (a byte
249
214
  * chunk through the per-level streaming decoder below, everything else through the total
250
215
  * {@link decodeChunk}), and returns the snapshot-original's `boolean` when mirroring (so a caller's
@@ -255,18 +220,18 @@ export declare function isStreamTarget(value: unknown): value is StreamTargetInt
255
220
  * child-process pipe, a library, or OS buffering all produce this — carries its partial bytes to
256
221
  * the next write instead of decoding each half to `U+FFFD`. `stop()` flushes each decoder once, so
257
222
  * a codepoint left half-written at stop is still surfaced. A `string` chunk is already text and
258
- * passes through; an explicit NON-utf-8 buffer encoding (`latin1` / `hex` / `base64` / …) names a
223
+ * passes through; an explicit non-utf-8 buffer encoding (`latin1` / `hex` / `base64` / …) names a
259
224
  * self-contained per-write decode and is honored one-shot through {@link decodeChunk}.
260
225
  * - **Bounded buffers.** The total buffer and each per-stream bucket are each capped at `limit`
261
226
  * (oldest dropped first), never unbounded — the same retention precedent as the core `Capture`.
262
- * - **Lifecycle (§10).** `start` / `stop` toggle interception (emitting `start` / `stop`);
263
- * `destroy()` stops (restoring the PRISTINE `write`) then destroys the emitter.
227
+ * - **Lifecycle.** `start` / `stop` toggle interception (emitting `start` / `stop`);
228
+ * `destroy()` stops (restoring the pristine `write`) then destroys the emitter.
264
229
  *
265
230
  * @example
266
231
  * ```ts
267
232
  * const capture = new ProcessCapture({ levels: ['stderr'], mirror: true })
268
233
  * capture.start()
269
- * process.stderr.write('a library diagnostic\n') // captured AND still written to the terminal
234
+ * process.stderr.write('a library diagnostic\n') // captured and still written to the terminal
270
235
  * capture.messages('stderr') // [{ level: 'stderr', text: 'a library diagnostic\n', time: … }]
271
236
  * capture.stop() // process.stderr.write restored
272
237
  * ```
@@ -285,7 +250,7 @@ export declare class ProcessCapture implements ProcessCaptureInterface {
285
250
  }
286
251
 
287
252
  /**
288
- * The observable events a {@link ProcessCaptureInterface} emits (AGENTS §13) — mirrors the core
253
+ * Declares the observable events a {@link ProcessCaptureInterface} emits — mirrors the core
289
254
  * `Capture`'s `CaptureEventMap`, but the captured record is a {@link CapturedChunk} (stream-keyed).
290
255
  *
291
256
  * @remarks
@@ -293,84 +258,85 @@ export declare class ProcessCapture implements ProcessCaptureInterface {
293
258
  * {@link CapturedChunk}. The hook a live log viewer / tee subscribes to.
294
259
  * - `start` / `stop` — the interception toggled on / off (pure signals, empty tuples).
295
260
  *
296
- * Listener isolation is the emitter's (§13): a listener throw routes to the emitter's `error`
261
+ * Listener isolation is the emitter's: a listener throw routes to the emitter's `error`
297
262
  * handler, never onto this map — so a buggy `capture` listener can never escape into the host's
298
263
  * `process.stdout.write` call (which would crash the program).
299
264
  *
300
- * Declared as a `type` alias (not `interface extends EventMap`, §4.5): a type-literal satisfies the
265
+ * Declared as a `type` alias (not `interface extends EventMap`): a type-literal satisfies the
301
266
  * `EventMap` constraint structurally, whereas an interface lacks the index signature.
302
267
  */
303
268
  export declare type ProcessCaptureEventMap = {
304
- /** An intercepted process-stream write — the frozen {@link CapturedChunk}. */
269
+ /** Fires on an intercepted process-stream write — the frozen {@link CapturedChunk}. */
305
270
  readonly capture: readonly [chunk: CapturedChunk];
306
- /** Interception began (`process.*.write` patched). */
271
+ /** Fires after interception began (`process.*.write` patched). */
307
272
  readonly start: readonly [];
308
- /** Interception ended (`process.*.write` restored). */
273
+ /** Fires after interception ended (`process.*.write` restored). */
309
274
  readonly stop: readonly [];
310
275
  };
311
276
 
312
277
  /**
313
- * An observable interceptor of the RAW process output streams (AGENTS §13) — the server's
314
- * "own ALL output" capture. Where the core `Capture` patches `console.*` (the high-level read
278
+ * Declares an observable interceptor of the raw process output streams — the server's
279
+ * "own all output" capture. Where the core `Capture` patches `console.*` (the high-level read
315
280
  * side), this patches `process.stdout.write` / `process.stderr.write` (the low-level stream), so it
316
- * catches DIRECT `process.stdout.write`, third-party library output, and child-process pipes —
317
- * everything that reaches the streams, not just `console.*`.
281
+ * catches direct `process.stdout.write`, third-party library output, and child-process pipes —
282
+ * everything that reaches the streams, not only `console.*`.
318
283
  *
319
284
  * @remarks
320
- * - **Snapshot-at-start (the no-capture-loop principle).** `start()` snapshots the CURRENT
285
+ * - **Snapshot-at-start (the no-capture-loop principle).** `start()` snapshots the current
321
286
  * `process[stream].write` for each configured {@link StreamLevel}, then installs the wrappers. The
322
- * mirror replays through that snapshot — so a server sink created from the same streams BEFORE the
287
+ * mirror replays through that snapshot — so a server sink created from the same streams before the
323
288
  * capture is never re-captured. Create your sinks before installing a capture.
324
- * - **Idempotent + PROCESS-GLOBAL + NON-REENTRANT.** `start()` while `active` is a no-op (never
325
- * double-patches); `stop()` while inactive is a no-op. It patches the ONE global `process`, so at
326
- * most ONE process capture may be active at a time — running two concurrently interleaves their
289
+ * - **Idempotent + process-global + non-reentrant.** `start()` while `active` is a no-op (never
290
+ * double-patches); `stop()` while inactive is a no-op. It patches the one global `process`, so at
291
+ * most one process capture may be active at a time — running two concurrently interleaves their
327
292
  * buffers and clobbers each other's restore.
328
- * - **The wrapper NEVER throws and passes through backpressure.** A throw inside
293
+ * - **The wrapper never throws and passes through backpressure.** A throw inside
329
294
  * `process.stdout.write` would crash the host, so the wrapper builds its record through a total
330
295
  * decode, and returns the snapshot-original's boolean (or `true` when mirroring is off) so a
331
296
  * caller's backpressure handling keeps working.
332
297
  * - **Bounded buffers.** The total buffer and each per-stream bucket are each capped at `limit`
333
298
  * (oldest dropped first), never unbounded.
334
- * - **Lifecycle (§10).** `start` / `stop` toggle interception (emitting `start` / `stop`);
299
+ * - **Lifecycle.** `start` / `stop` toggle interception (emitting `start` / `stop`);
335
300
  * `destroy()` stops (restoring the pristine `write`) then destroys the emitter.
336
301
  */
337
302
  export declare interface ProcessCaptureInterface {
338
303
  readonly emitter: EmitterInterface<ProcessCaptureEventMap>;
339
- /** Whether interception is currently installed (`start`ed and not yet `stop`ped). */
304
+ /** Reports whether interception is installed (`start`ed and not yet `stop`ped). */
340
305
  readonly active: boolean;
341
- /** Begin intercepting the configured process streams (idempotent; emits `start`). */
306
+ /** Begins intercepting the configured process streams (idempotent; emits `start`). */
342
307
  start(): void;
343
- /** Restore the pristine `process.*.write` references (idempotent; emits `stop`). */
308
+ /** Restores the pristine `process.*.write` references (idempotent; emits `stop`). */
344
309
  stop(): void;
345
- /** A copy of the full captured buffer, oldest first (capped at `limit`). */
310
+ /** Returns a copy of the full captured buffer, oldest first (capped at `limit`). */
346
311
  messages(): readonly CapturedChunk[];
347
- /** A copy of the captured buffer for ONE {@link StreamLevel}, oldest first (capped at `limit`). */
312
+ /** Returns a copy of the captured buffer for one {@link StreamLevel}, oldest first (capped at `limit`). */
348
313
  messages(level: StreamLevel): readonly CapturedChunk[];
349
- /** Drop every buffered chunk (total + per-stream); interception is unaffected. */
314
+ /** Drops every buffered chunk (total + per-stream); interception is unaffected. */
350
315
  clear(): void;
351
- /** Stop interception (restoring the streams) and tear down the emitter. */
316
+ /** Stops interception (restoring the streams) and tears down the emitter. */
352
317
  destroy(): void;
353
318
  }
354
319
 
355
320
  /**
356
- * Options for {@link import('./factories.js').createProcessCapture} — every field optional, so a
357
- * bare `createProcessCapture()` buffers both streams without mirroring or forwarding.
321
+ * Holds the options for the {@link import('./ProcessCapture.js').ProcessCapture} constructor — every
322
+ * field optional, so a bare `new ProcessCapture()` buffers both streams without mirroring or
323
+ * forwarding.
358
324
  *
359
325
  * @remarks
360
- * - `on` — initial {@link ProcessCaptureEventMap} listeners, wired at construction (e.g.
326
+ * - `on` — initial {@link ProcessCaptureEventMap} listeners, wired at construction (for example
361
327
  * `{ capture: (c) => tee(c) }`).
362
- * - `error` — the listener-error handler forwarded to the entity's emitter (§13).
363
- * - `levels` — which streams to intercept; defaults to {@link import('./constants.js').DEFAULT_CAPTURE_LEVELS}
364
- * (both `stdout` and `stderr`). Narrow it (e.g. just `['stderr']`) to capture one stream.
365
- * - `mirror` — when `true`, each intercepted write is ALSO replayed to the snapshot-original
328
+ * - `error` — the listener-error handler forwarded to the entity's emitter.
329
+ * - `levels` — which streams to intercept; defaults to {@link import('./constants.js').STREAM_LEVELS}
330
+ * (`stdout` and `stderr`). Narrow it (for example to `['stderr']`) to capture one stream.
331
+ * - `mirror` — when `true`, each intercepted write is also replayed to the snapshot-original
366
332
  * `write` (bound to its stream), so the output still reaches the terminal while being captured;
367
333
  * defaults to `false` (capture-only, the program's output is swallowed into the buffer).
368
334
  * - `sink` — an optional {@link SinkInterface} each intercepted chunk is also written to
369
- * (`sink.write(text, level)` with the {@link StreamLevel} mapped to a {@link import('@src/core').LogLevel} via
335
+ * (`sink.write(text, level)` with the {@link StreamLevel} mapped to a {@link import('@src/core').LogLevel} through
370
336
  * {@link import('./constants.js').STREAM_LEVEL_MAP}), to tee captured output into the logging
371
337
  * pipeline / a file. Absent by default.
372
- * - `limit` — the bounded-buffer cap (total AND each per-stream bucket); defaults to
373
- * {@link import('./constants.js').DEFAULT_CAPTURE_LIMIT}. Retention is ALWAYS bounded.
338
+ * - `limit` — the bounded-buffer cap (total and each per-stream bucket); defaults to
339
+ * {@link import('./constants.js').DEFAULT_STREAM_LIMIT}. Retention is always bounded.
374
340
  */
375
341
  export declare interface ProcessCaptureOptions {
376
342
  readonly on?: EmitterHooks<ProcessCaptureEventMap>;
@@ -382,16 +348,16 @@ export declare interface ProcessCaptureOptions {
382
348
  }
383
349
 
384
350
  /**
385
- * A {@link SinkInterface} that also exposes the target terminal's {@link columns} width — the shape
351
+ * Declares a {@link SinkInterface} that also exposes the target terminal's {@link columns} width — the shape
386
352
  * {@link import('./factories.js').createServerSink} returns. It is a drop-in {@link SinkInterface}
387
353
  * (so a `Logger` / `Reporter` / `Spinner` / `Progress` takes it as `sink`) whose extra `columns`
388
354
  * getter lets a consumer size a `Reporter`'s layout to the live terminal. Its `styled` fact lets
389
- * the same consumer enable or disable its styler for the out target.
355
+ * the same consumer enable or disable its styler for the `stdout` target.
390
356
  *
391
357
  * @remarks
392
- * - `styled` is the `out` target's construction-time fact. The sink handles `err` through its own
393
- * independently inferred fact because the two targets can differ.
394
- * - `columns` is a getter, re-read on every access — so it reflects the CURRENT terminal width (a
358
+ * - `styled` is the `stdout` target's construction-time fact. The sink handles `stderr` through its
359
+ * own independently inferred fact because the two targets can differ.
360
+ * - `columns` is a getter, re-read on every access — so it reflects the current terminal width (a
395
361
  * resize is observed) unless a fixed `options.columns` was supplied, in which case it is constant.
396
362
  */
397
363
  export declare interface ServerSinkInterface extends SinkInterface {
@@ -400,30 +366,32 @@ export declare interface ServerSinkInterface extends SinkInterface {
400
366
  }
401
367
 
402
368
  /**
403
- * Options for {@link import('./factories.js').createServerSink} — all optional, so a bare
369
+ * Holds the options for {@link import('./factories.js').createServerSink} — all optional, so a bare
404
370
  * `createServerSink()` writes to the real process streams.
405
371
  *
406
372
  * @remarks
407
- * - `out` — the stream `info` / `debug` (and an omitted level) are written to; defaults to
408
- * `process.stdout`. Any {@link StreamTargetInterface} is accepted, so a test injects a fake.
409
- * - `err` — the stream `error` / `warn` are written to; defaults to `process.stderr`.
373
+ * - `stdout` — the stream `info` / `debug` (and an omitted level) are written to; defaults to
374
+ * `process.stdout`, whose name it mirrors. Any {@link StreamTargetInterface} is accepted, so a
375
+ * test injects a fake.
376
+ * - `stderr` — the stream `error` / `warn` are written to; defaults to `process.stderr`, whose
377
+ * name it mirrors.
410
378
  * - `styled` — an explicit styling decision for both targets. When omitted, each target infers its
411
379
  * own fact from `FORCE_COLOR`, `NO_COLOR`, and `isTTY` at construction.
412
380
  * - `environment` — the environment used for inference; defaults to `process.env`.
413
381
  * - `columns` — an explicit width override for {@link ServerSinkInterface.columns}. When omitted,
414
- * the sink reads the live `out.columns` (so it tracks a terminal resize), falling back to
415
- * {@link import('./constants.js').DEFAULT_COLUMNS} when the out stream is not a TTY.
382
+ * the sink reads the live `stdout.columns` (so it tracks a terminal resize), falling back to
383
+ * {@link import('./constants.js').DEFAULT_COLUMNS} when the `stdout` stream is not a TTY.
416
384
  */
417
385
  export declare interface ServerSinkOptions {
418
- readonly out?: StreamTargetInterface;
419
- readonly err?: StreamTargetInterface;
386
+ readonly stdout?: StreamTargetInterface;
387
+ readonly stderr?: StreamTargetInterface;
420
388
  readonly styled?: boolean;
421
389
  readonly environment?: Readonly<Record<string, string | undefined>>;
422
390
  readonly columns?: number;
423
391
  }
424
392
 
425
393
  /**
426
- * Each {@link StreamLevel}'s {@link LogLevel} for the optional sink forward — the projection a
394
+ * Maps each {@link StreamLevel} to its {@link LogLevel} for the optional sink forward — the projection a
427
395
  * process capture routes through when writing an intercepted chunk to a
428
396
  * {@link import('@src/core').SinkInterface}
429
397
  * (`sink.write(text, STREAM_LEVEL_MAP[level])`). `stderr` is conventionally the error/diagnostic
@@ -433,28 +401,28 @@ export declare interface ServerSinkOptions {
433
401
  export declare const STREAM_LEVEL_MAP: Readonly<Record<StreamLevel, LogLevel>>;
434
402
 
435
403
  /**
436
- * The two process streams a {@link import('./types.js').ProcessCaptureInterface} can intercept, in
404
+ * Lists the two process streams a {@link import('./types.js').ProcessCaptureInterface} can intercept, in
437
405
  * `stdout`-then-`stderr` order — the {@link StreamLevel} universe and the default configured set.
438
406
  */
439
407
  export declare const STREAM_LEVELS: readonly StreamLevel[];
440
408
 
441
409
  /**
442
- * Which process stream a {@link CapturedChunk} came from — the "level" axis of the process-stream
410
+ * Names which process stream a {@link CapturedChunk} came from — the "level" axis of the process-stream
443
411
  * {@link ProcessCaptureInterface}, the server analogue of the core `Capture`'s `CaptureLevel`.
444
412
  *
445
413
  * @remarks
446
- * DISTINCT from {@link import('@src/core').LogLevel}: a `StreamLevel` names the ORIGINATING process stream
414
+ * distinct from {@link import('@src/core').LogLevel}: a `StreamLevel` names the originating process stream
447
415
  * (`process.stdout` vs `process.stderr`), not a severity. It is a named value family (it indexes
448
416
  * {@link import('./constants.js').STREAM_LEVEL_MAP} to a {@link import('@src/core').LogLevel} for the optional sink
449
- * forward), never a binary toggle — so it stays a union (AGENTS §4.4).
417
+ * forward), never a binary toggle — so it stays a union.
450
418
  */
451
419
  export declare type StreamLevel = 'stdout' | 'stderr';
452
420
 
453
421
  /**
454
- * The minimal writable-stream shape the C-g server sink and process capture address — exactly the
422
+ * Declares the minimal writable-stream shape the server sink and process capture address — exactly the
455
423
  * slice of a Node `tty.WriteStream` / `process.stdout` they touch, and no more. A
456
424
  * {@link ServerSinkOptions} target and a {@link ProcessCaptureInterface}'s patched streams are
457
- * narrowed to this via {@link import('./helpers.js').isStreamTarget} (AGENTS §14 — narrow the
425
+ * narrowed to this through {@link import('./validators.js').isStreamTarget} (narrow the
458
426
  * boundary, never `as`), so a test can drive either with a hand-built fake stream that never
459
427
  * touches the real `process` streams.
460
428
  *
@@ -476,8 +444,28 @@ export declare interface StreamTargetInterface {
476
444
  readonly columns?: number;
477
445
  }
478
446
 
447
+ /**
448
+ * Names the completion callback `process.*.write` accepts as its last argument — the Node `write`
449
+ * callback shape, and the {@link StreamWriteFunction} companion.
450
+ *
451
+ * @remarks
452
+ * The capture wrapper forwards the callback verbatim to the mirror, so a caller's
453
+ * write-completion handler still fires.
454
+ */
479
455
  export declare type StreamWriteCallback = (error?: Error | null) => void;
480
456
 
457
+ /**
458
+ * Names the process-stream `write` method a {@link ProcessCaptureInterface} snapshots and swaps at
459
+ * the patch boundary — the write-side analogue of {@link import('@src/core').ConsoleMethod}.
460
+ *
461
+ * @remarks
462
+ * It is taken verbatim as `NodeJS.WriteStream['write']`, the overloaded
463
+ * `(chunk, encoding?, callback?) => boolean` of `process.stdout.write` / `process.stderr.write`.
464
+ * Using the canonical type rather than a hand-rolled approximation keeps snapshot and restore
465
+ * exact and lets the wrapper assign cleanly. A {@link StreamLevel} (`'stdout' | 'stderr'`) is
466
+ * itself the `process` property key, so `process[level]` indexes the matching `WriteStream`
467
+ * directly, with no lookup map.
468
+ */
481
469
  export declare type StreamWriteFunction = NodeJS.WriteStream['write'];
482
470
 
483
471
  export { }