Skip to main content
Get Started
Node.js Runtime

Resident Runner

Reuse a live guest process for low-latency repeated execution.

Each exec() / run() call starts a fresh guest process, which is the right default for isolation between runs. When you run many small snippets against the same runtime (an evaluation loop, a REPL, a test harness), that per-run process startup dominates. A resident runner keeps one guest process alive and evaluates each snippet in it, so repeated calls are fast.

A resident runner trades per-run process isolation for speed: snippets share the same process, so in-memory state can carry between calls. Use it for trusted, repeated evaluation. For isolation between runs, use exec() / run().

Creating and using a runner

createResidentRunner() returns a handle with exec() and dispose(). Dispose it when you are done so the live process is torn down.

import { NodeRuntime } from "secure-exec";

const rt = await NodeRuntime.create();
const runner = await rt.createResidentRunner();
try {
  const a = await runner.exec("console.log(1 + 1)");
  const b = await runner.exec("console.log(2 + 2)");
  console.log(a.stdout.trim(), b.stdout.trim()); // "2" "4"
} finally {
  await runner.dispose();
  await rt.dispose();
}

Each runner.exec(code, options?) resolves with the same { stdout, stderr, exitCode } shape as rt.exec(). Pass options.timeout to bound a single evaluation.

When to use it

A warm resident evaluation runs in roughly a millisecond, versus the hundreds of milliseconds a cold run pays to stand up a guest process. Reach for a resident runner when the same runtime runs many small snippets and the snippets are trusted to share a process. See the measured difference in Benchmarks.

TypeScript SDK reference

createResidentRunner() and the NodeRuntimeResidentRunner handle.