@orkestrel/console 0.0.12 → 0.0.14

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.
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let _orkestrel_emitter = require("@orkestrel/emitter");
3
2
  let _orkestrel_contract = require("@orkestrel/contract");
3
+ let _orkestrel_emitter = require("@orkestrel/emitter");
4
4
  //#region src/core/constants.ts
5
5
  /**
6
6
  * Maps each {@link Color} to its SGR foreground parameter — the 8 base colors at 30–37 and their
@@ -103,11 +103,11 @@ var ATTRIBUTES = Object.freeze([
103
103
  /** Holds the SGR RESET parameter (0) — terminates a styled run, clearing all colors and attributes. */
104
104
  var RESET_CODE = 0;
105
105
  /**
106
- * Holds the ESC control character (`U+001B`) that begins every ANSI escape sequence. Built
106
+ * Holds the escape control character (`U+001B`) that begins every ANSI escape sequence. Built
107
107
  * with `String.fromCharCode` so no raw control character appears in source.
108
108
  */
109
109
  var ESC = String.fromCharCode(27);
110
- /** Holds the BEL control character (`U+0007`) that can terminate an OSC sequence. */
110
+ /** Holds the bell control character (`U+0007`) that can terminate an OSC sequence. */
111
111
  var BEL = String.fromCharCode(7);
112
112
  /** Holds the Control Sequence Introducer (`ESC[`) that opens every SGR sequence. */
113
113
  var CSI = `[`;
@@ -172,9 +172,12 @@ var LEVEL_COLORS = Object.freeze({
172
172
  error: "red"
173
173
  });
174
174
  /**
175
- * Sets the default bounded-retention cap for a {@link import('./types.js').LoggerInterface} — at
176
- * most this many recent records are kept (oldest dropped first). Retention is always bounded — the
177
- * oldest record is dropped after the cap is reached; a consumer overrides it through `options.limit`.
175
+ * Sets the default bounded-retention cap for a {@link import('./types.js').LoggerInterface} —
176
+ * `1000`, so at most that many recent records are kept and retention is always bounded.
177
+ *
178
+ * @remarks
179
+ * The oldest record is dropped after the cap is reached; a consumer overrides the cap through
180
+ * `options.limit`.
178
181
  */
179
182
  var DEFAULT_LOG_LIMIT = 1e3;
180
183
  /** Sets the default {@link LogLevel} threshold a logger gates at when none is supplied — `info`. */
@@ -192,9 +195,12 @@ var LOG_LEVELS = Object.freeze([
192
195
  ]);
193
196
  /**
194
197
  * Holds the complete {@link BorderChars} junction set for each {@link BorderStyle} — the standard
195
- * Unicode box-drawing glyphs at the four line weights. The renderers ({@link
196
- * import('./helpers.js').renderBox} / {@link import('./helpers.js').renderTable}) look the
197
- * style up here, so no glyph literal lives in a renderer. Deeply frozen.
198
+ * Unicode box-drawing glyphs at each line weight, deeply frozen.
199
+ *
200
+ * @remarks
201
+ * The renderers ({@link import('./helpers.js').renderBox} /
202
+ * {@link import('./helpers.js').renderTable}) look the style up here, so no glyph literal lives in
203
+ * a renderer.
198
204
  *
199
205
  * @remarks
200
206
  * `round` shares `single`'s edges and tees — only its corners differ (the rounded `╭╮╰╯`).
@@ -291,7 +297,7 @@ var STATUS_LEVELS = Object.freeze([
291
297
  * Sets the default visible column width for the width-aware renderers — the separator rule and a
292
298
  * {@link import('./helpers.js').renderBox} with no explicit `width`, and the reporter's
293
299
  * `section` rule. A sane terminal default (80 columns); a caller overrides it per-call or through
294
- * {@link import('./types.js').ReporterOptions}`.width`.
300
+ * {@link import('./types.js').ReporterOptions.width}.
295
301
  */
296
302
  var DEFAULT_WIDTH = 80;
297
303
  /** Sets the default horizontal padding inside a box's edges ({@link import('./helpers.js').renderBox}) — one cell. */
@@ -308,9 +314,9 @@ var SEPARATOR_FILL = "─";
308
314
  */
309
315
  var SEPARATOR_TITLE_GAP = " ";
