aboutsummaryrefslogtreecommitdiff
path: root/docs/api/spawn.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/api/spawn.md')
-rw-r--r--docs/api/spawn.md336
1 files changed, 336 insertions, 0 deletions
diff --git a/docs/api/spawn.md b/docs/api/spawn.md
new file mode 100644
index 000000000..876a0577d
--- /dev/null
+++ b/docs/api/spawn.md
@@ -0,0 +1,336 @@
+Spawn child processes with `Bun.spawn` or `Bun.spawnSync`.
+
+## Spawn a process
+
+Provide a command as an array of strings. The result of `Bun.spawn()` is a `Bun.Subprocess` object.
+
+```ts
+Bun.spawn(["echo", "hello"]);
+```
+
+The second argument to `Bun.spawn` is a parameters object that can be used ton configure the subprocess.
+
+```ts
+const proc = Bun.spawn(["echo", "hello"], {
+ cwd: "./path/to/subdir", // specify a working direcory
+ env: { ...process.env, FOO: "bar" }, // specify environment variables
+ onExit(proc, exitCode, signalCode, error) {
+ // exit handler
+ },
+});
+
+proc.pid; // process ID of subprocess
+```
+
+## Input stream
+
+By default, the input stream of the subprocess is undefined; it can be configured with the `stdin` parameter.
+
+```ts
+const proc = Bun.spawn(["cat"], {
+ stdin: await fetch(
+ "https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js",
+ ),
+});
+
+const text = await new Response(proc.stdout).text();
+console.log(text); // "const input = "hello world".repeat(400); ..."
+```
+
+{% table %}
+
+---
+
+- `null`
+- **Default.** Provide no input to the subprocess
+
+---
+
+- `"pipe"`
+- Return a `FileSink` for fast incremental writing
+
+---
+
+- `"inherit"`
+- Inherit the `stdin` of the parent process
+
+---
+
+- `Bun.file()`
+- Read from the specified file.
+
+---
+
+- `TypedArray | DataView`
+- Use a binary buffer as input.
+
+---
+
+- `Response`
+- Use the response `body` as input.
+
+---
+
+- `Request`
+- Use the request `body` as input.
+
+---
+
+- `number`
+- Read from the file with a given file descriptor.
+
+{% /table %}
+
+The `"pipe"` option lets incrementally write to the subprocess's input stream from the parent process.
+
+```ts
+const proc = Bun.spawn(["cat"], {
+ stdin: "pipe", // return a FileSink for writing
+});
+
+// enqueue string data
+proc.stdin!.write("hello");
+
+// enqueue binary data
+const enc = new TextEncoder();
+proc.stdin!.write(enc.encode(" world!"));
+
+// send buffered data
+proc.stdin!.flush();
+
+// close the input stream
+proc.stdin!.end();
+```
+
+## Output streams
+
+You can read results from the subprocess via the `stdout` and `stderr` properties. By default these are instances of `ReadableStream`.
+
+```ts
+const proc = Bun.spawn(["echo", "hello"]);
+const text = await new Response(proc.stdout).text();
+console.log(text); // => "hello"
+```
+
+Configure the output stream by passing one of the following values to `stdout/stderr`:
+
+{% table %}
+
+---
+
+- `"pipe"`
+- **Default for `stdout`.** Pipe the output to a `ReadableStream` on the returned `Subprocess` object.
+
+---
+
+- `"inherit"`
+- **Default for `stderr`.** Inherit from the parent process.
+
+---
+
+- `Bun.file()`
+- Write to the specified file.
+
+---
+
+- `null`
+- Write to `/dev/null`.
+
+---
+
+- `number`
+- Write to the file with the given file descriptor.
+
+{% /table %}
+
+## Exit handling
+
+Use the `onExit` callback to listen for the process exiting or being killed.
+
+```ts
+const proc = Bun.spawn(["echo", "hello"], {
+ onExit(proc, exitCode, signalCode, error) {
+ // exit handler
+ },
+});
+```
+
+For convenience, the `exited` property is a `Promise` that resolves when the process exits.
+
+```ts
+const proc = Bun.spawn(["echo", "hello"]);
+
+await proc.exited; // resolves when process exit
+proc.killed; // boolean — was the process killed?
+proc.exitCode; // null | number
+proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...
+```
+
+To kill a process:
+
+```ts
+const proc = Bun.spawn(["echo", "hello"]);
+proc.kill();
+proc.killed; // true
+
+proc.kill(); // specify an exit code
+```
+
+The parent `bun` process will not terminate until all child processes have exited. Use `proc.unref()` to detach the child process from the parent.
+
+```
+const proc = Bun.spawn(["echo", "hello"]);
+proc.unref();
+```
+
+## Blocking API
+
+Bun provides a synchronous equivalent of `Bun.spawn` called `Bun.spawnSync`. This is a blocking API that supports the same inputs and parameters as `Bun.spawn`. It returns a `SyncSubprocess` object, which differs from `Subprocess` in a few ways.
+
+1. It contains a `success` property that indicates whether the process exited with a zero exit code.
+2. The `stdout` and `stderr` properties are instances of `Buffer` instead of `ReadableStream`.
+3. There is no `stdin` property. Use `Bun.spawn` to incrementally write to the subprocess's input stream.
+
+```ts
+const proc = Bun.spawnSync(["echo", "hello"]);
+
+console.log(proc.stdout!.toString());
+// => "hello\n"
+```
+
+As a rule of thumb, the asynchronous `Bun.spawn` API is better for HTTP servers and apps, and `Bun.spawnSync` is better for building command-line tools.
+
+## Benchmarks
+
+{%callout%}
+⚡️ Under the hood, `Bun.spawn` and `Bun.spawnSync` use [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).
+{%/callout%}
+
+Bun's `spawnSync` spawns processes 60% faster than the Node.js `child_process` module.
+
+```bash
+$ bun spawn.mjs
+cpu: Apple M1 Max
+runtime: bun 0.2.0 (arm64-darwin)
+
+benchmark time (avg) (min … max) p75 p99 p995
+--------------------------------------------------------- -----------------------------
+spawnSync echo hi 888.14 µs/iter (821.83 µs … 1.2 ms) 905.92 µs 1 ms 1.03 ms
+$ node spawn.node.mjs
+cpu: Apple M1 Max
+runtime: node v18.9.1 (arm64-darwin)
+
+benchmark time (avg) (min … max) p75 p99 p995
+--------------------------------------------------------- -----------------------------
+spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms 2.52 ms
+```
+
+## Reference
+
+```ts
+interface Bun {
+ spawn(command: string[], options?: SpawnOptions): Subprocess;
+ spawnSync(command: string[], options?: SpawnOptions): SyncSubprocess;
+}
+
+interface SpawnOptions {
+ cwd?: string;
+ env?: Record<string, string>;
+ stdin?:
+ | "pipe"
+ | "inherit"
+ | "ignore"
+ | ReadableStream
+ | BunFile
+ | Blob
+ | Response
+ | Request
+ | number
+ | null;
+ stdout?:
+ | "pipe"
+ | "inherit"
+ | "ignore"
+ | BunFile
+ | TypedArray
+ | DataView
+ | null;
+ stderr?:
+ | "pipe"
+ | "inherit"
+ | "ignore"
+ | BunFile
+ | TypedArray
+ | DataView
+ | null;
+ onExit?: (
+ proc: Subprocess,
+ exitCode: number | null,
+ signalCode: string | null,
+ error: Error | null,
+ ) => void;
+}
+
+interface Subprocess {
+ readonly pid: number;
+ readonly stdin?: number | ReadableStream | FileSink;
+ readonly stdout?: number | ReadableStream;
+ readonly stderr?: number | ReadableStream;
+
+ readonly exited: Promise<number>;
+
+ readonly exitCode: number | undefined;
+ readonly signalCode: Signal | null;
+ readonly killed: boolean;
+
+ ref(): void;
+ unref(): void;
+ kill(code?: number): void;
+}
+
+interface SyncSubprocess {
+ readonly pid: number;
+ readonly success: boolean;
+ readonly stdout: Buffer;
+ readonly stderr: Buffer;
+}
+
+type Signal =
+ | "SIGABRT"
+ | "SIGALRM"
+ | "SIGBUS"
+ | "SIGCHLD"
+ | "SIGCONT"
+ | "SIGFPE"
+ | "SIGHUP"
+ | "SIGILL"
+ | "SIGINT"
+ | "SIGIO"
+ | "SIGIOT"
+ | "SIGKILL"
+ | "SIGPIPE"
+ | "SIGPOLL"
+ | "SIGPROF"
+ | "SIGPWR"
+ | "SIGQUIT"
+ | "SIGSEGV"
+ | "SIGSTKFLT"
+ | "SIGSTOP"
+ | "SIGSYS"
+ | "SIGTERM"
+ | "SIGTRAP"
+ | "SIGTSTP"
+ | "SIGTTIN"
+ | "SIGTTOU"
+ | "SIGUNUSED"
+ | "SIGURG"
+ | "SIGUSR1"
+ | "SIGUSR2"
+ | "SIGVTALRM"
+ | "SIGWINCH"
+ | "SIGXCPU"
+ | "SIGXFSZ"
+ | "SIGBREAK"
+ | "SIGLOST"
+ | "SIGINFO";
+```