@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.
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/dist/src/browser/index.d.ts +214 -0
- package/dist/src/browser/index.js +275 -0
- package/dist/src/browser/index.js.map +1 -0
- package/dist/src/core/index.cjs +2183 -0
- package/dist/src/core/index.cjs.map +1 -0
- package/dist/src/core/index.d.cts +2299 -0
- package/dist/src/core/index.d.ts +2299 -0
- package/dist/src/core/index.js +2105 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/server/index.cjs +375 -0
- package/dist/src/server/index.cjs.map +1 -0
- package/dist/src/server/index.d.cts +443 -0
- package/dist/src/server/index.d.ts +443 -0
- package/dist/src/server/index.js +363 -0
- package/dist/src/server/index.js.map +1 -0
- package/package.json +106 -0
|
@@ -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 { }
|