first commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# SpawnEngine
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`SpawnEngine` uses `node:child_process.spawn` for the strongest isolation the package offers: a fresh OS process spawned per execution, pinned to `process.execPath`, with a sanitised environment that strips identity-revealing keys (`HOME`/`USER`/`SHELL`/`LOGNAME`) and case-insensitive substring matches against `SECRET`/`KEY`/`TOKEN`/`PASSWORD`/`CREDENTIAL`/`AUTH`. Code is sent to the child over stdin as a JSON envelope; the result is read from stdout the same way. Timeouts are enforced with `child.kill("SIGKILL")` so the running script cannot intercept termination.
|
||||
|
||||
The child process runs `runner.ts` (shipped via the `./runner` subpath export). The engine resolves the runner path relative to its own module URL by default, but callers can pin a custom path via the `runnerPath` option — useful for tests that want to drive the engine against a controlled runner fixture.
|
||||
|
||||
## Features
|
||||
|
||||
- Real `node:child_process.spawn` — true OS-process isolation.
|
||||
- Sanitised env via `sanitiseEnv` (exported for direct testing).
|
||||
- Injectable `ProcessSpawner` seam (defaults to `node:child_process.spawn`).
|
||||
- Configurable `runnerPath` and `execPath`.
|
||||
- Timeout enforcement via `SIGKILL` after `Promise.race`.
|
||||
- Failure modes: timeout, no-output, parse-error, all surfaced via the
|
||||
structured `SandboxResult` `error` field.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SpawnEngine } from "@suaveplan/sandbox/spawn-engine";
|
||||
|
||||
const engine = new SpawnEngine({
|
||||
timeout: 5000,
|
||||
maxOutputSize: 1024,
|
||||
memoryLimit: 50 * 1024 * 1024,
|
||||
});
|
||||
const r = await engine.execute("1 + 1");
|
||||
engine.destroy();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Inject a custom spawner for tests, plus a stripped-down env for tightest
|
||||
control:
|
||||
|
||||
```ts
|
||||
import { SpawnEngine } from "@suaveplan/sandbox/spawn-engine";
|
||||
|
||||
const engine = new SpawnEngine({
|
||||
timeout: 5000,
|
||||
maxOutputSize: 1024,
|
||||
memoryLimit: 5e7,
|
||||
env: { PATH: process.env.PATH },
|
||||
spawner: (cmd, args, opts) => myFakeSpawn(cmd, args, opts),
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `new SpawnEngine(options: SpawnEngineOptions)`.
|
||||
- `execute(code, context?, options?): Promise<SandboxResult>`.
|
||||
- `registerModule(mod): void`.
|
||||
- `unregisterModule(name): boolean`.
|
||||
- `getRunnerPath(): string`.
|
||||
- `getSanitisedEnv(): Record<string, string>`.
|
||||
- `reset(): void`.
|
||||
- `destroy(): void`.
|
||||
- `sanitiseEnv(env)` — pure helper exported for direct testing.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `raceChild` helper consolidates the four ways a child can finish:
|
||||
clean exit with parseable JSON (`ok`), clean exit with non-JSON or empty
|
||||
output (`no-output` / `parse-error`), error event before close
|
||||
(`parse-error`), and timeout (`timeout`). Settling is idempotent — only the
|
||||
first outcome counts — so a timeout that races with a clean exit doesn't
|
||||
double-resolve. The kill on timeout is wrapped in try/catch because
|
||||
`child.kill` raises on already-exited processes on some platforms.
|
||||
Reference in New Issue
Block a user