Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
# progress
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`createProgress` renders a fixed-width ASCII progress bar for tracking long-running operations with a known total. It writes bar updates to any `WritableStreamLike` output — TTY or non-TTY — using in-place line clearing on TTY terminals and newline-separated lines on non-TTY streams (CI, pipes). The bar updates are driven by explicit `update(current)` calls from the caller; no internal timer is used.
The instance is `AbortSignal`-aware: aborting the signal silently makes all subsequent `update()` calls no-ops, allowing calling code to cancel a long operation without crashing.
## Features
- Fixed-width block bar with configurable `width` (default 40 characters) using `█` fill and `░` empty characters
- Percentage and `current/total` count displayed after the bar: `[████████████░░░░░░░░] 60% (60/100)`
- TTY mode: clears the current line and re-renders in-place via `clearLine` / `cursorTo`
- Non-TTY mode: writes each update on its own line for readable CI output
- `render()` returns the bar string without writing to the stream — useful for embedding in larger output
- `complete()` forces 100% and appends a newline; idempotent (safe to call multiple times)
- `AbortSignal` support: abort event listener marks the instance done; pre-fired signal respected via guard check in `update()`
## Usage: Basic Example
```ts
import { createProgress } from "@suaveplan/cli/progress/progress";
const p = createProgress({ total: 100 });
for (let i = 0; i <= 100; i++) {
p.update(i);
await sleep(20);
}
p.complete();
```
## Usage: Advanced Example
```ts
import { createProgress } from "@suaveplan/cli/progress/progress";
import { Writable } from "node:stream";
const controller = new AbortController();
const p = createProgress({
total: files.length,
width: 60,
signal: controller.signal,
});
let processed = 0;
for (const file of files) {
if (controller.signal.aborted) break;
await processFile(file);
p.update(++processed);
}
p.complete();
```
## API Reference
### `createProgress(opts: ProgressOptions): ProgressInstance`
Creates a progress bar instance. No output is written until `update()` or `complete()` is called.
**`opts.total: number`** — the maximum value (100%). Must be positive.
**`opts.width?: number`** — width of the bar in characters (default `40`).
**`opts.stream?: WritableStreamLike`** — output target (default `process.stdout`). TTY in-place rendering requires `clearLine` and `cursorTo` methods plus `isTTY === true`.
**`opts.signal?: AbortSignal`** — when aborted, future `update()` calls become no-ops and the bar stops advancing.
### `ProgressInstance`
**`update(current: number): void`** — updates the displayed progress to `current`. Values are clamped to `[0, total]`. No-op if `complete()` was already called or the signal was aborted.
**`complete(): void`** — writes a 100% bar with a trailing newline and marks the instance done. Idempotent.
**`render(): string`** — returns the current bar string without writing to the stream.
### `ProgressOptionsSchema`
Zod schema for `ProgressOptions` — validates `total` (positive number) and `width` (optional positive number). Excludes `stream` and `signal`.
### `WritableStreamLike`
Re-exported from `../spinner/spinner.types.js`. Any object with `write(text: string): void` satisfies this interface; `clearLine`, `cursorTo`, and `isTTY` are optional and enable TTY mode when present.
## Implementation Notes
Progress percentage is computed as `Math.min(1, current / total)` to handle over-total updates gracefully. The filled block count is `Math.round(pct * width)` — rounding avoids a full-bar display at 99% on small widths. The `complete()` guard (`done` flag) prevents double-writes when called multiple times. The `AbortSignal` listener uses `{ once: true }` so it self-removes after the first event.