@orkestrel/console 0.0.1

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.
@@ -0,0 +1,2299 @@
1
+ import { EmitterErrorHandler } from '@orkestrel/emitter';
2
+ import { EmitterHooks } from '@orkestrel/emitter';
3
+ import { EmitterInterface } from '@orkestrel/emitter';
4
+
5
+ /**
6
+ * Pad (or, when over budget, truncate) `text` to exactly `target` VISIBLE columns, positioning
7
+ * it by `alignment`. The width primitive the box / table renderers align every cell with.
8
+ *
9
+ * @remarks
10
+ * Measures with {@link width} (visible code points, ANSI-aware), so a styled string aligns by
11
+ * its visible content, not its escape codes. When `width(text) < target`, the deficit is added
12
+ * as spaces — all trailing (`left`), all leading (`right`), or split with the extra space on
13
+ * the right (`center`). When `width(text) > target`, the VISIBLE characters are sliced to
14
+ * `target` (a defensive guard — the renderers size columns to fit, so this is rarely hit; it
15
+ * slices the stripped text, so it never bisects an escape sequence into a broken half).
16
+ *
17
+ * @param text - The cell content (may be styled)
18
+ * @param target - The visible column count to fit `text` into
19
+ * @param alignment - Where to position `text` within the width; defaults to `left`
20
+ * @returns `text` fitted to exactly `target` visible columns
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * align('hi', 5) // 'hi '
25
+ * align('hi', 5, 'right') // ' hi'
26
+ * align('hi', 5, 'center') // ' hi '
27
+ * ```
28
+ */
29
+ export declare function align(text: string, target: number, alignment?: Alignment): string;
30
+
31
+ /**
32
+ * Horizontal text alignment within a fixed-width cell — the conventional three-value set a
33
+ * {@link ColumnSpec} (and the box / separator title) aligns by. A value pair / set, not a
34
+ * binary toggle (§4.4), so it stays a union.
35
+ */
36
+ export declare type Alignment = 'left' | 'center' | 'right';
37
+
38
+ /**
39
+ * Matches any ANSI/VT escape sequence — CSI (SGR color/style plus cursor/erase/scroll,
40
+ * including colon-parameterized SGR), OSC / DCS / PM / APC / SOS string sequences
41
+ * (titles, hyperlinks, device strings), the `nF` charset-select family, and the
42
+ * two-byte `Fp` / `Fe` / `Fs` sequences (e.g. `ESC 7`, `ESC D`, `ESC c` RIS). Global, so
43
+ * `strip` removes every occurrence.
44
+ *
45
+ * @remarks
46
+ * A global `RegExp` carries a mutable `lastIndex`; a scan must build a FRESH `RegExp`
47
+ * from this one's `source` + `flags` rather than reuse this instance's `lastIndex`. This
48
+ * is the canonical definition, not a shared scanner. The alternation is ORDERED so the
49
+ * CSI / string-family arms (which can start with a byte a later single-byte arm would
50
+ * also match) win first; every arm uses disjoint, non-nested character classes, so the
51
+ * match is linear in input length — no catastrophic backtracking (ReDoS-safe) even on an
52
+ * adversarial run of digits inside an unterminated CSI. Built from `String.fromCharCode`
53
+ * so no control-character literal appears in a regex source (the codebase idiom).
54
+ */
55
+ export declare const ANSI_PATTERN: RegExp;
56
+
57
+ /**
58
+ * The cross-environment default {@link RendererInterface} — renders style DATA as ANSI
59
+ * SGR escape codes, exactly as `Scheduler` is the `setTimeout` default for its seam. It
60
+ * is the single styling output the whole console / terminal system uses in a terminal;
61
+ * the browser `%c` / CSS renderer (the C-f branch) implements the SAME contract over
62
+ * the SAME {@link Style}, so retargeting changes the renderer, never the style model.
63
+ *
64
+ * @remarks
65
+ * - **Style is DATA in, SGR string out.** It reads the style's `foreground` /
66
+ * `background` / `attributes` and emits one `ESC[…m` sequence whose parameters are the
67
+ * mapped SGR numbers (foreground 30–37 / 90–97, background 40–47 / 100–107, attributes
68
+ * 1 / 2 / 3 / 4 / 7 / 9), followed by `text`, terminated by the reset `ESC[0m`.
69
+ * - **Multiple attributes compose** — their codes join with `;` in a single sequence
70
+ * (`ESC[1;4;31m` for bold + underline + red), so one open and one reset wrap the run.
71
+ * - **`default` and unset colors emit no code** — a `default` (or absent) `foreground` /
72
+ * `background` leaves the terminal's own ink.
73
+ * - **The empty style and the empty string pass through** — when there is nothing to
74
+ * apply (no colors, no attributes) or `text` is `''`, `text` is returned VERBATIM with
75
+ * no escape codes, so an unstyled render never injects a stray reset.
76
+ * - **Stateless and event-free** — no fields, no events; safe to share one instance.
77
+ */
78
+ export declare class ANSIRenderer implements RendererInterface {
79
+ #private;
80
+ /**
81
+ * Wrap `text` in the SGR codes for `style`. Returns `text` unchanged when the style
82
+ * is empty or `text` is `''`.
83
+ */
84
+ render(style: Style, text: string): string;
85
+ }
86
+
87
+ /**
88
+ * A text-style attribute — the six standard SGR text effects.
89
+ *
90
+ * @remarks
91
+ * Style as DATA: an `Attribute` is a name. The ANSI renderer maps each to its SGR
92
+ * on-code (`bold` → 1, `dim` → 2, `italic` → 3, `underline` → 4, `inverse` → 7,
93
+ * `strikethrough` → 9), composing several at once; a browser renderer maps the same
94
+ * names to CSS (`font-weight`, `font-style`, `text-decoration`, …).
95
+ */
96
+ export declare type Attribute = 'bold' | 'dim' | 'italic' | 'underline' | 'inverse' | 'strikethrough';
97
+
98
+ /**
99
+ * Each {@link Attribute}'s SGR "on" parameter — `bold` 1, `dim` 2, `italic` 3,
100
+ * `underline` 4, `inverse` 7, `strikethrough` 9. The renderer composes several by
101
+ * joining their codes with `;` in one SGR sequence.
102
+ */
103
+ export declare const ATTRIBUTE_CODES: Readonly<Record<Attribute, number>>;
104
+
105
+ /**
106
+ * Every {@link Attribute}, frozen — the attributes the styler exposes as chainable
107
+ * accessors. The source of truth for the attribute axis.
108
+ */
109
+ export declare const ATTRIBUTES: readonly Attribute[];
110
+
111
+ /**
112
+ * Each {@link Color}'s SGR BACKGROUND parameter — the 8 base colors at 40–47 and their
113
+ * bright variants at 100–107. `default` is intentionally absent (it emits no code).
114
+ */
115
+ export declare const BACKGROUND_CODES: Readonly<Record<Exclude<Color, 'default'>, number>>;
116
+
117
+ /**
118
+ * The default EMPTY-cell glyph {@link import('./helpers.js').renderBar} draws the remaining run of a
119
+ * progress bar with — the light-shade block `░` (U+2591). A single visible cell; a consumer overrides
120
+ * it via {@link import('./types.js').ProgressBarOptions}`.empty`.
121
+ */
122
+ export declare const BAR_EMPTY = "\u2591";
123
+
124
+ /**
125
+ * The default FILLED-cell glyph {@link import('./helpers.js').renderBar} draws the completed run of a
126
+ * progress bar with — the full block `█` (U+2588). A single visible cell; a consumer overrides it via
127
+ * {@link import('./types.js').ProgressBarOptions}`.fill`.
128
+ */
129
+ export declare const BAR_FILL = "\u2588";
130
+
131
+ /** The BEL control character (`U+0007`) that can terminate an OSC sequence. */
132
+ export declare const BEL: string;
133
+
134
+ /**
135
+ * The complete {@link BorderChars} junction set for each {@link BorderStyle} — the standard
136
+ * Unicode box-drawing glyphs at the four line weights. The renderers ({@link
137
+ * import('./helpers.js').renderBox} / {@link import('./helpers.js').renderTable}) look the
138
+ * style up here, so no glyph literal lives in a renderer. Deeply frozen.
139
+ *
140
+ * @remarks
141
+ * `round` shares `single`'s edges and tees — only its corners differ (the rounded `╭╮╰╯`).
142
+ */
143
+ export declare const BORDER_CHARS: Readonly<Record<BorderStyle, BorderChars>>;
144
+
145
+ /**
146
+ * One complete box-drawing junction set for a {@link BorderStyle} — every glyph the box /
147
+ * table renderers need to frame content and rule a table. Plain data (the value lives in
148
+ * {@link BORDER_CHARS}); the renderers read these so no glyph literal is hard-coded in a
149
+ * renderer.
150
+ *
151
+ * @remarks
152
+ * - `horizontal` / `vertical` — the edge run characters.
153
+ * - `topLeft` / `topRight` / `bottomLeft` / `bottomRight` — the four corners.
154
+ * - `cross` — the four-way `┼` junction (a table's interior grid crossing).
155
+ * - `teeDown` / `teeUp` / `teeRight` / `teeLeft` — the `┬` / `┴` / `├` / `┤` three-way
156
+ * junctions where a separator meets an edge (a table's column separators at the top, the
157
+ * header rule, and the bottom).
158
+ */
159
+ export declare interface BorderChars {
160
+ readonly horizontal: string;
161
+ readonly vertical: string;
162
+ readonly topLeft: string;
163
+ readonly topRight: string;
164
+ readonly bottomLeft: string;
165
+ readonly bottomRight: string;
166
+ readonly cross: string;
167
+ readonly teeDown: string;
168
+ readonly teeUp: string;
169
+ readonly teeRight: string;
170
+ readonly teeLeft: string;
171
+ }
172
+
173
+ /**
174
+ * A box-drawing border style — the four standard Unicode line weights the renderers frame
175
+ * with. Each selects a full junction set in {@link BORDER_CHARS} (corners, edges, and the
176
+ * `T` / cross junctions a table needs). A named, fixed set (an external-spec value family),
177
+ * never a toggle — so it stays a union.
178
+ *
179
+ * @remarks
180
+ * `single` (`┌─┐`), `double` (`╔═╗`), `round` (`╭─╮` — single edges, rounded corners), and
181
+ * `heavy` (`┏━┓`). The renderer looks the style up in {@link BORDER_CHARS}; styling the
182
+ * border (a color) is a separate, orthogonal concern handled by the optional `styler`.
183
+ */
184
+ export declare type BorderStyle = 'single' | 'double' | 'round' | 'heavy';
185
+
186
+ /**
187
+ * Options for {@link import('./helpers.js').renderBox} — content framed in box-drawing
188
+ * characters.
189
+ *
190
+ * @remarks
191
+ * - `content` — the body text; embedded newlines split it into lines, each framed on its own
192
+ * row. Every row is padded to the inner width measured by {@link import('./helpers.js').width}
193
+ * (the VISIBLE width), so ANSI-styled content stays aligned inside the frame.
194
+ * - `title` — an optional caption embedded in the TOP border.
195
+ * - `padding` — horizontal cells of blank padding inside each vertical edge; defaults to
196
+ * {@link DEFAULT_PADDING}.
197
+ * - `border` — the {@link BorderStyle}; defaults to {@link DEFAULT_BORDER} (`single`).
198
+ * - `width` — the total visible width of the box. When omitted, the box hugs its widest line
199
+ * (plus padding + borders); when supplied, narrower lines pad out and the box is exactly
200
+ * that wide (content wider than the budget is not truncated — `renderTable` is the
201
+ * width-bounded renderer).
202
+ * - `styler` — colors the border (and title) when supplied; alignment is unaffected.
203
+ */
204
+ export declare interface BoxOptions {
205
+ readonly content: string;
206
+ readonly title?: string;
207
+ readonly padding?: number;
208
+ readonly border?: BorderStyle;
209
+ readonly width?: number;
210
+ readonly styler?: StylerInterface;
211
+ }
212
+
213
+ /**
214
+ * An observable console interceptor (AGENTS §13) — it takes control of the global `console.*` on
215
+ * the READ side. While `active`, every configured `console.x` call is captured as a frozen
216
+ * {@link CapturedMessage}, buffered (total + by level, bounded), emitted on `capture`, and — per
217
+ * options — mirrored to the real console and/or forwarded to a {@link SinkInterface}.
218
+ *
219
+ * @remarks
220
+ * - **Snapshot-at-start (the no-capture-loop principle).** `start()` snapshots the CURRENT
221
+ * `console[level]` for each configured {@link CaptureLevel}, then installs the wrappers. The
222
+ * mirror writes through that snapshot — so our OWN console sink output (the Logger / Reporter,
223
+ * which snapshot the real `console` at creation) is never recaptured: `Capture` catches
224
+ * THIRD-PARTY `console.*`, not our writes. Create your loggers BEFORE installing a capture.
225
+ * - **Idempotent + PROCESS-GLOBAL + NON-REENTRANT.** `start()` while already `active` is a no-op
226
+ * (never double-patches); `stop()` while inactive is a no-op. It patches the ONE global
227
+ * `console`, so at most ONE capture may be active at a time — running two concurrently
228
+ * interleaves their buffers and clobbers each other's restore.
229
+ * - **Bounded buffers.** `messages()` / `messages(level)` — the total buffer and each by-level
230
+ * bucket are each capped at `limit`
231
+ * (oldest dropped first), never unbounded — the same retention precedent as {@link Logger}.
232
+ * - **Lifecycle (§10).** `start` / `stop` toggle interception (emitting `start` / `stop`);
233
+ * `destroy()` stops (restoring `console`) then destroys the emitter.
234
+ *
235
+ * @example
236
+ * ```ts
237
+ * const capture = new Capture({ levels: ['warn', 'error'], mirror: true })
238
+ * capture.start()
239
+ * console.warn('third-party noise') // captured AND mirrored to the real console
240
+ * capture.messages('warn') // [{ level: 'warn', text: 'third-party noise', time: … }]
241
+ * capture.stop() // console.warn restored
242
+ * ```
243
+ */
244
+ export declare class Capture implements CaptureInterface {
245
+ #private;
246
+ constructor(options?: CaptureOptions);
247
+ get emitter(): EmitterInterface<CaptureEventMap>;
248
+ get active(): boolean;
249
+ start(): void;
250
+ stop(): void;
251
+ messages(): readonly CapturedMessage[];
252
+ messages(level: CaptureLevel): readonly CapturedMessage[];
253
+ clear(): void;
254
+ destroy(): void;
255
+ }
256
+
257
+ /**
258
+ * Each {@link CaptureLevel}'s {@link LogLevel} for the optional sink forward — the projection the
259
+ * Capture routes through when writing an intercepted call to a {@link
260
+ * import('./types.js').SinkInterface} (`sink.write(text, CAPTURE_LEVEL_MAP[level])`). `warn` /
261
+ * `error` / `debug` / `info` map to their matching {@link LogLevel}; `log` maps to `info` (a plain
262
+ * console log is informational — the default stream), so a stream-aware sink routes `warn` / `error`
263
+ * captures to the right stream. The source of truth for the capture-to-log projection.
264
+ */
265
+ export declare const CAPTURE_LEVEL_MAP: Readonly<Record<CaptureLevel, LogLevel>>;
266
+
267
+ /**
268
+ * Every {@link CaptureLevel}, frozen — the `console.*` methods a {@link
269
+ * import('./types.js').CaptureInterface} intercepts by default (and the source of truth for the
270
+ * capture-level axis; drives exhaustive tests). The universal console methods: `log`, `info`,
271
+ * `warn`, `error`, `debug`.
272
+ */
273
+ export declare const CAPTURE_LEVELS: readonly CaptureLevel[];
274
+
275
+ /**
276
+ * One captured console call — an immutable, serializable record of a single intercepted
277
+ * `console.*` invocation. A {@link CaptureInterface} builds one per call, freezes it, buffers it
278
+ * (total + by level), and emits it on `capture`; every consumer reads this exact shape.
279
+ *
280
+ * @remarks
281
+ * - `level` — the {@link CaptureLevel} naming which `console.x` was called.
282
+ * - `text` — the call's arguments stringified into one line (see
283
+ * {@link import('./helpers.js').formatArgs}): an `Error` → `name: message`, a plain object →
284
+ * circular-safe `JSON.stringify`, anything else → `String(arg)`, all space-joined.
285
+ * - `time` — the capture instant as epoch milliseconds (`Date.now()`); a plain number so the
286
+ * record stays serializable (no `Date` to clone) and orderable — the same convention as
287
+ * {@link LogRecord.time}.
288
+ * - The value is frozen at construction — a consumer reads it, never mutates it.
289
+ */
290
+ export declare interface CapturedMessage {
291
+ readonly level: CaptureLevel;
292
+ readonly text: string;
293
+ readonly time: number;
294
+ }
295
+
296
+ /**
297
+ * The observable events a {@link CaptureInterface} emits (AGENTS §13).
298
+ *
299
+ * @remarks
300
+ * - `capture` — the core event: fires for EVERY intercepted `console.*` call (one per call,
301
+ * while active), carrying the frozen {@link CapturedMessage}. The hook a live console viewer /
302
+ * tee rides.
303
+ * - `start` / `stop` — the lifecycle signals: `start` fires when interception is installed (the
304
+ * first `start()` on an inactive capture), `stop` when it is torn down (a `stop()` on an active
305
+ * capture, and from `destroy()`); both are pure signals (empty tuples) so a consumer can mirror
306
+ * the global-patch lifecycle (e.g. log that capture is engaged). They earn their place by
307
+ * bracketing the process-global side effect a consumer needs to observe.
308
+ *
309
+ * Listener isolation is the emitter's (§13): a listener throw routes to the emitter's `error`
310
+ * handler, never onto this map — so a buggy `capture` listener can never perturb interception (or
311
+ * the underlying program's own `console.*` call). Declared as a `type` alias (not
312
+ * `interface extends EventMap`, §4.5): a type-literal satisfies the `EventMap` constraint
313
+ * structurally, whereas an interface lacks the index signature.
314
+ */
315
+ export declare type CaptureEventMap = {
316
+ /** An intercepted `console.*` call — the frozen {@link CapturedMessage}. */
317
+ readonly capture: readonly [message: CapturedMessage];
318
+ /** Interception was installed (an inactive capture's `start()`). */
319
+ readonly start: readonly [];
320
+ /** Interception was torn down (an active capture's `stop()` / `destroy()`). */
321
+ readonly stop: readonly [];
322
+ };
323
+
324
+ /**
325
+ * An observable console interceptor (AGENTS §13) — it takes control of the global `console.*` on
326
+ * the READ side: while `active`, every configured `console.x` call is captured as a frozen
327
+ * {@link CapturedMessage}, buffered (total + by level, bounded), emitted on `capture`, and —
328
+ * per options — mirrored to the real console and/or forwarded to a {@link SinkInterface}.
329
+ *
330
+ * @remarks
331
+ * - **Snapshot-at-start.** `start()` snapshots the CURRENT `console[level]` for each configured
332
+ * {@link CaptureLevel}, then installs the wrappers. The mirror writes through that snapshot, so
333
+ * our OWN console sink output (the Logger / Reporter, which snapshot the real `console` at
334
+ * creation) is never recaptured — `Capture` catches THIRD-PARTY `console.*`, not our writes
335
+ * (the no-capture-loop principle). Create your loggers BEFORE installing a capture.
336
+ * - **Idempotent + non-reentrant.** `start()` while already `active` is a no-op (it never
337
+ * double-patches), and `stop()` while inactive is a no-op. It is PROCESS-GLOBAL — it patches the
338
+ * one global `console` — so at most ONE capture may be active at a time; running two
339
+ * concurrently interleaves their buffers and clobbers each other's restore.
340
+ * - **Bounded buffers.** `messages()` returns a copy of the whole buffer (oldest first);
341
+ * `messages(level)` a copy of one {@link CaptureLevel}'s bucket — each capped at `limit`
342
+ * (oldest dropped first), never unbounded. `clear()` empties them (it does NOT stop interception).
343
+ * - **Lifecycle (§10).** `start` / `stop` toggle interception; `destroy()` stops (restoring
344
+ * `console`) then destroys the emitter (its listeners go).
345
+ */
346
+ export declare interface CaptureInterface {
347
+ readonly emitter: EmitterInterface<CaptureEventMap>;
348
+ /** Whether interception is currently installed (between `start()` and `stop()`). */
349
+ readonly active: boolean;
350
+ /** Snapshot the configured `console.*` and install the interceptors — a no-op when already `active`. */
351
+ start(): void;
352
+ /** Restore the snapshot-original `console.*` — a no-op when not `active`. */
353
+ stop(): void;
354
+ /** A copy of the whole captured buffer, oldest first (capped at `limit`). */
355
+ messages(): readonly CapturedMessage[];
356
+ /** A copy of the captured buffer for ONE {@link CaptureLevel}, oldest first (capped at `limit`). */
357
+ messages(level: CaptureLevel): readonly CapturedMessage[];
358
+ /** Drop every buffered message (total + by level); does NOT stop interception. */
359
+ clear(): void;
360
+ /** Tear down — `stop()` (restoring `console`) then destroy the emitter. */
361
+ destroy(): void;
362
+ }
363
+
364
+ /**
365
+ * One intercepted `console` method — the names a {@link CaptureInterface} patches and reports
366
+ * under. A fixed set keyed off the universal `console.*` methods (`console.log` / `info` / `warn`
367
+ * / `error` / `debug`); a named value family (it indexes {@link CAPTURE_LEVEL_MAP} to a
368
+ * {@link LogLevel} for the optional sink forward), never a binary toggle — so it stays a union.
369
+ *
370
+ * @remarks
371
+ * DISTINCT from {@link LogLevel}: a `CaptureLevel` names the ORIGINATING console method (which
372
+ * `console.x` was called), not a severity threshold — there is no ordering and no gating (every
373
+ * configured method is captured). `log` and `info` are separate methods (both default-stream),
374
+ * mapped to the sink's default / `info` stream respectively; `warn` / `error` / `debug` map to
375
+ * their matching {@link LogLevel}. The default configured set is {@link DEFAULT_CAPTURE_LEVELS}.
376
+ */
377
+ export declare type CaptureLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
378
+
379
+ /**
380
+ * Options for `createCapture` / the {@link CaptureInterface} constructor.
381
+ *
382
+ * @remarks
383
+ * - `on` — the reserved {@link EmitterHooks} key (§8): initial listeners for the
384
+ * {@link CaptureEventMap}, wired at construction (e.g. `{ capture: (m) => tee(m) }`).
385
+ * - `error` — the emitter's listener-error handler (§13); a listener throw routes here.
386
+ * - `levels` — which `console.*` methods to intercept; defaults to {@link DEFAULT_CAPTURE_LEVELS}
387
+ * (all five). Only the listed methods are patched — an unlisted method is left untouched and
388
+ * its calls pass through normally.
389
+ * - `mirror` — when `true`, each intercepted call is ALSO forwarded to the snapshot-original
390
+ * `console` method, so the program's own console output still appears while being captured;
391
+ * defaults to `false` (capture silently). Mirrors through the method snapshotted AT `start()`,
392
+ * never the live (re-patched) one — no echo loop.
393
+ * - `sink` — an optional {@link SinkInterface} each intercepted call is also written to
394
+ * (`sink.write(text, level)` with the {@link CaptureLevel} mapped to a {@link LogLevel} via
395
+ * {@link CAPTURE_LEVEL_MAP}), to tee captured output into the logging pipeline / a file. Absent
396
+ * ⇒ no forward.
397
+ * - `limit` — the bounded buffer cap: at most this many recent messages are retained per buffer
398
+ * (the total buffer and EACH by-level bucket; oldest dropped first). Defaults to
399
+ * {@link DEFAULT_CAPTURE_LIMIT}; never unbounded (a long capture can't grow without bound — the
400
+ * same retention precedent as {@link LoggerInterface}).
401
+ */
402
+ export declare interface CaptureOptions {
403
+ readonly on?: EmitterHooks<CaptureEventMap>;
404
+ readonly error?: EmitterErrorHandler;
405
+ readonly levels?: readonly CaptureLevel[];
406
+ readonly mirror?: boolean;
407
+ readonly sink?: SinkInterface;
408
+ readonly limit?: number;
409
+ }
410
+
411
+ /**
412
+ * The structured outcome of {@link import('./factories.js').withCapture} — the wrapped function's
413
+ * own return `value` plus the {@link CapturedMessage}s intercepted while it ran.
414
+ *
415
+ * @remarks
416
+ * - `value` — whatever the wrapped `fn` returned (its `T`).
417
+ * - `messages` — the buffer captured during the run, oldest first (a copy; the capture is stopped
418
+ * and discarded by the time this is returned).
419
+ */
420
+ export declare interface CaptureResult<T> {
421
+ readonly value: T;
422
+ readonly messages: readonly CapturedMessage[];
423
+ }
424
+
425
+ /**
426
+ * The cell at `index` of a (possibly ragged) row — `''` when the row is shorter than the
427
+ * column count, so a short row pads out instead of throwing (the ragged-row guard
428
+ * {@link renderTable} reads every cell through).
429
+ *
430
+ * @param row - The row's cells
431
+ * @param index - The column index to read
432
+ * @returns The cell text, or `''` when the row has no cell at `index`
433
+ */
434
+ export declare function cellAt(row: readonly string[], index: number): string;
435
+
436
+ /**
437
+ * A named terminal color — the 8 standard base colors, their 8 bright variants, and
438
+ * `default` (the target's own default ink, emitting no color code).
439
+ *
440
+ * @remarks
441
+ * Style as DATA: a `Color` is a name, not an escape sequence. The renderer maps it to
442
+ * its target's codes — the ANSI renderer to SGR 30–37 / 90–97 (foreground) and 40–47 /
443
+ * 100–107 (background); a browser renderer maps the SAME names to CSS colors.
444
+ * `default` means "leave the target's default" and contributes no code.
445
+ */
446
+ export declare type Color = 'black' | 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'brightBlack' | 'brightRed' | 'brightGreen' | 'brightYellow' | 'brightBlue' | 'brightMagenta' | 'brightCyan' | 'brightWhite' | 'default';
447
+
448
+ /**
449
+ * Every named {@link Color} except `default`, frozen — the colors the styler exposes as
450
+ * chainable accessors. The source of truth for the color axis; the styler drives its
451
+ * accessors from this array so the literals live in one place.
452
+ */
453
+ export declare const COLORS: readonly Exclude<Color, 'default'>[];
454
+
455
+ /**
456
+ * One column of a {@link TableOptions} — its header label and how its cells align.
457
+ *
458
+ * @remarks
459
+ * - `label` — the header text shown in the table's first row.
460
+ * - `align` — how this column's header and cells align within the column width; defaults to
461
+ * {@link DEFAULT_ALIGN} (`left`). The column is sized to the widest VISIBLE content
462
+ * (header or any cell, measured by {@link import('./helpers.js').width}), so a styled cell
463
+ * never breaks the column.
464
+ */
465
+ export declare interface ColumnSpec {
466
+ readonly label: string;
467
+ readonly align?: Alignment;
468
+ }
469
+
470
+ /**
471
+ * An error thrown by the console layer.
472
+ *
473
+ * @remarks
474
+ * Carries a {@link ConsoleErrorCode} and an optional `context` bag. Thrown for: an
475
+ * internal invariant violated at a defensive, structurally-unreachable guard
476
+ * (`INVARIANT`) — the one throw site in this codebase today.
477
+ */
478
+ export declare class ConsoleError extends Error {
479
+ readonly code: ConsoleErrorCode;
480
+ readonly context?: Readonly<Record<string, unknown>>;
481
+ constructor(code: ConsoleErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
482
+ }
483
+
484
+ /**
485
+ * A machine-readable error code for a {@link import('./errors.js').ConsoleError}.
486
+ *
487
+ * @remarks
488
+ * `INVARIANT` — an internal invariant / unreachable-guard was violated (a defensive
489
+ * check that should be structurally impossible to trip). The sole code today; §21
490
+ * forbids speculating a richer taxonomy before a second throw site exists.
491
+ */
492
+ export declare type ConsoleErrorCode = 'INVARIANT';
493
+
494
+ export declare type ConsoleMethod = (...args: unknown[]) => void;
495
+
496
+ /**
497
+ * Matches every C0 control character EXCEPT `\t` / `\n` / `\r` (which are meaningful
498
+ * whitespace), plus DEL (`0x7F`) — the non-printing bytes {@link
499
+ * import('./helpers.js').stripControls} removes. Global, ASCII-only source (no raw
500
+ * control-character literal), so a scan builds a fresh `RegExp` the same way as
501
+ * {@link ANSI_PATTERN} to avoid a mutated `lastIndex`.
502
+ *
503
+ * @remarks
504
+ * Deliberately SEPARATE from {@link ANSI_PATTERN}: `strip()` must stay pure ANSI-escape
505
+ * removal (width / alignment computations depend on it leaving raw C0 bytes alone), while
506
+ * C0-stripping is an ADDITIONAL, orthogonal pass a non-TTY output sink applies on top.
507
+ */
508
+ export declare const CONTROL_PATTERN: RegExp;
509
+
510
+ /**
511
+ * Create the cross-environment default {@link RendererInterface} — the ANSI / SGR
512
+ * renderer that turns style DATA into terminal escape codes. The default behind
513
+ * {@link createStyler}; construct one directly to render a {@link import('./types.js').Style}
514
+ * without the fluent surface, or to share one instance across stylers.
515
+ *
516
+ * @returns A stateless ANSI {@link RendererInterface}
517
+ *
518
+ * @example
519
+ * ```ts
520
+ * import { createANSIRenderer } from '@src/core'
521
+ *
522
+ * const renderer = createANSIRenderer()
523
+ * renderer.render({ foreground: 'red', attributes: ['bold'] }, 'alert') // '\x1b[1;31malert\x1b[0m'
524
+ * ```
525
+ */
526
+ export declare function createANSIRenderer(): RendererInterface;
527
+
528
+ /**
529
+ * Create an observable {@link CaptureInterface} — console interception on the READ side. While
530
+ * `active`, every configured `console.*` call is captured as a frozen
531
+ * {@link import('./types.js').CapturedMessage}, buffered (total + by level, bounded), emitted on
532
+ * `capture`, and — per options — mirrored to the real console and/or forwarded to a
533
+ * {@link SinkInterface}.
534
+ *
535
+ * @param options - See {@link CaptureOptions}
536
+ * @returns A {@link CaptureInterface} (inactive until `start()`)
537
+ *
538
+ * @remarks
539
+ * - **Snapshot-at-start — no capture loop.** `start()` snapshots the CURRENT `console[level]` per
540
+ * configured level, then patches; the mirror writes through that snapshot. Our OWN console sink
541
+ * output (the Logger / Reporter, which snapshot `console` at creation) is never recaptured —
542
+ * `Capture` catches THIRD-PARTY `console.*`, not our writes. Create your loggers FIRST.
543
+ * - **PROCESS-GLOBAL + NON-REENTRANT.** It patches the one global `console`, so at most ONE
544
+ * capture may be active at a time; two concurrent captures interleave and clobber each other's
545
+ * restore. Prefer {@link withCapture} for a scoped, self-restoring capture.
546
+ * - **Bounded.** `options.limit` (default {@link import('./constants.js').DEFAULT_CAPTURE_LIMIT})
547
+ * caps both the total buffer and each by-level bucket; never unbounded.
548
+ *
549
+ * @example
550
+ * ```ts
551
+ * import { createCapture } from '@src/core'
552
+ *
553
+ * const capture = createCapture({ levels: ['warn', 'error'] })
554
+ * capture.start()
555
+ * console.error('boom') // captured, NOT mirrored (mirror defaults to false)
556
+ * capture.messages('error') // [{ level: 'error', text: 'boom', time: … }]
557
+ * capture.stop()
558
+ * ```
559
+ */
560
+ export declare function createCapture(options?: CaptureOptions): CaptureInterface;
561
+
562
+ /**
563
+ * Create the default {@link SinkInterface} — a console sink that routes by level and writes
564
+ * through the `console` methods SNAPSHOTTED at creation. The default output target behind
565
+ * {@link createLogger}.
566
+ *
567
+ * @returns A console {@link SinkInterface}
568
+ *
569
+ * @remarks
570
+ * - **Snapshotted — no capture loop.** It captures `console.log` / `console.warn` /
571
+ * `console.error` AT CALL TIME and writes through those references. So when a later
572
+ * `Capture` (C-d) PATCHES `console.*`, this sink still reaches the REAL streams — the
573
+ * writer and the capturer never feed each other (the no-capture-loop principle). Create
574
+ * the sink (or the logger) BEFORE installing a capture for this to hold.
575
+ * - **Routes by level.** `error` → the snapshotted `console.error`, `warn` →
576
+ * `console.warn`, every other level → `console.log`. The `level` is supplied by the
577
+ * logger; an omitted `level` goes to `console.log`.
578
+ *
579
+ * @example
580
+ * ```ts
581
+ * import { createConsoleSink } from '@src/core'
582
+ *
583
+ * const sink = createConsoleSink() // snapshots console.* now
584
+ * sink.write('boom', 'error') // → the real console.error, even after a later console patch
585
+ * ```
586
+ */
587
+ export declare function createConsoleSink(): SinkInterface;
588
+
589
+ /**
590
+ * Create an observable, leveled {@link LoggerInterface} — the entry point into structured
591
+ * logging. Each `debug` / `info` / `warn` / `error` call builds a frozen
592
+ * {@link import('./types.js').LogRecord}, gates it by severity, retains a bounded tail,
593
+ * ALWAYS emits it on `entry` (the transport seam), and — unless `silent` — writes a styled
594
+ * line to its sink.
595
+ *
596
+ * @param options - See {@link LoggerOptions}
597
+ * @returns A {@link LoggerInterface}
598
+ *
599
+ * @remarks
600
+ * - **Record + event = transport (§13).** Subscribe `logger.emitter.on('entry', …)` to tee
601
+ * records to a file / JSON / remote transport; the event fires for every accepted record,
602
+ * even when `silent` (silence suppresses only the SINK WRITE).
603
+ * - **Bounded retention.** `entries()` returns the recent records, capped at `options.limit`
604
+ * (default {@link DEFAULT_LOG_LIMIT}); never unbounded.
605
+ * - **Sink + styler defaults.** `options.sink` defaults to {@link createConsoleSink} (the
606
+ * snapshotted, level-routing console sink); `options.styler` to {@link createStyler} (ANSI).
607
+ * Styling is orthogonal to level — a level only chooses a label color.
608
+ *
609
+ * @example
610
+ * ```ts
611
+ * import { createLogger } from '@src/core'
612
+ *
613
+ * const logger = createLogger({ name: 'http', level: 'info' })
614
+ * logger.info('request', { method: 'GET', path: '/' })
615
+ * logger.debug('verbose') // dropped — below the info threshold
616
+ * ```
617
+ */
618
+ export declare function createLogger(options?: LoggerOptions): LoggerInterface;
619
+
620
+ /**
621
+ * Create an event-free {@link LoggerManagerInterface} — a §9 registry of named loggers plus
622
+ * a convenience fan-out. It mints + stores {@link LoggerInterface}s keyed by name (its
623
+ * defaults flowing into each), looks them up, removes them, and broadcasts a one-off log to
624
+ * every registered logger.
625
+ *
626
+ * @param options - See {@link LoggerManagerOptions}
627
+ * @returns A {@link LoggerManagerInterface}
628
+ *
629
+ * @remarks
630
+ * - **Defaults flow in.** `options.level` / `sink` / `styler` / `limit` / `silent` are the
631
+ * defaults flowed into every `register`ed logger unless that call's options override them.
632
+ * - **Event-free.** The manager carries NO emitter (each registered logger owns its own
633
+ * observable `emitter`) — it is a pure registry.
634
+ *
635
+ * @example
636
+ * ```ts
637
+ * import { createLoggerManager } from '@src/core'
638
+ *
639
+ * const loggers = createLoggerManager({ level: 'warn' })
640
+ * loggers.register('http')
641
+ * loggers.register('db', { level: 'debug' }) // overrides the default
642
+ * loggers.warn('slow', { ms: 900 }) // fans out to both
643
+ * ```
644
+ */
645
+ export declare function createLoggerManager(options?: LoggerManagerOptions): LoggerManagerInterface;
646
+
647
+ /**
648
+ * Create an update-driven, observable {@link ProgressInterface} — a live progress bar. Each
649
+ * `update(current)` recomputes the bar, writes `\r` + bar to its sink, and emits `{ current, total }`
650
+ * on `update`; `complete` / `failure` commit a final line. The leading `\r` is the sink's to redraw on —
651
+ * a TTY sink (C-g) overwrites, a plain sink (C-f) degrades to a fresh line. NO self-timer — the caller
652
+ * drives the bar.
653
+ *
654
+ * @param options - See {@link ProgressOptions} (`total` is required)
655
+ * @returns A {@link ProgressInterface}
656
+ *
657
+ * @remarks
658
+ * - **Universal + update-driven.** Built on the one styler + the one sink (no `node:*`, no
659
+ * `process.stdout`); progress advances only when the caller reports it. `current` is always clamped
660
+ * to `[0, total]`. `options.sink` defaults to {@link createConsoleSink}, `options.styler` to
661
+ * {@link createStyler} (ANSI).
662
+ * - **Observable (§13).** Subscribe `progress.emitter.on('update', …)` to mirror progress without a
663
+ * terminal; `complete` signals a successful finish.
664
+ *
665
+ * @example
666
+ * ```ts
667
+ * import { createProgress } from '@src/core'
668
+ *
669
+ * const progress = createProgress({ total: 100, message: 'downloading' })
670
+ * progress.update(40)
671
+ * progress.complete('done')
672
+ * ```
673
+ */
674
+ export declare function createProgress(options: ProgressOptions): ProgressInterface;
675
+
676
+ /**
677
+ * Create a lean, event-free {@link ReporterInterface} — the entry point into narrative
678
+ * reporting. Each verb (`section` / `step` / `timing` / `status` / `table` / `tree` / `box` /
679
+ * `line` / `blank`) formats through the shared styler + the pure layout renderers and writes to
680
+ * the sink — human / build-run narration over the SAME substrate the logger uses.
681
+ *
682
+ * @param options - See {@link ReporterOptions}
683
+ * @returns A {@link ReporterInterface}
684
+ *
685
+ * @remarks
686
+ * - **One styler, one sink.** `options.styler` defaults to {@link createStyler} (ANSI) and
687
+ * `options.sink` to {@link createConsoleSink} (the snapshotted, level-routing console sink) —
688
+ * no second colorizer. A `status('error', …)` routes to the sink's error stream.
689
+ * - **Width-aware.** `options.width` (default {@link DEFAULT_WIDTH}) sizes `section` and a
690
+ * `box` with no explicit width; the renderers align on VISIBLE width so styled content keeps
691
+ * its columns.
692
+ * - **Event-free (§13).** The reporter carries no emitter — a pure formatting front-end, like
693
+ * the renderers and `Scheduler`. Reach for a {@link createLogger} when you need observable,
694
+ * leveled, transportable records instead.
695
+ *
696
+ * @example
697
+ * ```ts
698
+ * import { createReporter } from '@src/core'
699
+ *
700
+ * const reporter = createReporter()
701
+ * reporter.section('Build')
702
+ * reporter.step('bundling', { index: 2, total: 5 }) // [2/5] bundling
703
+ * reporter.status('success', 'built in 1.2s') // ✔ built in 1.2s
704
+ *
705
+ * // Disable color (a non-TTY) — every line is plain.
706
+ * const plain = createReporter({ styler: createStyler({ enabled: false }) })
707
+ * ```
708
+ */
709
+ export declare function createReporter(options?: ReporterOptions): ReporterInterface;
710
+
711
+ /**
712
+ * Create a self-driving, observable {@link SpinnerInterface} — a live activity spinner. `start()`
713
+ * arms a periodic timer that advances a glyph cycle, writing each `\r` + frame line to its sink and
714
+ * emitting it on `frame`; `success` / `failure` commit a final `✔` / `✖` line. The leading `\r` is the
715
+ * sink's to redraw on — a TTY sink (C-g) overwrites for a smooth animation, a plain sink (C-f)
716
+ * degrades to a fresh line.
717
+ *
718
+ * @param options - See {@link SpinnerOptions}
719
+ * @returns A {@link SpinnerInterface} (inactive until `start()`)
720
+ *
721
+ * @remarks
722
+ * - **Universal + leak-free.** Built on `setInterval` + the one styler + the one sink (no `node:*`,
723
+ * no `process.stdout`); the timer is ALWAYS cleared on `success` / `failure` / `stop` / `destroy`, so
724
+ * it never leaks. `start()` is idempotent (no second timer while `active`).
725
+ * - **Observable (§13).** Subscribe `spinner.emitter.on('frame', …)` to mirror the animation without
726
+ * a terminal; `start` / `stop` bracket the timer lifecycle. `options.sink` defaults to
727
+ * {@link createConsoleSink}, `options.styler` to {@link createStyler} (ANSI).
728
+ *
729
+ * @example
730
+ * ```ts
731
+ * import { createSpinner } from '@src/core'
732
+ *
733
+ * const spinner = createSpinner({ message: 'building' })
734
+ * spinner.start()
735
+ * spinner.success('built in 1.2s') // ✔ built in 1.2s — timer cleared, line committed
736
+ * ```
737
+ */
738
+ export declare function createSpinner(options?: SpinnerOptions): SpinnerInterface;
739
+
740
+ /**
741
+ * Create the fluent, composable {@link StylerInterface} — the consumer-facing styling
742
+ * API. It builds a {@link import('./types.js').Style} under the hood and renders it
743
+ * through a {@link RendererInterface} (the ANSI default), so `styler.red.bold('hi')`
744
+ * yields styled text. Chains are immutable, so a base styler is freely reusable.
745
+ *
746
+ * @param options - See {@link StylerOptions}
747
+ * @returns A base {@link StylerInterface}
748
+ *
749
+ * @remarks
750
+ * - `options.renderer` swaps the output target without touching the style model — pass a
751
+ * browser `%c` / CSS renderer (the C-f branch) to retarget; defaults to the ANSI
752
+ * renderer (the cross-environment default).
753
+ * - `options.enabled` is the no-color switch: when `false`, the styler returns text
754
+ * VERBATIM (for a non-TTY, `NO_COLOR`, or piped output); defaults to `true`.
755
+ *
756
+ * @example
757
+ * ```ts
758
+ * import { createStyler } from '@src/core'
759
+ *
760
+ * const style = createStyler()
761
+ * style.red.bold('error') // bold red
762
+ * style.red(style.underline('link')) // composes either way
763
+ *
764
+ * // Disable for a non-TTY — every call returns its text unchanged.
765
+ * const plain = createStyler({ enabled: false })
766
+ * plain.green('ok') // 'ok'
767
+ * ```
768
+ */
769
+ export declare function createStyler(options?: StylerOptions): StylerInterface;
770
+
771
+ /** The Control Sequence Introducer (`ESC[`) that opens every SGR sequence. */
772
+ export declare const CSI: string;
773
+
774
+ /** The default cell {@link Alignment} a {@link import('./types.js').ColumnSpec} uses when none is given — `left`. */
775
+ export declare const DEFAULT_ALIGN: Alignment;
776
+
777
+ /**
778
+ * The default visible cell count of a progress-bar TRACK — the glyph run {@link
779
+ * import('./helpers.js').renderBar} fills (and a {@link import('./types.js').ProgressInterface} sizes
780
+ * its bar to). Thirty cells is a compact, terminal-friendly default; a consumer overrides it via
781
+ * `options.width`. Distinct from {@link DEFAULT_WIDTH} (the renderers' 80-column line width) — a bar
782
+ * track is one inline element, not a full-width rule.
783
+ */
784
+ export declare const DEFAULT_BAR_WIDTH = 30;
785
+
786
+ /** The default {@link BorderStyle} the box / table renderers frame with when none is given — `single`. */
787
+ export declare const DEFAULT_BORDER: BorderStyle;
788
+
789
+ /**
790
+ * The default set of {@link CaptureLevel}s a Capture patches when `options.levels` is omitted —
791
+ * all five universal `console.*` methods ({@link CAPTURE_LEVELS}). A consumer narrows it (e.g. just
792
+ * `['warn', 'error']`) via `options.levels`.
793
+ */
794
+ export declare const DEFAULT_CAPTURE_LEVELS: readonly CaptureLevel[];
795
+
796
+ /**
797
+ * The default bounded-buffer cap for a {@link import('./types.js').CaptureInterface} — at most this
798
+ * many recent {@link CapturedMessage}s are retained per buffer (the total buffer AND each by-level
799
+ * bucket; oldest dropped first). Capture retention is ALWAYS bounded so a long-running capture can
800
+ * never grow without bound (the same retention precedent as {@link DEFAULT_LOG_LIMIT}); a consumer
801
+ * overrides it via `options.limit`.
802
+ */
803
+ export declare const DEFAULT_CAPTURE_LIMIT = 1000;
804
+
805
+ /** The default {@link LogLevel} threshold a logger gates at when none is supplied — `info`. */
806
+ export declare const DEFAULT_LOG_LEVEL: LogLevel;
807
+
808
+ /**
809
+ * The default bounded-retention cap for a {@link import('./types.js').LoggerInterface} — at
810
+ * most this many recent records are kept (oldest dropped first). Retention is ALWAYS bounded
811
+ * (never the unbounded buffer scsr leaked); a consumer overrides it via `options.limit`.
812
+ */
813
+ export declare const DEFAULT_LOG_LIMIT = 1000;
814
+
815
+ /** The default horizontal padding inside a box's edges ({@link import('./helpers.js').renderBox}) — one cell. */
816
+ export declare const DEFAULT_PADDING = 1;
817
+
818
+ /**
819
+ * The default timer period in milliseconds between a {@link import('./types.js').SpinnerInterface}'s
820
+ * frames — the `setInterval` interval `start()` arms. Eighty milliseconds (≈12.5 frames/second) is
821
+ * the conventional spinner cadence: fast enough to read as motion, slow enough not to thrash a
822
+ * terminal. A consumer overrides it via `options.interval`.
823
+ */
824
+ export declare const DEFAULT_SPINNER_INTERVAL = 80;
825
+
826
+ /**
827
+ * The default visible column width for the width-aware renderers — the separator rule and a
828
+ * {@link import('./helpers.js').renderBox} with no explicit `width`, and the reporter's
829
+ * `section` rule. A sane terminal default (80 columns); a caller overrides it per-call or via
830
+ * {@link import('./types.js').ReporterOptions}`.width`.
831
+ */
832
+ export declare const DEFAULT_WIDTH = 80;
833
+
834
+ /**
835
+ * The EMPTY {@link Style} — no foreground, no background, no attributes — frozen. The
836
+ * neutral starting point a base styler builds from, and what a renderer passes through
837
+ * unchanged (it carries no codes). Deeply frozen, so it is safe to share as the base.
838
+ */
839
+ export declare const EMPTY_STYLE: Style;
840
+
841
+ /**
842
+ * The ESC control character (`U+001B`) that begins every ANSI escape sequence. Built
843
+ * with `String.fromCharCode` so no raw control character appears in source.
844
+ */
845
+ export declare const ESC: string;
846
+
847
+ /**
848
+ * Each {@link Color}'s SGR FOREGROUND parameter — the 8 base colors at 30–37 and their
849
+ * bright variants at 90–97. `default` is intentionally absent (it emits no code).
850
+ */
851
+ export declare const FOREGROUND_CODES: Readonly<Record<Exclude<Color, 'default'>, number>>;
852
+
853
+ /**
854
+ * Stringify a captured `console.*` argument list into ONE line — the text of a {@link
855
+ * import('./types.js').CapturedMessage}. Each argument is rendered by {@link stringifyValue} and
856
+ * the parts are space-joined, mirroring how a console concatenates its arguments.
857
+ *
858
+ * @remarks
859
+ * Total and never throws (it composes {@link stringifyValue}, which is total) — a `Capture` builds
860
+ * every message through this, so intercepting `console.*` can never crash the underlying program.
861
+ * An empty argument list yields `''` (an empty `console.log()` is captured as a blank line).
862
+ *
863
+ * @param args - The arguments a `console.*` method was called with
864
+ * @returns The arguments stringified and space-joined into one line
865
+ *
866
+ * @example
867
+ * ```ts
868
+ * formatArgs(['count', 3, { ok: true }]) // 'count 3 {"ok":true}'
869
+ * formatArgs([]) // ''
870
+ * ```
871
+ */
872
+ export declare function formatArgs(args: readonly unknown[]): string;
873
+
874
+ /**
875
+ * Format a millisecond duration as a compact human string — `…ms` below one second, `…s`
876
+ * (seconds to 2 decimal places) at or above one second. The timing rendering behind
877
+ * {@link import('./types.js').ReporterInterface.timing}.
878
+ *
879
+ * @remarks
880
+ * `999 → '999ms'`, `1000 → '1.00s'`, `1230 → '1.23s'` (the threshold is {@link SECOND_MS}).
881
+ * Pure and deterministic; kept a shared helper so the layout and the reporter stay decoupled.
882
+ *
883
+ * @param ms - The duration in milliseconds
884
+ * @returns The formatted duration (`'<n>ms'` or `'<n>s'`)
885
+ */
886
+ export declare function formatDuration(ms: number): string;
887
+
888
+ /**
889
+ * Format a {@link LogRecord} into a single styled line — the default human line layout a
890
+ * {@link import('./types.js').LoggerInterface} writes to its sink.
891
+ *
892
+ * @remarks
893
+ * Layout: `{time} {LEVEL} {[name]} {message}{ data}` — the ISO timestamp (dimmed), the
894
+ * upper-cased level label (colored by {@link LEVEL_COLORS} — styling ORTHOGONAL to level),
895
+ * the originating logger's `name` in brackets (omitted when absent), the message, and the
896
+ * structured `data` appended as compact JSON (omitted when absent / empty). Coloring flows
897
+ * through the injected `styler`, so a disabled styler yields a plain line and a browser
898
+ * `%c` styler (C-f) retargets it — the layout never changes. Pure: same record + styler →
899
+ * same line.
900
+ *
901
+ * @param record - The {@link LogRecord} to render
902
+ * @param styler - The {@link StylerInterface} the labels are colored through
903
+ * @returns The formatted, styled line (no trailing newline — the sink's target adds it)
904
+ *
905
+ * @example
906
+ * ```ts
907
+ * formatRecord({ level: 'warn', message: 'low disk', time: 0, name: 'fs' }, createStyler())
908
+ * // '<dim>1970-01-01T00:00:00.000Z</> <yellow>WARN</> [fs] low disk'
909
+ * ```
910
+ */
911
+ export declare function formatRecord(record: LogRecord, styler: StylerInterface): string;
912
+
913
+ /**
914
+ * Format a {@link LogRecord}'s `time` (epoch milliseconds) as an ISO-8601 timestamp string.
915
+ *
916
+ * @remarks
917
+ * Deterministic and serializable — `new Date(time).toISOString()`, e.g.
918
+ * `1716900000000 → '2024-05-28T12:40:00.000Z'`. The timestamp portion of the formatted log
919
+ * line; kept a pure helper so the line layout and the logger stay decoupled.
920
+ *
921
+ * @param time - Epoch milliseconds (a record's `time`)
922
+ * @returns The ISO-8601 timestamp
923
+ */
924
+ export declare function formatTime(time: number): string;
925
+
926
+ /**
927
+ * Narrow an unknown caught value to a {@link ConsoleError}.
928
+ *
929
+ * @param value - The value to test (typically a `catch` binding)
930
+ * @returns `true` when `value` is a {@link ConsoleError}
931
+ *
932
+ * @example
933
+ * ```ts
934
+ * try {
935
+ * createStyler().style
936
+ * } catch (error) {
937
+ * if (isConsoleError(error) && error.code === 'INVARIANT') report(error)
938
+ * }
939
+ * ```
940
+ */
941
+ export declare function isConsoleError(value: unknown): value is ConsoleError;
942
+
943
+ /**
944
+ * Each {@link LogLevel}'s default label {@link Color} — the level's VISUAL treatment, which
945
+ * is a styling choice ORTHOGONAL to the level itself (never a separate pseudo-level). The
946
+ * logger colors the level label through its styler with these; swapping a color never
947
+ * changes leveling. `debug` is cyan, `info` blue, `warn` yellow, `error` red.
948
+ *
949
+ * @remarks
950
+ * Excludes `default` so each value indexes a real styler accessor (the styler exposes a
951
+ * getter per non-`default` {@link Color}) — a level always renders in a concrete color.
952
+ */
953
+ export declare const LEVEL_COLORS: Readonly<Record<LogLevel, Exclude<Color, 'default'>>>;
954
+
955
+ /**
956
+ * Each {@link LogLevel}'s numeric SEVERITY — the ascending order the level gate compares
957
+ * through (`debug` 0 < `info` 1 < `warn` 2 < `error` 3). A record is kept when its level's
958
+ * severity is at or above the logger's threshold. The source of truth for level ordering.
959
+ */
960
+ export declare const LEVEL_SEVERITY: Readonly<Record<LogLevel, number>>;
961
+
962
+ /**
963
+ * Every {@link LogLevel}, in ascending severity order — the levels a logger exposes as
964
+ * methods and the manager fans out to. The source of truth for the level axis (drives
965
+ * exhaustive tests); aligned with {@link LEVEL_SEVERITY}.
966
+ */
967
+ export declare const LEVELS: readonly LogLevel[];
968
+
969
+ /**
970
+ * An observable, leveled logger (AGENTS §13) — the entry point into the structured-logging
971
+ * pipeline. Each `debug` / `info` / `warn` / `error` call builds a frozen {@link LogRecord},
972
+ * gates it by severity, retains a bounded tail of accepted records, ALWAYS emits it on
973
+ * `entry` (the transport seam), and — unless `silent` — formats it into a styled line and
974
+ * writes it to its {@link SinkInterface}.
975
+ *
976
+ * @remarks
977
+ * - **Record + event = transport (§13).** An accepted record is frozen and emitted on
978
+ * `entry` BEFORE anything else observable — every file / JSON / remote transport rides
979
+ * `emitter.on('entry')`. The event fires even when `silent`: silence suppresses only the
980
+ * SINK WRITE, never the record or the event, so transports keep flowing.
981
+ * - **Leveled gate.** A record whose {@link LogLevel} is below the logger's `level` threshold
982
+ * is dropped ENTIRELY — no record built past the level check, no event, no retention, no
983
+ * write (see {@link meetsLevel}).
984
+ * - **Bounded retention.** Accepted records accrue in a ring capped at `limit` (default
985
+ * {@link DEFAULT_LOG_LIMIT}); the oldest is dropped when full. `entries()` returns a copy,
986
+ * oldest first; `clear()` empties it. NEVER unbounded (scsr's leak).
987
+ * - **Styled write, orthogonal to level.** The line ({@link formatRecord}) is colored through
988
+ * the injected `styler` (the ANSI default, or a browser `%c` styler at C-f) — a level only
989
+ * chooses a label color; styling is not a level. A disabled styler yields a plain line.
990
+ * - **Snapshotted sink.** The default {@link createConsoleSink} writes to the `console`
991
+ * methods captured at creation, so a later `Capture` patching `console` can't loop the
992
+ * sink's output back into itself.
993
+ *
994
+ * @example
995
+ * ```ts
996
+ * const logger = new Logger({ name: 'http', level: 'info' })
997
+ * logger.emitter.on('entry', (record) => archive(record)) // transport hook
998
+ * logger.info('request', { method: 'GET', path: '/' }) // styled line to the console sink
999
+ * logger.debug('verbose') // dropped — below the `info` threshold
1000
+ * logger.entries() // [the info record]
1001
+ * ```
1002
+ */
1003
+ export declare class Logger implements LoggerInterface {
1004
+ #private;
1005
+ constructor(options?: LoggerOptions);
1006
+ get emitter(): EmitterInterface<LoggerEventMap>;
1007
+ get level(): LogLevel;
1008
+ get name(): string | undefined;
1009
+ debug(message: string, data?: Record<string, unknown>): void;
1010
+ info(message: string, data?: Record<string, unknown>): void;
1011
+ warn(message: string, data?: Record<string, unknown>): void;
1012
+ error(message: string, data?: Record<string, unknown>): void;
1013
+ entries(): readonly LogRecord[];
1014
+ clear(): void;
1015
+ destroy(): void;
1016
+ }
1017
+
1018
+ /**
1019
+ * The observable events a {@link LoggerInterface} emits (AGENTS §13) — the transport seam.
1020
+ *
1021
+ * @remarks
1022
+ * `entry` fires for EVERY accepted record (one that passed the level gate), carrying the
1023
+ * frozen {@link LogRecord} — even when the logger is `silent` (silence suppresses only the
1024
+ * SINK WRITE, never the event, so transports keep receiving records). Listener isolation is
1025
+ * the emitter's (§13): a listener throw routes to the emitter's `error` handler, never onto
1026
+ * this map — so a buggy transport can never perturb logging.
1027
+ *
1028
+ * Declared as a `type` alias (not `interface extends EventMap`, §4.5): a type-literal
1029
+ * satisfies the `EventMap` constraint structurally, whereas an interface lacks the index signature.
1030
+ */
1031
+ export declare type LoggerEventMap = {
1032
+ /** A record was logged (passed the level gate) — the frozen {@link LogRecord}. */
1033
+ readonly entry: readonly [record: LogRecord];
1034
+ };
1035
+
1036
+ /**
1037
+ * An observable, leveled logger — builds a frozen {@link LogRecord} per call, gates it by
1038
+ * severity, retains a bounded tail, emits it on `entry`, and (unless silent) writes a
1039
+ * styled line to its {@link SinkInterface}.
1040
+ *
1041
+ * @remarks
1042
+ * - **Leveled.** Each of `debug` / `info` / `warn` / `error` builds a record at that
1043
+ * {@link LogLevel}; a record below the logger's `level` threshold is dropped entirely (no
1044
+ * event, no retention, no write).
1045
+ * - **Transport seam (§13).** An accepted record ALWAYS fires `entry` (even when `silent`),
1046
+ * carrying the frozen {@link LogRecord} — the hook every file / JSON / remote transport rides.
1047
+ * - **Bounded retention.** `entries()` returns the recent records, capped at `limit` (oldest
1048
+ * dropped first) — never an unbounded buffer. `clear()` empties it.
1049
+ * - **Styled write.** Unless `silent`, the record is formatted (timestamp + level label +
1050
+ * `name` + message + trailing `data`) and colored through the injected `styler`, then
1051
+ * written to `sink`. Styling is orthogonal to level (the level only chooses a color).
1052
+ * - **Lifecycle.** `destroy()` clears retention and destroys the emitter (its listeners go).
1053
+ */
1054
+ export declare interface LoggerInterface {
1055
+ readonly emitter: EmitterInterface<LoggerEventMap>;
1056
+ readonly level: LogLevel;
1057
+ readonly name?: string;
1058
+ /** Log at `debug` — dropped unless the logger's `level` is `debug`. */
1059
+ debug(message: string, data?: Record<string, unknown>): void;
1060
+ /** Log at `info`. */
1061
+ info(message: string, data?: Record<string, unknown>): void;
1062
+ /** Log at `warn`. */
1063
+ warn(message: string, data?: Record<string, unknown>): void;
1064
+ /** Log at `error`. */
1065
+ error(message: string, data?: Record<string, unknown>): void;
1066
+ /** The bounded tail of recent {@link LogRecord}s, oldest first (capped at `limit`). */
1067
+ entries(): readonly LogRecord[];
1068
+ /** Drop every retained record (does not touch listeners). */
1069
+ clear(): void;
1070
+ /** Tear down — clear retention and destroy the emitter. */
1071
+ destroy(): void;
1072
+ }
1073
+
1074
+ /**
1075
+ * An event-free registry of named {@link Logger}s plus a convenience fan-out — the §9
1076
+ * manager over the logging layer (a registry, never observable itself; each {@link Logger}
1077
+ * owns its own `emitter`).
1078
+ *
1079
+ * @remarks
1080
+ * - **Registry (§9).** Loggers live in an insertion-ordered `Map` keyed by `name`.
1081
+ * `register(name, options?)` mints a {@link Logger} named `name` — the manager's default
1082
+ * `level` / `sink` / `styler` / `limit` / `silent` flow in unless `options` OVERRIDES them
1083
+ * (`name` is always the registry key, so any `options.name` is ignored) — stores it (a
1084
+ * re-`register` of the same name OVERWRITES, last write wins), and returns it. `count` is
1085
+ * the map size, `logger(name)` looks one up, `loggers()` lists them in insertion order.
1086
+ * - **Removal (§9.2).** `remove()` clears ALL, `remove(name)` drops ONE (`true` if present),
1087
+ * `remove(names)` drops a batch (`true` if any was removed). `clear()` empties the registry.
1088
+ * (Removal does NOT `destroy` the returned loggers — a caller still holding one keeps using
1089
+ * it; the manager simply stops tracking it.)
1090
+ * - **Fan-out.** `debug` / `info` / `warn` / `error(message, data?)` forward the one call to
1091
+ * EVERY registered logger; each gates / emits / writes per its own `level` and `sink`. A
1092
+ * fan-out over an empty registry is a no-op.
1093
+ * - **Event-free.** No emitter, no events — the manager is a pure registry; observability is
1094
+ * per-{@link Logger}.
1095
+ *
1096
+ * @example
1097
+ * ```ts
1098
+ * const manager = new LoggerManager({ level: 'warn' })
1099
+ * manager.register('http') // inherits the `warn` default
1100
+ * manager.register('db', { level: 'debug' }) // overrides to `debug`
1101
+ * manager.warn('slow', { ms: 900 }) // fans out to both loggers
1102
+ * manager.count // 2
1103
+ * ```
1104
+ */
1105
+ export declare class LoggerManager implements LoggerManagerInterface {
1106
+ #private;
1107
+ constructor(options?: LoggerManagerOptions);
1108
+ get count(): number;
1109
+ register(name: string, options?: LoggerOptions): LoggerInterface;
1110
+ logger(name: string): LoggerInterface | undefined;
1111
+ loggers(): readonly LoggerInterface[];
1112
+ debug(message: string, data?: Record<string, unknown>): void;
1113
+ info(message: string, data?: Record<string, unknown>): void;
1114
+ warn(message: string, data?: Record<string, unknown>): void;
1115
+ error(message: string, data?: Record<string, unknown>): void;
1116
+ remove(names: readonly string[]): boolean;
1117
+ remove(name: string): boolean;
1118
+ remove(): void;
1119
+ clear(): void;
1120
+ }
1121
+
1122
+ /**
1123
+ * An event-free registry of named {@link LoggerInterface}s plus a convenience fan-out — the
1124
+ * §9 manager over the logging layer. It mints + stores loggers keyed by `name`, looks them
1125
+ * up, removes them, and broadcasts a one-off log to EVERY registered logger.
1126
+ *
1127
+ * @remarks
1128
+ * - **Registry (§9).** `register(name, options?)` mints a {@link LoggerInterface} (named
1129
+ * `name`, the manager's defaults flowing in unless `options` overrides them), stores it
1130
+ * (a re-`register` of the same name OVERWRITES — last write wins), and returns it.
1131
+ * `logger(name)` looks one up; `loggers()` lists them in insertion order; `count` is the size.
1132
+ * - **Removal (§9.2).** `remove()` clears ALL, `remove(name)` drops ONE, `remove(names)` drops
1133
+ * a batch (`true` when any was removed). `clear()` empties the registry.
1134
+ * - **Fan-out.** `debug` / `info` / `warn` / `error(message, data?)` forward the call to every
1135
+ * registered logger (each gates / emits / writes per its own `level` and `sink`).
1136
+ * - **Event-free.** No emitter, no events — each logger carries its own observability; the
1137
+ * manager is a pure registry.
1138
+ */
1139
+ export declare interface LoggerManagerInterface {
1140
+ readonly count: number;
1141
+ register(name: string, options?: LoggerOptions): LoggerInterface;
1142
+ logger(name: string): LoggerInterface | undefined;
1143
+ loggers(): readonly LoggerInterface[];
1144
+ /** Fan out a `debug` log to every registered logger. */
1145
+ debug(message: string, data?: Record<string, unknown>): void;
1146
+ /** Fan out an `info` log to every registered logger. */
1147
+ info(message: string, data?: Record<string, unknown>): void;
1148
+ /** Fan out a `warn` log to every registered logger. */
1149
+ warn(message: string, data?: Record<string, unknown>): void;
1150
+ /** Fan out an `error` log to every registered logger. */
1151
+ error(message: string, data?: Record<string, unknown>): void;
1152
+ remove(): void;
1153
+ remove(name: string): boolean;
1154
+ remove(names: readonly string[]): boolean;
1155
+ clear(): void;
1156
+ }
1157
+
1158
+ /**
1159
+ * Options for `createLoggerManager` / the {@link LoggerManagerInterface} constructor.
1160
+ *
1161
+ * @remarks
1162
+ * The manager is an event-free registry (§9) — it carries NO emitter of its own (each
1163
+ * registered {@link LoggerInterface} owns its observable `emitter`). These options supply
1164
+ * the DEFAULTS flowed into every logger the manager mints, unless a per-`register` override
1165
+ * wins: `level` (default threshold), `sink` (shared output target), `styler` (shared
1166
+ * coloring), `limit` (retention cap), and `silent`.
1167
+ */
1168
+ export declare interface LoggerManagerOptions {
1169
+ readonly level?: LogLevel;
1170
+ readonly sink?: SinkInterface;
1171
+ readonly styler?: StylerInterface;
1172
+ readonly limit?: number;
1173
+ readonly silent?: boolean;
1174
+ }
1175
+
1176
+ /**
1177
+ * Options for `createLogger` / the {@link LoggerInterface} constructor.
1178
+ *
1179
+ * @remarks
1180
+ * - `on` — the reserved {@link EmitterHooks} key (§8): initial listeners for the
1181
+ * {@link LoggerEventMap}, wired at construction (e.g. `{ entry: (r) => sink2.write(...) }`).
1182
+ * - `error` — the emitter's listener-error handler (§13); a listener throw routes here.
1183
+ * - `level` — the severity THRESHOLD; records below it are dropped. Defaults to `info`.
1184
+ * - `name` — the logger's name, stamped onto every {@link LogRecord} (`record.name`) and
1185
+ * shown in the formatted line. A manager registers each logger under its name.
1186
+ * - `sink` — where formatted lines are written; defaults to
1187
+ * {@link import('./factories.js').createConsoleSink} (the snapshotted-console sink).
1188
+ * - `styler` — the {@link StylerInterface} the line is colored through; defaults to
1189
+ * {@link import('./factories.js').createStyler} (ANSI). Styling is orthogonal to level.
1190
+ * - `limit` — the bounded retention cap: at most this many recent records are kept
1191
+ * (oldest dropped first). Defaults to {@link DEFAULT_LOG_LIMIT}; never unbounded.
1192
+ * - `silent` — when `true`, suppresses the SINK WRITE only; `entry` still fires and the
1193
+ * record is still retained. Defaults to `false`.
1194
+ */
1195
+ export declare interface LoggerOptions {
1196
+ readonly on?: EmitterHooks<LoggerEventMap>;
1197
+ readonly error?: EmitterErrorHandler;
1198
+ readonly level?: LogLevel;
1199
+ readonly name?: string;
1200
+ readonly sink?: SinkInterface;
1201
+ readonly styler?: StylerInterface;
1202
+ readonly limit?: number;
1203
+ readonly silent?: boolean;
1204
+ }
1205
+
1206
+ /**
1207
+ * The severity level of a {@link LogRecord} — one coherent, ascending-severity scale.
1208
+ *
1209
+ * @remarks
1210
+ * Ordered least-to-most severe: `debug` < `info` < `warn` < `error`. A {@link LoggerInterface}
1211
+ * gates by THRESHOLD — a record at or above the logger's `level` is kept (and written),
1212
+ * one below it is dropped (see {@link LEVEL_SEVERITY} for the numeric order). A level is a
1213
+ * level — its visual treatment (color) is a separate styling concern, NEVER a pseudo-level
1214
+ * like `success` / `ready`.
1215
+ */
1216
+ export declare type LogLevel = 'debug' | 'info' | 'warn' | 'error';
1217
+
1218
+ /**
1219
+ * One immutable, serializable log entry — the universal record the whole logging system
1220
+ * carries. A {@link LoggerInterface} builds one per call, freezes it, retains a bounded
1221
+ * tail of them, and emits it on `entry`; every sink / transport consumes this exact shape.
1222
+ *
1223
+ * @remarks
1224
+ * - `level` — the record's {@link LogLevel}.
1225
+ * - `message` — the human message text.
1226
+ * - `time` — the creation instant as epoch milliseconds (`Date.now()`); a plain number so
1227
+ * the record stays serializable (no `Date` to clone) and orderable.
1228
+ * - `name` — the originating logger's `name`, when it has one (a manager-registered logger
1229
+ * is keyed by name; an anonymous logger omits it).
1230
+ * - `data` — optional structured context (a flat `Record<string, unknown>`), absent when
1231
+ * no context was supplied. The top-level object is a FROZEN COPY taken at log time (a
1232
+ * later mutation of the caller's original object never reaches the retained record);
1233
+ * nested values remain BY REFERENCE (only the top level is copied + frozen).
1234
+ * - The value is frozen at construction — a consumer reads it, never mutates it.
1235
+ */
1236
+ export declare interface LogRecord {
1237
+ readonly level: LogLevel;
1238
+ readonly message: string;
1239
+ readonly time: number;
1240
+ readonly name?: string;
1241
+ readonly data?: Readonly<Record<string, unknown>>;
1242
+ }
1243
+
1244
+ /**
1245
+ * Whether a record at `level` passes a logger gated at `threshold` — i.e. its severity is
1246
+ * at or above the threshold's.
1247
+ *
1248
+ * @remarks
1249
+ * The level gate (AGENTS §5 — the comparison lives here, not inlined in the logger). Reads
1250
+ * the ascending {@link LEVEL_SEVERITY} order: `meetsLevel('warn', 'error')` is `true`
1251
+ * (error ≥ warn), `meetsLevel('warn', 'info')` is `false` (info < warn).
1252
+ *
1253
+ * @param threshold - The logger's configured minimum {@link LogLevel}
1254
+ * @param level - The record's {@link LogLevel}
1255
+ * @returns `true` when `level` is at least as severe as `threshold`
1256
+ *
1257
+ * @example
1258
+ * ```ts
1259
+ * meetsLevel('info', 'error') // true
1260
+ * meetsLevel('error', 'warn') // false
1261
+ * ```
1262
+ */
1263
+ export declare function meetsLevel(threshold: LogLevel, level: LogLevel): boolean;
1264
+
1265
+ /**
1266
+ * Color `text` through `styler`, or return it verbatim when `styler` is `undefined` — the
1267
+ * single optional-styling primitive every renderer applies to its border / title / connector
1268
+ * glyphs (AGENTS §5 — the ONE styler seam, shared, never re-hand-rolled per renderer).
1269
+ *
1270
+ * @remarks
1271
+ * The renderers all take an OPTIONAL `styler`: present ⇒ glyphs are colored, absent ⇒ plain.
1272
+ * Folding that `styler === undefined ? text : styler(text)` ternary into one exported helper
1273
+ * keeps the renderers terse and the styling decision in one tested place. A disabled styler
1274
+ * (`enabled: false`) is still a styler — it returns its text verbatim — so passing one paints
1275
+ * a no-op, exactly as omitting it does.
1276
+ *
1277
+ * @param styler - The {@link StylerInterface} to color with, or `undefined` for no styling
1278
+ * @param text - The glyphs / text to color
1279
+ * @returns `styler(text)` when a styler is given, else `text` unchanged
1280
+ */
1281
+ export declare function paint(styler: StylerInterface | undefined, text: string): string;
1282
+
1283
+ /**
1284
+ * An update-driven, observable progress bar (AGENTS §13) — {@link update} recomputes the bar via
1285
+ * {@link renderBar}, writes `\r` + bar to its {@link SinkInterface}, and emits the `{ current, total }`
1286
+ * on `update`. The leading `\r` is what an overwrite-capable sink (the C-g TTY sink) redraws on; a
1287
+ * plain sink (C-f) degrades to a fresh, non-overwriting line — the line-OVERWRITE is the SINK's job.
1288
+ * UNIVERSAL — the one {@link StylerInterface} + the one {@link SinkInterface}, no `node:*`, no
1289
+ * `process.stdout`. NO self-timer (unlike {@link import('./Spinner.js').Spinner}) — the caller drives it.
1290
+ *
1291
+ * @remarks
1292
+ * - **Update-driven.** Each {@link update} clamps `current` to `[0, total]`, renders the bar (filled
1293
+ * to `current / total`, with the trailing `percent (current/total)` + message) via {@link renderBar},
1294
+ * emits `update`, and writes `'\r' + bar`. Progress advances only when the caller reports it.
1295
+ * - **Outcome lines.** {@link complete} renders a FULL bar (`current = total`) + message, terminated by
1296
+ * a newline, emits a final `update` then `complete`, and marks `completed`. {@link failure} renders the
1297
+ * bar at its CURRENT fill + message + newline and routes to the sink's error stream (no `complete` —
1298
+ * the work did not finish). Both are terminal: a later {@link update} is ignored once `active` is false.
1299
+ * - **Bounded.** `current` is always clamped to `[0, total]`; {@link completed} reports whether
1300
+ * {@link complete} has run; {@link active} is `true` until a {@link complete} / {@link failure}.
1301
+ * - **Lifecycle (§10).** {@link destroy} destroys the emitter (there is no timer to clear).
1302
+ *
1303
+ * @example
1304
+ * ```ts
1305
+ * const progress = new Progress({ total: 100, message: 'downloading' })
1306
+ * progress.update(40) // ████████████░░░░░░░░░░░░░░░░░░ 40% (40/100) downloading
1307
+ * progress.update(80, 'almost there')
1308
+ * progress.complete('done') // a full bar, committed with a newline
1309
+ * ```
1310
+ */
1311
+ export declare class Progress implements ProgressInterface {
1312
+ #private;
1313
+ constructor(options: ProgressOptions);
1314
+ get emitter(): EmitterInterface<ProgressEventMap>;
1315
+ get active(): boolean;
1316
+ get completed(): boolean;
1317
+ get current(): number;
1318
+ get total(): number;
1319
+ update(current: number, message?: string): void;
1320
+ complete(message?: string): void;
1321
+ failure(message?: string): void;
1322
+ destroy(): void;
1323
+ }
1324
+
1325
+ /**
1326
+ * Options for the pure {@link import('./helpers.js').renderBar} renderer — a determinate progress
1327
+ * bar string (`█████░░░░░ 50% (5/10)`), width-aware and styler-optional.
1328
+ *
1329
+ * @remarks
1330
+ * - `current` / `total` — the filled fraction is `current / total`, clamped to `[0, total]` (a
1331
+ * `current` past `total` renders a full bar, a negative one an empty bar) — so a caller's overrun
1332
+ * never produces an over-long bar. A `total` of `0` (or below) renders a full bar (nothing to do).
1333
+ * - `width` — the visible cell count of the bar TRACK (the glyph run between no brackets); defaults
1334
+ * to {@link DEFAULT_BAR_WIDTH}. The percentage + `(current/total)` count follow the track.
1335
+ * - `fill` — the filled-cell glyph; defaults to {@link BAR_FILL} (`█`). `empty` — the empty-cell
1336
+ * glyph; defaults to {@link BAR_EMPTY} (`░`). Sized in VISIBLE columns ({@link
1337
+ * import('./helpers.js').width}), so a multi-cell glyph still yields a `width`-wide track.
1338
+ * - `styler` — colors the FILLED run when supplied (the empty run + the trailing label stay plain);
1339
+ * the layout is identical with or without color, since the track is measured on visible width.
1340
+ */
1341
+ export declare interface ProgressBarOptions {
1342
+ readonly current: number;
1343
+ readonly total: number;
1344
+ readonly width?: number;
1345
+ readonly fill?: string;
1346
+ readonly empty?: string;
1347
+ readonly styler?: StylerInterface;
1348
+ }
1349
+
1350
+ /**
1351
+ * The observable events a {@link ProgressInterface} emits (AGENTS §13).
1352
+ *
1353
+ * @remarks
1354
+ * - `update` — the core event: fires on every `update(current)` (and on `complete` / `failure`),
1355
+ * carrying the `{ current, total }` progress (the clamped `current`). The hook a non-sink consumer
1356
+ * rides to observe progress without a terminal.
1357
+ * - `complete` — the terminal signal: fires once from `complete()` (a successful finish), a pure
1358
+ * signal (empty tuple) so a consumer can observe the bar reaching its end. (`failure()` emits a final
1359
+ * `update` and routes its line to the error stream, but is NOT a `complete` — completion means the
1360
+ * work finished successfully.)
1361
+ *
1362
+ * Listener isolation is the emitter's (§13). Declared as a `type` alias (not
1363
+ * `interface extends EventMap`, §4.5): a type-literal satisfies the `EventMap` constraint
1364
+ * structurally, whereas an interface lacks the index signature.
1365
+ */
1366
+ export declare type ProgressEventMap = {
1367
+ /** Progress advanced — the clamped `{ current, total }` (fires on `update` and on `complete` / `failure`). */
1368
+ readonly update: readonly [progress: {
1369
+ readonly current: number;
1370
+ readonly total: number;
1371
+ }];
1372
+ /** The bar reached its end via `complete()` (a successful finish). */
1373
+ readonly complete: readonly [];
1374
+ };
1375
+
1376
+ /**
1377
+ * An update-driven, observable progress bar (AGENTS §13) — `update(current)` recomputes the bar via
1378
+ * {@link import('./helpers.js').renderBar}, writes `\r` + bar to its {@link SinkInterface}, and emits
1379
+ * the `{ current, total }` on `update`. The line-OVERWRITE is the sink's job (a TTY sink overwrites
1380
+ * on the `\r`; a plain sink degrades to a fresh line). NO self-timer — the caller drives it.
1381
+ *
1382
+ * @remarks
1383
+ * - **Update-driven.** Each `update(current, message?)` clamps `current` to `[0, total]`, renders
1384
+ * the bar (filled to `current / total`, with the trailing `percent (current/total)` + message),
1385
+ * emits `update`, and writes `'\r' + bar`. There is no internal timer (unlike {@link
1386
+ * SpinnerInterface}) — progress advances only when the caller reports it.
1387
+ * - **Outcome lines.** `complete(message?)` renders a FULL bar (`current = total`) + message,
1388
+ * terminated by a newline, emits a final `update` then `complete`, and marks `completed`.
1389
+ * `failure(message?)` renders the bar at its CURRENT fill + message + newline and routes to the sink's
1390
+ * error stream (no `complete` — the work did not finish). Both are terminal: a later `update` after
1391
+ * a `complete` / `failure` is ignored (`active` is `false`).
1392
+ * - **Bounded.** `current` is always clamped to `[0, total]`; `completed` reports whether
1393
+ * `complete()` has run; `active` is `true` until a `complete` / `failure`.
1394
+ */
1395
+ export declare interface ProgressInterface {
1396
+ readonly emitter: EmitterInterface<ProgressEventMap>;
1397
+ /** Whether the bar is still advancing (before any `complete()` / `failure()`). */
1398
+ readonly active: boolean;
1399
+ /** Whether `complete()` has run (the bar finished successfully). */
1400
+ readonly completed: boolean;
1401
+ /** The current value, clamped to `[0, total]`. */
1402
+ readonly current: number;
1403
+ /** The target value the bar fills toward. */
1404
+ readonly total: number;
1405
+ /** Report progress: clamp `current`, re-render the bar, emit `update`, write `\r` + bar. Ignored once terminal. */
1406
+ update(current: number, message?: string): void;
1407
+ /** Finish successfully — render a FULL bar + newline, emit a final `update` then `complete`. */
1408
+ complete(message?: string): void;
1409
+ /** Finish unsuccessfully — render the bar at its current fill + newline to the error stream (no `complete`). */
1410
+ failure(message?: string): void;
1411
+ /** Tear down — destroy the emitter. */
1412
+ destroy(): void;
1413
+ }
1414
+
1415
+ /**
1416
+ * Options for `createProgress` / the {@link ProgressInterface} constructor.
1417
+ *
1418
+ * @remarks
1419
+ * - `on` — the reserved {@link EmitterHooks} key (§8): initial listeners for the
1420
+ * {@link ProgressEventMap}, wired at construction.
1421
+ * - `error` — the emitter's listener-error handler (§13); a listener throw routes here.
1422
+ * - `total` — the value `current` advances toward (the `100%` point); the only REQUIRED option.
1423
+ * - `message` — text shown after the bar; defaults to `''`. Overridden per-`update` and by a
1424
+ * `complete` / `failure` argument.
1425
+ * - `width` — the bar track's visible cell count, handed to {@link import('./helpers.js').renderBar};
1426
+ * defaults to {@link DEFAULT_BAR_WIDTH}.
1427
+ * - `sink` — where each `\r` + bar line is written; defaults to
1428
+ * {@link import('./factories.js').createConsoleSink}. A TTY sink (C-g) overwrites on the `\r`.
1429
+ * - `styler` — the {@link StylerInterface} the filled run is colored through; defaults to
1430
+ * {@link import('./factories.js').createStyler} (ANSI). The ONE styler the whole system shares.
1431
+ */
1432
+ export declare interface ProgressOptions {
1433
+ readonly on?: EmitterHooks<ProgressEventMap>;
1434
+ readonly error?: EmitterErrorHandler;
1435
+ readonly total: number;
1436
+ readonly message?: string;
1437
+ readonly width?: number;
1438
+ readonly sink?: SinkInterface;
1439
+ readonly styler?: StylerInterface;
1440
+ }
1441
+
1442
+ /**
1443
+ * Render a determinate progress bar string — a filled / empty glyph track followed by the percentage
1444
+ * and the `(current/total)` count (`█████░░░░░ 50% (5/10)`). Pure: same {@link ProgressBarOptions} →
1445
+ * same string. The animation-layer sibling of the C-c `render*` renderers (box / table / tree /
1446
+ * separator), shared so a {@link import('./types.js').ProgressInterface} and any direct caller draw
1447
+ * the ONE bar — never a second, hand-rolled one (AGENTS §5; scsr shipped three).
1448
+ *
1449
+ * @remarks
1450
+ * - **Fill fraction, clamped.** The filled cell count is `round((current / total) · width)` with
1451
+ * `current` clamped to `[0, total]`, so an overrun never over-fills and a negative never under-fills.
1452
+ * A `total <= 0` renders a FULL track (there is nothing to fill toward — the work is trivially done).
1453
+ * - **Width-aware track.** The filled run is `fill` tiled to the filled cell count and the empty run
1454
+ * `empty` tiled to the remainder, each via {@link repeatTo} — so the TRACK is exactly `width` VISIBLE
1455
+ * columns even for a multi-cell glyph (its escape codes / extra cells never break the width).
1456
+ * - **Styling.** `options.styler` colors the FILLED run only (the empty run + the trailing
1457
+ * `percent (count)` label stay plain), through {@link paint}; the layout is identical with or
1458
+ * without color, since the track is measured on visible width.
1459
+ * - **Label.** The percentage is the rounded `current / total` (e.g. `50%`); the count is the CLAMPED
1460
+ * `current` over `total` (`(5/10)`), a single space separating the track, the percent, and the count.
1461
+ *
1462
+ * @param options - See {@link ProgressBarOptions}
1463
+ * @returns The rendered bar line (no trailing newline)
1464
+ *
1465
+ * @example
1466
+ * ```ts
1467
+ * renderBar({ current: 5, total: 10, width: 10 }) // '█████░░░░░ 50% (5/10)'
1468
+ * renderBar({ current: 10, total: 10, width: 4 }) // '████ 100% (10/10)'
1469
+ * ```
1470
+ */
1471
+ export declare function renderBar(options: ProgressBarOptions): string;
1472
+
1473
+ /**
1474
+ * Render `content` framed in box-drawing characters, optionally captioned, width-aware so
1475
+ * styled content stays aligned inside the frame. Pure: same {@link BoxOptions} → same string.
1476
+ *
1477
+ * @remarks
1478
+ * - **Lines.** `content` is split on `\n`; each line is padded (left-aligned) to the inner
1479
+ * width by {@link align} — measured on VISIBLE width, so a styled line never breaks the
1480
+ * right edge. The inner width is the widest line's visible width (or `width − borders −
1481
+ * 2·padding` when an explicit `width` is given and is wider), plus `padding` blank cells
1482
+ * inside each {@link BorderChars.vertical} edge.
1483
+ * - **Title.** An optional `title` is embedded in the TOP border (` title `), the remaining
1484
+ * top edge drawn as fill; a title wider than the inner width widens the box to fit it.
1485
+ * - **Border + styling.** The {@link BorderStyle} (`options.border`, default
1486
+ * {@link DEFAULT_BORDER}) selects the glyph set from {@link BORDER_CHARS}; `options.styler`
1487
+ * colors the frame + title when given (content cells are written as supplied).
1488
+ * - **Multi-line result.** Returns the box as `\n`-joined rows (top, one row per content line,
1489
+ * bottom) with no trailing newline.
1490
+ *
1491
+ * @param options - See {@link BoxOptions}
1492
+ * @returns The framed box (multiple lines joined by `\n`)
1493
+ */
1494
+ export declare function renderBox(options: BoxOptions): string;
1495
+
1496
+ /**
1497
+ * A swappable style renderer — the seam that turns style DATA into output for ONE
1498
+ * target. The cross-environment default is the ANSI renderer (SGR escape codes); a
1499
+ * browser `%c` / CSS renderer implements the SAME contract over the SAME {@link Style}
1500
+ * model, so it drops in without touching the style data (the C-f browser branch).
1501
+ */
1502
+ export declare interface RendererInterface {
1503
+ /**
1504
+ * Render `text` wrapped in the target codes for `style`. The EMPTY style (no colors,
1505
+ * no attributes) and the empty string both return `text` unchanged — no wrapping.
1506
+ */
1507
+ render(style: Style, text: string): string;
1508
+ }
1509
+
1510
+ /**
1511
+ * Render a horizontal rule — an optional centered title embedded in a line of fill characters,
1512
+ * to a fixed visible width. Pure: same {@link SeparatorOptions} → same string.
1513
+ *
1514
+ * @remarks
1515
+ * - **Plain rule.** With no `title`, returns `fill` repeated to `width` visible columns.
1516
+ * - **Titled rule.** With a `title`, centers ` title ` (one {@link SEPARATOR_TITLE_GAP} each
1517
+ * side) in the line, splitting the remaining fill between the two sides (the extra column,
1518
+ * when the remainder is odd, goes to the right). The visible width stays exactly `width`,
1519
+ * even when the title is styled (the title's escape codes don't count toward the budget) —
1520
+ * a title at least as wide as `width` yields just the gapped title (no fill).
1521
+ * - **Styling.** When `options.styler` is given, the fill runs (and the embedded title) are
1522
+ * colored through it; the layout is identical with or without color, since width is measured
1523
+ * on the visible content (AGENTS — width-aware via {@link width}).
1524
+ *
1525
+ * @param options - See {@link SeparatorOptions}
1526
+ * @returns The rule line (no trailing newline)
1527
+ *
1528
+ * @example
1529
+ * ```ts
1530
+ * renderSeparator({ width: 10 }) // '──────────'
1531
+ * renderSeparator({ title: 'Build', width: 13 }) // '── Build ──' (centered)
1532
+ * ```
1533
+ */
1534
+ export declare function renderSeparator(options: SeparatorOptions): string;
1535
+
1536
+ /**
1537
+ * Render a bordered grid of `columns` + `rows` with per-column alignment and width-aware
1538
+ * column sizing. Pure: same {@link TableOptions} → same string.
1539
+ *
1540
+ * @remarks
1541
+ * - **Column sizing — visible width.** Each column is sized to the widest VISIBLE width
1542
+ * ({@link width}) among its header label and its cells, so an already-styled cell never
1543
+ * breaks the column (its escape codes don't count toward the width).
1544
+ * - **Ragged rows.** A row shorter than the column count is padded with empty cells; a longer
1545
+ * row is truncated to the column count — a ragged input never throws.
1546
+ * - **Alignment.** Each cell is positioned by its column's {@link ColumnSpec.align} (default
1547
+ * {@link DEFAULT_ALIGN}) via {@link align}.
1548
+ * - **Frame.** The {@link BorderStyle} (`options.border`, default {@link DEFAULT_BORDER})
1549
+ * draws the outer frame, the header rule (a `teeRight … cross … teeLeft` line), and the
1550
+ * `vertical` column separators; `options.styler` colors the frame + header labels when
1551
+ * given. Returns the table as `\n`-joined rows (top, header, rule, one row per data row,
1552
+ * bottom), no trailing newline.
1553
+ *
1554
+ * @param options - See {@link TableOptions}
1555
+ * @returns The rendered table (multiple lines joined by `\n`)
1556
+ */
1557
+ export declare function renderTable(options: TableOptions): string;
1558
+
1559
+ /**
1560
+ * Render a nested {@link TreeNode} tree with box-drawing connectors. Pure: same
1561
+ * {@link TreeOptions} → same string.
1562
+ *
1563
+ * @remarks
1564
+ * The `root` label is the unindented first line; its descendants are drawn beneath it with
1565
+ * {@link TREE_CHARS} — `├─ ` before each child but the last, `└─ ` before the last, and the
1566
+ * carried prefix using `│ ` under an ancestor that still has later siblings or ` ` under a
1567
+ * last ancestor (so the guides line up exactly under the branch they descend from). Node
1568
+ * labels are written as given (an already-styled label is honored); `options.styler` colors
1569
+ * the connectors when supplied. Returns the tree as `\n`-joined lines, no trailing newline.
1570
+ *
1571
+ * @param options - See {@link TreeOptions}
1572
+ * @returns The rendered tree (multiple lines joined by `\n`)
1573
+ *
1574
+ * @example
1575
+ * ```ts
1576
+ * renderTree({ root: { label: 'root', children: [{ label: 'a' }, { label: 'b' }] } })
1577
+ * // root
1578
+ * // ├─ a
1579
+ * // └─ b
1580
+ * ```
1581
+ */
1582
+ export declare function renderTree(options: TreeOptions): string;
1583
+
1584
+ /**
1585
+ * Render the connector-prefixed lines for a {@link TreeNode} list — the recursive core
1586
+ * behind {@link renderTree}. Each child is drawn as `prefix` + its connector (`├─ ` for
1587
+ * any but the last, `└─ ` for the last) + its label, with its own descendants recursed
1588
+ * beneath under the carried guide (`│ ` under a non-last node, ` ` under the last).
1589
+ *
1590
+ * @remarks
1591
+ * A centralized, exported recursion branch (AGENTS §5) so it is directly testable and
1592
+ * reusable outside {@link renderTree}'s top-level `root.label` framing.
1593
+ *
1594
+ * @param nodes - The sibling {@link TreeNode}s to render at this depth
1595
+ * @param prefix - The guide/gap string carried in from the ancestor chain (`''` at the root)
1596
+ * @param styler - The {@link StylerInterface} connectors are colored through, when supplied
1597
+ * @returns The rendered lines for `nodes` and all their descendants
1598
+ *
1599
+ * @example
1600
+ * ```ts
1601
+ * renderTreeChildren([{ label: 'a' }, { label: 'b' }], '')
1602
+ * // ['├─ a', '└─ b']
1603
+ * ```
1604
+ */
1605
+ export declare function renderTreeChildren(nodes: readonly TreeNode[], prefix: string, styler?: StylerInterface): readonly string[];
1606
+
1607
+ /**
1608
+ * Repeat `unit` until it fills exactly `count` VISIBLE columns, trimming a trailing partial
1609
+ * unit so the run is never over-wide — the fill primitive the separator + box edges draw with.
1610
+ *
1611
+ * @remarks
1612
+ * Counts in code points ({@link width}-consistent), so a multi-cell or astral `unit` is laid
1613
+ * down whole and the result is sliced to exactly `count` visible columns. `count <= 0` (or an
1614
+ * empty / zero-width `unit`) yields `''`.
1615
+ *
1616
+ * @param unit - The (possibly multi-character) fill unit
1617
+ * @param count - The visible column count to fill
1618
+ * @returns `unit` tiled to exactly `count` visible columns
1619
+ *
1620
+ * @example
1621
+ * ```ts
1622
+ * repeatTo('─', 4) // '────'
1623
+ * repeatTo('=-', 5) // '=-=-='
1624
+ * ```
1625
+ */
1626
+ export declare function repeatTo(unit: string, count: number): string;
1627
+
1628
+ /**
1629
+ * A lean, event-free narrative reporter (AGENTS §13) — the composable verb set for human /
1630
+ * build-run output. Each verb FORMATS its line through the shared {@link StylerInterface} and
1631
+ * the pure layout renderers ({@link renderSeparator} / {@link renderBox} / {@link renderTable}
1632
+ * / {@link renderTree}) and WRITES it to a {@link SinkInterface} — the SAME styler + sink
1633
+ * substrate the logger uses, never a second colorizer.
1634
+ *
1635
+ * @remarks
1636
+ * - **A SMALL set, not a grab-bag.** `section` / `step` / `timing` / `status` / `table` /
1637
+ * `tree` / `box` / `line` / `blank`. No spinner / bar (the animation chunk), no buffering /
1638
+ * capture (the capture chunk), no level retention (the logger). Just format + write.
1639
+ * - **`status` is a narrative OUTCOME, not a log level.** Its {@link StatusLevel} (`success` /
1640
+ * `error` / `warn` / `info`) is distinct from {@link import('./types.js').LogLevel}: an icon
1641
+ * ({@link STATUS_ICONS}) + a color ({@link STATUS_COLORS}), with `error` routed to the sink's
1642
+ * error stream (the `level` hint forwarded to {@link SinkInterface.write}) — there is no
1643
+ * gating and no severity ordering.
1644
+ * - **Width-aware.** `section` (and a `box` with no explicit `width`) lay out to the reporter's
1645
+ * `#width`; the renderers measure on VISIBLE width (ANSI-aware), so styled content aligns.
1646
+ * - **Event-free (§13).** No `#emitter` — a pure formatting front-end with no observable
1647
+ * lifecycle (like the renderers and `Scheduler`). It is reusable and holds no per-call state.
1648
+ *
1649
+ * @example
1650
+ * ```ts
1651
+ * const reporter = new Reporter()
1652
+ * reporter.section('Build')
1653
+ * reporter.step('compiling', { index: 1, total: 3 }) // [1/3] compiling
1654
+ * reporter.timing('bundle', 1234) // bundle … 1.23s
1655
+ * reporter.status('success', 'done') // ✔ done
1656
+ * ```
1657
+ */
1658
+ export declare class Reporter implements ReporterInterface {
1659
+ #private;
1660
+ constructor(options?: ReporterOptions);
1661
+ section(title: string): void;
1662
+ step(message: string, position?: StepPosition): void;
1663
+ timing(label: string, ms: number): void;
1664
+ status(level: StatusLevel, message: string): void;
1665
+ table(options: TableOptions): void;
1666
+ tree(options: TreeOptions): void;
1667
+ box(options: BoxOptions): void;
1668
+ line(text: string): void;
1669
+ blank(count?: number): void;
1670
+ }
1671
+
1672
+ /**
1673
+ * A lean, event-free narrative reporter — the composable verb set for human / build-run
1674
+ * output (sections, steps, timings, outcomes, tables, trees, boxes), formatting through the
1675
+ * shared {@link StylerInterface} + layout renderers and writing to a {@link SinkInterface}.
1676
+ *
1677
+ * @remarks
1678
+ * - **A SMALL composable set**, not a grab-bag: `section` / `step` / `timing` / `status` /
1679
+ * `table` / `tree` / `box` / `line` / `blank`. Coloring is the ONE styler; layout is the
1680
+ * pure renderers ({@link import('./helpers.js').renderSeparator} /
1681
+ * {@link import('./helpers.js').renderBox} / {@link import('./helpers.js').renderTable} /
1682
+ * {@link import('./helpers.js').renderTree}). No second colorizer, no spinner / bar (that
1683
+ * is the animation chunk), no buffering / capture (that is the capture chunk).
1684
+ * - **`status` is a narrative outcome, not a log level.** Its {@link StatusLevel} is
1685
+ * `success` / `error` / `warn` / `info` (DISTINCT from {@link LogLevel}); `error` routes to
1686
+ * the sink's error stream.
1687
+ * - **Event-free (§13).** No emitter — a pure formatting front-end. Each verb FORMATS then
1688
+ * WRITES immediately; there is no retained state worth observing.
1689
+ */
1690
+ export declare interface ReporterInterface {
1691
+ /** Write a titled separator block — a section heading framed by a horizontal rule. */
1692
+ section(title: string): void;
1693
+ /** Write a step line, optionally prefixed with its `[index/total]` {@link StepPosition}. */
1694
+ step(message: string, position?: StepPosition): void;
1695
+ /** Write a timing line — `label … 1.23s` (sub-second shown as `…ms`). */
1696
+ timing(label: string, ms: number): void;
1697
+ /** Write an icon + colored outcome line for `level` (`error` routes to the error stream). */
1698
+ status(level: StatusLevel, message: string): void;
1699
+ /** Render a {@link TableOptions} grid through {@link import('./helpers.js').renderTable} and write it. */
1700
+ table(options: TableOptions): void;
1701
+ /** Render a {@link TreeOptions} tree through {@link import('./helpers.js').renderTree} and write it. */
1702
+ tree(options: TreeOptions): void;
1703
+ /** Render a {@link BoxOptions} frame through {@link import('./helpers.js').renderBox} and write it. */
1704
+ box(options: BoxOptions): void;
1705
+ /** Write one raw line, colored through the styler if any styling is embedded — no prefix, no icon. */
1706
+ line(text: string): void;
1707
+ /** Write `count` blank lines (default `1`). */
1708
+ blank(count?: number): void;
1709
+ }
1710
+
1711
+ /**
1712
+ * Options for {@link createReporter} / the {@link ReporterInterface} constructor.
1713
+ *
1714
+ * @remarks
1715
+ * - `sink` — where every formatted line is written; defaults to
1716
+ * {@link import('./factories.js').createConsoleSink} (the snapshotted, level-routing console
1717
+ * sink) — the SAME seam the logger writes through. A `status('error', …)` passes the
1718
+ * `error` level so a stream-aware sink routes it to `stderr`.
1719
+ * - `styler` — the {@link StylerInterface} every line is colored through; defaults to
1720
+ * {@link import('./factories.js').createStyler} (ANSI). The ONE styler the whole system
1721
+ * shares — no second colorizer. A disabled styler yields plain narration.
1722
+ * - `width` — the default column width handed to the separator / box renderers (the section
1723
+ * rule, a `box` with no explicit width); defaults to {@link DEFAULT_WIDTH}.
1724
+ *
1725
+ * Event-free (§13): the reporter has no `on` / `error` — it is a formatting front-end with no
1726
+ * observable lifecycle, so (like the renderers and `Scheduler`) it carries no emitter.
1727
+ */
1728
+ export declare interface ReporterOptions {
1729
+ readonly sink?: SinkInterface;
1730
+ readonly styler?: StylerInterface;
1731
+ readonly width?: number;
1732
+ }
1733
+
1734
+ /** The full SGR reset sequence (`ESC[0m`) appended after a styled run. */
1735
+ export declare const RESET: string;
1736
+
1737
+ /** The SGR RESET parameter (0) — terminates a styled run, clearing all colors and attributes. */
1738
+ export declare const RESET_CODE = 0;
1739
+
1740
+ /**
1741
+ * The number of milliseconds at or above which {@link import('./helpers.js').formatDuration}
1742
+ * (and so `Reporter.timing`) switches from a `…ms` rendering to a `…s` (seconds, 2 d.p.)
1743
+ * rendering — exactly one second.
1744
+ */
1745
+ export declare const SECOND_MS = 1000;
1746
+
1747
+ /** The default fill character {@link import('./helpers.js').renderSeparator} draws its rule with — `─`. */
1748
+ export declare const SEPARATOR_FILL = "\u2500";
1749
+
1750
+ /**
1751
+ * The single padding cell on each side of a separator's embedded title (` title `) — keeps the
1752
+ * title from butting against the rule. One space.
1753
+ */
1754
+ export declare const SEPARATOR_TITLE_GAP = " ";
1755
+
1756
+ /**
1757
+ * Options for {@link import('./helpers.js').renderSeparator} — a horizontal rule, optionally
1758
+ * carrying a centered title.
1759
+ *
1760
+ * @remarks
1761
+ * - `title` — text to embed in the rule (e.g. a section heading). Omitted ⇒ an unbroken line.
1762
+ * - `width` — the visible column count of the whole rule; defaults to {@link DEFAULT_WIDTH}.
1763
+ * - `fill` — the single character the rule is drawn with; defaults to {@link SEPARATOR_FILL}
1764
+ * (`─`). The VISIBLE width of the rule is `width` regardless of the fill's escape codes.
1765
+ * - `styler` — colors the rule (and the embedded title) when supplied; the layout is
1766
+ * identical with or without it, since width is measured on the visible content.
1767
+ */
1768
+ export declare interface SeparatorOptions {
1769
+ readonly title?: string;
1770
+ readonly width?: number;
1771
+ readonly fill?: string;
1772
+ readonly styler?: StylerInterface;
1773
+ }
1774
+
1775
+ /**
1776
+ * The minimal output primitive — the seam every formatted line is written through. A
1777
+ * `Sink` is the ONE place text leaves the logging system; redirect output (to a file, a
1778
+ * buffer, a test recorder, the browser `%c` path, a server TTY) by supplying a different
1779
+ * `SinkInterface`, with no change to the logger.
1780
+ *
1781
+ * @remarks
1782
+ * - **`write(text)` is the whole contract.** A custom sink (file / buffer / recorder)
1783
+ * implements just `write(text)` and ignores the rest — the optional `level` exists ONLY so
1784
+ * a stream-aware sink can ROUTE. The logger passes the originating record's {@link LogLevel}.
1785
+ * - **The default {@link import('./factories.js').createConsoleSink} routes by level** —
1786
+ * `error` → `console.error`, `warn` → `console.warn`, everything else → `console.log` — and
1787
+ * writes to the UNDERLYING `console` methods SNAPSHOTTED at creation, so a later `Capture`
1788
+ * that patches `console` can never feed the sink's own output back into itself (the
1789
+ * no-capture-loop principle). The same `level` seam lets the C-g server TTY sink send
1790
+ * `error` / `warn` to `stderr`.
1791
+ */
1792
+ export declare interface SinkInterface {
1793
+ /**
1794
+ * Write one already-formatted chunk of output. `text` receives ONE LINE WITHOUT its
1795
+ * terminator — the sink's target supplies it (e.g. `console.log`; the server TTY sink
1796
+ * appends one) — UNLESS `text` begins with `\r`: that is an in-place REDRAW frame (the
1797
+ * Spinner / Progress animation protocol) carrying its own line endings, written verbatim.
1798
+ * `level` is the originating record's {@link LogLevel} — supplied so a stream-aware sink
1799
+ * can route (e.g. `error` to `stderr`); a plain sink ignores it.
1800
+ */
1801
+ write(text: string, level?: LogLevel): void;
1802
+ }
1803
+
1804
+ /**
1805
+ * A self-driving, observable activity spinner (AGENTS §13) — a glyph cycle that advances on a
1806
+ * periodic timer, writing each `\r` + frame line to its {@link SinkInterface} and emitting it on
1807
+ * `frame`. The leading `\r` is what an overwrite-capable sink (the C-g TTY sink) redraws on; a plain
1808
+ * sink (C-f) degrades to a fresh, non-overwriting line — the line-OVERWRITE is the SINK's job, never
1809
+ * the spinner's. UNIVERSAL — `setInterval` + the one {@link StylerInterface} + the one
1810
+ * {@link SinkInterface}, no `node:*`, no `process.stdout`.
1811
+ *
1812
+ * @remarks
1813
+ * - **Self-driving but deterministically testable.** `start()` arms a `setInterval` that calls
1814
+ * {@link tick} each `interval`; each {@link tick} builds the styled `glyph + message` line for the
1815
+ * current frame, emits it on `frame`, writes `'\r' + line` to the sink, then advances the frame
1816
+ * index (wrapping). A test drives frames by calling {@link tick} directly (NO real clock) and proves
1817
+ * the timer arms / clears with fake timers.
1818
+ * - **Leak-free timer.** The interval is ALWAYS cleared on {@link success} / {@link failure} /
1819
+ * {@link stop} / {@link destroy} — `#handle` is the single source of `active`, set on arm and unset
1820
+ * on clear, so a spinner never leaks a running interval.
1821
+ * - **Idempotent `start`.** A {@link start} while already `active` is a no-op (it never arms a second
1822
+ * timer).
1823
+ * - **Outcome lines.** {@link success} / {@link failure} clear the timer then write + emit a FINAL line —
1824
+ * the {@link STATUS_ICONS} `✔` / `✖` (colored via {@link STATUS_COLORS}) + the message — terminated
1825
+ * by a newline (the activity is over; the line is committed, not overwritten). {@link failure} routes to
1826
+ * the sink's error stream.
1827
+ * - **Lifecycle (§10).** {@link stop} clears the timer and LEAVES the current line; {@link destroy}
1828
+ * stops then destroys the emitter. {@link update} swaps the message and re-renders immediately when
1829
+ * `active`.
1830
+ *
1831
+ * @example
1832
+ * ```ts
1833
+ * const spinner = new Spinner({ message: 'building' })
1834
+ * spinner.start() // arms the timer, paints the first frame to the sink
1835
+ * spinner.update('bundling') // message changes, re-rendered at once
1836
+ * spinner.success('built in 1.2s') // ✔ built in 1.2s — timer cleared, line committed
1837
+ * ```
1838
+ */
1839
+ export declare class Spinner implements SpinnerInterface {
1840
+ #private;
1841
+ constructor(options?: SpinnerOptions);
1842
+ get emitter(): EmitterInterface<SpinnerEventMap>;
1843
+ get active(): boolean;
1844
+ get message(): string;
1845
+ start(): void;
1846
+ tick(): void;
1847
+ update(message: string): void;
1848
+ success(message?: string): void;
1849
+ failure(message?: string): void;
1850
+ stop(): void;
1851
+ destroy(): void;
1852
+ }
1853
+
1854
+ /**
1855
+ * The default spinner frame cycle a {@link import('./types.js').SpinnerInterface} advances through —
1856
+ * the ten braille-pattern glyphs (U+2800 block) that read as a smoothly rotating dot, the universal
1857
+ * terminal-spinner convention. Frozen; a consumer swaps the whole cycle via `options.frames`.
1858
+ *
1859
+ * @remarks
1860
+ * Braille glyphs are single visible cells, so every frame occupies one column — the spinner glyph
1861
+ * never shifts the message beside it as it advances. The source of truth for the default frame axis.
1862
+ */
1863
+ export declare const SPINNER_FRAMES: readonly string[];
1864
+
1865
+ /**
1866
+ * The observable events a {@link SpinnerInterface} emits (AGENTS §13).
1867
+ *
1868
+ * @remarks
1869
+ * - `frame` — the core event: fires once per advance (every `tick()`, whether driven by the internal
1870
+ * timer or called directly) AND on the final `success` / `failure` line, carrying the rendered frame
1871
+ * line (the SAME text written to the sink, minus the leading `\r`). The hook a non-sink consumer
1872
+ * (a test, a remote mirror) rides to observe the animation without a terminal.
1873
+ * - `start` / `stop` — the lifecycle signals bracketing the internal timer: `start` fires when the
1874
+ * timer is armed (the first `start()` on an inactive spinner), `stop` when it is cleared (a
1875
+ * `stop()` / `success()` / `failure()` on an active spinner, and from `destroy()`); both pure signals
1876
+ * (empty tuples) so a consumer can observe the activity lifecycle.
1877
+ *
1878
+ * Listener isolation is the emitter's (§13): a listener throw routes to the emitter's `error`
1879
+ * handler, never onto this map. Declared as a `type` alias (not `interface extends EventMap`, §4.5):
1880
+ * a type-literal satisfies the `EventMap` constraint structurally, whereas an interface lacks the
1881
+ * index signature.
1882
+ */
1883
+ export declare type SpinnerEventMap = {
1884
+ /** A frame was produced (a `tick()` advance or the final `success` / `failure` line) — the rendered line. */
1885
+ readonly frame: readonly [line: string];
1886
+ /** The internal timer was armed (an inactive spinner's `start()`). */
1887
+ readonly start: readonly [];
1888
+ /** The internal timer was cleared (an active spinner's `stop()` / `success()` / `failure()` / `destroy()`). */
1889
+ readonly stop: readonly [];
1890
+ };
1891
+
1892
+ /**
1893
+ * A self-driving, observable activity spinner (AGENTS §13) — a glyph cycle that advances on a
1894
+ * periodic timer, writing each `\r` + frame line to its {@link SinkInterface} and emitting it on
1895
+ * `frame`. The line-OVERWRITE is the sink's job (a TTY sink overwrites on the `\r`; a plain sink
1896
+ * degrades to a fresh line).
1897
+ *
1898
+ * @remarks
1899
+ * - **Self-driving but deterministically testable.** `start()` arms a `setInterval` (universal — no
1900
+ * `node:*`) that calls `tick()` each `interval`; each `tick()` advances the frame index, builds the
1901
+ * styled `glyph + message` line, emits it on `frame`, and writes `'\r' + line` to the sink. A test
1902
+ * drives frames by calling `tick()` directly (NO real clock) and proves the timer arms / clears
1903
+ * with fake timers — the timer is ALWAYS cleared on `success` / `failure` / `stop` / `destroy`, so it
1904
+ * never leaks.
1905
+ * - **Idempotent `start`.** A `start()` while already `active` is a no-op (it never arms a second
1906
+ * timer). `active` reflects whether the timer is currently armed.
1907
+ * - **Outcome lines.** `success(message?)` / `failure(message?)` clear the timer, then write + emit a
1908
+ * FINAL line — the {@link STATUS_ICONS} `✔` / `✖` (colored via {@link STATUS_COLORS}) + the
1909
+ * message — terminated by a newline (the activity is over; the line is committed, not overwritten).
1910
+ * `failure` routes to the sink's error stream.
1911
+ * - **Lifecycle (§10).** `stop()` clears the timer and LEAVES the current line (no final write);
1912
+ * `destroy()` stops then destroys the emitter. `update(message)` swaps the message (re-rendering
1913
+ * immediately when active, so the change shows without waiting for the next tick).
1914
+ */
1915
+ export declare interface SpinnerInterface {
1916
+ readonly emitter: EmitterInterface<SpinnerEventMap>;
1917
+ /** Whether the internal timer is currently armed (between `start()` and `stop` / `success` / `failure`). */
1918
+ readonly active: boolean;
1919
+ /** The current message shown beside the glyph. */
1920
+ readonly message: string;
1921
+ /** Arm the periodic timer and render the first frame — a no-op when already `active`. */
1922
+ start(): void;
1923
+ /** Advance one frame: build the line, emit `frame`, and write `\r` + line to the sink. */
1924
+ tick(): void;
1925
+ /** Change the message; re-renders immediately when `active` so the change shows at once. */
1926
+ update(message: string): void;
1927
+ /** Stop with a SUCCESS line — clear the timer, write + emit `✔ message` + newline. */
1928
+ success(message?: string): void;
1929
+ /** Stop with a FAILURE line — clear the timer, write + emit `✖ message` + newline (error stream). */
1930
+ failure(message?: string): void;
1931
+ /** Clear the timer and LEAVE the current line (no final write) — a no-op when not `active`. */
1932
+ stop(): void;
1933
+ /** Tear down — `stop()` then destroy the emitter. */
1934
+ destroy(): void;
1935
+ }
1936
+
1937
+ /**
1938
+ * Options for `createSpinner` / the {@link SpinnerInterface} constructor.
1939
+ *
1940
+ * @remarks
1941
+ * - `on` — the reserved {@link EmitterHooks} key (§8): initial listeners for the
1942
+ * {@link SpinnerEventMap}, wired at construction.
1943
+ * - `error` — the emitter's listener-error handler (§13); a listener throw routes here.
1944
+ * - `message` — the text shown beside the spinner glyph; defaults to `''` (a bare glyph). Changed
1945
+ * live via `update(message)` and overridden by a `success` / `failure` argument.
1946
+ * - `frames` — the cycle of glyph frames the spinner advances through; defaults to
1947
+ * {@link SPINNER_FRAMES} (the braille set `⠋⠙⠹…`). Each `tick()` advances to the next, wrapping.
1948
+ * - `interval` — the timer period in milliseconds between frames; defaults to
1949
+ * {@link DEFAULT_SPINNER_INTERVAL}. The timer is ALWAYS cleared on `success` / `failure` / `stop` /
1950
+ * `destroy`, so it never leaks; tests drive frames deterministically via `tick()` (no real clock).
1951
+ * - `sink` — where each `\r` + frame line is written; defaults to
1952
+ * {@link import('./factories.js').createConsoleSink}. A TTY sink (C-g) overwrites on the `\r`.
1953
+ * - `styler` — the {@link StylerInterface} the glyph is colored through; defaults to
1954
+ * {@link import('./factories.js').createStyler} (ANSI). The ONE styler the whole system shares.
1955
+ */
1956
+ export declare interface SpinnerOptions {
1957
+ readonly on?: EmitterHooks<SpinnerEventMap>;
1958
+ readonly error?: EmitterErrorHandler;
1959
+ readonly message?: string;
1960
+ readonly frames?: readonly string[];
1961
+ readonly interval?: number;
1962
+ readonly sink?: SinkInterface;
1963
+ readonly styler?: StylerInterface;
1964
+ }
1965
+
1966
+ /**
1967
+ * Each {@link StatusLevel}'s {@link Color} — the icon + message color a `status` line renders
1968
+ * in (`success` green, `error` red, `warn` yellow, `info` blue). The VISUAL treatment of a
1969
+ * narrative outcome, colored through the reporter's styler; orthogonal to leveling, like
1970
+ * {@link LEVEL_COLORS}. Excludes `default` so each value indexes a real styler accessor.
1971
+ */
1972
+ export declare const STATUS_COLORS: Readonly<Record<StatusLevel, Exclude<Color, 'default'>>>;
1973
+
1974
+ /**
1975
+ * Each {@link StatusLevel}'s icon glyph — the leading mark a {@link
1976
+ * import('./types.js').ReporterInterface.status} outcome line shows: `success` ✔, `error` ✖,
1977
+ * `warn` ⚠, `info` ℹ. The narrative-outcome counterpart to a log level's label; frozen.
1978
+ */
1979
+ export declare const STATUS_ICONS: Readonly<Record<StatusLevel, string>>;
1980
+
1981
+ /**
1982
+ * Every {@link StatusLevel}, frozen — the outcomes a `status` line supports (drives exhaustive
1983
+ * tests). The source of truth for the status axis; aligned with {@link STATUS_ICONS} /
1984
+ * {@link STATUS_COLORS}.
1985
+ */
1986
+ export declare const STATUS_LEVELS: readonly StatusLevel[];
1987
+
1988
+ /**
1989
+ * A narrative outcome level — the four states {@link ReporterInterface.status} reports, each
1990
+ * with its own icon + color ({@link STATUS_ICONS} / {@link STATUS_COLORS}).
1991
+ *
1992
+ * @remarks
1993
+ * DISTINCT from {@link LogLevel} (`debug` / `info` / `warn` / `error`): a `StatusLevel` is a
1994
+ * narrative OUTCOME (did the step success?), not a log SEVERITY threshold — there is no
1995
+ * ordering and no gating. `success` (`✔`, green), `error` (`✖`, red), `warn` (`⚠`, yellow),
1996
+ * `info` (`ℹ`, blue). `error` routes to the sink's error stream (the `level` hint passed to
1997
+ * {@link SinkInterface.write}); the other three go to the default stream.
1998
+ */
1999
+ export declare type StatusLevel = 'success' | 'error' | 'warn' | 'info';
2000
+
2001
+ /**
2002
+ * A step's position in a sequence — the `{ index, total }` a {@link ReporterInterface.step}
2003
+ * renders as a `[2/5]` prefix.
2004
+ *
2005
+ * @remarks
2006
+ * Both are 1-based for display (`{ index: 2, total: 5 }` ⇒ `[2/5]`); the reporter formats
2007
+ * them verbatim, so a caller controls the numbering. Omitting the position renders a bare
2008
+ * step line with no prefix.
2009
+ */
2010
+ export declare interface StepPosition {
2011
+ readonly index: number;
2012
+ readonly total: number;
2013
+ }
2014
+
2015
+ /**
2016
+ * Stringify ONE captured console argument into a line fragment — the per-argument rule behind
2017
+ * {@link formatArgs}: an `Error` → `name: message`, a plain object / array → circular-safe JSON,
2018
+ * anything else (string, number, boolean, `null`, `undefined`, symbol, function) → `String(value)`.
2019
+ *
2020
+ * @remarks
2021
+ * - **Total + never throws.** Like a guard (§14), this never throws on adversarial input — a value
2022
+ * carrying a circular reference, a `BigInt`, or a throwing `toJSON` is rendered, not raised. The
2023
+ * `JSON.stringify` runs with a circular-guard replacer (a seen-set drops a back-reference as
2024
+ * `'[Circular]'`); should `JSON.stringify` still throw (e.g. a `BigInt`), the value falls back to
2025
+ * `String(value)`. So a `Capture` can never crash the program whose `console.*` it intercepts.
2026
+ * - **`Error` first.** An `Error` renders as `name: message` (e.g. `TypeError: bad`) — the useful
2027
+ * one-line form, since `JSON.stringify(error)` is `{}` (its fields are non-enumerable).
2028
+ * - **Objects → JSON.** A non-null `object` (including an array) is `JSON.stringify`d; a primitive
2029
+ * (or `null` / `undefined` / `function` / `symbol`) goes through `String`.
2030
+ *
2031
+ * @param value - One console argument (any value)
2032
+ * @returns The argument's one-line string form
2033
+ *
2034
+ * @example
2035
+ * ```ts
2036
+ * stringifyValue('hi') // 'hi'
2037
+ * stringifyValue({ a: 1 }) // '{"a":1}'
2038
+ * stringifyValue(new TypeError('bad')) // 'TypeError: bad'
2039
+ * const cycle: Record<string, unknown> = {}
2040
+ * cycle.self = cycle
2041
+ * stringifyValue(cycle) // '{"self":"[Circular]"}'
2042
+ * ```
2043
+ */
2044
+ export declare function stringifyValue(value: unknown): string;
2045
+
2046
+ /**
2047
+ * Remove every ANSI escape sequence from `text`, returning the plain visible string.
2048
+ *
2049
+ * @remarks
2050
+ * Strips SGR color/style codes AND other CSI controls (cursor, erase) plus OSC
2051
+ * sequences (titles, hyperlinks) — see {@link ANSI_PATTERN}. A FRESH `RegExp` is built
2052
+ * per call from the canonical pattern's `source` + `flags`, so the shared global
2053
+ * pattern's `lastIndex` is never mutated across calls (re-entrant and deterministic).
2054
+ *
2055
+ * @param text - Any string, styled or plain
2056
+ * @returns `text` with all ANSI escapes removed
2057
+ *
2058
+ * @example
2059
+ * ```ts
2060
+ * strip('\x1b[31mred\x1b[0m') // 'red'
2061
+ * ```
2062
+ */
2063
+ export declare function strip(text: string): string;
2064
+
2065
+ /**
2066
+ * Remove every non-printing C0 control character from `text` EXCEPT `\t` / `\n` / `\r`
2067
+ * (meaningful whitespace), plus DEL — returning the sanitized string.
2068
+ *
2069
+ * @remarks
2070
+ * Deliberately SEPARATE from {@link strip} (ANSI-escape removal only, so `width` /
2071
+ * `align` stay untouched) — this is the additional pass a non-TTY output sink applies
2072
+ * on top of `strip`, so a captured `\x07` bell or stray `\x00` never reaches a log file
2073
+ * / non-terminal target. A FRESH `RegExp` is built per call from {@link CONTROL_PATTERN}'s
2074
+ * `source` + `flags`, the same re-entrant idiom as `strip`.
2075
+ *
2076
+ * @param text - Any string, possibly carrying raw control bytes
2077
+ * @returns `text` with C0 controls (other than tab/newline/CR) and DEL removed
2078
+ *
2079
+ * @example
2080
+ * ```ts
2081
+ * stripControls('a\x07b\nc') // 'ab\nc'
2082
+ * ```
2083
+ */
2084
+ export declare function stripControls(text: string): string;
2085
+
2086
+ /**
2087
+ * Text style as DATA — a frozen, readonly record of a foreground color, a background
2088
+ * color, and a set of text attributes. The single style value the whole console /
2089
+ * terminal system shares; a {@link RendererInterface} renders it for one target.
2090
+ *
2091
+ * @remarks
2092
+ * - `foreground` / `background` are absent (not `'default'`) when unset — the renderer
2093
+ * emits a color code only for a set, non-`default` color.
2094
+ * - `attributes` is a de-duplicated, order-stable list (a set modelled as an array so
2095
+ * the value stays plain JSON data — no `Set` to clone or serialize). An empty list +
2096
+ * no colors is the EMPTY style, which renders text unchanged.
2097
+ * - The value is deeply frozen; compose a new style with the styler rather than mutating.
2098
+ */
2099
+ export declare interface Style {
2100
+ readonly foreground?: Color;
2101
+ readonly background?: Color;
2102
+ readonly attributes: readonly Attribute[];
2103
+ }
2104
+
2105
+ /**
2106
+ * The fluent, composable styler — the consumer-facing API over the style engine. It
2107
+ * builds a {@link Style} (style as DATA) and renders it through an injected
2108
+ * {@link RendererInterface} (the ANSI default, or a browser `%c` renderer at C-f). Each
2109
+ * color / attribute accessor is immutable copy-on-write: it returns a NEW styler's
2110
+ * surface with the token added, so `styler.red.bold('hi')` composes without mutating,
2111
+ * and a base styler is freely reusable.
2112
+ *
2113
+ * @remarks
2114
+ * - **Callable surface.** A `Styler` is not itself callable; its {@link surface} getter
2115
+ * returns the {@link StylerInterface} — a render FUNCTION carrying the chainable
2116
+ * accessors. The accessors are installed as LAZY getters (`Object.defineProperties`),
2117
+ * so a chain materializes only the stylers it actually walks — never the full tree —
2118
+ * and the recursion terminates. The factory returns that surface; this class is the
2119
+ * engine behind it.
2120
+ * - **Immutable.** `#foreground` and `#attribute` return a fresh `Styler` (the style is
2121
+ * rebuilt, never mutated). A later color of the same channel WINS (last write); a
2122
+ * repeated attribute is idempotent (de-duplicated, order preserved).
2123
+ * - **`enabled` switch.** When `false`, the render function returns text VERBATIM — no
2124
+ * renderer call, no escape codes (for a non-TTY / `NO_COLOR` / piped output).
2125
+ * - **Event-free** — a pure styling primitive (AGENTS §13), like `Scheduler`.
2126
+ */
2127
+ export declare class Styler {
2128
+ #private;
2129
+ constructor(renderer: RendererInterface, enabled: boolean, style: Style);
2130
+ /** The accumulated style DATA — the empty style on a base styler. */
2131
+ get style(): Style;
2132
+ /** Whether styling is applied; when `false`, the surface returns text unchanged. */
2133
+ get enabled(): boolean;
2134
+ /**
2135
+ * The fluent {@link StylerInterface} value — a render function (`text => string`) with
2136
+ * `style`, `enabled`, and every {@link Color} / {@link Attribute} as a LAZY accessor
2137
+ * (each computes the next styler's surface only when read). This is what consumers
2138
+ * hold and call.
2139
+ *
2140
+ * @remarks
2141
+ * The accessors are defined as getters (not eagerly-merged values), so accessing one
2142
+ * builds exactly one child styler — the tree is never fully materialized and the
2143
+ * construction terminates. The assembled function is then narrowed to
2144
+ * {@link StylerInterface} through {@link #isSurface} (a real structural check), so no
2145
+ * type assertion is used (AGENTS §1 / §14 — narrow, never assert).
2146
+ */
2147
+ get surface(): StylerInterface;
2148
+ }
2149
+
2150
+ /**
2151
+ * The fluent, composable styling surface — the consumer-facing API. It is BOTH a
2152
+ * function (call it with text to render the accumulated style) AND a record of
2153
+ * chainable accessors: every {@link Color} and {@link Attribute} is a getter returning a
2154
+ * NEW styler with that token added, so `styler.red.bold('hi')` and
2155
+ * `styler.red(styler.bold('hi'))` both work and nothing is mutated.
2156
+ *
2157
+ * @remarks
2158
+ * - Each accessor returns a fresh `StylerInterface` (immutable, copy-on-write) — a base
2159
+ * styler is reusable and the chains never interfere.
2160
+ * - Calling the styler builds the {@link Style} under the hood and renders it through the
2161
+ * injected renderer. When `enabled` is `false`, it returns the text verbatim.
2162
+ * - `style` exposes the accumulated style DATA (the empty style on a base styler), and
2163
+ * `enabled` reflects the switch — both inspectable and testable.
2164
+ * - A later color of the same channel wins (`styler.red.blue` is blue); a repeated
2165
+ * attribute is idempotent (`styler.bold.bold` carries one `bold`).
2166
+ */
2167
+ export declare interface StylerInterface {
2168
+ /** Render the accumulated style around `text` (verbatim when `enabled` is `false`). */
2169
+ (text: string): string;
2170
+ /** The accumulated style DATA — the empty style on a base styler. */
2171
+ readonly style: Style;
2172
+ /** Whether styling is applied; when `false`, calls return text unchanged. */
2173
+ readonly enabled: boolean;
2174
+ readonly black: StylerInterface;
2175
+ readonly red: StylerInterface;
2176
+ readonly green: StylerInterface;
2177
+ readonly yellow: StylerInterface;
2178
+ readonly blue: StylerInterface;
2179
+ readonly magenta: StylerInterface;
2180
+ readonly cyan: StylerInterface;
2181
+ readonly white: StylerInterface;
2182
+ readonly brightBlack: StylerInterface;
2183
+ readonly brightRed: StylerInterface;
2184
+ readonly brightGreen: StylerInterface;
2185
+ readonly brightYellow: StylerInterface;
2186
+ readonly brightBlue: StylerInterface;
2187
+ readonly brightMagenta: StylerInterface;
2188
+ readonly brightCyan: StylerInterface;
2189
+ readonly brightWhite: StylerInterface;
2190
+ readonly bold: StylerInterface;
2191
+ readonly dim: StylerInterface;
2192
+ readonly italic: StylerInterface;
2193
+ readonly underline: StylerInterface;
2194
+ readonly inverse: StylerInterface;
2195
+ readonly strikethrough: StylerInterface;
2196
+ }
2197
+
2198
+ /**
2199
+ * Options for {@link createStyler}.
2200
+ *
2201
+ * @remarks
2202
+ * - `renderer` — the {@link RendererInterface} every style renders through; defaults to
2203
+ * the ANSI renderer (the cross-environment default), so the styler works unchanged in
2204
+ * any terminal. Inject a browser `%c` renderer (C-f) to retarget with no other change.
2205
+ * - `enabled` — the no-color switch. When `false`, the styler returns text VERBATIM
2206
+ * (for a non-TTY, a `NO_COLOR` environment, or piped output); defaults to `true`.
2207
+ */
2208
+ export declare interface StylerOptions {
2209
+ readonly renderer?: RendererInterface;
2210
+ readonly enabled?: boolean;
2211
+ }
2212
+
2213
+ /**
2214
+ * Options for {@link import('./helpers.js').renderTable} — a bordered grid of columns + rows
2215
+ * with per-column alignment and width-aware sizing.
2216
+ *
2217
+ * @remarks
2218
+ * - `columns` — the {@link ColumnSpec}s, left to right; their `label`s form the header row.
2219
+ * - `rows` — the body, one `readonly string[]` per row. A short row is padded with empty
2220
+ * cells, an over-long row is truncated to the column count, so a ragged input never throws.
2221
+ * - `border` — the {@link BorderStyle} the frame + header rule + column separators draw in;
2222
+ * defaults to {@link DEFAULT_BORDER} (`single`).
2223
+ * - `styler` — colors the border + header labels when supplied; the cells are written as
2224
+ * given (already-styled cells are honored — their VISIBLE width drives column sizing, never
2225
+ * their raw `.length`).
2226
+ */
2227
+ export declare interface TableOptions {
2228
+ readonly columns: readonly ColumnSpec[];
2229
+ readonly rows: readonly (readonly string[])[];
2230
+ readonly border?: BorderStyle;
2231
+ readonly styler?: StylerInterface;
2232
+ }
2233
+
2234
+ /**
2235
+ * The tree connectors {@link import('./helpers.js').renderTree} draws — the `├─` branch (a
2236
+ * non-last child), the `└─` corner (the last child), the `│ ` guide (carried down through an
2237
+ * earlier branch's descendants), and the ` ` gap (under a last branch). Frozen.
2238
+ */
2239
+ export declare const TREE_CHARS: Readonly<{
2240
+ branch: "├─ ";
2241
+ corner: "└─ ";
2242
+ guide: "│ ";
2243
+ gap: " ";
2244
+ }>;
2245
+
2246
+ /**
2247
+ * One node of a {@link TreeOptions} tree — a label plus optional children, recursively.
2248
+ *
2249
+ * @remarks
2250
+ * - `label` — the node's text (a single visible line; it may already be styled).
2251
+ * - `children` — the node's sub-nodes, rendered indented beneath it with `├─` / `└─`
2252
+ * connectors and `│` guides; omitted (or empty) ⇒ a leaf.
2253
+ */
2254
+ export declare interface TreeNode {
2255
+ readonly label: string;
2256
+ readonly children?: readonly TreeNode[];
2257
+ }
2258
+
2259
+ /**
2260
+ * Options for {@link import('./helpers.js').renderTree} — a nested {@link TreeNode} tree drawn
2261
+ * with box-drawing connectors.
2262
+ *
2263
+ * @remarks
2264
+ * - `root` — the top {@link TreeNode}; its `label` is the unindented first line and its
2265
+ * `children` descend beneath it (`├─` for each but the last, `└─` for the last, `│` guides
2266
+ * carried down through earlier branches).
2267
+ * - `styler` — colors the connectors when supplied; node labels are written as given.
2268
+ */
2269
+ export declare interface TreeOptions {
2270
+ readonly root: TreeNode;
2271
+ readonly styler?: StylerInterface;
2272
+ }
2273
+
2274
+ /**
2275
+ * The visible width of `text` — its length after ANSI escapes are stripped, counted in
2276
+ * Unicode code points (so an astral character such as an emoji counts as one, not the
2277
+ * two UTF-16 units `String.length` would report).
2278
+ *
2279
+ * @remarks
2280
+ * The basis for terminal layout (box / table / progress alignment): the column count a
2281
+ * styled string occupies, independent of its escape codes. It does NOT account for
2282
+ * wide (CJK / fullwidth) glyphs occupying two cells — a deliberate, documented
2283
+ * simplification at this layer; callers needing east-asian width handle it above.
2284
+ *
2285
+ * @param text - Any string, styled or plain
2286
+ * @returns The count of visible code points
2287
+ *
2288
+ * @example
2289
+ * ```ts
2290
+ * width('\x1b[1mhi\x1b[0m') // 2
2291
+ * ```
2292
+ */
2293
+ export declare function width(text: string): number;
2294
+
2295
+ export declare function withCapture<T>(fn: () => Promise<T>, options?: CaptureOptions): Promise<CaptureResult<T>>;
2296
+
2297
+ export declare function withCapture<T>(fn: () => T, options?: CaptureOptions): CaptureResult<T>;
2298
+
2299
+ export { }