first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -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.