import type { Readable } from "node:stream"; import { Context, Effect, Semaphore } from "effect"; /** What a captured command produced. */ export type RunResult = { readonly stdout: string; readonly stderr: string; readonly exitCode: number; /** The `timeoutMs` budget fired and the process group was SIGKILLed. */ readonly timedOut: boolean; }; export type RunOptions = { /** Working directory. Defaults to the current process's cwd. */ readonly cwd?: string; /** Written to the child's stdin, which is then closed. */ readonly input?: string; /** Budget after which the process group is SIGKILLed and `timedOut` is set. */ readonly timeoutMs?: number; /** Merged over `process.env`. */ readonly env?: Record; /** Compatibility cancellation for Promise/TanStack callers. */ readonly signal?: AbortSignal; }; /** Streaming deliberately has no `signal` or `timeoutMs` compatibility fields. */ export type RunStreamingOptions = { /** Working directory. Defaults to the current process's cwd. */ readonly cwd?: string; /** Merged over `process.env`. */ readonly env?: Record; /** Called once per sanitized line of stdout and stderr, as they arrive. */ readonly onLine?: (line: string) => void; /** Opt-in lifecycle deadline. The child is killed and fully joined. */ readonly killAfterMs?: number; }; declare const ProcSpawnError_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "ProcSpawnError"; } & Readonly; /** The child could not be started at all. */ export declare class ProcSpawnError extends ProcSpawnError_base<{ readonly argv: readonly string[]; readonly cause: unknown; }> { get message(): string; } declare const ProcReadError_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "ProcReadError"; } & Readonly; /** One of the child's streams failed, or a line callback threw. */ export declare class ProcReadError extends ProcReadError_base<{ readonly argv: readonly string[]; readonly stream: "stdout" | "stderr" | "stdin"; readonly cause: unknown; }> { get message(): string; } declare const ProcNonZeroExitError_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "ProcNonZeroExitError"; } & Readonly; /** The command ran to completion with a nonzero exit code. */ export declare class ProcNonZeroExitError extends ProcNonZeroExitError_base<{ readonly argv: readonly string[]; readonly result: RunResult; }> { get message(): string; } declare const ProcTimeoutError_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "ProcTimeoutError"; } & Readonly; /** The command blew its `timeoutMs` budget and was killed. */ export declare class ProcTimeoutError extends ProcTimeoutError_base<{ readonly argv: readonly string[]; readonly timeoutMs: number; readonly result: RunResult; }> { get message(): string; } declare const ProcInterruptedError_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "ProcInterruptedError"; } & Readonly; /** An external `AbortSignal` cancelled the command. */ export declare class ProcInterruptedError extends ProcInterruptedError_base<{ readonly argv: readonly string[]; }> { get message(): string; } /** Every expected failure the functions in this module can produce. */ export type ProcError = ProcSpawnError | ProcReadError | ProcNonZeroExitError | ProcTimeoutError | ProcInterruptedError; /** * How many `run` calls may hold a live child at once. The default is a shared * 8-permit semaphore; override it for a whole program with * `Effect.provideService(ProcConcurrency, Semaphore.makeUnsafe(n))`. */ export declare const ProcConcurrency: Context.Reference; /** * Run a command and capture its output. * * `cwd` defaults to the current process's working directory. Waits for a * concurrency permit first, so a saturated program queues rather than forking * unboundedly. Interrupting the fiber kills the process group and joins it * before the effect finishes interrupting. */ export declare const run: (argv: readonly string[], opts?: RunOptions | undefined) => Effect.Effect; /** * Run a command and return its trimmed stdout, failing with the precise * expected cause: `ProcTimeoutError` when the budget blew, and * `ProcNonZeroExitError` when the command itself failed. */ export declare function runOk(argv: readonly string[], opts?: RunOptions): Effect.Effect; /** Whether the command exited zero. Output is captured and discarded. */ export declare function runQuiet(argv: readonly string[], opts?: RunOptions): Effect.Effect; /** * Make one line of subprocess output safe to log or render: strip ANSI escape * and OSC sequences, keep only the text after the last carriage return (so a * progress bar reports its final state), turn tabs into spaces, and drop the * remaining control bytes. */ export declare function sanitizeLine(line: string): string; /** * Drain a readable stream, calling `onLine` once per sanitized line and once * more for a trailing fragment without a newline. Interrupting the effect * removes the listeners and destroys the stream. */ export declare function streamLines(readable: Readable, onLine: (line: string) => void): Effect.Effect; /** * Run a command, reporting stdout and stderr line by line as they arrive, and * resolve its exit code. * * `killAfterMs` is opt-in on purpose: an always-armed deadline would put a * ceiling on legitimately long commands like `pnpm install`. When it fires it * emits one more line, SIGKILLs the process group, and still joins fully, so a * hung teardown command cannot strand its caller. */ export declare const runStreaming: (argv: readonly string[], opts?: RunStreamingOptions | undefined) => Effect.Effect; /** * The part of a `ChildProcess` that `terminateSubprocess` needs. A real * `ChildProcess` satisfies it, and so does a test double. */ export interface TerminableProcess { readonly exitCode: number | null; kill(signal?: NodeJS.Signals): unknown; once(event: "exit", listener: () => void): unknown; removeListener(event: "exit", listener: () => void): unknown; } /** * Terminate a child this module did not spawn, typically one with inherited * stdio for an interactive handoff, without letting scope shutdown wait * forever: SIGTERM, a bounded grace period, SIGKILL, then a full join. * * Signals the process itself rather than its group, because an inherited-stdio * child usually shares the caller's process group. */ export declare const terminateSubprocess: (proc: TerminableProcess, graceMs?: any) => Effect.Effect; export {};