# lifecycle _(no submodules)_ ## Purpose This module owns the idempotency wrapper around a `VmSandboxHandle`'s `start` / `dispose` lifecycle. The host's lifecycle driver may invoke these hooks multiple times (e.g. a hot-reload's stop+start pair, or a test harness's deterministic shutdown sequence); the wrapper makes the double-invocation case a silent no-op rather than a throw, and reflects under-the-hood sandbox throws via the bridge's `LIFECYCLE_FAILED` code so the host's error boundary sees a uniform shape. Splitting the wrapper into its own module keeps the bridge factory small (no inline state machine) and gives the conformance harness a direct seam for asserting idempotency without standing up the full bridge. ## Features - Idempotent `start` — second call returns without re-invoking `sandbox.start()`. - Idempotent `dispose` — second call returns without re-invoking `sandbox.dispose()`. - Forbidden start-after-dispose — calling `start()` after `dispose()` raises `LIFECYCLE_FAILED` so the host can fail the hot-reload cleanly rather than silently swallowing the request. - Error wrapping — underlying sandbox throws surface as `SandboxVmBridgeError` with `code: LIFECYCLE_FAILED` and the original error preserved as `cause`. - Inspection helpers — `hasStarted()` and `isDisposed()` for the bridge's internal request-pipeline guard. ## Usage: Basic ```ts import { createSandboxLifecycleForwarder } from "@suaveplan/api-sandbox-vm-host-bridge/lifecycle/lifecycle"; const forwarder = createSandboxLifecycleForwarder(sandbox); await forwarder.start(); forwarder.dispose(); forwarder.dispose(); // no-op ``` ## Usage: Advanced ```ts import { createSandboxLifecycleForwarder } from "@suaveplan/api-sandbox-vm-host-bridge/lifecycle/lifecycle"; const forwarder = createSandboxLifecycleForwarder(sandbox); try { await forwarder.start(); } catch (err) { // err.code === "SAND_VM_BRIDGE.LIFECYCLE_FAILED" // err.cause is the original SandboxVmError rollbackJournal.markFailed(err); } ``` ## API Reference - `interface SandboxLifecycleForwarder` — `start()`, `dispose()`, `hasStarted()`, `isDisposed()`. - `createSandboxLifecycleForwarder(sandbox: VmSandboxHandle): SandboxLifecycleForwarder` — factory; returns a frozen object. ## Implementation Notes The internal `started` / `disposed` flags are kept in a closure rather than on the returned object so the sandbox cannot mutate them via a property-set on the forwarder. The forwarder itself is `Object.freeze`d so consumers cannot patch `dispose` to skip the cleanup. Errors raised inside `dispose()` are caught and rewrapped — the alternative (letting them propagate raw) would mean the host's error boundary sees a mixed shape (sometimes `SandboxVmError`, sometimes `SandboxVmBridgeError`); the wrap keeps the boundary's branch arm tight.