import type { ExecutionFrame } from '../frame.ts';
import type { SharedInput } from '../../io/async_line_iterator.ts';
import { type ByteSource, IOResult } from '../../io/types.ts';
import type { ExecutionNode, StatusWriter } from '../types.ts';
import { type Recorder, type StreamOwner } from '../../shell/descriptors.ts';
import { Channel, type JobConsole } from '../../shell/console/index.ts';
import { type TSNodeLike } from '../../shell/types.ts';
import type { SessionState } from '../session/session.ts';
/**
 * Run a test, the left of `&&`/`||` or a negated command where bash ignores
 * `set -e`: nothing it runs exits for a failure, a function body or a
 * subshell included. Mirrors Python's ignoring_errexit.
 */
export declare function ignoringErrexit<T>(session: SessionState, fn: () => Promise<T>): Promise<T>;
/** Whether `set -e` ends the shell after this statement, marking the shell
 * as ending when it does. */
export declare function errexitActs(node: TSNodeLike, status: number, session: SessionState): boolean;
/**
 * Record a finished statement's exit status: `$?` and `${PIPESTATUS[@]}`
 * together.
 *
 * The one function every status write goes through, so the two can never
 * disagree. `handlePipe` parks its per-segment statuses on the session,
 * and the boundary that closes the pipeline claims them here; a boundary
 * with nothing parked stamps its own one-element status, which is what a
 * simple command, a function call or a subshell leaves in bash. A
 * *transparent* statement (a group, a loop, a negation, a redirected
 * pipeline: see `pipelineTransparent`) claims what was parked but never
 * overwrites, because bash reports the last pipeline that ran *inside* it
 * (`{ false | true; }` keeps `1 0`).
 */
export declare function recordStatus(session: SessionState, code: number, transparent?: boolean): void;
/**
 * The status a line found, taken before its first statement runs and
 * put back if the caller aborts the line.
 *
 * An aborted invocation is the caller's outcome, not the shell's, so it
 * must leave `$?` where it was. But the abort lands on one await inside
 * the line, and every statement before that await has already stamped
 * through `recordStatus`. The status write refuses a statement that
 * settles after the caller was released; this is for the ones that
 * landed before it, and only a copy taken before the line can undo them.
 *
 * The three fields travel together because they are one shell fact:
 * `$?`, `${PIPESTATUS[@]}`, and the per-segment statuses a pipeline
 * parked for its boundary to claim. Restoring one without the others
 * would leave a state no bash line produces.
 */
export interface StatusSnapshot {
    lastExitCode: number;
    pipeStatus: readonly number[];
    pipeStatusPending: readonly number[] | null;
}
/** Capture `$?` and `${PIPESTATUS[@]}` before a line runs. */
export declare function snapshotStatus(session: SessionState): StatusSnapshot;
/**
 * Put back the status a line found, for a line the caller aborted.
 * Statements inside the line may already have stamped their own status
 * before the abort landed, and an aborted invocation is the caller's
 * outcome, not the shell's.
 *
 * Only what this line overwrote, though. Two `execute()` calls can
 * share a session, and a snapshot taken before a concurrent line
 * finished is older than that line's result: putting it back would
 * resurrect a value the shell had already moved past. So the restore
 * happens only while the last stamp is still this line's. When nobody
 * has stamped since the snapshot the status already equals it and
 * declining is the same thing; when someone else did, declining is the
 * point.
 */
export declare function restoreStatus(session: SessionState, snapshot: StatusSnapshot, writer: StatusWriter | null): void;
/**
 * Park the status just recorded again, for the boundary that closes the
 * enclosing statement to claim rather than stamp over. A conditional
 * list that short-circuits has closed its left pipeline and runs
 * nothing else, and bash reports the list as that pipeline:
 * `true | false && true` keeps `0 1`. The list is not a pipeline of its
 * own, so without this its boundary would stamp the aggregate `1`.
 */
export declare function carryStatus(session: SessionState): void;
/**
 * Whether bash forks to run a command: a subshell or a program, whose
 * redirects expand in the child and whose closed pipe ends it alone; a
 * builtin, a function or a compound command runs in the shell itself.
 * `command -v` is the builtin, `command X` masks functions. The expanded
 * `words` decide once there are any; before, a name only an expansion spells
 * (`$cmd`) counts as a program. Mirrors Python's forks.
 */
export declare function forks(command: TSNodeLike | null, session: SessionState, words?: readonly string[] | null): boolean;
/**
 * Whether what a statement wrote came from a child bash forks for it, so a
 * closed pipe ends that writer alone: a program, a subshell or a pipeline,
 * redirected or negated too. A command is judged by the producer its output
 * names, what its head ran as (`$cmd`). Mirrors Python's writer_forks.
 */
export declare function writerForks(statement: TSNodeLike | null, session: SessionState, io: IOResult): boolean;
/**
 * Deliver a writer's own answer to its stdout's reader going away (`tee -p`)
 * in place of SIGPIPE, if it has one. Mirrors Python's answer_closed.
 */
