# @effectionx/process

> Spawn and manage child processes with structured concurrency

Latest version **0.8.3** (published 2026-09-03) · MIT license · 0 weekly downloads

## Install

```sh
npm install @effectionx/process
pnpm add @effectionx/process
yarn add @effectionx/process
bun add @effectionx/process
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.8.3 |
| Published | 2026-09-03 |
| First published | 2025-10-18 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 6 |
| Unpacked size | 93.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 12 |
| Author | engineering@frontside.com |
| Maintainers | frontsidejack |
| Keywords | process |

## Links

- npm: https://www.npmjs.com/package/@effectionx/process
- Repository: https://github.com/thefrontside/effectionx
- Homepage: https://github.com/thefrontside/effectionx#readme
- Issues: https://github.com/thefrontside/effectionx/issues
- npm.io page: https://npm.io/package/@effectionx/process

## Dependencies (6)

- [cross-spawn](https://npm.io/package/cross-spawn.md) ^7
- [ctrlc-windows](https://npm.io/package/ctrlc-windows.md) ^2
- [shellwords-ts](https://npm.io/package/shellwords-ts.md) ^3.0.1
- [@effectionx/node](https://npm.io/package/@effectionx/node.md) 0.2.5
- [@effectionx/scope-eval](https://npm.io/package/@effectionx/scope-eval.md) 0.1.3
- [@effectionx/context-api](https://npm.io/package/@effectionx/context-api.md) 0.6.0

## Recent versions

- 0.8.3 (latest) — 2026-09-03
- 0.8.2 — 2026-08-10
- 0.8.1 — 2026-06-15
- 0.8.0 — 2026-04-06
- 0.7.4 — 2026-03-22
- 0.7.3 — 2026-02-23
- 0.7.2 — 2026-02-12
- 0.7.1 — 2026-01-21
- 0.7.0 — 2026-01-18
- 0.6.2 — 2025-12-12
- 0.6.1 — 2025-12-08
- 0.6.0 — 2025-12-08
- 0.5.0 — 2025-10-18

## README

# 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()` and `expect()` methods

## Basic Usage

### Running a Command

Use `exec()` to run a command and wait for it to complete:

```typescript
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:

```typescript
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(process.stdout)) {
      console.log(chunk);
      yield* each.next();
    }
  });

  // Wait for the process to complete
  yield* process.expect();
});
```

### Handling Process Output With Middleware

By default, we log the output, but you can remove or add additional handling of output lines per `stdout` and `stderr`.

```typescript
import { each, main, spawn } from "effection";
import { exec } from "@effectionx/process";

await main(function* () {
  let process = yield* exec("npm install");

  yield* process.around({
    *stdout(line) {
      // it does this by default
      process.stdout.write(line);
    },
    *stderr(line) {
      // it does this by default
      process.stderr.write(line);
    },
  });

  // Wait for the process to complete
  yield* process.expect();
});
```

### Sending Input to stdin

Write to a process's stdin:

```typescript
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 code
- **`expect()`** - Throws an `ExecError` if the process exits with a non-zero
  code

```typescript
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:

```typescript
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:

```typescript
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

```typescript
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:

```typescript
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>;
}
```

---
_Source: https://npm.io/package/@effectionx/process · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