310
316
  /**
311
- * Sets the number of milliseconds at or above which {@link import('./helpers.js').formatDuration}
312
- * (and so `Reporter.timing`) switches from a `…ms` rendering to a `…s` (seconds, 2 d.p.)
313
- * rendering — exactly one second.
317
+ * Sets the millisecond threshold at or above which {@link import('./helpers.js').formatDuration}
318
+ * (and so `Reporter.timing`) switches from a `…ms` rendering to a `…s` (seconds, 2 d.p.) rendering
319
+ * — `1000`, exactly one second.
314
320
  */
315
321
  var SECOND_MS = 1e3;
316
322
  /**
@@ -327,17 +333,20 @@ var CAPTURE_LEVELS = Object.freeze([
327
333
  "debug"
328
334
  ]);
329
335
  /**
330
- * Sets the default bounded-buffer cap for a {@link import('./types.js').CaptureInterface} — at most this
331
- * many recent {@link CapturedMessage}s are retained per buffer (the total buffer and each by-level
332
- * bucket; oldest dropped first). Capture retention is always bounded so a long-running capture can
333
- * never grow without bound (the same retention precedent as {@link DEFAULT_LOG_LIMIT}); a consumer
334
- * overrides it through `options.limit`.
336
+ * Sets the default bounded-buffer cap for a {@link import('./types.js').CaptureInterface} — `1000`,
337
+ * so at most that many recent {@link CapturedMessage}s are retained per buffer (the total buffer
338
+ * and each by-level bucket; oldest dropped first) and retention is always bounded.
339
+ *
340
+ * @remarks
341
+ * A long-running capture can never grow without bound (the same retention precedent as
342
+ * {@link DEFAULT_LOG_LIMIT}); a consumer overrides the cap through `options.limit`.
335
343
  */
336
344
  var DEFAULT_CAPTURE_LIMIT = 1e3;
337
345
  /**
338
346
  * Maps each {@link CaptureLevel} to its {@link LogLevel} for the optional sink forward — the projection the
339
347
  * Capture routes through when writing an intercepted call to a {@link
340
- * import('./types.js').SinkInterface} (`sink.write(text, CAPTURE_LEVEL_MAP[level])`). `warn` /
348
+ * import('./types.js').SinkInterface}. `sink.write(text, CAPTURE_LEVEL_MAP[level])` is the call
349
+ * this map backs. `warn` /
341
350
  * `error` / `debug` / `info` map to their matching {@link LogLevel}; `log` maps to `info` (a plain
342
351
  * console log is informational — the default stream), so a stream-aware sink routes `warn` / `error`
343
352
  * captures to the right stream. The source of truth for the capture-to-log projection.
@@ -350,11 +359,14 @@ var CAPTURE_LEVEL_MAP = Object.freeze({
350
359
  debug: "debug"
351
360
  });
352
361
  /**
353
- * Holds the default spinner frame cycle a {@link import('./types.js').SpinnerInterface} advances through —
354
- * the ten braille-pattern glyphs (U+2800 block) that read as a smoothly rotating dot, the universal
355
- * terminal-spinner convention. Frozen; a consumer swaps the whole cycle through `options.frames`.
362
+ * Holds the default spinner frame cycle a {@link import('./types.js').SpinnerInterface} advances
363
+ * through — the braille-pattern glyphs (`⠋⠙⠹…`, the U+2800 block) that read as a smoothly rotating
364
+ * dot.
356
365
  *
357
366
  * @remarks
367
+ * The cycle is the universal terminal-spinner convention. Frozen; a consumer swaps the whole
368
+ * cycle through `options.frames`.
369
+ *
358
370
  * Braille glyphs are single visible cells, so every frame occupies one column — the spinner glyph
359
371
  * never shifts the message beside it as it advances. The source of truth for the default frame axis.
360
372
  */
@@ -371,35 +383,43 @@ var SPINNER_FRAMES = Object.freeze([
371
383
  "⠏"
372
384
  ]);
373
385
  /**
374
- * Sets the default timer period in milliseconds between a {@link import('./types.js').SpinnerInterface}'s
375
- * frames — the `setInterval` interval `start()` arms. Eighty milliseconds (≈12.5 frames/second) is
376
- * the conventional spinner cadence: fast enough to read as motion, slow enough not to thrash a
377
- * terminal. A consumer overrides it through `options.interval`.
386
+ * Sets the default timer period between a {@link import('./types.js').SpinnerInterface}'s frames —
387
+ * the `setInterval` interval `start()` arms, `80` ms (≈12.5 frames/second).
388
+ *
389
+ * @remarks
390
+ * That is the conventional spinner cadence: fast enough to read as motion, slow enough not to
391
+ * thrash a terminal. A consumer overrides it through `options.interval`.
378
392
  */
