@orkestrel/console 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # @orkestrel/console
2
2
 
3
- A unified output-control system for the `@orkestrel` line one
4
- environment-agnostic engine composing five concerns over a shared substrate:
5
- a **style engine** (`Styler` + `ANSIRenderer`, style as data), **structured
6
- logging** (`Logger`, `LoggerManager`), **narrative reporting** (`Reporter`),
7
- **console & stream capture** (`Capture`, `ProcessCapture`), and **live
8
- animations** (`Spinner`, `Progress`). Built to sit beside `@orkestrel/emitter`
9
- (observable lifecycle), reusing it as it takes shape.
3
+ > A unified output-control system for a terminal, a browser, and a server: a style engine over
4
+ > frozen `Style` data, structured logging whose record and `entry` event are the transport seam,
5
+ > narrative reporting, console and stream capture, and live animations — one engine, environment
6
+ > sinks, with the platform backend swapped at the `Sink` seam.
7
+
8
+ Install the package, build a `Logger` or a `Reporter`, and swap its `sink` to move the same code
9
+ between a terminal, a browser, and a server. Part of the `@orkestrel` line, built to sit beside
10
+ `@orkestrel/emitter` (observable lifecycle), reusing it as it takes shape.
10
11
 
11
12
  ## Install
12
13
 
@@ -25,27 +26,27 @@ npm install @orkestrel/console
25
26
  The same code retargets to any environment by swapping the `sink`:
26
27
 
27
28
  ```ts
28
- import { createLogger, createReporter, createSpinner } from '@src/core'
29
+ import { Logger, Reporter, Spinner } from '@orkestrel/console'
29
30
 
30
- const logger = createLogger({ name: 'http', level: 'info' }) // ANSI to the console by default
31
+ const logger = new Logger({ name: 'http', level: 'info' }) // ANSI to the console by default
31
32
  logger.info('request', { method: 'GET', path: '/' }) // a styled, leveled line + an `entry` event
32
33
  logger.emitter.on('entry', (record) => archive(record)) // the transport seam — file / JSON / remote
33
34
 
34
- const reporter = createReporter()
35
+ const reporter = new Reporter()
35
36
  reporter.section('Build')
36
37
  reporter.step('bundling', { index: 2, total: 5 }) // [2/5] bundling
37
38
  reporter.status('success', 'built in 1.2s') // ✔ built in 1.2s
38
39
 
39
- const spinner = createSpinner({ message: 'deploying' })
40
+ const spinner = new Spinner({ message: 'deploying' })
40
41
  spinner.start() // a self-driving glyph cycle, `\r`-redrawn by an overwrite-capable sink
41
- spinner.success('deployed') // ✔ deployed — the timer cleared, the line committed
42
+ spinner.succeed('deployed') // ✔ deployed — the timer cleared, the line committed
42
43
  ```
43
44
 
44
45
  Style is data — a `Style` is a frozen record rendered through a swappable
45
46
  `RendererInterface` (`ANSIRenderer` by default):
46
47
 
47
48
  ```ts
48
- import { createStyler } from '@src/core'
49
+ import { createStyler } from '@orkestrel/console'
49
50
 
50
51
  const styler = createStyler()
51
52
  console.log(styler.red.bold('hi')) // renders through the injected renderer
@@ -54,9 +55,9 @@ console.log(styler.red.bold('hi')) // renders through the injected renderer
54
55
  Take control of `console.*` on the read side with `Capture`:
55
56
 
56
57
  ```ts
57
- import { createCapture } from '@src/core'
58
+ import { Capture } from '@orkestrel/console'
58
59
 
59
- const capture = createCapture({ mirror: true })
60
+ const capture = new Capture({ mirror: true })
60
61
  capture.start()
61
62
  console.log('hello')
62
63
  capture.messages() // [{ level: 'log', text: 'hello', time: ... }]
@@ -64,24 +65,25 @@ capture.stop()
64
65
  ```
65
66
 
66
67
  On the server, `ProcessCapture` takes over the whole `process` output surface
67
- (direct `process.stdout`/`stderr` writes, not just `console.*`):
68
+ (direct `process.stdout`/`stderr` writes, not `console.*`):
68
69
 
69
70
  ```ts
