@rcompat/io
v0.13.0
Published
Standard library input/output
Readme
@rcompat/io
Standard input/output utilities for JavaScript runtimes.
What is @rcompat/io?
A cross-runtime module providing access to standard streams (stdin, stdout, stderr) and process utilities like executing commands, spawning processes, and reading user input. Works consistently across Node, Deno, and Bun.
Installation
npm install @rcompat/iopnpm add @rcompat/ioyarn add @rcompat/iobun add @rcompat/ioUsage
Standard streams
import io from "@rcompat/io";
// write to stdout (no newline)
io.stdout.write("Hello, ");
io.stdout.write("World!\n");
// write to stderr
io.stderr.write("Error: something went wrong\n");Check if running in a terminal
import io from "@rcompat/io";
if (io.isatty()) {
// interactive terminal - show colors, prompts, etc.
console.log("\x1b[32mGreen text!\x1b[0m");
} else {
// piped or redirected - plain output
console.log("Plain text");
}Run a command
import io from "@rcompat/io";
// capture output and exit status
const result = await io.run("echo hello");
console.log(result.stdout); // "hello\n"
console.log(result.success); // true
// with options
const files = await io.run("ls -la", { cwd: "/tmp", timeout: 5_000 });Spawn a process
import io from "@rcompat/io";
const child = io.spawn("cat", { cwd: "." });
const { stdin, stdout } = child;
// write to process stdin (Web WritableStream)
const writer = stdin.getWriter();
await writer.write(new TextEncoder().encode("Hello from stdin!"));
await writer.close();
// read from process stdout (Web ReadableStream)
const reader = stdout.getReader();
const { value } = await reader.read();
console.log(new TextDecoder().decode(value)); // "Hello from stdin!"
const status = await child.status;
console.log(status.code); // 0Find an executable
import io from "@rcompat/io";
const node = await io.which("node");
console.log(node); // "/usr/local/bin/node"
const bun = await io.which("bun");
console.log(bun); // "/home/user/.bun/bin/bun"API Reference
stdin
import io from "@rcompat/io";
io.stdin;The standard input stream. A ReadStream for reading user input.
stdout
import io from "@rcompat/io";
io.stdout.write(data: string | Uint8Array): boolean;The standard output stream. Use write() to output without a newline.
stderr
import io from "@rcompat/io";
io.stderr.write(data: string | Uint8Array): boolean;The standard error stream. Use for error messages and diagnostics.
isatty
import io from "@rcompat/io";
io.isatty(): boolean;Returns true if stdout is connected to a terminal (TTY).
run
import io from "@rcompat/io";
io.run(command: string, options?: RunOptions): Promise<RunResult>;Runs a shell command, captures its output, and returns its exit status. A completed process does not reject solely because its exit code is nonzero. Startup and stream failures still reject.
| Parameter | Type | Description |
| --------- | ------------- | -------------------------- |
| command | string | Shell command to execute |
| options | RunOptions | Optional execution options |
interface RunResult {
code: number | null;
signal: ProcessSignal | null;
stderr: string;
stdout: string;
success: boolean;
timedOut: boolean;
}spawn
import io from "@rcompat/io";
io.spawn(command: string, options?: SpawnOptions): ChildProcess;Spawns a process and immediately returns a handle with Web Streams, lifecycle status, and process control.
| Parameter | Type | Description |
| --------- | -------------- | ------------------------- |
| command | string | Command to spawn |
| options | SpawnOptions | Portable spawn options |
interface SpawnOptions {
cwd?: string;
env?: Record<string, string | undefined>;
killSignal?: ProcessSignal; // timeout signal; default: "SIGKILL"
stdio?: "pipe" | "inherit"; // default: "pipe"
timeout?: number; // milliseconds
}
interface ChildProcess {
pid: number;
stdin: WritableStream<Uint8Array> | null;
stdout: ReadableStream<Uint8Array> | null;
stderr: ReadableStream<Uint8Array> | null;
status: Promise<ExitStatus>;
kill(signal?: ProcessSignal): void;
}
interface ExitStatus {
code: number | null;
signal: ProcessSignal | null;
success: boolean;
timedOut: boolean;
}Commands run through the platform shell. With stdio: "inherit", the child
inherits the current process's standard streams and its stream properties are
null. Nonzero exits resolve status with success: false; status rejects only
when the process cannot be started or monitored.
On POSIX platforms, spawned commands run in an isolated process group.
kill() and timeout handling signal the entire group so descendant processes
cannot continue working or keep captured output streams open after the command
has been terminated. A run() timeout covers process exit and complete output
capture, not only the lifetime of the wrapper shell.
which
import io from "@rcompat/io";
io.which(command: string): Promise<string>;Finds the full path to an executable. Cross-platform (uses which on Unix,
where on Windows).
Examples
Run a command and process output
import io from "@rcompat/io";
async function getBranch() {
const result = await io.run("git branch --show-current");
return result.success ? result.stdout.trim() : null;
}
const branch = await getBranch();
console.log(`Current branch: ${branch ?? "not a git repo"}`);Stream processing with spawn
import io from "@rcompat/io";
const child = io.spawn("ping -c 3 localhost", { cwd: "." });
const { stdout } = child;
const reader = stdout.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
process.stdout.write(decoder.decode(value));
}
const status = await child.status;
if (!status.success) console.error(status);Check for required tools
import io from "@rcompat/io";
async function checkDependencies(tools) {
const missing = [];
for (const tool of tools) {
try {
await io.which(tool);
} catch {
missing.push(tool);
}
}
return missing;
}
const missing = await checkDependencies(["git", "node", "docker"]);
if (missing.length > 0) {
console.error(`Missing tools: ${missing.join(", ")}`);
process.exit(1);
}Conditional formatting
import io from "@rcompat/io";
function log(message, color = 32) {
if (io.isatty()) {
io.stdout.write(`\x1b[${color}m${message}\x1b[0m\n`);
} else {
io.stdout.write(`${message}\n`);
}
}
log("Success!", 32); // green in terminal, plain when piped
log("Warning!", 33); // yellow in terminal
log("Error!", 31); // red in terminalCross-Runtime Compatibility
| Runtime | Supported | | ------- | --------- | | Node.js | ✓ | | Deno | ✓ | | Bun | ✓ |
No configuration required — just import and use.
License
MIT
Contributing
See CONTRIBUTING.md in the repository root.
