flagstaff
API reference

flagstaff/loop

Every export of flagstaff/loop, with its signature and doc comment: hoist, manualClock, frameWriter, plus 6 types.

The frame loop (R1). hoist() raises a component, update() changes its state, lower() takes it down and leaves the static line behind. Which of the four projections runs is the output policy's decision (roundel/policy), read once from the runtime; this file never looks at a stream's isTTY or an environment variable itself (U2).

import { hoist, manualClock, frameWriter } from 'flagstaff/loop';

Functions

frameWriter

The family's one frame writer over out — see FrameWriter.

function frameWriter(out: Writer): FrameWriter;
ParameterType
outWriter

Returns FrameWriter

hoist

Hoist a component in its initial state. Pass { json: true } when the run was asked for --json.

function hoist<S>(component: Component<S>, rt: Runtime, initial: S, opts?: ModeOptions): Hoisted<S>;
ParameterType
componentComponent<S>
rtRuntime
initialS
opts (optional)ModeOptions

Returns Hoisted<S>

manualClock

A clock that moves only when told (R9): the same frames every run, so a spinner snapshots.

function manualClock(start?: number): ManualClock;
ParameterType
start (optional)number

Returns ManualClock

Interfaces

Clock

Time as the loop sees it: burgee's Runtime.clock satisfies it, so does manualClock().

interface Clock {
    now(): number;
    schedule(fn: () => void, ms: number): () => void;
}

FrameWriter

The frame-writing seam (controlroom R3, R16). paint takes the whole frame, one string per line, and writes only what changed since the last paint, wrapped in synchronized output; a frame identical to the last writes nothing. release forgets the painted frame without erasing it: what is on screen becomes scrollback, and the next paint starts at the cursor.

interface FrameWriter {
    paint(lines: readonly string[]): void;
    release(): void;
}

Hoisted

interface Hoisted<S> {
    readonly mode: OutputMode;
    update(state: S): void;
    /** Lower the flag: the final state's static projection is what stays on screen. */
    lower(state?: S): void;
}

ManualClock

interface ManualClock extends Clock {
    /** Advance by `ms`, running every callback that falls due, earliest first. */
    tick(ms: number): void;
}

Runtime

What the loop needs from the world: the policy's slice plus the two streams and a clock.

interface Runtime extends PolicyRuntime {
    stdout: Writer;
    stderr: Writer;
    clock: Clock;
}

Writer

interface Writer {
    write(chunk: string): unknown;
    /**
     * How wide the terminal is, read at every paint so a resize is honoured. A frame wider than
     * this wraps on screen, and the rows it wraps onto have to be erased with the rest; a Node
     * TTY stream carries it, and a writer without it is taken to be `DEFAULT_COLUMNS` wide.
     */
    readonly columns?: number | undefined;
    /**
     * How tall the terminal is, read at every paint like the width (frames-fit-the-screen R1).
     * A writer without it — a pipe — is unbounded, and no frame is clipped.
     */
    readonly rows?: number | undefined;
}

On this page