# core/adapters/shell.adapter.ts

> 77 lines of code and 9 definitions.

Tree: GovLab Pipeline
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/pipeline#file-pipeline-core-adapters-shell-adapter-ts
Source text: https://banes-lab.com/source/pipeline/core/adapters/shell.adapter.ts.txt

Listed in [core/adapters](https://banes-lab.com/api/source/pipeline/core/adapters.md).

## Definitions

- `relay` (lexical_declaration, line 63, exported)
- `runTee` (lexical_declaration, line 55, exported)
- `spawnCommand` (lexical_declaration, line 7)
- `runCaptured` (lexical_declaration, line 20, exported)
- `collect` (lexical_declaration, line 28, exported)
- `runLive` (lexical_declaration, line 42, exported)
- `child` (lexical_declaration, line 61, exported)
- `chunks` (lexical_declaration, line 62, exported)
- `text` (lexical_declaration, line 64, exported)

## Contained in

- [core/adapters](https://banes-lab.com/anatomy/pipeline/folder-pipeline-core-adapters.md)

## Linked from

- [core/adapters](https://banes-lab.com/anatomy/pipeline/folder-pipeline-core-adapters.md)

## Source

```typescript
import type { CapturedOutcome, ShellDescriptor } from "#types/shell.types";
import { FALLBACK_EXIT, FLAG_SEPARATOR } from "#configuration/constants/shell.constants";
import type { WriteStream } from "node:tty";
import process from "node:process";
import { spawn } from "node:child_process";

const spawnCommand = function spawnCommand(
    shell: ShellDescriptor,
    command: string,
    stdio: "inherit" | "pipe",
    cwd?: string,
): ReturnType<typeof spawn> {
    return spawn(shell.shell, [...shell.flag.split(FLAG_SEPARATOR), command], {
        cwd,
        stdio,
        windowsVerbatimArguments: true,
    });
};

export const runCaptured = async function runCaptured(
    shell: ShellDescriptor,
    command: string,
    cwd?: string,
): Promise<CapturedOutcome> {
    return new Promise((resolve) => {
        const child = spawnCommand(shell, command, "pipe", cwd);
        const chunks: string[] = [];
        const collect = (buffer: Buffer): void => {
            chunks.push(buffer.toString("utf8"));
        };
        child.stdout?.on("data", collect);
        child.stderr?.on("data", collect);
        child.on("error", (error: Error) => {
            chunks.push(`${error.message}\n`);
        });
        child.on("close", (code: number | null) => {
            resolve({ code: code ?? FALLBACK_EXIT, out: chunks.join("") });
        });
    });
};

export const runLive = async function runLive(shell: ShellDescriptor, command: string): Promise<number> {
    return new Promise((resolve) => {
        const child = spawnCommand(shell, command, "inherit");
        child.on("error", (error: Error) => {
            process.stderr.write(`${error.message}\n`);
            resolve(FALLBACK_EXIT);
        });
        child.on("close", (code: number | null) => {
            resolve(code ?? FALLBACK_EXIT);
        });
    });
};

export const runTee = async function runTee(
    shell: ShellDescriptor,
    command: string,
    cwd?: string,
): Promise<CapturedOutcome> {
    return new Promise((resolve) => {
        const child = spawnCommand(shell, command, "pipe", cwd);
        const chunks: string[] = [];
        const relay = (buffer: Buffer, stream: WriteStream): void => {
            const text = buffer.toString("utf8");
            chunks.push(text);
            stream.write(text);
        };
        child.stdout?.on("data", (buffer: Buffer) => {
            relay(buffer, process.stdout);
        });
        child.stderr?.on("data", (buffer: Buffer) => {
            relay(buffer, process.stderr);
        });
        child.on("error", (error: Error) => {
            chunks.push(`${error.message}\n`);
        });
        child.on("close", (code: number | null) => {
            resolve({ code: code ?? FALLBACK_EXIT, out: chunks.join("") });
        });
    });
};
```
