# SessionGraphNode _(no submodules)_ ## Purpose `SessionGraphNode` is the internal mutable graph node powering the hierarchical session tree at the heart of `@suaveplan/game-server`. Every node holds a stable id, a closed-vocabulary session type, a metadata bag, the mutable bidirectional parent/children links that form the tree, and the two timestamps that mark when the node was created and last touched. The class is named `SessionGraphNode` deliberately — Wave A's public `SessionNode` interface (in `core/types`) is a read-only structural view of exactly this shape. exposes the read-only interface to consumers and uses the mutable class internally, so callers cannot accidentally mutate the tree by reaching into `node.children.push(...)` through the public manager surface. ## Features - Implements Wave A's `SessionNode` interface structurally (the class is the canonical implementation; the interface is the read-only view). - Injectable `Clock` — no `Date.now()` calls; the entire tree is deterministic under `FakeClock`. - Bidirectional parent/children link maintenance via `addChild` / `removeChild` with idempotent attach/detach semantics. - Tree-walking helpers: `getDepth`, `getDescendants` (DFS pre-order), `getAncestors`, `getRoot`, `isAncestorOf`, `isDescendantOf`. - `toJSON()` returning a `SerializedSessionNode` shape for `SessionStore` persistence. ## Usage: Basic ```typescript import { SessionGraphNode } from "../session-node/index.js"; const clock = () => 1700000000000; const root = new SessionGraphNode( "venue1", "venue", { createdAt: 0, updatedAt: 0 }, clock ); const table = new SessionGraphNode( "table1", "table", { createdAt: 0, updatedAt: 0 }, clock, root ); root.addChild(table); console.log(table.getDepth()); // 1 console.log(table.getRoot().id); // "venue1" ``` ## Usage: Advanced ```typescript import { SessionGraphNode } from "../session-node/index.js"; // Persistence round-trip: serialise, then restore with original timestamps. const node = new SessionGraphNode("s1", "game", meta, clock); const json = node.toJSON(); const restored = new SessionGraphNode( json.id, json.type, json.metadata, clock, null, { createdAt: json.createdAt, updatedAt: json.updatedAt } ); // Removing a child clears the bidirectional link. root.removeChild(table); console.log(table.parent); // null console.log(root.children.length); // 0 ``` ## API Reference ### Constructor `new SessionGraphNode(id, type, metadata, clock, parent?, timestamps?)` `timestamps` is optional — passed only when restoring from persistence to preserve the original `createdAt` / `updatedAt` stamps. ### Methods - `addChild(child)` — attach a child; idempotent (re-adding is a no-op). - `removeChild(child)` — returns `true` if removed, `false` if not a child. - `updateMetadata(patch)` — shallow-merge patch; refreshes `updatedAt`. - `getDepth()` — 0 for a root, otherwise the distance to the root. - `getDescendants()` — depth-first pre-order array, excluding `this`. - `getAncestors()` — parent-to-root array. - `getRoot()` — the topmost ancestor (or `this`). - `isAncestorOf(other)` / `isDescendantOf(other)` — strict ancestry tests. - `toJSON()` — serialised plain object suitable for storage adapters. ### Read-only properties `id`, `type`, `createdAt`, `updatedAt`. ### Mutable properties `parent` (managed by `addChild`/`removeChild`), `children`, `metadata`. ## Implementation Notes The bidirectional parent/children invariant is held by `addChild` and `removeChild`. Direct mutation of `node.children` or `node.parent` is discouraged — callers should always go through these methods so the counterpart link stays in sync. The class is internal to the engine package and is not part of the public re-export surface; consumers should import `SessionNode` from `core/types` instead. The constructor accepts an optional `timestamps` parameter solely for the persistence round-trip path — without it, a freshly restored node would have its `createdAt` set to the moment of restoration, losing the original creation time. With it, `SessionStore.restore` produces nodes that are byte-equivalent to the ones that were originally persisted.