379
393
  var DEFAULT_SPINNER_INTERVAL = 80;
380
394
  /**
381
- * Holds the default filled-cell glyph {@link import('./helpers.js').renderBar} draws the completed run of a
382
- * progress bar with — the full block `█` (U+2588). A single visible cell; a consumer overrides it
383
- * through {@link import('./types.js').BarOptions}`.fill`.
395
+ * Holds the default filled-cell glyph {@link import('./helpers.js').renderBar} draws the completed
396
+ * run of a progress bar with — the full block `█` (U+2588). A single visible cell; a consumer
397
+ * overrides it through {@link import('./types.js').BarOptions.fill}.
384
398
  */
385
399
  var BAR_FILL = "█";
386
400
  /**
387
- * Holds the default empty-cell glyph {@link import('./helpers.js').renderBar} draws the remaining run of a
388
- * progress bar with — the light-shade block `░` (U+2591). A single visible cell; a consumer overrides
389
- * it through {@link import('./types.js').BarOptions}`.empty`.
401
+ * Holds the default empty-cell glyph {@link import('./helpers.js').renderBar} draws the remaining
402
+ * run of a progress bar with — the light-shade block `░` (U+2591). A single visible cell; a
403
+ * consumer overrides it through {@link import('./types.js').BarOptions.empty}.
390
404
  */
391
405
  var BAR_EMPTY = "░";
392
406
  /**
393
- * Sets the default visible cell count of a progress-bar track — the glyph run {@link
394
- * import('./helpers.js').renderBar} fills (and a {@link import('./types.js').ProgressInterface} sizes
395
- * its bar to). Thirty cells is a compact, terminal-friendly default; a consumer overrides it through
396
- * `options.width`. Distinct from {@link DEFAULT_WIDTH} (the renderers' 80-column line width) — a bar
397
- * track is one inline element, not a full-width rule.
407
+ * Sets the default visible cell count of a progress-bar track — the glyph run
408
+ * {@link import('./helpers.js').renderBar} fills, and the width a
409
+ * {@link import('./types.js').ProgressInterface} sizes its bar to. `30` cells.
410
+ *
411
+ * @remarks
412
+ * Thirty cells is a compact, terminal-friendly default; a consumer overrides it through
413
+ * `options.width`. It is distinct from {@link DEFAULT_WIDTH} (the renderers' 80-column line
414
+ * width) — a bar track is one inline element, not a full-width rule.
398
415
  */
399
416
  var DEFAULT_BAR_WIDTH = 30;
400
417
  /**
401
- * Holds the default {@link Theme} — every role bound to its default {@link Style}, deeply frozen.
402
- * The base {@link import('./factories.js').createTheme} merges over, and the theme every
418
+ * Holds the default {@link Theme} — every role bound to its default {@link Style}, assembled from
419
+ * {@link LEVEL_COLORS}, {@link STATUS_ICONS}, and {@link STATUS_COLORS} and deeply frozen.
420
+ *
421
+ * @remarks
422
+ * It is the base {@link import('./factories.js').createTheme} merges over, and the theme every
403
423
  * entity uses when none is supplied.
404
424
  *
405
425
  * @remarks
@@ -468,12 +488,12 @@ var DEFAULT_THEME = Object.freeze({
468
488
  //#endregion
469
489
  //#region src/core/errors.ts
470
490
  /**
471
- * Represents an error thrown by the console layer.
491
+ * Carries a {@link ConsoleErrorCode} and an optional `context` bag the error the console layer
492
+ * throws for an internal invariant violated at a defensive guard.
472
493
  *
473
494
  * @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`).
495
+ * `INVARIANT` is the code for a guard that is structurally unreachable, so a `catch` branches on
496
+ * `error.code` rather than parsing the message.
477
497
  */
478
498
  var ConsoleError = class extends Error {
479
499
  code;
@@ -486,7 +506,7 @@ var ConsoleError = class extends Error {
486
506
  }
487
507
  };
488
508
  /**
489
- * Narrows an unknown caught value to a {@link ConsoleError}.
509
+ * Narrows an unknown caught value to a {@link ConsoleError} — the guard a `catch` branches on.
490
510
  *
491
511
  * @param value - The value to test (typically a `catch` binding)
492
512
  * @returns True if `value` is a {@link ConsoleError}; false otherwise
@@ -501,7 +521,7 @@ var ConsoleError = class extends Error {
501
521
  * ```
502
522
  */
