# Contexts

Every call on a [VM](/secure-exec/docs/vms) starts with fresh JavaScript memory.
A **context** keeps it: variables and imports from one call are still there in
the next. Use one for a REPL, a notebook, or an agent that builds on its earlier
steps.

Files, packages, and processes belong to the VM, not to a context, so you do not
need a context for those.

## Keep state between calls

secure-exec/examples/contexts/src/index.ts:

```ts
import { createVm } from "secure-exec";

const vm = await createVm();

// A context keeps JavaScript variables and imports between calls, like a REPL.
const context = await vm.createContext();

await context.execute("globalThis.cart = []");
await context.execute(`cart.push({ item: "coffee", price: 4 })`);
await context.execute(`cart.push({ item: "bagel", price: 3 })`);

const total = await context.evaluate<number>(
	"cart.reduce((sum, line) => sum + line.price, 0)",
);
console.log(total.outcome === "succeeded" ? total.value : total.error); // 7
```

Each call is its own ES module, so top-level `const` and `let` stay scoped to
that call. Put values on `globalThis` to share them. A context holds memory and
does nothing between calls.

## TypeScript

secure-exec/examples/contexts/src/index.ts:

```ts
// TypeScript shares the same state. Pass the context's id to `vm.typescript`.
const typed = await vm.typescript.evaluate<number>(
	"(globalThis.cart as { price: number }[]).length",
	{ contextId: context.contextId },
);
console.log(typed.outcome === "succeeded" ? typed.value : typed.error); // 2
```

## Isolation

secure-exec/examples/contexts/src/index.ts:

```ts
// Calls outside the context never see its state, even in the same VM.
const outside = await vm.javascript.evaluate<string>("typeof globalThis.cart");
console.log(outside.outcome === "succeeded" ? outside.value : outside.error); // undefined
```

## Run in parallel

A context runs **one call at a time**. A second call while one is running fails
with `execution_busy` instead of queueing. For parallel work, create several
contexts in the same VM. Each has its own memory, and they share the VM's files
and packages.

secure-exec/examples/contexts/src/parallel.ts:

```ts
import { createVm } from "secure-exec";

const vm = await createVm();

// A context runs one call at a time. For parallel work, create several: each
// has its own JavaScript memory, and they share the VM's files and packages.
const shards = await Promise.all([
	vm.createContext(),
	vm.createContext(),
	vm.createContext(),
]);

const started = performance.now();
const results = await Promise.all(
	shards.map((context, shard) =>
		context.evaluate<number>(
			"new Promise((resolve) => setTimeout(() => resolve(inputs.shard * 10), 500))",
			{ inputs: { shard } },
		),
	),
);

console.log(results.map((result) => ("value" in result ? result.value : null))); // [0, 10, 20]
console.log(`${Math.round(performance.now() - started)}ms`); // well under the 1500ms that running them one after another takes

await vm.dispose();
```

When parallel contexts need shared mutable state, keep it in a file, or in a
[background process](/secure-exec/docs/long-running-code) that they all call.

## Reset and dispose

secure-exec/examples/contexts/src/index.ts:

```ts
// `reset` clears the state. `dispose` deletes the context and leaves the VM.
await context.reset();
const afterReset = await context.evaluate<string>("typeof globalThis.cart");
console.log(
	afterReset.outcome === "succeeded" ? afterReset.value : afterReset.error,
); // undefined

await context.dispose();
await vm.dispose();
```

`context.dispose()` deletes the context and leaves the VM running. Disposing the
VM removes every context in it.