70
- import { createProcessCapture } from '@src/server'
71
+ import { ProcessCapture } from '@orkestrel/console/server'
71
72
 
72
- const capture = createProcessCapture({ levels: ['stderr'], mirror: true })
73
+ const capture = new ProcessCapture({ levels: ['stderr'], mirror: true })
73
74
  capture.start()
74
75
  ```
75
76
 
76
77
  ## Guide
77
78
 
78
- See [guides/src/console.md](./guides/src/console.md) for the full documented
79
+ See [guides/console.md](./guides/console.md) for the full documented
79
80
  surface — styling, logging, reporting, capture, and animations.
80
81
 
81
82
  ## Package
82
83
 
83
- Published as three environment-scoped entry points per the `exports` field
84
- in `package.json`: `.` (the shared, environment-agnostic core engine, the
84
+ Published as the `.`, `./server`, and `./browser` environment-scoped entry
85
+ points per the `exports` field in `package.json`: `.` (the shared,
86
+ environment-agnostic core engine, the
85
87
  default ANSI renderer, and the console sink), `./server` (adds the server
86
88
  sink and `ProcessCapture`), and `./browser` (adds the browser sink
87
89
  translating ANSI to `console.log('%c…', css)`). Core and `./server` ship
@@ -1,24 +1,27 @@
1
- import { Attribute } from '@orkestrel/console';
2
- import { Color } from '@orkestrel/console';
3
- import { SinkInterface } from '@orkestrel/console';
1
+ import type { Attribute } from '@orkestrel/console';
2
+ import type { Color } from '@orkestrel/console';
3
+ import type { SinkInterface } from '@orkestrel/console';
4
4
 
5
5
  /**
6
- * Translate an ANSI-styled string into a browser `console.log`-ready {@link ConsoleOutput} — a
7
- * `%c`-segmented format string and the parallel array of CSS declarations, so a DevTools console
8
- * renders the SAME styling a terminal would (the C-f sink calls `console[method](format, ...styles)`).
6
+ * Translates an ANSI-styled string into a browser `console.log`-ready {@link ConsoleOutput} — a
7
+ * `%c`-segmented format string and the parallel array of CSS declarations, an optional partial
8
+ * {@link BrowserPalette} overriding the CSS per named lookup.
9
9
  *
10
10
  * @remarks
11
+ * A DevTools console then renders the same styling a terminal would; the browser sink calls
12
+ * `console[method](format, ...styles)`.
13
+ *
11
14
  * - **SGR runs → `%c` segments.** The text is scanned for SGR sequences ({@link SGR_PATTERN} —
12
- * `ESC[…m`); each delimits a run. A run carrying VISIBLE text emits one `%c` directive plus that
15
+ * `ESC[…m`); each delimits a run. A run carrying visible text emits one `%c` directive plus that
13
16
  * text into `format` and the run's accumulated CSS into `styles`, so the browser switches style at
14
17
  * each `%c`. Foreground / background / attribute codes accumulate; the reset code (`0`, or a bare
15
- * `ESC[m`) clears the accumulated style back to none. A later color of the same channel REPLACES
18
+ * `ESC[m`) clears the accumulated style back to none. A later color of the same channel replaces
16
19
  * the earlier one; an attribute is added once. Non-SGR escapes (cursor / erase / OSC) are not style
17
20
  * and are left in the text verbatim.
18
- * - **`%`-safe.** Every LITERAL `%` in the text is doubled to `%%` so the console never treats it as
21
+ * - **`%`-safe.** Every literal `%` in the text is doubled to `%%` so the console never treats it as
19
22
  * a directive — only the `%c`s this function inserts are real directives. So `format`'s real `%c`
20
23
  * count always equals `styles.length`, and `console.log(format, ...styles)` lines up exactly.
21
- * - **Plain text short-circuits.** A string with NO SGR sequence yields `{ format: <escaped text>,
24
+ * - **Plain text short-circuits.** A string with no SGR sequence yields `{ format: <escaped text>,
22
25
  * styles: [] }` — no `%c`, no styles (the text is still `%`-escaped).
23
26
  * - **Partial palette.** A supplied palette overrides only its named colors and attributes. Every
24
27
  * omitted entry resolves through {@link COLOR_HEX} or {@link ATTRIBUTE_CSS}, so defaults and
@@ -40,14 +43,15 @@ import { SinkInterface } from '@orkestrel/console';
40
43
  export declare function ansiToConsole(text: string, palette?: BrowserPalette): ConsoleOutput;
41
44
 
42
45
  /**
43
- * Each text-{@link Attribute}'s SGR "on" number its equivalent CSS declaration — the browser
44
- * counterpart to the terminal's SGR text effects (`bold` 1 → `font-weight:bold`, `dim` 2
45
- * `opacity:0.6`, `italic` 3 → `font-style:italic`, `underline` 4 → `text-decoration:underline`,
46
- * `inverse` 7 → best-effort, `strikethrough` 9 → `text-decoration:line-through`). Keyed by the SGR
47
- * NUMBER (derived from core's {@link ATTRIBUTE_CODES}) so the sink looks a parameter up directly
48
- * while scanning a run.
46
+ * Maps each text-{@link Attribute}'s SGR "on" number to its equivalent CSS declaration — the
47
+ * browser counterpart to the terminal's SGR text effects (`bold` 1 → `font-weight:bold`, `dim` 2
48
+ * `opacity:0.6`, `italic` 3 → `font-style:italic`, `underline` 4 → `text-decoration:underline`,
49
+ * `inverse` 7 → best-effort, `strikethrough` 9 → `text-decoration:line-through`).
49
50
  *
50
51
  * @remarks
52
+ * Keyed by the SGR number (derived from core's {@link ATTRIBUTE_CODES}) so the sink looks a
53
+ * parameter up directly while scanning a run.
54
+ *
51
55
  * `inverse` (SGR 7) has no faithful single-declaration CSS equivalent (it swaps the fore/back inks,
52
56
  * which depends on the live colors); it maps to a best-effort `filter:invert(100%)` — documented as
53
57
  * approximate, never silently dropped. Deeply frozen.
@@ -55,8 +59,8 @@ export declare function ansiToConsole(text: string, palette?: BrowserPalette): C
55
59
  export declare const ATTRIBUTE_CSS: Readonly<Record<number, string>>;
56
60
 
57
61
  /**
58
- * Partial browser CSS overrides for the core color and attribute axes. Omitted entries retain the
59
- * built-in browser mappings, so one override changes only its named value.
62
+ * Holds partial browser CSS overrides for the core color and attribute axes a named `color` or
63
+ * `attribute` entry replaces only that entry, and every omission keeps its default.
60
64
  *
61
65
  * @remarks
62
66
  * - `color` maps a named non-default {@link Color} to the CSS color value used for both foreground
@@ -69,18 +73,16 @@ export declare interface BrowserPalette {
69
73
  }
70
74
 
71
75
  /**
72
- * Options for {@link import('./factories.js').createBrowserSink}.
73
- *
74
- * @remarks
75
- * `palette` partially overrides the browser's default color and attribute CSS mappings.
76
+ * Configures {@link import('./factories.js').createBrowserSink} — the optional `palette` partially
77
+ * overriding the browser's named color and attribute CSS mappings.
76
78
  */
77
79
  export declare interface BrowserSinkOptions {
78
80
  readonly palette?: BrowserPalette;
79
81
  }
80
82
 
81
83
  /**
82
- * Each named {@link Color}'s hex value — the 16 standard terminal colors a browser DevTools
83
- * console renders the SAME {@link Color} names as. The source of truth for the BROWSER color
84
+ * Maps each named {@link Color} to its hex value — the 16 standard terminal colors a browser DevTools
85
+ * console renders the same {@link Color} names as. The source of truth for the browser color
84
86
  * axis: the ANSI renderer maps a `Color` name to an SGR number, and this maps the same name to
85
87
  * the CSS color the `%c` sink paints with, so a browser shows the same 16 colors a terminal does.
86
88
  *
@@ -91,20 +93,20 @@ export declare interface BrowserSinkOptions {
91
93
  export declare const COLOR_HEX: Readonly<Record<Exclude<Color, 'default'>, string>>;
92
94
 
93
95
  /**
94
- * The `console.log`-ready output {@link import('./helpers.js').ansiToConsole} produces from
95
- * an ANSI-styled string — a format string of `%c`-prefixed segments and the parallel array
96
- * of CSS declarations, ready to spread into a browser `console` call as
97
- * `console.log(format, ...styles)`.
96
+ * Represents the `console.log`-ready output {@link import('./helpers.js').ansiToConsole} produces
97
+ * from an ANSI-styled string — a format string of `%c`-prefixed segments and the parallel array of
98
+ * CSS declarations, ready to spread into a browser `console` call as `console.log(format,
99
+ * ...styles)`.
98
100
  *
99
101
  * @remarks
100
102
  * - `format` — the text with each styled run prefixed by one `%c` directive (the directive
101
- * the browser console consumes to switch the active style) and every LITERAL `%` doubled to
103
+ * the browser console consumes to switch the active style) and every literal `%` doubled to
102
104
  * `%%` so it is not mistaken for a directive. A plain (no-ANSI) input yields the text
103
- * verbatim with NO `%c` and an empty `styles` (still `%`-escaped).
105
+ * verbatim with no `%c` and an empty `styles` (still `%`-escaped).
104
106
  * - `styles` — one CSS declaration string per `%c` in `format`, in order: the browser applies
105
107
  * `styles[n]` from the n-th `%c` onward. Each entry is the accumulated style for that run
106
- * (an SGR reset clears it back to `''`). `format`'s `%c` count always equals `styles.length`,
107
- * so the spread `console.log(format, ...styles)` lines up exactly.
108
+ * (an SGR reset clears it back to the empty declaration string). `format`'s `%c` count always
109
+ * equals `styles.length`, so the spread `console.log(format, ...styles)` lines up exactly.
108
110
  */
109
111
  export declare interface ConsoleOutput {
110
112
  readonly format: string;
@@ -112,53 +114,56 @@ export declare interface ConsoleOutput {
112
114
  }
113
115
 
114
116
  /**
115
- * Create the browser `%c` {@link SinkInterface} — the C-f browser output backend. `write(text, level?)`
117
+ * Creates the browser `%c` {@link SinkInterface} — the browser output backend. `write(text, level?)`
116
118
  * translates the ANSI-styled `text` into a browser `console` call (`console[method](format, ...styles)`)
117
- * via {@link ansiToConsole}, so a DevTools console renders the SAME styling a terminal does. Drop it in
118
- * as a logger / reporter / spinner sink (`createLogger({ sink: createBrowserSink() })`) to retarget the
119
- * core output to the browser console with no change to the core.
119
+ * through {@link ansiToConsole}, an optional partial {@link BrowserPalette} overriding the named
120
+ * color and attribute CSS.
120
121
  *
121
122
  * @param options - See {@link BrowserSinkOptions}
122
123
  * @returns A browser `%c` {@link SinkInterface}
123
124
  *
124
125
  * @remarks
126
+ * Drop it in as a logger / reporter / spinner sink (`new Logger({ sink: createBrowserSink() })`)
127
+ * to retarget the core output to the browser console with no change to the core.
128
+ *
125
129
  * - **ANSI → `%c` at the sink.** The core produces ANSI strings; this sink parses the SGR runs and
126
130
  * re-emits them as a `console.log`-ready `%c` format string + parallel CSS array ({@link ansiToConsole}
127
131
  * — pure, total, and `%`-safe), so the styling survives the trip to a console that can't render ANSI.
128
132
  * `options.palette` supplies partial named color and attribute overrides to that translation.
129
133
  * - **Routes by level.** `error` → `console.error`, `warn` → `console.warn`, every other level (and an
130
- * omitted level) → `console.log` — the SAME routing as core's `createConsoleSink`, so a logger's level
131
- * reaches the matching DevTools stream.
134
+ * omitted level) → `console.log` — the same routing as core's `createConsoleSink`, so a logger's level
135
+ * reaches the matching DevTools stream. Both call the one
136
+ * {@link import('@orkestrel/console').selectWriter} leaf, which is what keeps them identical.
132
137
  * - **Animation degrade (locked).** A browser console cannot overwrite a line, so a `text` beginning with
133
- * a carriage return `\r` (a spinner / progress redraw) has the leading `\r` STRIPPED and is written as a
134
- * fresh, non-overwriting line — the locked browser degrade. Only a LEADING `\r` is stripped; an interior
138
+ * a carriage return `\r` (a spinner / progress redraw) has the leading `\r` stripped and is written as a
139
+ * fresh, non-overwriting line — the locked browser degrade. Only a leading `\r` is stripped; an interior
135
140
  * one is left to the console.
136
- * - **Snapshotted — no capture loop.** It captures `console.log` / `console.warn` / `console.error` AT
137
- * CREATION and writes through those references, so a later `Capture` that PATCHES `console.*` can never
138
- * feed this sink's output back into itself (the no-capture-loop principle, AGENTS / the core sink's
139
- * precedent). Create the sink (or the logger) BEFORE installing a capture.
141
+ * - **Snapshotted — no capture loop.** It captures `console.log` / `console.warn` / `console.error` at
142
+ * creation and writes through those references, so a later `Capture` that patches `console.*` can never
143
+ * feed this sink's output back into itself (the no-capture-loop principle, following the core
144
+ * sink's precedent). Create the sink (or the logger) before installing a capture.
140
145
  *
141
146
  * @example
142
147
  * ```ts
143
- * import { createLogger } from '@src/core'
144
- * import { createBrowserSink } from '@src/browser'
148
+ * import { Logger } from '@orkestrel/console'
149
+ * import { createBrowserSink } from '@orkestrel/console/browser'
145
150
  *
146
- * const logger = createLogger({ name: 'app', sink: createBrowserSink() })
151
+ * const logger = new Logger({ name: 'app', sink: createBrowserSink() })
147
152
  * logger.error('boom') // → console.error('%c…', 'color:#cd0000;…') in DevTools
148
153
  * ```
149
154
  */
150
155
  export declare function createBrowserSink(options?: BrowserSinkOptions): SinkInterface;
151
156
 
152
157
  /**
153
- * The browser console directive that switches the active style — one `%c` prefixes every styled run
154
- * in the {@link import('./types.js').ConsoleOutput} format string, consuming the next entry of the
155
- * parallel CSS array. The single source of truth for the directive token.
158
+ * Names the browser console directive that switches the active style — one `%c` prefixes every
159
+ * styled run in the {@link import('./types.js').ConsoleOutput} format string, consuming the next
160
+ * entry of the parallel CSS array. The single source of truth for the directive token.
156
161
  */
157
162
  export declare const DIRECTIVE = "%c";
158
163
 
159
164
  /**
160
- * Double every literal `%` in `text` to `%%` — the `%`-escape that keeps a browser console from
161
- * reading a stray `%` (e.g. in `50%` or `%s`) as a format directive. The single escape the
165
+ * Doubles every literal `%` in `text` to `%%` — the `%`-escape that keeps a browser console from
166
+ * reading a stray `%` (for example in `50%` or `%s`) as a format directive. The single escape the
162
167
  * {@link ansiToConsole} translation applies to every text segment before assembling the format
163
168
  * string (so only the `%c`s it inserts are real directives).
164
169
  *
@@ -173,56 +178,62 @@ export declare const DIRECTIVE = "%c";
173
178
  export declare function escapePercent(text: string): string;
174
179
 
175
180
  /**
176
- * Parse an SGR parameter list (the `;`-separated numeric string captured by {@link SGR_PATTERN})
177
- * into its numeric codes — `'1;31'` → `[1, 31]`. An EMPTY list (a bare `ESC[m`) yields `[0]`, since
178
- * the SGR spec treats a parameterless sequence as a reset; an empty field within a list (`'1;;4'`)
179
- * likewise counts as a `0` reset, matching the spec.
181
+ * Walks an SGR parameter list (the `;`-separated numeric string captured by {@link SGR_PATTERN})
182
+ * and returns its numeric codes — `'1;31'` → `[1, 31]`, a bare or empty field becoming a `0`
183
+ * reset. It is total: every input yields a code list.
180
184
  *
181
185
  * @param parameters - The raw `;`-separated parameter string (the regex capture)
182
- * @returns The parsed SGR codes (a parameterless / empty field becoming `0`)
186
+ * @returns The SGR codes found (a parameterless / empty field becoming `0`)
187
+ *
188
+ * @remarks
189
+ * An empty list (a bare `ESC[m`) yields `[0]`, because the SGR spec treats a parameterless
190
+ * sequence as a reset; an empty field within a list (`'1;;4'`) likewise counts as a `0` reset,
191
+ * matching the spec, and a non-numeric field yields `NaN`, which the caller then ignores.
183
192
  *
184
193
  * @example
185
194
  * ```ts
186
- * parseParameters('1;31') // [1, 31]
187
- * parseParameters('') // [0]
195
+ * scanParameters('1;31') // [1, 31]
196
+ * scanParameters('') // [0]
188
197
  * ```
189
198
  */
190
- export declare function parseParameters(parameters: string): readonly number[];
199
+ export declare function scanParameters(parameters: string): readonly number[];
191
200
 
192
201
  /**
193
- * Matches one SGR sequence (`ESC[ <params> m`) and CAPTURES its `;`-separated numeric parameters —
194
- * the subset of ANSI {@link import('@src/core').strip} cares about that carries STYLE (color /
202
+ * Matches one SGR sequence (`ESC[ <params> m`) and captures its `;`-separated numeric parameters —
203
+ * the subset of ANSI {@link import('@orkestrel/console').strip} cares about that carries style (color /
195
204
  * attribute / reset), as opposed to cursor / erase / OSC sequences. Global, so the scanner walks
196
205
  * every SGR run in a string; built from core's {@link ESC} so no control-character literal appears
197
206
  * in source (the codebase idiom). The capture group is the parameter list (`''` for a bare `ESC[m`,
198
207
  * which the spec treats as a reset).
199
208
  *
200
209
  * @remarks
201
- * A global `RegExp` carries a mutable `lastIndex`; a scan builds a FRESH `RegExp` from this one's
210
+ * A global `RegExp` carries a mutable `lastIndex`; a scan builds a fresh `RegExp` from this one's
202
211
  * `source` + `flags` rather than reuse this instance, so concurrent scans never collide. This is the
203
212
  * canonical definition, not a shared scanner.
204
213
  */
205
214
  export declare const SGR_PATTERN: RegExp;
206
215
 
207
216
  /**
208
- * The immutable accumulator {@link import('./helpers.js').ansiToConsole} carries across a run while
209
- * translating SGR codes to CSS — a single `foreground` and `background` declaration (each channel
210
- * REPLACEABLE by a later color of the same channel) plus an ordered, de-duplicated list of attribute
211
- * declarations. An SGR reset empties all three; {@link import('./helpers.js').ansiToConsole} folds
212
- * it into the `;`-joined CSS string a run emits.
217
+ * Represents the immutable scan state {@link import('./helpers.js').ansiToConsole} replaces while
218
+ * translating SGR codes to CSS — an optional `foreground` and `background` declaration plus a
219
+ * readonly list of attribute declarations.
213
220
  *
214
221
  * @remarks
222
+ * A later color of the same channel replaces that channel; an SGR reset drops both channels and
223
+ * empties the list; {@link import('./helpers.js').ansiToConsole} folds the state into the
224
+ * `;`-joined CSS string a run emits.
225
+ *
215
226
  * Each SGR sequence produces a new frozen value; earlier run snapshots never drift when a later
216
- * sequence changes a channel. A channel holds the FULL CSS declaration (`'color:#cd0000'`, not a
217
- * bare hex), or `''` when unset.
218
- * - `foreground` — the current `color:<hex>` declaration, or `''` (unset / post-reset).
219
- * - `background` — the current `background:<hex>` declaration, or `''`.
227
+ * sequence changes a channel. A channel holds the full CSS declaration (`'color:#cd0000'`, not a
228
+ * bare hex), and is absent when unset.
229
+ * - `foreground` — the current `color:<hex>` declaration; absent when unset or after a reset.
230
+ * - `background` — the current `background:<hex>` declaration; absent on the same terms.
220
231
  * - `attributes` — the active text-effect declarations in insertion order (`'font-weight:bold'`, …),
221
232
  * each present at most once.
222
233
  */
223
234
  export declare interface StyleAccumulator {
224
- readonly foreground: string;
225
- readonly background: string;
235
+ readonly foreground?: string;
236
+ readonly background?: string;
226
237
  readonly attributes: readonly string[];
227
238
  }
228
239