npm package: @neabyte/exec-command
Description: Execute shell commands in Node.js with timeouts, abort control, and streaming output
Author: NeaByteLab
Latest version: 0.1.1

# Exec Command [![](https://img.shields.io/npm/v/@neabyte/exec-command.svg)](https://www.npmjs.org/package/@neabyte/exec-command) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Node.js CI](https://github.com/NeaByteLab/Exec-Command/actions/workflows/ci.yaml/badge.svg)](https://github.com/NeaByteLab/Exec-Command/actions/workflows/ci.yaml)

Execute shell commands in Node.js with timeouts, abort control, and streaming output.

## 📦 Installation

```bash
npm install @neabyte/exec-command
```

## 🔧 Module Support

```typescript
// CommonJS
const exec = require('@neabyte/exec-command')

// ESM (ES Modules)
import exec from '@neabyte/exec-command'
```

---

## 📖 API Usage

### Basic Execution
```typescript
import exec from '@neabyte/exec-command'

// Simple command execution - collects all output before resolving
const result = await exec('echo "Hello World"')
console.log(result.stdout) // "Hello World"
console.log(result.stderr) // ""
console.log(result.exitCode) // 0
console.log(result.kill) // Function to terminate the process
```

### Streaming Output
```typescript
// Real-time output streaming - processes data as it arrives
const stream = exec('ping -c 5 google.com', { stream: true })

// Process output in real-time using async iteration
for await (const chunk of stream.output) {
  console.log(chunk.toString()) // Buffer containing raw output
}

// Wait for process completion
await stream.promise // Returns ExecResult when done

// Access kill method for early termination
stream.kill() // Terminates the process immediately
```

### Process Control
```typescript
// Kill running processes - works for both streaming and buffered modes
const stream = exec('ping -c 20 google.com', { stream: true })

setTimeout(() => {
  stream.kill() // Kill with default SIGTERM signal
  stream.kill('SIGKILL') // Kill with specific signal
}, 5000)

// Buffered process can also be killed
const buffered = exec('long-running-command')
buffered.kill() // Terminates immediately, promise will reject
```

### Timeout Control
```typescript
// Set timeout (in milliseconds) - process will be killed if it runs too long
const result = await exec('slow-command', { timeout: 5000 })
// Process will be killed after 5 seconds with SIGTERM, then SIGKILL after 5 more seconds

// No timeout (default)
const result2 = await exec('command', { timeout: 0 })
// Process runs indefinitely until completion or manual kill
```

### Working Directory
```typescript
// Execute in specific directory - changes the current working directory
const result = await exec('ls', { cwd: '/path/to/directory' })
// Lists files in the specified directory

// Use relative paths
const result2 = await exec('pwd', { cwd: './src' })
// Shows the absolute path of the src directory
```

### Environment Variables
```typescript
// Custom environment variables - merged with existing process.env
const result = await exec('echo $MY_VAR', {
  env: { MY_VAR: 'Hello World' }
})
// Output: "Hello World"

// Multiple environment variables
const result2 = await exec('node -e "console.log(process.env.VAR1, process.env.VAR2)"', {
  env: {
    VAR1: 'First',
    VAR2: 'Second'
  }
})
// Output: "First Second"

// Override existing environment variables
const result3 = await exec('echo $HOME', {
  env: { HOME: '/custom/home' }
})
// Output: "/custom/home"
```

### Shell Commands
```typescript
// Complex shell commands with operators - automatically detected and executed with shell
const result = await exec('ls -la | grep .txt')
// Lists files and filters for .txt files

// Chained commands with &&
const result2 = await exec('cd /tmp && pwd')
// Changes to /tmp directory and shows current path

// Environment variable expansion
const result3 = await exec('echo $HOME && echo $USER')
// Shows home directory and username

// Pipes and redirections
const result4 = await exec('echo "Hello" | wc -c')
// Counts characters in "Hello" (output: 6)

// Complex shell operations
const result5 = await exec('find . -name "*.js" | head -5')
// Finds first 5 JavaScript files
```

---

## ⚙️ Options Reference

All options are optional and can be combined:

```typescript
interface ExecOptions {
  /** Timeout duration in milliseconds (0 = no timeout) */
  timeout?: number
  /** Enable streaming mode for real-time output */
  stream?: boolean
  /** Working directory for command execution */
  cwd?: string
  /** Environment variables to pass to the process */
  env?: Record<string, string | undefined>
}
```

### Option Details

- **`timeout`**: Kill process after X milliseconds (0 = no timeout)
- **`stream`**: `true` = real-time output, `false` = collect all output first
- **`cwd`**: Run command in this directory
- **`env`**: Add these environment variables

### Return Types

**Buffered Mode** (`stream: false`):
```typescript
interface ExecResult {
  stdout: string                             // Standard output
  stderr: string                             // Standard error
  exitCode: number                           // Process exit code
  kill(signal?: string | number): void       // Kill function
  then: Promise<ExecResult>['then']          // Promise methods
  catch: Promise<ExecResult>['catch']
  finally: Promise<ExecResult>['finally']
}
```

**Streaming Mode** (`stream: true`):
```typescript
interface ExecStream {
  output: AsyncIterable<Buffer>              // Real-time output stream
  promise: Promise<ExecResult>               // Completion promise
  kill(signal?: string | number): void       // Kill function
}
```

---

## 📄 License

This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.