UNPKG

dax

Version:

Cross platform shell tools inspired by zx.

396 lines 19 kB
import * as dntShim from "../../../../../../_dnt.shims.js"; import type { FsFileWrapper, Path } from "../../../path/0.3.2/mod.js"; import { type ConsoleSize, type RenderInterval, type StaticTextContainer, type TextItem } from "../../../console-static-text/0.3.4/mod.js"; import { Buffer } from "../../../../@std/io/0.225.3/buffer.js"; import type { CommandBuilder, KillSignal } from "./command.js"; import type { ByteRingBuffer } from "./byteRingBuffer.js"; import { LineRingBuffer } from "./lineRingBuffer.js"; import type { Closer, Reader, ReaderSync, Writer, WriterSync } from "../../../../@std/io/0.225.3/types.js"; import type { CommandPipeWriter } from "./commandHandler.js"; export type { Closer, Reader, ReaderSync, Writer, WriterSync }; /** * Owns the static text scope and the list of active tail segments. A single * shared instance handles all `InheritTailWriter`s in the process so parallel * commands interleave into the same pinned region. Tests can construct * their own with a stand-in `StaticTextContainer` to assert on the emitted * ANSI bytes; pass `interval: null` to skip the periodic refresh and drive * `container.refresh()` manually. */ export declare class TailRenderer implements Disposable { #private; readonly container: StaticTextContainer; constructor(options?: { container?: StaticTextContainer; interval?: RenderInterval | null; }); [Symbol.dispose](): void; /** @internal */ register(seg: InheritTailState): void; /** @internal */ unregister(seg: InheritTailState): void; /** @internal */ logAbove(items: TextItem[]): void; } export type PipeReader = Reader | ReaderSync; export type PipeWriter = Writer | WriterSync; /** An awaitable that resolves to an object exposing a `readable` stream — * e.g. a `RequestBuilder` that resolves to a response with `.readable`. */ export interface AwaitableReadable { /** Resolves to an object that exposes a `ReadableStream<Uint8Array>` via * its `readable` property. */ then<TResult1 = { readable: dntShim.ReadableStream<Uint8Array>; }, TResult2 = never>(onfulfilled?: ((value: { readable: dntShim.ReadableStream<Uint8Array>; }) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): PromiseLike<TResult1 | TResult2>; } /** Behaviour to use for stdin. * @value "inherit" - Sends the stdin of the process to the shell (default). * @value "null" - Does not pipe or redirect the pipe. */ export type ShellPipeReaderKind = "inherit" | "null" | Reader | dntShim.ReadableStream<Uint8Array> | Uint8Array | CommandBuilder | FsFileWrapper | Path | AwaitableReadable; /** * The behaviour to use for a shell pipe. * @value "inherit" - Sends the output directly to the current process' corresponding pipe (default). * @value "null" - Does not pipe or redirect the pipe. * @value "piped" - Captures the pipe without outputting. * @value "inheritPiped" - Captures the pipe with outputting. */ export type ShellPipeWriterKind = "inherit" | "null" | "piped" | "inheritPiped" | WriterSync | dntShim.WritableStream<Uint8Array> | FsFileWrapper | Path; export declare class NullPipeReader implements Reader { read(_p: Uint8Array): Promise<number | null>; } export declare class NullPipeWriter implements WriterSync { writeSync(p: Uint8Array): number; } export declare class ShellPipeWriter { #private; constructor(kind: ShellPipeWriterKind, inner: PipeWriter); get kind(): ShellPipeWriterKind; get inner(): PipeWriter; write(p: Uint8Array): number | Promise<number>; writeAll(data: Uint8Array): void | Promise<void>; writeText(text: string): void | Promise<void>; writeLine(text: string): void | Promise<void>; } export declare class CapturingBufferWriter implements Writer { #private; constructor(innerWriter: Writer, buffer: Buffer); getBuffer(): Buffer; write(p: Uint8Array): Promise<number>; } export declare class CapturingBufferWriterSync implements WriterSync { #private; constructor(innerWriter: WriterSync, buffer: Buffer); getBuffer(): Buffer; writeSync(p: Uint8Array): number; } /** Async tap that mirrors writes into a {@link ByteRingBuffer} for use as * "errorTail" — the ring keeps the trailing N bytes of whatever the * command sent through this stream, so they can be surfaced in the thrown * `Error` even when the underlying sink (a piped command, a `WritableStream`, * a file on disk) doesn't expose what was written. Pairs with * {@link ErrorTailCaptureWriterSync}. */ export declare class ErrorTailCaptureWriter implements Writer { #private; readonly ring: ByteRingBuffer; constructor(innerWriter: Writer, ring: ByteRingBuffer); write(p: Uint8Array): Promise<number>; } /** Sync variant of {@link ErrorTailCaptureWriter}. */ export declare class ErrorTailCaptureWriterSync implements WriterSync { #private; readonly ring: ByteRingBuffer; constructor(innerWriter: WriterSync, ring: ByteRingBuffer); writeSync(p: Uint8Array): number; } export declare class InheritStaticTextBypassWriter implements WriterSync { #private; constructor(innerWriter: WriterSync); writeSync(p: Uint8Array): number; flush(): void; } /** * Default number of lines to show in `.tailDisplay()` mode. Docker Compose * shows a comparable-sized window; small enough not to dominate the terminal, * large enough to convey progress. */ export declare const DEFAULT_INHERIT_TAIL_LINES = 5; /** * Default number of lines to retain for error-time flushback. On error the * live tail (5 lines) rarely shows what actually broke, so we keep a larger * buffer and promote it to scrollback when the command fails. */ export declare const DEFAULT_INHERIT_TAIL_ERROR_LINES = 80; /** Context passed to a `TailMaxLines` callback at draw time. */ export interface TailMaxLinesContext { /** Current terminal size, or `undefined` if the host isn't a TTY. */ size: ConsoleSize | undefined; } /** Total number of rows the tail occupies — header included — so two * commands tailing at `"50%"` each compose into a full screen instead of * spilling. When a header is shown the visible output count is `maxLines - 1`, * clamped to at least 1 row of output (so a headered tail is always at * least 2 rows total). Accepts: * - A literal `number`, taken as-is. * - A string like `"50%"`, resolved against the terminal's row count at draw * time so the tail re-fits if the user resizes mid-run. * - A function called per draw — for cases neither form covers, e.g. * `(ctx) => Math.min(10, (ctx.size?.rows ?? 24) - 5)` to leave headroom. */ export type TailMaxLines = number | `${number}%` | ((ctx: TailMaxLinesContext) => number); /** Context passed to a `TailHeader` callback at draw time. */ export interface TailHeaderContext { /** The raw command text being run. */ command: string; /** Current terminal size, or `undefined` if the host isn't a TTY. */ size: ConsoleSize | undefined; } /** Header rendered above the live tail. * - `undefined` (default): `Running <command>` while running, `Ran <command>` in scrollback (when `printCommand()` is set). * - `false`: no header. * - `string`: rendered verbatim — you supply any styling. * - function: called per draw with `{ command, size }`; the result is rendered verbatim. * * Regardless of this setting, the error path still emits `> <command>` to * scrollback so failed commands stay unambiguous in logs. */ export type TailHeader = string | false | ((ctx: TailHeaderContext) => string); /** * Construction-time options for `InheritTailWriter`. The user-facing * `.tailDisplay()` API maps onto this plus a few post-construction setters * for header/promote behavior. */ export interface InheritTailWriterOptions { /** Number of visible tail lines. See {@link TailMaxLines}. * @default 5 */ maxLines?: TailMaxLines; /** Treat the inner writer as TTY-attached. Defaults to `process.stderr.isTTY`. * When false, writes pass through to the inner writer untouched (no pinned * region) — used when the host process isn't a terminal. */ isTty?: boolean; /** Renderer hosting the pinned scrolling region. Defaults to the global * one tied to `staticText` + `renderInterval`. Tests pass a custom * renderer to capture the emitted ANSI bytes in isolation. */ renderer?: TailRenderer; /** Header text or per-draw callback (already pre-bound — accepts only * `{ size }`). Set in the constructor so the segment is fully labeled * before it gets registered with the renderer; otherwise the live area * could paint a header-less segment for one tick. */ header?: string | ((ctx: { size: ConsoleSize | undefined; }) => string); /** When true, render `header` as-is (no `Running` / `Ran` framing). */ headerVerbatim?: boolean; /** Always-shown command label on the error scrollback path, even when * `header` is hidden or customized. */ errorHeader?: string; /** When true, promote the header to scrollback as `Ran <cmd>` (or the * verbatim header) on success — typically wired to `.printCommand()`. */ promoteHeaderOnSuccess?: boolean; } /** * Default size of the per-stream error-context ring buffer. 8 KiB is enough * to capture the last few screens worth of output — large enough to convey * what went wrong without ballooning memory for long-running commands. */ export declare const DEFAULT_ERROR_TAIL_BYTES: number; /** * Configuration for `.errorTail()`. When enabled, dax silently retains * the trailing N bytes of stdout and/or stderr and appends them to the * thrown `Error.message` if the command exits with a non-zero code. * Targets streams the user can't see — piped to another command, * redirected to a file, sent to a `WritableStream`, or discarded with * `"null"`. Streams routed to the terminal (`"inherit"` / * `"inheritPiped"`) are skipped since the bytes already reached the * user's scrollback. */ export interface ErrorTailOptions { /** Per-stream cap on retained bytes. Once exceeded, the oldest bytes * are dropped first. * @default 8192 (8 KiB) */ maxBytes?: number; /** Capture the trailing bytes of stdout. * @default true */ stdout?: boolean; /** Capture the trailing bytes of stderr. * @default true */ stderr?: boolean; /** Capture stdout and stderr into a single interleaved buffer instead * of separate per-stream buffers. When true, the error message shows * output in the order it was written rather than grouped by stream. * @default false */ combined?: boolean; } /** * Configuration for `.tailDisplay()`. Pass `true` to enable with defaults, * or an options object to customize. */ export interface TailDisplayOptions { /** Number of visible tail lines. See {@link TailMaxLines}. * @default 5 */ maxLines?: TailMaxLines; /** Header rendered above the live tail. See {@link TailHeader}. */ header?: TailHeader; } /** Internal: a maxLines value normalized into a uniform per-draw callback, * so the renderer doesn't dispatch on type each tick. */ type ResolvedMaxLinesFn = (ctx: TailMaxLinesContext) => number; /** Internal: a header value normalized into a uniform per-draw callback. * The user-facing `(ctx: TailHeaderContext) => string` is pre-bound with * the command text by command.ts before reaching state, so the renderer * (which only knows about `size`) can call it uniformly. */ type ResolvedHeaderFn = (ctx: { size: ConsoleSize | undefined; }) => string; declare class InheritTailState { #private; readonly resolveMaxLines: ResolvedMaxLinesFn; readonly lines: LineRingBuffer; readonly enabled: boolean; /** Per-draw callback that produces the header text. `undefined` means * "no header" (live tail renders no label). Percentages, raw strings and * user callbacks are all normalized into this single shape upstream. */ headerFn: ResolvedHeaderFn | undefined; /** When true, the header is rendered verbatim instead of being framed * with the built-in `Running <text>` / `Ran <text>` styling. Set when * the user supplies a custom header string or function. */ headerVerbatim: boolean; /** Always-present fallback shown on the error path, even if the live * header is hidden or customized — so error scrollback is unambiguous * about which command failed. */ errorHeader: string | undefined; promoteHeaderOnSuccess: boolean; readonly renderer: TailRenderer; constructor(renderer: TailRenderer, maxLines: TailMaxLines, isTty: boolean, maxErrorLines?: number); get omittedLineCount(): number; /** Visible output rows after subtracting the header (if any) from the * total `maxLines` budget. Floors to 1 so a headered segment always shows * at least one line. */ visibleLineCount(ctx: TailMaxLinesContext): number; addRef(): void; setHeader(header: string | ResolvedHeaderFn | undefined, options?: { verbatim?: boolean; }): void; appendLines(newLines: string[]): void; release(errored: boolean, trailing: string[]): void; } /** * Docker-style partial scrolling writer. * * Instead of streaming command output straight to the terminal, this buffers * output line-by-line and shows only the most recent `maxLines` lines inside * a static text scope pinned to the bottom of the terminal. When the command * finishes successfully, the scope is cleared (the header is promoted to * scrollback); on error the retained tail is flushed above so the user can * see what happened. * * Falls back to writing directly to `innerWriter` when the host process is * not attached to a TTY, since there's nowhere to anchor a scrolling region. * * Pass an existing writer as the second argument to share its scrolling * region — used internally when both stdout and stderr are tailed, so the * two streams interleave into one scroll area with a single header. */ export declare class InheritTailWriter implements WriterSync, Disposable { #private; constructor(innerWriter: WriterSync, options?: InheritTailWriterOptions); constructor(innerWriter: WriterSync, sibling: InheritTailWriter); /** Snapshot of the live tail (last visible-window retained lines, oldest * first). The visible-window size is `maxLines - 1` when a header is shown * (recomputed from the current console size for percentage / callback * `maxLines`). */ get tailLines(): readonly string[]; /** Number of completed lines that were dropped from the retained ring * buffer because its capacity is bounded. Rendered as * `...N lines omitted...` above the retained tail when the command fails. */ get omittedLineCount(): number; /** * Sets a label rendered above the tail lines that identifies what this * scrolling region is showing. Accepts a literal string (collapsed to * a single line) or a per-draw callback that receives the current * `{ size }`. `undefined` removes the header. Long text is truncated to * the terminal width. * * When `options.verbatim` is true, the text is rendered as-is (no * built-in `Running` / `Ran` framing). Used by `.tailDisplay({ header })` * so the caller has full control over styling. */ setHeader(header: string | ((ctx: { size: ConsoleSize | undefined; }) => string) | undefined, options?: { verbatim?: boolean; }): void; /** * Sets the command label preserved on the error scrollback path even * when the live header is hidden or customized — so `> <command>` is * always shown when a tailed command fails, regardless of `.tailDisplay` * header config. */ setErrorHeader(text: string | undefined): void; /** * Controls whether the `Ran <command>` header is promoted to scrollback * on successful finalize. Defaults to `false` — on success the live tail * clears silently unless the caller opts in (command.ts enables it when * `.printCommand()` was set, so the command stays visible in scrollback). * The error-path header (`> <command>` + retained tail) is always emitted * regardless of this flag since it's diagnostic. */ setPromoteHeaderOnSuccess(value: boolean): void; writeSync(p: Uint8Array): number; /** * Clears the scrolling region. Called on successful command completion. * * If a header was set it's promoted to scrollback via `logAbove` before * the scope is disposed, so "which commands ran" remains visible after * the transient tail clears. When multiple writers share a scope the * scope is only disposed once all of them have finalized. * * Any partial pending line is still passed to `release` so that — if a * sibling writer subsequently errors — the success side's last partial * line is preserved in the error scrollback. The success path itself * never renders trailing lines, so there's no visual cost when no * sibling errors. */ finalize(): void; [Symbol.dispose](): void; /** * Promotes the header + retained tail above the static region before * clearing it. Called when the command errored so the user has visible * context. When multiple writers share a scope, any writer finalizing * for error causes the shared region to use the error path once all * writers have finalized. */ finalizeForError(): void; } /** * Header rendered above the live tail while the command is in flight: * `Running <command>` in bold cyan. */ export declare function formatTailHeader(text: string, size: ConsoleSize | undefined): string; /** * Past-tense header promoted to scrollback when a tailed command completes * successfully: `Ran <command>` in bold green. */ export declare function formatRanHeader(text: string, size: ConsoleSize | undefined): string; export interface PipedBufferListener extends WriterSync, Closer { setError(err: Error): void; } export declare class PipedBuffer implements WriterSync { #private; constructor(); getBuffer(): Buffer | undefined; setError(err: Error): void; close(): void; writeSync(p: Uint8Array): number; setListener(listener: PipedBufferListener): void; } export declare class PipeSequencePipe implements Reader, WriterSync { #private; close(): void; writeSync(p: Uint8Array): number; read(p: Uint8Array): Promise<number | null>; } export declare function pipeReaderToWritable(reader: Reader, writable: dntShim.WritableStream<Uint8Array>, signal: AbortSignal): Promise<void>; export declare function pipeReadableToWriterSync(readable: dntShim.ReadableStream<Uint8Array>, writer: ShellPipeWriter | CommandPipeWriter, signal: AbortSignal | KillSignal): Promise<void>; //# sourceMappingURL=pipes.d.ts.map