import type { Writable } from 'node:stream'; import type { Step } from './steps.js'; /** Where one of a child's output streams goes: a descriptor the child writes to, or a pipe nmr reads. */ export type OutputChannel = number | 'pipe'; /** The channel each of a child's output streams runs on. */ export interface OutputChannels { stderr: OutputChannel; stdout: OutputChannel; } export interface RunCommandOptions { /** The channel each of the child's output streams runs on. */ channels: OutputChannels; /** When true, suppress output on success and write captured output to stderr on failure. */ quiet?: boolean; /** Stream that subprocess stdout flows to in non-quiet mode. Defaults to `process.stdout`. */ stdout?: Writable; /** Stream that subprocess stderr flows to (and that quiet-mode failure output is written to). Defaults to `process.stderr`. */ stderr?: Writable; /** Environment for the subprocess. Defaults to `process.env`. */ env?: NodeJS.ProcessEnv; } /** What a step list needs to run. Each step's channels follow its kind, so the caller chooses none of them. */ export type RunStepsOptions = Omit; /** A bounded copy of what a step list's own commands wrote, each stream kept apart from the other. */ export interface RetainedOutput { stderr: Buffer; stdout: Buffer; } export interface RunStepsResult { exitCode: number; /** * What the list's opaque steps produced, absent unless every one of them was captured whole and there was * at least one to capture. */ retained?: RetainedOutput; } /** How a command ended, and the exit code that carries that ending to nmr's own caller. */ type CommandCompletion = { outcome: 'exited'; exitCode: number; } | { outcome: 'signaled'; exitCode: number; signal: NodeJS.Signals; } | { outcome: 'spawn-failed'; exitCode: number; error: Error; }; export type RunCommandResult = CommandCompletion & { /** * Output retained while the command ran, elided in the middle when it overran the bound. * `undefined` where the stream was handed to the child directly, or where capture stopped early * and the retained copy would understate what the command produced. */ stdout: Buffer | undefined; stderr: Buffer | undefined; }; /** * Returns the channel a stream runs on: the stream's own descriptor when it is a terminal and output is not * being withheld, so the child's terminal detection matches nmr's, and a pipe everywhere else. */ export declare function resolveChannel(stream: Writable, quiet: boolean): OutputChannel; /** * Returns the channel a step running an nmr process below this one runs on: nmr's own descriptor whenever it * has one, so the child writes where nmr writes and no ancestor stands between them relaying bytes. A stream * carrying no descriptor, which is the one a test injects, falls back to a pipe. */ export declare function resolveInheritedChannel(stream: Writable): OutputChannel; /** * Runs a step list in order and resolves once one fails or all have run, carrying the `&&` semantics nmr * promises: the first non-zero exit ends the sequence and is the code returned. * * Sequencing here rather than in a spawned shell is what lets each step run on the channels its kind calls * for, and what makes a signal to nmr end the run: the steps after it are never reached, where a shell would * have gone on running them unsupervised. * * Carries back a bounded copy of what the list's own commands wrote, so a caller can retain it. Only opaque * steps contribute: a structural step is another nmr process reporting for the subtree beneath it, and its * output is that subtree's to account for. */ export declare function runSteps(steps: readonly Step[], cwd: string | undefined, options: RunStepsOptions): Promise; /** * Runs a command through a shell and resolves once it has ended. * * The caller chooses each stream's channel. A descriptor is handed to the child and nmr sees none of what * flows through it; a pipe is forwarded to the matching option stream as it arrives, with that destination's * back-pressure throttling the child, and a bounded copy is retained. Quiet mode withholds the forwarding, * discarding the retained copy on success and writing it to `options.stderr` on failure, so it has nothing to * report unless the caller chose a pipe on both streams. * * Piped stdout and stderr are ordered by arrival rather than by a shared descriptor, so their interleaving * is approximate. */ export declare function runCommand(command: string, cwd: string | undefined, options: RunCommandOptions): Promise; export {};