Files
suaveplan/packages/api-stack/api/api-sandbox-vm-host-bridge/src/lifecycle/lifecycle.md
2026-05-27 23:29:53 +01:00

2.8 KiB

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

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

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