503
523
  function isConsoleError(value) {
504
- return value instanceof ConsoleError;
524
+ return (0, _orkestrel_contract.isInstance)(value, ConsoleError);
505
525
  }
506
526
  //#endregion
507
527
  //#region src/core/helpers.ts
@@ -527,7 +547,8 @@ function strip(text) {
527
547
  }
528
548
  /**
529
549
  * Removes every non-printing C0 control character from `text` except `\t` / `\n` / `\r`
530
- * (meaningful whitespace), plus DEL — returning the sanitized string.
550
+ * (meaningful whitespace), plus DEL — a separate pass from {@link strip}, so `width` stays
551
+ * untouched.
531
552
  *
532
553
  * @remarks
533
554
  * Deliberately separate from {@link strip} (ANSI-escape removal only, so `width` /
@@ -570,7 +591,8 @@ function width(text) {
570
591
  return [...strip(text)].length;
571
592
  }
572
593
  /**
573
- * Snapshots and deeply freezes one {@link Style} value.
594
+ * Snapshots and deeply freezes one {@link Style} value, including an independent frozen copy of
595
+ * its `attributes`.
574
596
  *
575
597
  * @param style - The caller-owned style to snapshot
576
598
  * @returns A frozen style record with an independently frozen attributes list
@@ -741,9 +763,9 @@ function formatDuration(ms) {
741
763
  return ms < 1e3 ? `${ms}ms` : `${(ms / SECOND_MS).toFixed(2)}s`;
742
764
  }
743
765
  /**
744
- * Colors `text` through `styler`, or returns it verbatim when `styler` is `undefined` the
745
- * single optional-styling primitive every renderer applies to its border / title / connector
746
- * glyphs (the one styler seam, shared, never re-hand-rolled per renderer).
766
+ * Colors `text` through `styler` and an optional by-value {@link Style}, or returns it verbatim
767
+ * when `styler` is `undefined` — the single optional-styling primitive every renderer applies to
768
+ * its border / title / connector glyphs.
747
769
  *
748
770
  * @remarks
749
771
  * The renderers all take an optional `styler`: present ⇒ glyphs are colored, absent ⇒ plain.
@@ -928,8 +950,8 @@ function renderTable(options) {
928
950
  ].join("\n");
929
951
  }
930
952
  /**
931
- * Renders a nested {@link TreeNode} tree with box-drawing connectors. Pure: same
932
- * {@link TreeOptions} → same string.
953
+ * Renders a nested {@link TreeNode} tree whose connectors derive from the chosen `border` set.
954
+ * Pure: same {@link TreeOptions} → same string.
933
955
  *
934
956
  * @remarks
935
957
  * The `root` label is the unindented first line; its descendants are drawn beneath it with
@@ -958,10 +980,14 @@ function renderTree(options) {
958
980
  })].join("\n");
959
981
  }
960
982
  /**
961
- * Renders the connector-prefixed lines for a {@link TreeNode} list — the recursive core
962
- * behind {@link renderTree}. Each child is drawn as `prefix` + its connector (`├─ ` for
963
- * any but the last, `└─ ` for the last) + its label, with its own descendants recursed
964
- * beneath under the carried guide (`│ ` under a non-last node, ` ` under the last).
983
+ * Renders the connector-prefixed lines for a {@link TreeNode} list — the recursive core behind
984
+ * {@link renderTree}, whose third options argument requires `border` and groups the optional
985
+ * `styler` and `style`.
986
+ *
987
+ * @remarks
988
+ * Each child is drawn as `prefix` + its connector (`├─ ` for any but the last, `└─ ` for the
989
+ * last) + its label, with its own descendants recursed beneath under the carried guide (`│ `
990
+ * under a non-last node, ` ` under the last).
965
991
  *
966
992
  * @remarks
967
993
  * A centralized, exported recursion branch so it is directly testable and
@@ -1024,12 +1050,12 @@ function renderTreeChildren(nodes, prefix, options) {
1024
1050
  * ```
1025
1051
  */
