@facetlayer/subprocess-wrapper
v1.1.0
Published
A helpful wrapper around Node.js child processes. Adds line-based parsing, event listeners, and promise support.
Readme
subprocess-wrapper
A helpful wrapper around Node.js child processes. Adds line-based parsing, event listeners, and promise support.
Features
- Parses stdout and stderr as lines
- Event listeners for stdout/stderr output
- Output buffering with getters
- Promise-based process completion
- Error handling and process management
- Shell command convenience functions
Installation
npm install @facetlayer/subprocess-wrapperExamples
Running a subprcess and just get the parsed output:
import { runShellCommand } from '@facetlayer/subprocess-wrapper';
// Run a command and wait for completion
const result = await runShellCommand('ls', ['-la']);
const stdout: string[] = result.stdout;
console.log('The command printed:', result.stdout);Launching a subprocess and waiting for lifecycle events:
import { runShellCommand } from '@facetlayer/subprocess-wrapper';
const subprocess = startShellCommand('ping', ['google.com'], {
cwd: '/tmp/project',
onStdout: (line) => console.log('OUT:', line),
onStderr: (line) => console.log('ERR:', line)
});
await subprocess.waitForStart();
// ...
await subprocess.waitForExit();API Reference
High-Level Functions
runShellCommand(command, args?, options?)
Runs a shell command and returns a promise that resolves with the complete result.
This is a high-level convenience function that uses startShellCommand (below).
Similar to child_process.exec.
Does not throw an error if the subprocess had a non-zero error code. Check
the .exitCode of the result to see if the process had a non-zero code.
Parameters:
command: string- Executable to runargs?: string[]- Arguments passed to the executableoptions?: StartShellCommandOptions- Configuration options
Returns: Promise<SubprocessResult>
Example:
const result = await runShellCommand('echo', ['Hello World']);
console.log(result.stdout); // ['Hello World']
console.log(result.exitCode); // 0
console.log(result.failed()); // falsestartShellCommand(command, args?, options?)
Starts a shell command and returns a Subprocess instance for real-time interaction.
Similar to child_process.spawn, and accepts any child_process spawn options in addition to
wrapper-specific settings like output buffering and line listeners.
Parameters:
command: string- Executable to runargs?: string[]- Arguments passed to the executableoptions?: StartShellCommandOptions- Configuration options
Returns: Subprocess (see details below);
Example:
const subprocess = startShellCommand('tail', ['-f', '/var/log/system.log'], {
onStdout: (line) => console.log('LOG:', line),
enableOutputBuffering: false
});
// Stop the process after 10 seconds
setTimeout(() => subprocess.kill(), 10000);Classes
Subprocess
Main class for managing a subprocess with event-driven output handling.
Constructor:
new Subprocess(options?: { enableOutputBuffering?: boolean })Fields:
.proc
- The underlying ChildProcess object.
Methods:
spawn(command, args?, options?)
Spawns the subprocess.
command: string- Executable to runargs?: string[]- Arguments passed to the executableoptions?: SubprocessOptions- Node.js spawn options
Example:
const subprocess = new Subprocess();
subprocess.onStdout(line => console.log('Output:', line));
subprocess.spawn('ls', ['-la'], { cwd: '/tmp' });
await subprocess.waitForExit();onStdout(listener)
Adds a callback listener that will be triggered for each stdout line.
listener: (line: string) => void- Callback for each stdout line
Example:
subprocess.onStdout((line) => {
console.log('Received:', line);
});onStderr(listener)
Adds a callback listener that will be triggered for each stderr line.
listener: (line: string) => void- Callback for each stderr line
Example:
subprocess.onStderr((line) => {
console.error('Error output:', line);
});getStdout()
Returns all stdout lines as an array (requires enableOutputBuffering: true).
Returns: string[]
Example:
const subprocess = new Subprocess({ enableOutputBuffering: true });
subprocess.spawn('ls', ['-la']);
await subprocess.waitForExit();
const lines = subprocess.getStdout();
console.log('All output lines:', lines);getStderr()
Returns all stderr lines as an array (requires enableOutputBuffering: true).
Returns: string[]
waitForStart()
Returns a promise that resolves when the process successfully starts. Will throw a promise rejection if the process failed to start (such as an invalid command string).
waitForExit()
Returns a promise that resolves when the process exits.
Resolves to the numeric exit code value.
Does not throw a promise rejection for a non-zero exit code. Check the .exitCode value to see
if the process had a non-zero code.
Returns: Promise<number>
Example:
const subprocess = startShellCommand('sleep', ['5']);
console.log('Process started...');
const exitCode = await subprocess.waitForExit();
console.log('Process completed!');kill()
Terminates the running process.
Example:
const subprocess = startShellCommand('ping', ['google.com']);
// Kill after 5 seconds
setTimeout(() => {
subprocess.kill();
console.log('Process terminated');
}, 5000);SubprocessResult
Result object returned by runShellCommand().
Properties:
exitCode: number- Process exit codestdout: string[]- Array of stdout linesstderr: string[]- Array of stderr linessubprocess: Subprocess- Reference to the subprocess instance
Methods:
failed()
Returns true if the process failed (non-zero exit code).
Returns: boolean
asError()
Converts the result to an Error object (only if failed).
Returns: Error
Example:
const result = await runShellCommand('cat', ['nonexistent-file']);
if (result.failed()) {
throw result.asError(); // Throws error with stderr message
}stdoutAsString()
Returns stdout as a single string with newlines.
Returns: string
stderrAsString()
Returns stderr as a single string with newlines.
Returns: string
Example:
const result = await runShellCommand('ls', ['-la']);
console.log(result.stdoutAsString()); // Multi-line string outputOptions
StartShellCommandOptions
Configuration options for shell commands. This interface extends Node's SpawnOptions,
so you can pass any options supported by child_process.spawn.
interface StartShellCommandOptions extends SpawnOptions {
enableOutputBuffering?: boolean; // Buffer output for getStdout/getStderr
onStdout?: (line: string) => void; // Stdout line callback
onStderr?: (line: string) => void; // Stderr line callback
}Output buffering is enabled by default. Set enableOutputBuffering to false
if you want to avoid storing stdout/stderr lines in memory.
Usage Examples
Basic Command Execution
import { runShellCommand } from '@facetlayer/subprocess-wrapper';
// Simple command
const result = await runShellCommand('echo', ['Hello']);
console.log(result.stdout[0]); // "Hello"
// Command with arguments
const result2 = await runShellCommand('ls', ['-la', '/tmp']);
console.log(result2.stdout);Real-time Output Processing
import { startShellCommand } from '@facetlayer/subprocess-wrapper';
const subprocess = startShellCommand('npm', ['install'], {
cwd: '/path/to/project',
onStdout: (line) => console.log('📦', line),
onStderr: (line) => console.error('❌', line)
});
await subprocess.waitForExit();
console.log('Installation complete!');Long-running Process Management
const logWatcher = startShellCommand('tail', ['-f', '/var/log/app.log'], {
enableOutputBuffering: false,
onStdout: (line) => {
if (line.includes('ERROR')) {
console.error('🚨 Error detected:', line);
}
}
});
// Stop watching after 1 minute
setTimeout(() => logWatcher.kill(), 60000);Error Handling
try {
const result = await runShellCommand('invalid-command');
if (result.failed()) {
console.error('Command failed:', result.asError().message);
console.error('Stderr:', result.stderrAsString());
}
} catch (error) {
console.error('Execution error:', error.message);
}Custom Environment and Working Directory
const result = await runShellCommand('node', ['build.js'], {
cwd: '/path/to/project',
env: {
...process.env,
NODE_ENV: 'production',
BUILD_TARGET: 'web'
}
});Collecting Output Arrays
const subprocess = new Subprocess({ enableOutputBuffering: true });
subprocess.spawn('find', ['.', '-name', '*.js']);
await subprocess.waitForExit();
const jsFiles = subprocess.getStdout();
console.log(`Found ${jsFiles.length} JavaScript files:`);
jsFiles.forEach(file => console.log(file));Advanced Usage
Multiple Listeners
const subprocess = startShellCommand('build-script.sh');
// Add multiple stdout listeners
subprocess.onStdout((line) => logToFile(line));
subprocess.onStdout((line) => updateProgressBar(line));
subprocess.onStdout((line) => checkForWarnings(line));Process Monitoring
const subprocess = startShellCommand('long-running-process');
subprocess.onStdout((line) => console.log('OUT:', line));
subprocess.onStderr((line) => console.log('ERR:', line));
const timeout = setTimeout(() => {
console.log('Process taking too long, killing...');
subprocess.kill();
}, 30000);
await subprocess.waitForExit();
clearTimeout(timeout);
console.log('Process completed with code:', subprocess.exitCode);