import { - type CreateOSProcess,
- daemon,
- type Daemon,
- DaemonExitError,
- exec,
- type Exec,
- ExecError,
- type ExecOptions,
- type ExitStatus,
- type Process,
- type ProcessResult,
- type StdIO,
- type Writable
} from "@effectionx/process"Process
Execute and manage system processes with structured concurrency. A library for spawning and controlling child processes in Effection programs.
This package provides two main functions: exec() for running processes with a
finite lifetime, and daemon() for long-running processes like servers.
Features
- Stream-based access to stdout and stderr
- Writable stdin for sending input to processes
- Proper signal handling and cleanup on both POSIX and Windows
- Shell mode for complex commands with glob expansion
- Structured error handling with
join()andexpect()methods
Basic Usage
Running a Command
Use exec() to run a command and wait for it to complete:
import { main } from "effection";
import { exec } from "@effectionx/process";
await main(function* () {
// Run a command and get the result
let result = yield* exec("echo 'Hello World'").join();
console.log(result.stdout); // "Hello World\n"
console.log(result.code); // 0
});
Streaming Output
Access stdout and stderr as streams for real-time output processing:
import { each, main, spawn } from "effection";
import { exec } from "@effectionx/process";
await main(function* () {
let process = yield* exec("npm install");
// Stream stdout in real-time
yield* spawn(function* () {
for (let chunk of yield* each(yield* process.stdout)) {
console.log(chunk);
yield* each.next();
}
});
// Wait for the process to complete
yield* process.expect();
});
Sending Input to stdin
Write to a process's stdin:
import { main } from "effection";
import { exec } from "@effectionx/process";
await main(function* () {
let process = yield* exec("cat");
process.stdin.send("Hello from stdin!\n");
let result = yield* process.join();
console.log(result.stdout); // "Hello from stdin!\n"
});
join() vs expect()
Both methods wait for the process to complete and collect stdout/stderr, but they differ in error handling:
join()- Always returns the result, regardless of exit codeexpect()- Throws anExecErrorif the process exits with a non-zero code
import { main } from "effection";
import { exec, ExecError } from "@effectionx/process";
await main(function* () {
// join() returns result even on failure
let result = yield* exec("exit 1", { shell: true }).join();
console.log(result.code); // 1
// expect() throws on non-zero exit
try {
yield* exec("exit 1", { shell: true }).expect();
} catch (error) {
if (error instanceof ExecError) {
console.log(error.message); // Command failed with exit code 1
}
}
});
Running Daemons
Use daemon() for long-running processes like servers. Unlike exec(), a
daemon is expected to run forever - if it exits prematurely, it raises an error:
import { main, suspend } from "effection";
import { daemon } from "@effectionx/process";
await main(function* () {
// Start a web server
let server = yield* daemon("node server.js");
console.log(`Server started with PID: ${server.pid}`);
// The server will be automatically terminated when this scope exits
yield* suspend();
});
Options
The exec() and daemon() functions accept an options object:
interface ExecOptions {
// Additional arguments to pass to the command
arguments?: string[];
// Environment variables for the process
env?: Record<string, string>;
// Use shell to interpret the command (enables glob expansion, pipes, etc.)
// Can be true for default shell or a path to a specific shell
shell?: boolean | string;
// Working directory for the process
cwd?: string;
}
Examples
import { main } from "effection";
import { exec } from "@effectionx/process";
await main(function* () {
// Pass arguments
yield* exec("git", {
arguments: ["commit", "-m", "Initial commit"],
}).expect();
// Set environment variables
yield* exec("node app.js", {
env: { NODE_ENV: "production", PORT: "3000" },
}).expect();
// Use shell mode for complex commands
yield* exec("ls *.ts | wc -l", {
shell: true,
}).expect();
// Set working directory
yield* exec("npm install", {
cwd: "./packages/my-package",
}).expect();
});
Process Interface
The Process object returned by exec() provides:
interface Process {
// Process ID
readonly pid: number;
// Output streams
stdout: Stream<string>;
stderr: Stream<string>;
// Input stream
stdin: Writable<string>;
// Wait for completion (returns exit status)
join(): Operation<ExitStatus>;
// Wait for successful completion (throws on non-zero exit)
expect(): Operation<ExitStatus>;
}
API Reference
interface Daemon extends Operation<void>, Process
function daemon(command: string, ): Operation<Daemon>
Start a long-running process, like a web server that run perpetually. Daemon operations are expected to run forever, and if they exit pre-maturely before the operation containing them passes out of scope it raises an error.
Parameters
command: string
Return Type
Operation<Daemon>
interface Exec extends Operation<Process>
Methods
- join(): Operation<ProcessResult>
No documentation available.
- expect(): Operation<ProcessResult>
No documentation available.
function exec(command: string, ): Exec
Execute command with options. You should use this operation for processes
that have a finite lifetime and on which you may wish to synchronize on the
exit status. If you want to start a process like a server that spins up and runs
forever, consider using daemon()
Parameters
command: string
Return Type
interface Writable<T>
interface Process extends StdIO
The process type is what is returned by the exec operation. It has all of
standard io handles, and methods for synchronizing on return.
Properties
- pid: number
No documentation available.
Methods
- join(): Operation<ExitStatus>
Completes once the process has finished regardless of whether it was successful or not.
- expect(): Operation<ExitStatus>
Completes once the process has finished successfully. If the process does not complete successfully, it will raise an ExecError.
interface ExecOptions
Properties
- arguments: string[]
When not using passing the
shelloption all arguments must be passed as an array.- env: Record<string, string>
Map of environment variables to use for the process.
- shell: boolean | string
Create an intermediate shell process; defaults to
false. Useful if you need to handle glob expansion or passing environment variables. A truthy value will use an intermediate shell to interpret the command using the default system shell. However, if the value is a string, that will be used as the executable path for the intermediate shell.- cwd: string
Sets the working directory of the process
interface StdIO
Properties
- stdout: OutputStream
No documentation available.
- stderr: OutputStream
No documentation available.
- stdin: Writable<string>
No documentation available.
interface ExitStatus
interface ProcessResult extends ExitStatus
interface CreateOSProcess
class ExecError extends Error implements
Constructors
- new ExecError(status: ExitStatus, command: string, options: ExecOptions)
Methods
- message(): string
No documentation available.