1026
1052
  function stringifyValue(value) {
1027
- if (value instanceof Error) return `${value.name}: ${value.message}`;
1028
- if (value === null || typeof value !== "object") return String(value);
1053
+ if ((0, _orkestrel_contract.isError)(value)) return `${value.name}: ${value.message}`;
1054
+ if (!(0, _orkestrel_contract.isObject)(value)) return String(value);
1029
1055
  const seen = /* @__PURE__ */ new WeakSet();
1030
1056
  try {
1031
1057
  return JSON.stringify(value, (_key, nested) => {
1032
- if (nested !== null && typeof nested === "object") {
1058
+ if ((0, _orkestrel_contract.isObject)(nested)) {
1033
1059
  if (seen.has(nested)) return "[Circular]";
1034
1060
  seen.add(nested);
1035
1061
  }
@@ -1063,12 +1089,15 @@ function formatArgs(args) {
1063
1089
  }
1064
1090
  /**
1065
1091
  * Renders a determinate progress bar string — a filled / empty glyph track followed by the percentage
1066
- * and the `(current/total)` count (`█████░░░░░ 50% (5/10)`). Pure: same {@link BarOptions} →
1067
- * same string. The animation-layer sibling of the `render*` renderers (box / table / tree /
1068
- * separator), shared so a {@link import('./types.js').ProgressInterface} and any direct caller draw
1069
- * the one bar — never a second, hand-rolled one.
1092
+ * and the `(current/total)` count (`█████░░░░░ 50% (5/10)`). Pure and width-aware: same
1093
+ * {@link BarOptions} → same string.
1070
1094
  *
1071
1095
  * @remarks
1096
+ * It is the animation-layer sibling of the `render*` renderers (box / table / tree / separator),
1097
+ * shared so a {@link import('./types.js').ProgressInterface} and any direct caller draw the one
1098
+ * bar — never a second,
1099
+ * hand-rolled one.
1100
+ *
1072
1101
  * - **Fill fraction, clamped.** The filled cell count is `round((current / total) · width)` with
1073
1102
  * `current` clamped to `[0, total]`, so an overrun never over-fills and a negative never under-fills.
1074
1103
  * A `total <= 0` renders a full track (there is nothing to fill toward — the work is trivially done).
@@ -1103,12 +1132,13 @@ function renderBar(options) {
1103
1132
  //#region src/core/renderers/ANSIRenderer.ts
1104
1133
  /**
1105
1134
  * Implements the cross-environment default {@link RendererInterface} — renders style data as ANSI
1106
- * SGR escape codes, exactly as `Scheduler` is the `setTimeout` default for its seam. It
1107
- * is the single styling output the whole console / terminal system uses in a terminal;
1108
- * the browser `%c` / CSS renderer implements the same contract over
1109
- * the same {@link Style}, so retargeting changes the renderer, never the style model.
1135
+ * SGR escape codes, stateless and event-free.
1110
1136
  *
1111
1137
  * @remarks
1138
+ * It is the single styling output the whole console / terminal system uses in a terminal; the
1139
+ * browser `%c` / CSS renderer implements the same contract over the same {@link Style}, so
1140
+ * retargeting changes the renderer, never the style model.
1141
+ *
1112
1142
  * - **Style is data in, SGR string out.** It reads the style's `foreground` /
1113
1143
  * `background` / `attributes` and emits one `ESC[…m` sequence whose parameters are the
1114
1144
  * mapped SGR numbers (foreground 30–37 / 90–97, background 40–47 / 100–107, attributes
@@ -1144,7 +1174,8 @@ var ANSIRenderer = class {
1144
1174
  //#endregion
1145
1175
  //#region src/core/Retention.ts
1146
1176
  /**
1147
- * Implements the bounded, level-keyed retention engine both captures buffer through one capped total buffer
1177
+ * Implements the bounded, level-keyed retention engine the console and process captures buffer
1178
+ * through — one capped total buffer
1148
1179
  * plus one capped bucket per level, generic over the record type each capture carries.
1149
1180
  *
1150
1181
  * @remarks
@@ -1432,7 +1463,7 @@ var Styler = class Styler {
1432
1463
  return this.#attribute(attribute).surface;
1433
1464
  }
1434
1465
  #isSurface(value) {
1435
- return typeof value === "function" && "style" in value && "enabled" in value && "render" in value && "red" in value && "bold" in value;
1466
+ return (0, _orkestrel_contract.isFunction)(value) && "style" in value && "enabled" in value && "render" in value && "red" in value && "bold" in value;
1436
1467
  }
1437
1468
  #foreground(color) {
1438
1469
  return new Styler(this.#renderer, this.#enabled, Object.freeze({
@@ -1451,10 +1482,13 @@ var Styler = class Styler {
1451
1482
  //#endregion
1452
1483
  //#region src/core/factories.ts
1453
1484
  /**
1454
- * Creates the fluent, composable {@link StylerInterface} — the consumer-facing styling
1455
- * API. It builds a {@link import('./types.js').Style} under the hood and renders it
1456
- * through a {@link import('./types.js').RendererInterface} (the ANSI default), so
1457
- * `styler.red.bold('hi')` yields styled text. Chains are immutable, so a base styler is freely reusable.
1485
+ * Creates the fluent, composable {@link StylerInterface} — ANSI by default, retargeted by a
1486
+ * `renderer` and stripped of color by `enabled: false`.
1487
+ *
1488
+ * @remarks
1489
+ * It builds a {@link import('./types.js').Style} under the hood and renders it through a
1490
+ * {@link import('./types.js').RendererInterface}, so `styler.red.bold('hi')` yields styled text.
1491
+ * Chains are immutable, so a base styler is freely reusable.
1458
1492
  *
1459
1493
  * @param options - See {@link StylerOptions}
1460
1494
  * @returns A base {@link StylerInterface}
@@ -1901,12 +1935,14 @@ var Reporter = class {
1901
1935
  /**
1902
1936
  * Implements a self-driving, observable activity spinner — a glyph cycle that advances on a
1903
1937
  * periodic timer, writing each `\r` + frame line to its {@link SinkInterface} and emitting it on
1904
- * `frame`. The leading `\r` is what an overwrite-capable sink (the TTY sink) redraws on; a plain
1905
- * sink degrades to a fresh, non-overwriting line — the line-overwrite is the sink's job, never
1906
- * the spinner's. Universal — `setInterval` + the one {@link StylerInterface} + the one
1907
- * {@link SinkInterface}, no `node:*`, no `process.stdout`.
1938
+ * `frame`. The timer is always cleared on an outcome, so the spinner is leak-free.
1908
1939
  *
1909
1940
  * @remarks
1941
+ * The leading `\r` is what an overwrite-capable sink (the TTY sink) redraws on; a plain sink
1942
+ * degrades to a fresh, non-overwriting line — the line-overwrite is the sink's job, never the
1943
+ * spinner's. Universal — `setInterval` + the one {@link StylerInterface} + the one
1944
+ * {@link SinkInterface}, no `node:*`, no `process.stdout`.
1945
+ *
1910
1946
  * - **Self-driving but deterministically testable.** `start()` arms a `setInterval` that calls
1911
1947
  * {@link tick} each `interval`; each {@link tick} builds the styled `glyph + message` line for the
1912
1948
  * current frame, emits it on `frame`, writes `'\r' + line` to the sink, then advances the frame
@@ -2017,13 +2053,16 @@ var Spinner = class {
2017
2053
  //#region src/core/Progress.ts
2018
2054
  /**
2019
2055
  * Implements an update-driven, observable progress bar — {@link update} recomputes the bar through
2020
- * {@link renderBar}, writes `\r` + bar to its {@link SinkInterface}, and emits the `{ current, total }`
2021
- * on `update`. The leading `\r` is what an overwrite-capable sink (the TTY sink) redraws on; a
2022
- * plain sink degrades to a fresh, non-overwriting line — the line-overwrite is the sink's job.
2023
- * Universal — the one {@link StylerInterface} + the one {@link SinkInterface}, no `node:*`, no
2024
- * `process.stdout`. No self-timer (unlike {@link import('./Spinner.js').Spinner}) — the caller drives it.
2056
+ * {@link renderBar}, writes `\r` + bar to its {@link SinkInterface}, and emits the `{ current,
2057
+ * total }` on `update`. No self-timer, unlike {@link import('./Spinner.js').Spinner} the caller
2058
+ * drives it.
2025
2059
  *
2026
2060
  * @remarks
2061
+ * The leading `\r` is what an overwrite-capable sink (the TTY sink) redraws on; a plain sink
2062
+ * degrades to a fresh, non-overwriting line — the line-overwrite is the sink's job. Universal —
2063
+ * the one {@link StylerInterface} + the one {@link SinkInterface}, no `node:*`, no
2064
+ * `process.stdout`.
2065
+ *
2027
2066
  * - **Update-driven.** Each {@link update} clamps `current` to `[0, total]`, renders the bar (filled
2028
2067
  * to `current / total`, with the trailing `percent (current/total)` + message) through {@link renderBar},
2029
2068
  * emits `update`, and writes `'\r' + bar`. Progress advances only when the caller reports it.