Child Processes
Effect models running external commands as two pieces: a Command value
that describes what to run, and the ChildProcessSpawner service that
actually runs it. A Command is a plain, immutable description — building or
composing one never starts a process — so you can construct, pipe, and reuse
commands freely. These APIs live under effect/unstable/process, and the Node
implementation comes from NodeServices.layer (or the narrower
NodeChildProcessSpawner.layer).
import { NodeServices } from "@effect/platform-node"import { Console, Context, Effect, Layer, Schema, Stream, String } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
// Wrap process failures in a domain error rather than leaking PlatformError.class DevToolsError extends Schema.TaggedErrorClass<DevToolsError>()( "DevToolsError", { cause: Schema.Defect }) {}
class DevTools extends Context.Service<DevTools, { readonly nodeVersion: Effect.Effect<string, DevToolsError> readonly runLintFix: Effect.Effect<void, DevToolsError>}>()("app/DevTools") { static readonly layer = Layer.effect( DevTools, Effect.gen(function*() { // Running a command requires a `ChildProcessSpawner`. const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
// `spawner.string` runs a command and collects ALL of its stdout into a // single string. Ideal for short, finite commands. const nodeVersion = spawner.string( ChildProcess.make("node", ["--version"]) ).pipe( Effect.map(String.trim), Effect.mapError((cause) => new DevToolsError({ cause })) )
const runLintFix = Effect.gen(function*() { // `spawner.spawn` returns a handle so you can stream output WHILE the // process is still running. It adds a `Scope` requirement to manage // the process lifecycle. const handle = yield* spawner.spawn( ChildProcess.make("pnpm", ["lint-fix"], { env: { FORCE_COLOR: "1" }, // set env vars for the child extendEnv: true // ...on top of the parent's env }) )
// `handle.all` interleaves stdout and stderr as a byte Stream. yield* handle.all.pipe( Stream.decodeText(), Stream.splitLines, Stream.runForEach((line) => Console.log(`[lint-fix] ${line}`)) )
// Wait for the process to finish and inspect its exit code. const exitCode = yield* handle.exitCode if (exitCode !== ChildProcessSpawner.ExitCode(0)) { return yield* new DevToolsError({ cause: new Error(`lint-fix exited with ${exitCode}`) }) } }).pipe( Effect.mapError((cause) => cause instanceof DevToolsError ? cause : new DevToolsError({ cause }) ), // `spawn` is scoped; `Effect.scoped` provides the scope and ensures the // child process is cleaned up when the effect completes or fails. Effect.scoped )
return { nodeVersion, runLintFix } as const }) ).pipe( // Provide the spawner from the Node platform services. Layer.provide(NodeServices.layer) )}Describing a command
Section titled “Describing a command”ChildProcess.make builds a Command. It accepts a program name with an array
of arguments, or a tagged-template form for shorthand. Arguments are passed
directly to the OS — they are not interpreted by a shell — so values
containing spaces or special characters are safe without quoting:
import { ChildProcess } from "effect/unstable/process"
// Explicit program + args (recommended; no shell quoting pitfalls).const a = ChildProcess.make("git", ["commit", "-m", "a message with spaces"])
// Tagged-template shorthand for simple commands.const b = ChildProcess.make`node --version`
// Configure the working directory, environment, and more via options.const c = ChildProcess.make("npm", ["test"], { cwd: "packages/core", env: { CI: "true" }, extendEnv: true})A Command is itself an Effect — yielding it spawns the process and returns a
handle (it calls spawn under the hood). The combinators below
(setCwd, setEnv, prefix, pipeTo) return a new Command without mutating
the original.
Collecting output
Section titled “Collecting output”When a command runs to completion and you want its output, the spawner offers convenience methods that run the command and return the result directly — no handle, no scope:
spawner.string(command)— all stdout as a singlestring.spawner.lines(command)— stdout split into an array of lines.spawner.exitCode(command)— just the exit code.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const changedFiles = Effect.fn("changedFiles")(function*(baseRef: string) { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
// `lines` is perfect for line-oriented commands like `git diff`. const files = yield* spawner.lines( ChildProcess.make("git", ["diff", "--name-only", `${baseRef}...HEAD`]) )
return files.filter((file) => file.endsWith(".ts"))})Each of these collectors accepts an optional { includeStderr?: boolean } to
fold stderr into the collected output.
Pipelines
Section titled “Pipelines”ChildProcess.pipeTo connects the output of one command to the input of
another, mirroring a shell pipe. The result is a single Command, so it runs as
a real OS pipeline rather than buffering through your process:
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const recentSubjects = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
// Equivalent to: git log --pretty=format:%s -n 20 | head -n 5 const command = ChildProcess.make("git", [ "log", "--pretty=format:%s", "-n", "20" ]).pipe( ChildProcess.pipeTo(ChildProcess.make("head", ["-n", "5"])) )
return yield* spawner.lines(command)})By default pipeTo connects stdout to stdin. Pass { from: "stderr" } or
{ from: "all" } to pipe a different stream, and { to: "fd3" } to target a
custom file descriptor on the destination.
Feeding stdin
Section titled “Feeding stdin”A command can take its standard input from a Stream of bytes. Set stdin in
the options to a Stream<Uint8Array, PlatformError> (use Stream.encodeText
to convert from text), and the spawner pipes it into the process:
import { Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const sortLines = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
// Pipe three lines of text into `sort` via the child's stdin. const input = Stream.make("banana\n", "apple\n", "cherry\n").pipe( Stream.encodeText )
return yield* spawner.lines( ChildProcess.make("sort", [], { stdin: input }) ) // => ["apple", "banana", "cherry"]})When you have a handle from spawn, handle.stdin is a Sink you can run a
stream into for interactive, incremental writes:
import { Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const program = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn(ChildProcess.make`cat`)
// Write to the process input by running a stream into `handle.stdin`. yield* Stream.make("hello\n").pipe( Stream.encodeText, Stream.run(handle.stdin) )}).pipe(Effect.scoped)Streaming a running process
Section titled “Streaming a running process”For long-running commands — build watchers, log tailers, servers — you want to
react to output as it arrives. spawner.spawn returns a ChildProcessHandle
exposing:
handle.stdout,handle.stderr,handle.all— output as byteStreams.handle.stdin— aSinkto write to the process input.handle.exitCode— an effect that completes when the process exits.handle.isRunning,handle.pid, andhandle.killfor control.
You can also stream output without a handle using spawner.streamLines or
spawner.streamString, which return a Stream directly:
import { Console, Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const tailBuild = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
yield* spawner.streamLines( ChildProcess.make("npm", ["run", "build", "--", "--watch"]), { includeStderr: true } // interleave stderr with stdout ).pipe( Stream.runForEach((line) => Console.log(`[build] ${line}`)) )})Inspecting exit codes and errors
Section titled “Inspecting exit codes and errors”Exit codes are branded with ExitCode to keep them distinct from ordinary
numbers. Compare against ChildProcessSpawner.ExitCode(0) to check for success:
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const typecheck = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
const code = yield* spawner.exitCode( ChildProcess.make("tsc", ["--noEmit"]) )
return code === ChildProcessSpawner.ExitCode(0) ? "passed" : "failed"})Failures to spawn, write to, or read from a process surface in the error
channel as a PlatformError — not as a defect. A non-zero exit code, by
contrast, is a successful run that produced a non-zero code, so you check it
explicitly as above. Wrap PlatformError into a domain error with
Effect.mapError, or match on it with Effect.catchTag:
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const safe = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner return yield* spawner.string(ChildProcess.make`does-not-exist`)}).pipe( // PlatformError carries a `_tag` such as "SystemError" / "BadArgument". Effect.catch((error) => Effect.succeed(`failed: ${error._tag}`)))Running it
Section titled “Running it”Provide the spawner and run with NodeRuntime.runMain. Note how the program is
written entirely against the abstract ChildProcessSpawner — only the layer
mentions Node:
import { NodeRuntime } from "@effect/platform-node"import { Effect } from "effect"
// `DevTools` from the first example provides its own layer.const program = Effect.gen(function*() { const tools = yield* DevTools yield* Effect.log(`node=${yield* tools.nodeVersion}`)}).pipe(Effect.provide(DevTools.layer))
NodeRuntime.runMain(program)NodeServices.layer bundles the ChildProcessSpawner together with the other
core platform services (FileSystem, Path, Crypto, Terminal, Stdio). If you only
need to run processes, the narrower NodeChildProcessSpawner.layer provides just
the spawner. On Bun, use BunChildProcessSpawner.layer from
@effect/platform-bun.
import { NodeChildProcessSpawner } from "@effect/platform-node"import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const program = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner return yield* spawner.string(ChildProcess.make`echo hi`)}).pipe( // Provide only the spawner, not the full NodeServices bundle. Effect.provide(NodeChildProcessSpawner.layer))To consume command output with the full set of stream operators — buffering, batching, decoding — see the Streaming section.
API reference: ChildProcess
Section titled “API reference: ChildProcess”The ChildProcess module builds and combines Command values. Everything here
is pure: nothing runs until you spawn the command (by yielding it or passing it
to a ChildProcessSpawner method).
Builds a StandardCommand. Supports three calling conventions: a tagged
template, an options object followed by a tagged template, or an explicit
(command, args?, options?) array form.
import { ChildProcess } from "effect/unstable/process"
const t = ChildProcess.make`git status` // template formconst o = ChildProcess.make({ cwd: "/tmp" })`ls -la` // options + templateconst a = ChildProcess.make("git", ["status"]) // array form// => all three are StandardCommand values (not yet running)pipeTo
Section titled “pipeTo”Connects the output of one command to the input of another, producing a single
PipedCommand. Defaults to stdout → stdin; customize with
{ from, to }.
import { ChildProcess } from "effect/unstable/process"
const pipeline = ChildProcess.make`cat package.json`.pipe( ChildProcess.pipeTo(ChildProcess.make`grep name`))// => PipedCommand equivalent to: cat package.json | grep nameprefix
Section titled “prefix”Prepends another command in front of an existing one. For pipelines, only the
leftmost command is prefixed. Useful for wrappers like time or nice.
import { ChildProcess } from "effect/unstable/process"
const timed = ChildProcess.make`echo foo`.pipe(ChildProcess.prefix`time`)// => runs: time echo foosetCwd
Section titled “setCwd”Returns a new command with its working directory set. For pipelines, applies to every command in the pipeline.
import { ChildProcess } from "effect/unstable/process"
const cmd = ChildProcess.make`ls -la`.pipe(ChildProcess.setCwd("/tmp"))// => command that runs `ls -la` in /tmpsetEnv
Section titled “setEnv”Returns a new command with environment variables merged in (overriding duplicate keys). For pipelines, applies to every command in the pipeline.
import { ChildProcess } from "effect/unstable/process"
const cmd = ChildProcess.make`node script.js`.pipe( ChildProcess.setEnv({ NODE_ENV: "test" }))// => command with NODE_ENV=test added to its environmentisCommand
Section titled “isCommand”Type guard: returns true if a value is any Command.
import { ChildProcess } from "effect/unstable/process"
ChildProcess.isCommand(ChildProcess.make`ls`) // => trueChildProcess.isCommand("ls") // => falseisStandardCommand
Section titled “isStandardCommand”Narrows a Command to a StandardCommand (a single program, not a pipeline).
import { ChildProcess } from "effect/unstable/process"
ChildProcess.isStandardCommand(ChildProcess.make`ls`) // => trueisPipedCommand
Section titled “isPipedCommand”Narrows a Command to a PipedCommand (a pipeline built with pipeTo).
import { ChildProcess } from "effect/unstable/process"
const piped = ChildProcess.make`ls`.pipe( ChildProcess.pipeTo(ChildProcess.make`wc -l`))ChildProcess.isPipedCommand(piped) // => trueparseFdName
Section titled “parseFdName”Parses an fd name like "fd3" to its numeric index, returning undefined for
invalid names or indices below 3.
import { ChildProcess } from "effect/unstable/process"
ChildProcess.parseFdName("fd3") // => 3ChildProcess.parseFdName("fd1") // => undefined (must be >= 3)ChildProcess.parseFdName("stdout") // => undefinedfdName
Section titled “fdName”Creates an fd name string from a numeric index. The inverse of parseFdName.
import { ChildProcess } from "effect/unstable/process"
ChildProcess.fdName(3) // => "fd3"API reference: command types
Section titled “API reference: command types”Command
Section titled “Command”A union of StandardCommand | PipedCommand. Every Command is also an
Effect<ChildProcessHandle, PlatformError, ChildProcessSpawner | Scope>, so it
can be yielded directly to spawn.
StandardCommand
Section titled “StandardCommand”A single program: { _tag: "StandardCommand", command, args, options }. Produced
by make.
PipedCommand
Section titled “PipedCommand”A pipeline: { _tag: "PipedCommand", left, right, options }. Produced by
pipeTo.
CommandOptions
Section titled “CommandOptions”The options object accepted by make. It extends KillOptions and adds:
cwd, env, extendEnv, shell, detached, stdin, stdout, stderr, and
additionalFds.
import { ChildProcess } from "effect/unstable/process"
const cmd = ChildProcess.make("node", ["server.js"], { cwd: "/app", env: { PORT: "3000" }, extendEnv: true, // merge with process.env detached: false, killSignal: "SIGTERM", // from KillOptions forceKillAfter: "5 seconds"})KillOptions
Section titled “KillOptions”Controls termination: killSignal (defaults to "SIGTERM") and
forceKillAfter (a Duration.Input after which "SIGKILL" is sent). Used both
in CommandOptions and as the argument to handle.kill.
StdinConfig / StdoutConfig / StderrConfig
Section titled “StdinConfig / StdoutConfig / StderrConfig”Detailed per-stream configuration objects accepted by the stdin / stdout /
stderr options. StdinConfig carries stream (the CommandInput),
endOnDone, and encoding; StdoutConfig / StderrConfig carry stream (a
CommandOutput).
import { ChildProcess } from "effect/unstable/process"import { Stream } from "effect"
const cmd = ChildProcess.make("cat", [], { stdin: { stream: Stream.make("hi").pipe(Stream.encodeText), endOnDone: true, encoding: "utf-8" }, stdout: { stream: "pipe" }})CommandInput
Section titled “CommandInput”The set of stdin configurations: "pipe", "inherit", "ignore",
"overlapped", or a Stream<Uint8Array, PlatformError> piped into the process.
CommandOutput
Section titled “CommandOutput”The set of stdout/stderr configurations: "pipe", "inherit", "ignore",
"overlapped", or a Sink<Uint8Array, Uint8Array, never, PlatformError> that
receives the output.
PipeOptions
Section titled “PipeOptions”Options for pipeTo: from (a PipeFromOption) and to (a PipeToOption).
PipeFromOption / PipeToOption
Section titled “PipeFromOption / PipeToOption”PipeFromOption is "stdout" | "stderr" | "all" | `fd${number}` ;
PipeToOption is "stdin" | `fd${number}` .
import { ChildProcess } from "effect/unstable/process"
const p = ChildProcess.make`build`.pipe( ChildProcess.pipeTo(ChildProcess.make`tee log.txt`, { from: "all", to: "stdin" }))// => pipes interleaved stdout+stderr into tee's stdinAdditionalFdConfig
Section titled “AdditionalFdConfig”Configuration for extra file descriptors (fd3 and up) supplied via the
additionalFds option. Each entry is { type: "input", stream? } (parent
writes to child) or { type: "output", sink? } (parent reads from child).
import { ChildProcess } from "effect/unstable/process"
const cmd = ChildProcess.make("my-program", [], { additionalFds: { fd3: { type: "output" } // read child output on fd 3 via handle.getOutputFd(3) }})Signal
Section titled “Signal”A union of POSIX signal names ("SIGTERM", "SIGKILL", "SIGINT", …) accepted
by KillOptions.killSignal and handle.kill.
Encoding
Section titled “Encoding”A union of supported text encodings ("utf-8", "ascii", "hex", "base64",
…) used by StdinConfig.encoding.
TemplateExpression / TemplateExpressionItem
Section titled “TemplateExpression / TemplateExpressionItem”The values allowed inside make template literals: a string | number | boolean, or a readonly array of those (each element becomes a separate
argument).
import { ChildProcess } from "effect/unstable/process"
const files = ["a.ts", "b.ts"]const cmd = ChildProcess.make`prettier --write ${files}`// => prettier --write a.ts b.tsAPI reference: ChildProcessSpawner
Section titled “API reference: ChildProcessSpawner”The service that executes commands. Obtain it with yield* ChildProcessSpawner.ChildProcessSpawner. The convenience methods all accept an
optional { includeStderr?: boolean } to fold stderr into the output.
The primitive. Spawns a command and returns a scoped ChildProcessHandle for
streaming, writing stdin, and lifecycle control. Adds a Scope requirement.
import { Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const program = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn(ChildProcess.make`echo hi`) return yield* Stream.runCollect(handle.stdout) // => Chunk of bytes}).pipe(Effect.scoped)exitCode
Section titled “exitCode”Runs a command to completion and returns just its branded ExitCode. Manages
its own scope.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const code = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner return yield* spawner.exitCode(ChildProcess.make`true`)})// => ExitCode(0)string
Section titled “string”Runs a command and collects all output as a single string.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const out = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner return yield* spawner.string(ChildProcess.make`echo hello`)})// => "hello\n"Runs a command and collects output split into an Array<string> of lines.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const ls = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner return yield* spawner.lines(ChildProcess.make`ls`)})// => ["file-a.ts", "file-b.ts", ...]streamString
Section titled “streamString”Streams output as decoded text chunks (a Stream<string, PlatformError>)
without collecting it — good for long-running processes.
import { Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const program = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner yield* spawner.streamString(ChildProcess.make`tail -f log.txt`).pipe( Stream.runForEach((chunk) => Effect.log(chunk)) )})streamLines
Section titled “streamLines”Streams output split into lines as a Stream<string, PlatformError>. The
line-oriented counterpart of streamString.
import { Console, Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const program = Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner yield* spawner.streamLines(ChildProcess.make`tail -f log.txt`).pipe( Stream.runForEach((line) => Console.log(line)) )})API reference: ChildProcessHandle
Section titled “API reference: ChildProcessHandle”The handle returned by spawn. Bound to the surrounding Scope: when the scope
closes, the process is cleaned up.
The branded operating-system ProcessId of the running process.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn(ChildProcess.make`sleep 1`) return handle.pid // => ProcessId(12345)}).pipe(Effect.scoped)exitCode
Section titled “exitCode”An Effect<ExitCode, PlatformError> that completes with the branded exit code
when the process exits.
import { ChildProcessSpawner } from "effect/unstable/process"
// handle.exitCode// => yields ExitCode(0) on successChildProcessSpawner.ExitCode(0) // => the success code to compare againstisRunning
Section titled “isRunning”An Effect<boolean, PlatformError> reporting whether the process is still
running.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn(ChildProcess.make`sleep 5`) return yield* handle.isRunning // => true}).pipe(Effect.scoped)Terminates the process, optionally with custom KillOptions. Defaults to
SIGTERM.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn(ChildProcess.make`sleep 60`) yield* handle.kill({ killSignal: "SIGINT", forceKillAfter: "2 seconds" })}).pipe(Effect.scoped)A Sink<void, Uint8Array, never, PlatformError>. Run a byte stream into it to
write to the process’s standard input.
import { Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn(ChildProcess.make`cat`) yield* Stream.make("hi\n").pipe(Stream.encodeText, Stream.run(handle.stdin))}).pipe(Effect.scoped)stdout
Section titled “stdout”A Stream<Uint8Array, PlatformError> of the process’s standard output.
import { Stream } from "effect"// Stream.runCollect(handle.stdout) // => Chunk<Uint8Array>Streamstderr
Section titled “stderr”A Stream<Uint8Array, PlatformError> of the process’s standard error.
import { Stream } from "effect"// handle.stderr.pipe(Stream.decodeText, Stream.runForEach(Effect.log))StreamA Stream<Uint8Array, PlatformError> that interleaves stdout and stderr. Avoid
combining it with reading stdout/stderr separately, which can interleave
unexpectedly.
import { Stream } from "effect"// handle.all.pipe(Stream.decodeText, Stream.splitLines)StreamgetInputFd
Section titled “getInputFd”Returns an input Sink for an additional file descriptor configured via
additionalFds. Unconfigured fds return a draining sink.
import { Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn( ChildProcess.make("prog", [], { additionalFds: { fd3: { type: "input" } } }) ) yield* Stream.make("data").pipe( Stream.encodeText, Stream.run(handle.getInputFd(3)) )}).pipe(Effect.scoped)getOutputFd
Section titled “getOutputFd”Returns an output Stream for an additional output file descriptor.
Unconfigured fds return an empty stream.
import { Effect, Stream } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn( ChildProcess.make("prog", [], { additionalFds: { fd3: { type: "output" } } }) ) return yield* Stream.runCollect(handle.getOutputFd(3)) // => Chunk<Uint8Array>}).pipe(Effect.scoped)Removes the child from the parent’s reference count so the parent can exit
without waiting. Yields a Reref effect that re-references the child when run.
import { Effect } from "effect"import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
Effect.gen(function*() { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner const handle = yield* spawner.spawn(ChildProcess.make`./server`) const reref = yield* handle.unref // parent no longer blocked by child yield* Effect.sleep("1 second") yield* reref // restore default behavior}).pipe(Effect.scoped)API reference: branded values & lower-level helpers
Section titled “API reference: branded values & lower-level helpers”ExitCode
Section titled “ExitCode”A Brand.Branded<number, "ExitCode">. Construct or compare with
ChildProcessSpawner.ExitCode(n).
import { ChildProcessSpawner } from "effect/unstable/process"
ChildProcessSpawner.ExitCode(0) // => branded 0 (success)ProcessId
Section titled “ProcessId”A Brand.Branded<number, "ProcessId"> representing an OS process id, exposed as
handle.pid. Construct with ChildProcessSpawner.ProcessId(n).
import { ChildProcessSpawner } from "effect/unstable/process"
ChildProcessSpawner.ProcessId(12345) // => branded process idAn Effect<void, PlatformError> returned by handle.unref; running it
re-adds the child to the parent’s reference count.
makeHandle
Section titled “makeHandle”Constructs a ChildProcessHandle from its fields. Used when implementing a
custom platform spawner, not in application code.
ChildProcessSpawner.make
Section titled “ChildProcessSpawner.make”Builds a ChildProcessSpawner service from a single spawn implementation,
deriving exitCode, string, lines, streamString, and streamLines
automatically. This is how the Node and Bun backends are implemented.
import { ChildProcessSpawner } from "effect/unstable/process"
// const service = ChildProcessSpawner.make((command) => spawnImpl(command))ChildProcessSpawner.make // => derives the convenience helpers from `spawn`