export declare function answerClosed(io: IOResult, emit: (channel: Channel, data: Uint8Array) => Promise<unknown>): Promise<boolean>;
/**
 * Finalize a completed statement and seed $? for the next one.
 *
 * Every statement boundary must do the same dance: apply a VALUE
 * barrier so lazily finalized exit codes (grep's) are
 * concrete, then record the status the next statement's $? expands
 * to. Statement-list loops (program, subshell, brace group, if/loop/
 * case bodies, function bodies, && / || / ; lists) call this instead
 * of hand-rolling the triple, so a new construct cannot forget it. The
 * node, when the caller has it, decides whether the statement stamps
 * `PIPESTATUS` itself; without one it stamps.
 */
export declare function finishStatement(stdout: ByteSource | null, io: IOResult, session: SessionState, node?: TSNodeLike | null, execNode?: ExecutionNode | null): Promise<ByteSource | null>;
/**
 * A read the statement's output stream failed, as its own failure: `cat: -:
 * Bad file descriptor` on its stderr and status. Anything but a filesystem
 * error is rethrown. Mirrors Python's failed_read.
 */
export declare function failedRead(io: IOResult, err: unknown, execNode: ExecutionNode | null): Promise<void>;
/**
 * Exit status of an assignment-only statement.
 *
 * Bash: an assignment statement exits 0 unless expanding it ran
 * command substitutions, in which case the status of the last
 * substitution performed becomes the statement's own.
 */
/**
 * What the shell's fd 0 is bound to: the descriptor `exec <` opened, and
 * whether an `exec` left it unreadable (`exec <&-`). A construct takes
 * this as it starts, so `statementStdin` can tell an `exec` made inside
 * it from one made before it.
 */
export declare function fd0Binding(session: SessionState): readonly [SharedInput | null, boolean];
/**
 * The stdin one statement of a construct reads.
 *
 * bash's fd 0 is one descriptor, so an `exec <` replaces whatever a
 * construct was handed: `printf z | { exec < f; read a; }` reads `f`.
 * Every statement-list loop asks this before each statement. The
 * construct's own stdin stands while fd 0 is still what it was when the
 * construct started (`bound`); a construct handed none reads fd 0, the
 * descriptor `exec <` opened or EBADF after `exec <&-`.
 */
export declare function statementStdin(session: SessionState, stdin: ByteSource | null, bound: readonly [SharedInput | null, boolean]): ByteSource | null;
export declare function assignmentStatus(frame: ExecutionFrame, seqBefore: number): number;
/**
 * One piece of what a statement wrote, in order: its channel, its bytes, and
 * whether it went to the terminal through a copy, which an `exec` diversion of
 * the shell's own output leaves where it is. Mirrors Python's Written.
 */
export type Written = readonly [Channel, ByteSource, boolean];
/**
 * Output written as it is, stdout then stderr, none of it through a copy of
 * the terminal. Mirrors Python's as_written.
 */
export declare function asWritten(stdout: Uint8Array | null, stderr: Uint8Array | null): Written[];
/**
 * Run a statement into `recorder`: what it writes to an enclosing level's
 * stream lands among what it writes. Mirrors Python's recording.
 */
export declare function recording<T>(recorder: Recorder, run: () => Promise<T>): Promise<T>;
/**
 * Send what the jobs this shell started write into `recorder`, among what its
 * statement writes, until the returned call ends it. The recording lasts past
 * the statement, until `landLate` has landed what was recorded, so a job
 * writing in between cannot reach the shell's own output ahead of the
 * statement. Ended with `after`, what jobs write next waits for it. Mirrors
 * Python's record_jobs.
 */
export declare function recordJobs(session: SessionState, recorder: Recorder): (after?: Promise<void>) => void;
/**
 * Land what jobs wrote into `recorder` once the statement's output was taken,
 * and end the recording (`recordJobs`). What jobs write from the end on waits
 * until what was recorded has landed, so nothing written later overtakes it,
 * and nothing is left in `recorder` unread. Mirrors Python's land_late.
 */
export declare function landLate(recorder: Recorder, leave: (after?: Promise<void>) => void, own: StreamOwner | null, sink: JobConsole | null, allStdout: (ByteSource | null)[], mergedIo: IOResult): Promise<IOResult>;
/**
 * What a statement wrote that stays with the shell running it, taken off
 * `recorder`. Bytes written to the shell's terminal through a copy (`exec
 * 3>&1`, whose owner is `own`) stay, flagged; bytes written to an enclosing
 * level's stream go on there. What the statement returned rather than wrote
 * comes last, its stderr taken off `io`. Mirrors Python's statement_output.
 */
export declare function statementOutput(recorder: Recorder, stdout: ByteSource | null, io: IOResult, own: StreamOwner | null, sink: JobConsole | null): Promise<Written[]>;
/**
 * Put a statement's output where its shell's goes: the sink, in order, or the
 * stdout and stderr the shell returns. The result keeps its status. Mirrors
 * Python's land.
 */
export declare function land(written: readonly Written[], sink: JobConsole | null, allStdout: (ByteSource | null)[], mergedIo: IOResult): Promise<IOResult>;
//# sourceMappingURL=statement.d.ts.map