@graphrefly/graphrefly 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/dist/chunk-263BEJJO.js +115 -0
- package/dist/chunk-263BEJJO.js.map +1 -0
- package/dist/chunk-2GQLMQVJ.js +47 -0
- package/dist/chunk-2GQLMQVJ.js.map +1 -0
- package/dist/chunk-32N5A454.js +36 -0
- package/dist/chunk-32N5A454.js.map +1 -0
- package/dist/chunk-7TAQJHQV.js +103 -0
- package/dist/chunk-7TAQJHQV.js.map +1 -0
- package/dist/{chunk-VOQFK7YN.js → chunk-CWYPA63G.js} +109 -259
- package/dist/chunk-CWYPA63G.js.map +1 -0
- package/dist/{chunk-7IGHIFTT.js → chunk-HVBX5KIW.js} +15 -26
- package/dist/chunk-HVBX5KIW.js.map +1 -0
- package/dist/chunk-JFONSPNF.js +391 -0
- package/dist/chunk-JFONSPNF.js.map +1 -0
- package/dist/chunk-NZMBRXQV.js +2330 -0
- package/dist/chunk-NZMBRXQV.js.map +1 -0
- package/dist/{chunk-XWBVAO2R.js → chunk-PNUZM7PC.js} +20 -30
- package/dist/chunk-PNUZM7PC.js.map +1 -0
- package/dist/{chunk-ZTCDY5NQ.js → chunk-PX6PDUJ5.js} +34 -50
- package/dist/chunk-PX6PDUJ5.js.map +1 -0
- package/dist/chunk-XRFJJ2IU.js +2417 -0
- package/dist/chunk-XRFJJ2IU.js.map +1 -0
- package/dist/chunk-XTLYW4FR.js +6829 -0
- package/dist/chunk-XTLYW4FR.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +3489 -2286
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +6 -4
- package/dist/compat/nestjs/index.d.ts +6 -4
- package/dist/compat/nestjs/index.js +10 -8
- package/dist/core/index.cjs +1706 -1217
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +3 -2
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +37 -34
- package/dist/extra/index.cjs +7519 -6125
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +4 -4
- package/dist/extra/index.d.ts +4 -4
- package/dist/extra/index.js +63 -34
- package/dist/graph/index.cjs +3199 -2212
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +5 -3
- package/dist/graph/index.d.ts +5 -3
- package/dist/graph/index.js +24 -11
- package/dist/graph-BtdSRHUc.d.cts +1128 -0
- package/dist/graph-CEO2FkLY.d.ts +1128 -0
- package/dist/{index-DuN3bhtm.d.ts → index-B0tfuXwV.d.cts} +1697 -586
- package/dist/index-BFGjXbiP.d.cts +315 -0
- package/dist/{index-CgSiUouz.d.ts → index-BPlWVAKY.d.cts} +4 -4
- package/dist/index-BUj3ASVe.d.cts +406 -0
- package/dist/{index-VHA43cGP.d.cts → index-C59uSJAH.d.cts} +2 -2
- package/dist/index-CkElcUY6.d.ts +315 -0
- package/dist/index-DSPc5rkv.d.ts +406 -0
- package/dist/{index-BjtlNirP.d.cts → index-DgscL7v0.d.ts} +4 -4
- package/dist/{index-SFzE_KTa.d.cts → index-RXN94sHK.d.ts} +1697 -586
- package/dist/{index-8a605sg9.d.ts → index-jEtF4N7L.d.ts} +2 -2
- package/dist/index.cjs +9947 -7949
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +214 -37
- package/dist/index.d.ts +214 -37
- package/dist/index.js +919 -648
- package/dist/index.js.map +1 -1
- package/dist/meta-3QjzotRv.d.ts +41 -0
- package/dist/meta-B-Lbs4-O.d.cts +41 -0
- package/dist/node-C7PD3sn9.d.cts +1188 -0
- package/dist/node-C7PD3sn9.d.ts +1188 -0
- package/dist/{observable-DcBwQY7t.d.ts → observable-EyO-moQY.d.ts} +1 -1
- package/dist/{observable-C8Kx_O6k.d.cts → observable-axpzv1K2.d.cts} +1 -1
- package/dist/patterns/reactive-layout/index.cjs +3205 -2138
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +5 -3
- package/dist/patterns/reactive-layout/index.d.ts +5 -3
- package/dist/patterns/reactive-layout/index.js +7 -4
- package/dist/storage-CHT5WE9m.d.ts +182 -0
- package/dist/storage-DIgAr7M_.d.cts +182 -0
- package/package.json +2 -1
- package/dist/chunk-2UDLYZHT.js +0 -2117
- package/dist/chunk-2UDLYZHT.js.map +0 -1
- package/dist/chunk-4MQ2J6IG.js +0 -1631
- package/dist/chunk-4MQ2J6IG.js.map +0 -1
- package/dist/chunk-7IGHIFTT.js.map +0 -1
- package/dist/chunk-DOSLSFKL.js +0 -162
- package/dist/chunk-DOSLSFKL.js.map +0 -1
- package/dist/chunk-ECN37NVS.js +0 -6227
- package/dist/chunk-ECN37NVS.js.map +0 -1
- package/dist/chunk-G66H6ZRK.js +0 -111
- package/dist/chunk-G66H6ZRK.js.map +0 -1
- package/dist/chunk-VOQFK7YN.js.map +0 -1
- package/dist/chunk-WZ2Z2CRV.js +0 -32
- package/dist/chunk-WZ2Z2CRV.js.map +0 -1
- package/dist/chunk-XWBVAO2R.js.map +0 -1
- package/dist/chunk-ZTCDY5NQ.js.map +0 -1
- package/dist/graph-KsTe57nI.d.cts +0 -750
- package/dist/graph-mILUUqW8.d.ts +0 -750
- package/dist/index-B2SvPEbc.d.ts +0 -257
- package/dist/index-BHfg_Ez3.d.ts +0 -629
- package/dist/index-Bc_diYYJ.d.cts +0 -629
- package/dist/index-UudxGnzc.d.cts +0 -257
- package/dist/meta-BnG7XAaE.d.cts +0 -778
- package/dist/meta-BnG7XAaE.d.ts +0 -778
package/dist/core/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/index.ts","../../src/core/actor.ts","../../src/core/messages.ts","../../src/core/batch.ts","../../src/core/guard.ts","../../src/core/clock.ts","../../src/core/versioning.ts","../../src/core/node-base.ts","../../src/core/node.ts","../../src/core/bridge.ts","../../src/core/dynamic-node.ts","../../src/core/meta.ts","../../src/core/sugar.ts","../../src/core/timer.ts"],"sourcesContent":["/**\n * Core layer: message protocol, node primitive, lifecycle (Phase 0).\n */\nexport * from \"./actor.js\";\nexport * from \"./batch.js\";\nexport { type BridgeOptions, bridge, DEFAULT_DOWN } from \"./bridge.js\";\nexport { monotonicNs, wallClockNs } from \"./clock.js\";\nexport * from \"./dynamic-node.js\";\nexport * from \"./guard.js\";\nexport * from \"./messages.js\";\nexport {\n\ttype DescribeDetail,\n\ttype DescribeField,\n\ttype DescribeNodeOutput,\n\tresolveDescribeFields,\n} from \"./meta.js\";\nexport {\n\tCLEANUP_RESULT,\n\ttype CleanupResult,\n\tcleanupResult,\n\ttype Node,\n\ttype NodeActions,\n\ttype NodeDescribeKind,\n\ttype NodeFn,\n\ttype NodeOptions,\n\ttype NodeSink,\n\ttype NodeStatus,\n\ttype NodeTransportOptions,\n\tnode,\n\ttype OnMessageHandler,\n\ttype SubscribeHints,\n} from \"./node.js\";\nexport * from \"./sugar.js\";\nexport { ResettableTimer } from \"./timer.js\";\nexport {\n\tadvanceVersion,\n\tcreateVersioning,\n\tdefaultHash,\n\ttype HashFn,\n\tisV1,\n\ttype NodeVersionInfo,\n\ttype V0,\n\ttype V1,\n\ttype VersioningLevel,\n\ttype VersioningOptions,\n} from \"./versioning.js\";\n","/**\n * Who is performing an operation (attribution + ABAC input).\n *\n * @see GRAPHREFLY-SPEC — roadmap Phase 1.5 (Actor & Guard).\n */\nexport type Actor = {\n\ttype: \"human\" | \"llm\" | \"wallet\" | \"system\" | string;\n\tid: string;\n} & Record<string, unknown>;\n\n/** Default actor when none is passed ({@link normalizeActor}). */\nexport const DEFAULT_ACTOR: Actor = { type: \"system\", id: \"\" };\n\n/**\n * Fills missing `type` / `id` on an actor and returns {@link DEFAULT_ACTOR} when input is undefined.\n *\n * @param actor - Optional partial actor from a transport hint.\n * @returns A normalized `Actor` safe to pass to guards and graph APIs.\n *\n * @example\n * ```ts\n * import { normalizeActor } from \"@graphrefly/graphrefly-ts\";\n *\n * normalizeActor({ type: \"human\", id: \"u1\" });\n * ```\n */\nexport function normalizeActor(actor?: Actor): Actor {\n\tif (actor == null) return DEFAULT_ACTOR;\n\tconst { type, id, ...rest } = actor;\n\treturn {\n\t\ttype: type ?? \"system\",\n\t\tid: id ?? \"\",\n\t\t...rest,\n\t} as Actor;\n}\n","/**\n * GraphReFly message protocol — §1 `~/src/graphrefly/GRAPHREFLY-SPEC.md`.\n * Emissions are always `[[Type, Data?], ...]` (no single-tuple shorthand).\n *\n * ## Canonical message ordering (within a composite batch)\n *\n * When multiple message types appear in a single `down()` call, the canonical\n * delivery order is determined by **signal tier**:\n *\n * | Tier | Signals | Role | Batch behavior |\n * |------|------------------------|-------------------|-------------------------------------|\n * | 0 | START | Subscribe handshake | Immediate (never deferred) |\n * | 1 | DIRTY, INVALIDATE | Notification | Immediate (never deferred) |\n * | 2 | PAUSE, RESUME | Flow control | Immediate (never deferred) |\n * | 3 | DATA, RESOLVED | Value settlement | Deferred inside `batch()` |\n * | 4 | COMPLETE, ERROR | Terminal lifecycle | Deferred to after phase-2 |\n * | 5 | TEARDOWN | Destruction | Immediate (usually sent alone) |\n *\n * **Rule:** Within `downWithBatch`, messages are partitioned by tier and delivered\n * in tier order. This ensures phase-2 values (DATA/RESOLVED) reach sinks before\n * terminal signals (COMPLETE/ERROR) mark the node as done, preventing the\n * \"COMPLETE-before-DATA\" class of bugs. Sources that emit in canonical order\n * naturally partition correctly with zero overhead.\n *\n * Unknown message types (forward-compat) are tier 1 (immediate).\n *\n * ## Meta node bypass rules (centralized — GRAPHREFLY-SPEC §2.3)\n *\n * - **INVALIDATE** via `graph.signal()` — no-op on meta nodes (cached values preserved).\n * - **COMPLETE / ERROR** — not propagated from parent to meta (meta outlives terminal\n * state for post-mortem writes like setting `meta.error` after ERROR).\n * - **TEARDOWN** — propagated from parent to meta, releasing meta resources.\n */\n\n/**\n * Subscribe-time handshake (`START`). Delivered to each new sink at the top of\n * `subscribe()` — `[[START]]` for a SENTINEL (no cached value) node or\n * `[[START], [DATA, cached]]` for a node with a cached value.\n *\n * Semantics: \"upstream connected and ready to flow.\" A new sink receiving\n * `[[START]]` alone knows the upstream is alive but has no current value; a\n * sink receiving `[[START], [DATA, v]]` knows the upstream is ready\n * with value `v`. Absence of `START` before any other message means the\n * subscription was either never established or the node is terminal.\n *\n * Tier 0 — immediate, delivered before any DIRTY/DATA in the same batch.\n * Not forwarded through nodes — each node emits its own `START` to its own\n * new sinks.\n */\nexport const START = Symbol.for(\"graphrefly/START\");\n/** Value delivery (`DATA`, value). Tier 3 — deferred inside `batch()`. */\nexport const DATA = Symbol.for(\"graphrefly/DATA\");\n/** Phase 1: value about to change. Tier 1 — immediate. */\nexport const DIRTY = Symbol.for(\"graphrefly/DIRTY\");\n/** Phase 2: dirty pass completed, value unchanged. Tier 3 — deferred inside `batch()`. */\nexport const RESOLVED = Symbol.for(\"graphrefly/RESOLVED\");\n/** Clear cached state; do not auto-emit. Tier 1 — immediate. */\nexport const INVALIDATE = Symbol.for(\"graphrefly/INVALIDATE\");\n/** Suspend activity. Tier 2 — immediate. */\nexport const PAUSE = Symbol.for(\"graphrefly/PAUSE\");\n/** Resume after pause. Tier 2 — immediate. */\nexport const RESUME = Symbol.for(\"graphrefly/RESUME\");\n/** Permanent cleanup. Tier 5 — immediate (usually sent alone). */\nexport const TEARDOWN = Symbol.for(\"graphrefly/TEARDOWN\");\n/** Clean termination. Tier 4 — delivered after phase-2 in the same batch. */\nexport const COMPLETE = Symbol.for(\"graphrefly/COMPLETE\");\n/** Error termination. Tier 4 — delivered after phase-2 in the same batch. */\nexport const ERROR = Symbol.for(\"graphrefly/ERROR\");\n\n/** Known protocol type symbols (open set — other symbols are valid and forward). */\nexport const knownMessageTypes: readonly symbol[] = [\n\tSTART,\n\tDATA,\n\tDIRTY,\n\tRESOLVED,\n\tINVALIDATE,\n\tPAUSE,\n\tRESUME,\n\tTEARDOWN,\n\tCOMPLETE,\n\tERROR,\n];\n\n/** O(1) lookup for {@link isKnownMessageType} and {@link isLocalOnly}. */\nconst knownMessageSet: ReadonlySet<symbol> = new Set(knownMessageTypes);\n\n/** One protocol tuple: `[Type, optional payload]`. */\nexport type Message = readonly [symbol, unknown?];\n\n/**\n * A batch of tuples — the wire shape for `node.down()` / `node.up()`.\n */\nexport type Messages = readonly Message[];\n\n/**\n * Whether `t` is one of the built-in protocol symbols in this module.\n *\n * @param t — Message type symbol (unknown types are still valid; they must forward).\n * @returns `true` for `DATA`, `DIRTY`, `RESOLVED`, etc.\n *\n * @example\n * ```ts\n * import { DATA, DIRTY, isKnownMessageType } from \"@graphrefly/graphrefly-ts\";\n *\n * isKnownMessageType(DATA); // true\n * isKnownMessageType(Symbol(\"custom\")); // false\n * ```\n */\nexport function isKnownMessageType(t: symbol): boolean {\n\treturn knownMessageSet.has(t);\n}\n\n/**\n * Returns the signal tier for a message type (see module-level ordering table).\n *\n * - 0: subscribe handshake (START) — immediate, first in canonical order\n * - 1: notification (DIRTY, INVALIDATE) — immediate\n * - 2: flow control (PAUSE, RESUME) — immediate\n * - 3: value (DATA, RESOLVED) — deferred inside `batch()`\n * - 4: terminal (COMPLETE, ERROR) — delivered after phase-3\n * - 5: destruction (TEARDOWN) — immediate, usually alone\n * - 1 for unknown types (forward-compat: immediate)\n *\n * @param t — Message type symbol.\n * @returns Tier number (0–5).\n *\n * @example\n * ```ts\n * import { DATA, DIRTY, COMPLETE, TEARDOWN, messageTier, START } from \"@graphrefly/graphrefly-ts\";\n *\n * messageTier(START); // 0\n * messageTier(DIRTY); // 1\n * messageTier(DATA); // 3\n * messageTier(COMPLETE); // 4\n * messageTier(TEARDOWN); // 5\n * ```\n */\nexport function messageTier(t: symbol): number {\n\tif (t === START) return 0;\n\tif (t === DIRTY || t === INVALIDATE) return 1;\n\tif (t === PAUSE || t === RESUME) return 2;\n\tif (t === DATA || t === RESOLVED) return 3;\n\tif (t === COMPLETE || t === ERROR) return 4;\n\tif (t === TEARDOWN) return 5;\n\treturn 1; // unknown → immediate (after START)\n}\n\n/**\n * Returns whether this tuple is deferred by `batch()` (phase 2: `DATA` or `RESOLVED`).\n *\n * @param msg — Single message tuple.\n * @returns `true` if `msg` is `DATA` or `RESOLVED`.\n *\n * @example\n * ```ts\n * import { DATA, RESOLVED, DIRTY, isPhase2Message } from \"@graphrefly/graphrefly-ts\";\n *\n * isPhase2Message([DATA, 42]); // true\n * isPhase2Message([RESOLVED]); // true\n * isPhase2Message([DIRTY]); // false\n * ```\n */\nexport function isPhase2Message(msg: Message): boolean {\n\tconst t = msg[0];\n\treturn t === DATA || t === RESOLVED;\n}\n\n/**\n * Returns whether this message type is terminal (COMPLETE or ERROR).\n * Terminal messages are delivered after phase-2 in the same batch to prevent\n * the node from becoming terminal before value messages reach sinks.\n *\n * @param t — Message type symbol.\n * @returns `true` for `COMPLETE` or `ERROR`.\n *\n * @example\n * ```ts\n * import { COMPLETE, ERROR, DATA, isTerminalMessage } from \"@graphrefly/graphrefly-ts\";\n *\n * isTerminalMessage(COMPLETE); // true\n * isTerminalMessage(ERROR); // true\n * isTerminalMessage(DATA); // false\n * ```\n */\nexport function isTerminalMessage(t: symbol): boolean {\n\treturn t === COMPLETE || t === ERROR;\n}\n\n/**\n * Whether `t` is a graph-local signal that should NOT cross a wire/transport\n * boundary (SSE, WebSocket, worker bridge, persistence sinks).\n *\n * Local-only signals (tier 0–2): START, DIRTY, INVALIDATE, PAUSE, RESUME.\n * These are internal to the reactive graph — subscribe handshakes,\n * notification phases, and flow control have no semantics for remote consumers.\n *\n * Wire-crossing signals (tier 3+): DATA, RESOLVED, COMPLETE, ERROR, TEARDOWN.\n * Unknown message types (spec §1.3.6 forward-compat) also cross the wire.\n *\n * Individual adapters may further opt-in to local signals for observability\n * (e.g. SSE `includeDirty`, `includeResolved` options). Storage/persistence\n * adapters that need INVALIDATE for remote cache-clear should explicitly\n * forward it despite `isLocalOnly(INVALIDATE) === true`. The default is to\n * skip all local-only signals at the boundary.\n *\n * @param t — Message type symbol.\n * @returns `true` if the message should be kept local (not sent over wire).\n *\n * @example\n * ```ts\n * import { START, DIRTY, DATA, COMPLETE, isLocalOnly } from \"@graphrefly/graphrefly-ts\";\n *\n * isLocalOnly(START); // true — subscribe handshake\n * isLocalOnly(DIRTY); // true — notification phase\n * isLocalOnly(RESOLVED); // false — value settlement crosses wire\n * isLocalOnly(DATA); // false — value crosses wire\n * isLocalOnly(COMPLETE); // false — terminal crosses wire\n * ```\n *\n * @category core\n */\nexport function isLocalOnly(t: symbol): boolean {\n\t// Unknown types always cross the wire (spec §1.3.6 forward-compat).\n\t// messageTier returns 1 for unknowns, but unknowns are NOT local-only.\n\tif (!knownMessageSet.has(t)) return false;\n\treturn messageTier(t) < 3;\n}\n\n/**\n * Whether `t` should be propagated from a parent node to its companion meta nodes.\n * Only TEARDOWN propagates; COMPLETE/ERROR/INVALIDATE do not (meta outlives parent\n * terminal state for post-mortem writes).\n *\n * @param t — Message type symbol.\n * @returns `true` if the signal should reach meta nodes.\n *\n * @example\n * ```ts\n * import { TEARDOWN, COMPLETE, ERROR, propagatesToMeta } from \"@graphrefly/graphrefly-ts\";\n *\n * propagatesToMeta(TEARDOWN); // true\n * propagatesToMeta(COMPLETE); // false\n * propagatesToMeta(ERROR); // false\n * ```\n */\nexport function propagatesToMeta(t: symbol): boolean {\n\treturn t === TEARDOWN;\n}\n","import {\n\tDATA,\n\tisPhase2Message,\n\tisTerminalMessage,\n\ttype Message,\n\ttype Messages,\n\tmessageTier,\n\tRESOLVED,\n} from \"./messages.js\";\n\n/**\n * §1.3.7 — Inside a batch, DIRTY propagates immediately; DATA and RESOLVED are\n * deferred until the outermost `batch()` callback returns. Terminal signals\n * (COMPLETE, ERROR) are delivered after phase-2 messages in the same batch\n * (see canonical ordering in `messages.ts`).\n */\n\nconst MAX_DRAIN_ITERATIONS = 1000;\n\nlet batchDepth = 0;\nlet flushInProgress = false;\nconst pendingPhase2: Array<() => void> = [];\nconst pendingPhase3: Array<() => void> = [];\n\n/**\n * Returns whether the current call stack is inside a batch scope **or** while\n * deferred phase-2 work is draining.\n *\n * Matching Python's `is_batching()` semantics: nested emissions during drain\n * are deferred until the current drain pass completes, preventing ordering\n * bugs when callbacks trigger further DATA/RESOLVED.\n *\n * @returns `true` while inside `batch()` or while the drain loop is running.\n *\n * @example\n * ```ts\n * import { batch, isBatching } from \"@graphrefly/graphrefly-ts\";\n *\n * batch(() => {\n * console.log(isBatching()); // true\n * });\n * ```\n *\n * @category core\n */\nexport function isBatching(): boolean {\n\treturn batchDepth > 0 || flushInProgress;\n}\n\n/**\n * Runs `fn` inside a batch scope. Nested `batch()` calls share one deferral queue.\n * If `fn` throws (including from a nested `batch`), deferred DATA/RESOLVED for\n * that **outer** `batch` frame are discarded — phase-2 is not flushed after an\n * error. While the drain loop is running (`flushInProgress`), a nested `batch`\n * that throws must **not** clear the global queue (cross-language decision A4).\n *\n * During the drain loop, `isBatching()` remains true so nested `downWithBatch`\n * calls still defer phase-2 messages. The drain loop runs until the queue is\n * quiescent (no pending work remains). Per-emission try/catch ensures one\n * throwing callback does not orphan remaining emissions; the first error is\n * re-thrown after all emissions drain. Callbacks that ran before the throw may\n * have applied phase-2 — partial graph state is intentional (decision C1).\n *\n * @param fn — Synchronous work that may call `downWithBatch` / `node.down()`.\n * @returns `void` — all side-effects happen through `downWithBatch` and the\n * phase-2 drain that runs after `fn` returns.\n *\n * @example\n * ```ts\n * import { core } from \"@graphrefly/graphrefly-ts\";\n *\n * core.batch(() => {\n * core.downWithBatch(sink, [[core.DATA, 1]]);\n * });\n * ```\n *\n * @category core\n */\nexport function batch(fn: () => void): void {\n\tbatchDepth += 1;\n\tlet threw = false;\n\ttry {\n\t\tfn();\n\t} catch (e) {\n\t\tthrew = true;\n\t\tthrow e;\n\t} finally {\n\t\tbatchDepth -= 1;\n\t\tif (batchDepth === 0) {\n\t\t\tif (threw) {\n\t\t\t\t// Do not wipe the outer drain's queue (decision A4).\n\t\t\t\tif (!flushInProgress) {\n\t\t\t\t\tpendingPhase2.length = 0;\n\t\t\t\t\tpendingPhase3.length = 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdrainPending();\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction drainPending(): void {\n\tconst ownsFlush = !flushInProgress;\n\tif (ownsFlush) {\n\t\tflushInProgress = true;\n\t}\n\tconst errors: unknown[] = [];\n\ttry {\n\t\tlet iterations = 0;\n\t\t// Drain phase-2 first, then phase-3. If phase-3 callbacks enqueue new\n\t\t// phase-2 work, the outer loop catches it and drains phase-2 again\n\t\t// before re-entering phase-3.\n\t\twhile (pendingPhase2.length > 0 || pendingPhase3.length > 0) {\n\t\t\t// Phase-2 (DATA/RESOLVED) — parent node values settle here.\n\t\t\twhile (pendingPhase2.length > 0) {\n\t\t\t\titerations += 1;\n\t\t\t\tif (iterations > MAX_DRAIN_ITERATIONS) {\n\t\t\t\t\tpendingPhase2.length = 0;\n\t\t\t\t\tpendingPhase3.length = 0;\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`batch drain exceeded ${MAX_DRAIN_ITERATIONS} iterations — likely a reactive cycle`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst ops = pendingPhase2.splice(0);\n\t\t\t\tfor (const run of ops) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\trun();\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\terrors.push(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Phase-3 — meta companion emissions that must follow parent settlement.\n\t\t\tif (pendingPhase3.length > 0) {\n\t\t\t\titerations += 1;\n\t\t\t\tif (iterations > MAX_DRAIN_ITERATIONS) {\n\t\t\t\t\tpendingPhase2.length = 0;\n\t\t\t\t\tpendingPhase3.length = 0;\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`batch drain exceeded ${MAX_DRAIN_ITERATIONS} iterations — likely a reactive cycle`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst ops = pendingPhase3.splice(0);\n\t\t\t\tfor (const run of ops) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\trun();\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\terrors.push(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} finally {\n\t\tif (ownsFlush) {\n\t\t\tflushInProgress = false;\n\t\t}\n\t}\n\tif (errors.length === 1) {\n\t\tthrow errors[0];\n\t}\n\tif (errors.length > 1) {\n\t\tthrow new AggregateError(errors, \"batch drain: multiple callbacks threw\");\n\t}\n}\n\n/**\n * Splits a message array into three groups by signal tier (see `messages.ts`):\n *\n * - **immediate** — tier 0–2, 5: START, DIRTY, INVALIDATE, PAUSE, RESUME, TEARDOWN, unknown\n * - **deferred** — tier 3: DATA, RESOLVED (phase-2, deferred inside `batch()`)\n * - **terminal** — tier 4: COMPLETE, ERROR (delivered after phase-2)\n *\n * Order within each group is preserved.\n *\n * @param messages — One `down()` payload.\n * @returns Three groups in canonical delivery order.\n *\n * @example\n * ```ts\n * import { DATA, DIRTY, COMPLETE, partitionForBatch } from \"@graphrefly/graphrefly-ts\";\n *\n * partitionForBatch([[DIRTY], [DATA, 1], [COMPLETE]]);\n * // { immediate: [[DIRTY]], deferred: [[DATA, 1]], terminal: [[COMPLETE]] }\n * ```\n *\n * @category core\n */\nexport function partitionForBatch(messages: Messages): {\n\timmediate: Messages;\n\tdeferred: Messages;\n\tterminal: Messages;\n} {\n\tconst immediate: Message[] = [];\n\tconst deferred: Message[] = [];\n\tconst terminal: Message[] = [];\n\tfor (const m of messages) {\n\t\tif (isPhase2Message(m)) {\n\t\t\tdeferred.push(m);\n\t\t} else if (isTerminalMessage(m[0])) {\n\t\t\tterminal.push(m);\n\t\t} else {\n\t\t\timmediate.push(m);\n\t\t}\n\t}\n\treturn { immediate, deferred, terminal };\n}\n\n/**\n * Delivers messages downstream through `sink`, applying batch semantics and\n * canonical tier-based ordering (see `messages.ts`):\n *\n * 1. **Immediate** (tier 0–2, 5): START, DIRTY, INVALIDATE, PAUSE, RESUME, TEARDOWN,\n * unknown — delivered synchronously.\n * 2. **Phase-2** (tier 3): DATA, RESOLVED — deferred while `isBatching()`.\n * 3. **Terminal** (tier 4): COMPLETE, ERROR — always delivered after phase-2.\n * When batching, terminal is queued after deferred phase-2 in the pending list.\n * When not batching, terminal is delivered after phase-2 synchronously.\n *\n * This ordering prevents the \"COMPLETE-before-DATA\" class of bugs: terminal\n * signals never make a node terminal before phase-2 values reach sinks,\n * regardless of how the source assembled the message array.\n *\n * @param sink — Sink callback. May be called up to three times per invocation\n * (immediate, deferred, terminal) when not batching.\n * @param messages — Full `[[Type, Data?], ...]` array for one downstream delivery.\n * @param phase — Starting delivery phase (`2` = data, `3` = terminal). Default `2`.\n * @param options - Optional configuration.\n * @option strategy | `\"partition\"` or `\"sequential\"` | `\"partition\"` | `\"partition\"` groups by tier; `\"sequential\"` preserves message order within each tier using `messageTier()` classification.\n * @returns `void` — delivery is performed through `sink` callbacks, synchronously\n * or deferred into the active batch queue.\n *\n * @example\n * ```ts\n * import { core } from \"@graphrefly/graphrefly-ts\";\n *\n * core.downWithBatch((msgs) => console.log(msgs), [[core.DIRTY], [core.DATA, 42]]);\n * ```\n *\n * @category core\n */\n/** Delivery strategy for {@link downWithBatch}. Mirrors Python `DownStrategy`. */\nexport type DownStrategy = \"partition\" | \"sequential\";\n\nexport function downWithBatch(\n\tsink: (messages: Messages) => void,\n\tmessages: Messages,\n\tphase: 2 | 3 = 2,\n\toptions?: { strategy?: DownStrategy },\n): void {\n\tif (messages.length === 0) {\n\t\treturn;\n\t}\n\n\tif (options?.strategy === \"sequential\") {\n\t\t_downSequential(sink, messages, phase);\n\t\treturn;\n\t}\n\n\tconst queue = phase === 3 ? pendingPhase3 : pendingPhase2;\n\n\t// Fast path: single-message batches (most common in graph-internal propagation)\n\t// skip partitionForBatch allocation entirely.\n\tif (messages.length === 1) {\n\t\tconst t = messages[0][0];\n\t\tif (t === DATA || t === RESOLVED) {\n\t\t\tif (isBatching()) {\n\t\t\t\tqueue.push(() => sink(messages));\n\t\t\t} else {\n\t\t\t\tsink(messages);\n\t\t\t}\n\t\t} else if (isTerminalMessage(t)) {\n\t\t\t// Terminal single message: defer when batching so any preceding\n\t\t\t// phase-2 in the queue flushes first.\n\t\t\tif (isBatching()) {\n\t\t\t\tqueue.push(() => sink(messages));\n\t\t\t} else {\n\t\t\t\tsink(messages);\n\t\t\t}\n\t\t} else {\n\t\t\t// Immediate: deliver synchronously.\n\t\t\tsink(messages);\n\t\t}\n\t\treturn;\n\t}\n\t// Multi-message: three-way partition by tier.\n\tconst { immediate, deferred, terminal } = partitionForBatch(messages);\n\n\t// 1. Immediate signals (tier 0–2, 5) — deliver synchronously now.\n\tif (immediate.length > 0) {\n\t\tsink(immediate);\n\t}\n\n\t// 2. Deferred (tier 3) + Terminal (tier 4) — canonical order preserved.\n\tif (isBatching()) {\n\t\tif (deferred.length > 0) {\n\t\t\tqueue.push(() => sink(deferred));\n\t\t}\n\t\tif (terminal.length > 0) {\n\t\t\tqueue.push(() => sink(terminal));\n\t\t}\n\t} else {\n\t\tif (deferred.length > 0) {\n\t\t\tsink(deferred);\n\t\t}\n\t\tif (terminal.length > 0) {\n\t\t\tsink(terminal);\n\t\t}\n\t}\n}\n\n/**\n * Sequential strategy: walk messages one at a time. Phase-2 (DATA/RESOLVED) and\n * terminal (COMPLETE/ERROR) messages are deferred while batching; immediate\n * messages deliver synchronously. Matches graphrefly-py `_down_sequential`.\n *\n * Tier legend (after START introduction):\n * 0 = START — immediate, delivered before other tiers\n * 1 = DIRTY, INVALIDATE — immediate\n * 2 = PAUSE, RESUME — immediate\n * 3 = DATA, RESOLVED — deferred inside batch\n * 4 = COMPLETE, ERROR — deferred to phase-3 queue (drains after data)\n * 5 = TEARDOWN — deferred to phase-3 queue (drains after data)\n */\nfunction _downSequential(\n\tsink: (messages: Messages) => void,\n\tmessages: Messages,\n\tphase: 2 | 3 = 2,\n): void {\n\tconst dataQueue = phase === 3 ? pendingPhase3 : pendingPhase2;\n\tfor (const msg of messages) {\n\t\tconst tier = messageTier(msg[0]);\n\t\tif (tier === 3) {\n\t\t\t// Phase-2 (DATA/RESOLVED): defer while batching.\n\t\t\tif (isBatching()) {\n\t\t\t\tconst m = msg;\n\t\t\t\tdataQueue.push(() => sink([m]));\n\t\t\t} else {\n\t\t\t\tsink([msg]);\n\t\t\t}\n\t\t} else if (tier >= 4) {\n\t\t\t// Terminal + destruction (COMPLETE/ERROR/TEARDOWN): always route to\n\t\t\t// phase-3 queue — terminals and teardown must drain after all phase-2\n\t\t\t// work to prevent premature termination or early resource release.\n\t\t\tif (isBatching()) {\n\t\t\t\tconst m = msg;\n\t\t\t\tpendingPhase3.push(() => sink([m]));\n\t\t\t} else {\n\t\t\t\tsink([msg]);\n\t\t\t}\n\t\t} else {\n\t\t\t// Immediate (START, DIRTY, INVALIDATE, PAUSE, RESUME):\n\t\t\t// deliver synchronously.\n\t\t\tsink([msg]);\n\t\t}\n\t}\n}\n","import type { Actor } from \"./actor.js\";\n\n/**\n * Actions checked by {@link NodeGuard}. `write` covers both {@link Node.down} and\n * {@link Node.up} today; finer-grained strings may be added later (e.g. `\"write.data\"`).\n */\nexport type GuardAction = \"write\" | \"signal\" | \"observe\" | (string & {});\n\nexport type NodeGuard = (actor: Actor, action: GuardAction) => boolean;\n\nexport type GuardDeniedDetails = {\n\tactor: Actor;\n\taction: GuardAction;\n\t/** Registry or options name when known */\n\tnodeName?: string;\n};\n\n/**\n * Thrown when a {@link NodeGuard} denies an action for a given actor.\n *\n * Carries the rejected `actor`, `action`, and optional `nodeName` for diagnostic\n * messages and middleware error handling.\n *\n * @example\n * ```ts\n * import { GuardDenied, policy } from \"@graphrefly/graphrefly-ts\";\n *\n * const guard = policy((allow) => { allow(\"observe\"); });\n * try {\n * if (!guard({ type: \"llm\", id: \"agent-1\" }, \"write\")) {\n * throw new GuardDenied(\n * { actor: { type: \"llm\", id: \"agent-1\" }, action: \"write\", nodeName: \"userInput\" },\n * );\n * }\n * } catch (e) {\n * if (e instanceof GuardDenied) console.error(e.action, e.actor.type); // \"write\" \"llm\"\n * }\n * ```\n */\nexport class GuardDenied extends Error {\n\treadonly actor: Actor;\n\treadonly action: GuardAction;\n\treadonly nodeName?: string;\n\n\t/**\n\t * @param details - Actor, action, and optional node name for the denial.\n\t * @param message - Optional override for the default error message.\n\t */\n\tconstructor(details: GuardDeniedDetails, message?: string) {\n\t\tsuper(\n\t\t\tmessage ??\n\t\t\t\t`GuardDenied: action \"${String(details.action)}\" denied for actor type \"${String(details.actor.type)}\"`,\n\t\t);\n\t\tthis.name = \"GuardDenied\";\n\t\tthis.actor = details.actor;\n\t\tthis.action = details.action;\n\t\tthis.nodeName = details.nodeName;\n\t}\n\n\t/** Qualified registry path when known (roadmap diagnostics: same as {@link nodeName}). */\n\tget node(): string | undefined {\n\t\treturn this.nodeName;\n\t}\n}\n\ntype Where = (actor: Actor) => boolean;\n\ntype Rule = {\n\tkind: \"allow\" | \"deny\";\n\tactions: Set<GuardAction>;\n\twhere: Where;\n};\n\nfunction normalizeActions(action: GuardAction | readonly GuardAction[]): GuardAction[] {\n\tif (Array.isArray(action)) {\n\t\treturn [...action];\n\t}\n\treturn [action as GuardAction];\n}\n\nfunction matchesActions(set: Set<GuardAction>, action: GuardAction): boolean {\n\treturn set.has(action) || set.has(\"*\" as GuardAction);\n}\n\nexport type PolicyAllow = (\n\taction: GuardAction | readonly GuardAction[],\n\topts?: { where?: Where },\n) => void;\n\nexport type PolicyDeny = (\n\taction: GuardAction | readonly GuardAction[],\n\topts?: { where?: Where },\n) => void;\n\nexport type PolicyRuleData = {\n\teffect: \"allow\" | \"deny\";\n\taction: GuardAction | readonly GuardAction[];\n\tactorType?: string | readonly string[];\n\tactorId?: string | readonly string[];\n\tclaims?: Record<string, unknown>;\n};\n\n/**\n * Declarative guard builder. Precedence: any matching **deny** blocks even if an allow also matches.\n * If no rule matches, the guard returns `false` (deny-by-default). Aligned with graphrefly-py `policy()`.\n *\n * @param build - Callback that registers `allow(...)` / `deny(...)` rules in order.\n * @returns A `NodeGuard` for use as `node({ guard })`.\n *\n * @example\n * ```ts\n * const guard = policy((allow, deny) => {\n * allow(\"observe\");\n * deny(\"write\", { where: (a) => a.type === \"llm\" });\n * });\n * ```\n */\nexport function policy(build: (allow: PolicyAllow, deny: PolicyDeny) => void): NodeGuard {\n\tconst rules: Rule[] = [];\n\tconst allow: PolicyAllow = (action, opts) => {\n\t\trules.push({\n\t\t\tkind: \"allow\",\n\t\t\tactions: new Set(normalizeActions(action)),\n\t\t\twhere: opts?.where ?? (() => true),\n\t\t});\n\t};\n\tconst deny: PolicyDeny = (action, opts) => {\n\t\trules.push({\n\t\t\tkind: \"deny\",\n\t\t\tactions: new Set(normalizeActions(action)),\n\t\t\twhere: opts?.where ?? (() => true),\n\t\t});\n\t};\n\tbuild(allow, deny);\n\treturn (actor, action) => {\n\t\tlet denied = false;\n\t\tlet allowed = false;\n\t\tfor (const r of rules) {\n\t\t\tif (!matchesActions(r.actions, action)) continue;\n\t\t\tif (!r.where(actor)) continue;\n\t\t\tif (r.kind === \"deny\") {\n\t\t\t\tdenied = true;\n\t\t\t} else {\n\t\t\t\tallowed = true;\n\t\t\t}\n\t\t}\n\t\tif (denied) return false;\n\t\treturn allowed;\n\t};\n}\n\n/**\n * Rebuild a declarative guard from persisted policy data (snapshot-safe).\n *\n * Rules are deny-overrides, same semantics as {@link policy}.\n */\nexport function policyFromRules(rules: readonly PolicyRuleData[]): NodeGuard {\n\treturn policy((allow, deny) => {\n\t\tfor (const rule of rules) {\n\t\t\tconst actorTypes =\n\t\t\t\trule.actorType == null\n\t\t\t\t\t? null\n\t\t\t\t\t: new Set(Array.isArray(rule.actorType) ? rule.actorType : [rule.actorType]);\n\t\t\tconst actorIds =\n\t\t\t\trule.actorId == null\n\t\t\t\t\t? null\n\t\t\t\t\t: new Set(Array.isArray(rule.actorId) ? rule.actorId : [rule.actorId]);\n\t\t\tconst claimEntries = Object.entries(rule.claims ?? {});\n\t\t\tconst where: Where = (actor) => {\n\t\t\t\tif (actorTypes !== null && !actorTypes.has(String(actor.type))) return false;\n\t\t\t\tif (actorIds !== null && !actorIds.has(String(actor.id ?? \"\"))) return false;\n\t\t\t\tfor (const [key, value] of claimEntries) {\n\t\t\t\t\tif ((actor as Record<string, unknown>)[key] !== value) return false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\t\t\tif (rule.effect === \"deny\") {\n\t\t\t\tdeny(rule.action, { where });\n\t\t\t} else {\n\t\t\t\tallow(rule.action, { where });\n\t\t\t}\n\t\t}\n\t});\n}\n\nconst STANDARD_WRITE_TYPES = [\"human\", \"llm\", \"wallet\", \"system\"] as const;\n\n/**\n * Derives a best-effort `meta.access` hint string by probing `guard` with the\n * standard actor types `human`, `llm`, `wallet`, `system` for the `\"write\"` action\n * (roadmap 1.5). Aligned with graphrefly-py `access_hint_for_guard`.\n *\n * @param guard - Guard function to probe (typically from {@link policy}).\n * @returns `\"restricted\"` when no standard type is allowed; `\"both\"` when both\n * `human` and `llm` are allowed (plus optionally `system`); the single allowed\n * type name when only one passes; or a `\"+\"` joined list otherwise.\n *\n * @example\n * ```ts\n * import { policy, accessHintForGuard } from \"@graphrefly/graphrefly-ts\";\n *\n * const guardBoth = policy((allow) => { allow(\"write\"); });\n * accessHintForGuard(guardBoth); // \"both\"\n *\n * const guardHuman = policy((allow) => {\n * allow(\"write\", { where: (a) => a.type === \"human\" });\n * });\n * accessHintForGuard(guardHuman); // \"human\"\n * ```\n */\nexport function accessHintForGuard(guard: NodeGuard): string {\n\tconst allowed = STANDARD_WRITE_TYPES.filter((t) => guard({ type: t, id: \"\" }, \"write\"));\n\tif (allowed.length === 0) return \"restricted\";\n\tif (\n\t\tallowed.includes(\"human\") &&\n\t\tallowed.includes(\"llm\") &&\n\t\tallowed.every((t) => t === \"human\" || t === \"llm\" || t === \"system\")\n\t) {\n\t\treturn \"both\";\n\t}\n\tif (allowed.length === 1) return allowed[0];\n\treturn allowed.join(\"+\");\n}\n","/**\n * Centralised timestamp utilities.\n *\n * Convention: all graphrefly-ts timestamps use nanoseconds (`_ns` suffix).\n *\n * - {@link monotonicNs} — monotonic clock (ordering, durations, timeline events).\n * - {@link wallClockNs} — wall-clock (mutation attribution, cron emission).\n *\n * **Precision limits (JS platform):**\n *\n * - `monotonicNs`: effective ~microsecond precision. `performance.now()` returns\n * milliseconds with ~5µs resolution; the last 3 digits of the nanosecond value\n * are always zero. Python's `time.monotonic_ns()` gives true nanoseconds.\n *\n * - `wallClockNs`: ~256ns precision loss at current epoch. `Date.now() * 1e6`\n * produces values around 1.8×10¹⁸ which exceed IEEE 754's 2⁵³ safe integer\n * limit. Python's `time.time_ns()` (arbitrary-precision `int`) has no loss.\n * In practice this is irrelevant — JS is single-threaded, so sub-microsecond\n * timestamp collisions cannot occur.\n */\n\n/** Monotonic nanosecond timestamp via `performance.now()`. */\nexport function monotonicNs(): number {\n\treturn Math.trunc(performance.now() * 1_000_000);\n}\n\n/** Wall-clock nanosecond timestamp via `Date.now()`. */\nexport function wallClockNs(): number {\n\treturn Date.now() * 1_000_000;\n}\n","/**\n * Node versioning — GRAPHREFLY-SPEC §7.\n *\n * Progressive, optional versioning for node identity and change tracking.\n *\n * - **V0**: `id` + `version` — identity & change detection (~16 bytes overhead)\n * - **V1**: + `cid` + `prev` — content addressing & linked history (~60 bytes overhead)\n *\n * **Lifecycle notes:**\n * - Version advances only on DATA (not RESOLVED, INVALIDATE, or TEARDOWN).\n * - `resetOnTeardown` clears the cached value but does NOT reset versioning state.\n * After teardown, `v.cid` still reflects the last DATA value, not the cleared cache.\n * The invariant `hash(node.get()) === v.cid` only holds in `settled`/`resolved` status.\n * - Resubscribable nodes preserve versioning across subscription lifetimes (monotonic counter).\n */\n\nimport { createHash, randomUUID } from \"node:crypto\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** V0: identity + monotonic version counter. */\nexport type V0 = {\n\treadonly id: string;\n\tversion: number;\n};\n\n/** V1: V0 + content-addressed identifier + previous cid link. */\nexport type V1 = V0 & {\n\tcid: string;\n\tprev: string | null;\n};\n\n/** Union of all versioning info shapes. */\nexport type NodeVersionInfo = V0 | V1;\n\n/** Supported versioning levels (extensible to 2, 3 later). */\nexport type VersioningLevel = 0 | 1;\n\n/** Function that hashes a value to a hex string (for V1 cid). */\nexport type HashFn = (value: unknown) => string;\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface VersioningOptions {\n\t/** Override auto-generated id. */\n\tid?: string;\n\t/** Custom hash function for V1 cid (default: SHA-256 truncated to 16 hex chars). */\n\thash?: HashFn;\n}\n\n// ---------------------------------------------------------------------------\n// Default hash\n// ---------------------------------------------------------------------------\n\n/**\n * Canonicalize a value for deterministic cross-language hashing.\n *\n * - Integer-valued floats normalize to integer strings (`1.0` → `1`).\n * - `NaN`, `Infinity`, `-Infinity` are rejected (no JSON equivalent).\n * - `undefined` normalizes to `null`.\n * - Object keys are sorted lexicographically.\n *\n * This ensures TS `JSON.stringify` and Python `json.dumps(sort_keys=True)`\n * produce identical output for the same logical value.\n */\nexport function canonicalizeForHash(value: unknown): unknown {\n\tif (value === undefined) return null;\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isFinite(value)) {\n\t\t\tthrow new TypeError(`Cannot hash non-finite number: ${value}`);\n\t\t}\n\t\tif (Number.isInteger(value) && !Number.isSafeInteger(value)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`Cannot hash integer outside safe range (|n| > 2^53-1): ${value}. ` +\n\t\t\t\t\t\"Cross-language cid parity is not guaranteed for unsafe integers.\",\n\t\t\t);\n\t\t}\n\t\treturn value;\n\t}\n\tif (typeof value === \"string\" || typeof value === \"boolean\" || value === null) {\n\t\treturn value;\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.map(canonicalizeForHash);\n\t}\n\tif (typeof value === \"object\" && value !== null) {\n\t\tconst sorted: Record<string, unknown> = {};\n\t\tfor (const k of Object.keys(value as Record<string, unknown>).sort()) {\n\t\t\tsorted[k] = canonicalizeForHash((value as Record<string, unknown>)[k]);\n\t\t}\n\t\treturn sorted;\n\t}\n\t// Fallback: coerce to null (bigint, symbol, function)\n\treturn null;\n}\n\n/**\n * Default content hash: SHA-256 of deterministic JSON, truncated to 16 hex chars (~64-bit).\n * Uses {@link canonicalizeForHash} for cross-language parity with Python `default_hash`.\n */\nexport function defaultHash(value: unknown): string {\n\tconst canonical = canonicalizeForHash(value ?? null);\n\tconst json = JSON.stringify(canonical);\n\treturn createHash(\"sha256\").update(json).digest(\"hex\").slice(0, 16);\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create initial versioning state for a node.\n *\n * @param level - 0 for V0, 1 for V1.\n * @param initialValue - The node's initial cached value (used for V1 cid).\n * @param opts - Optional overrides (id, hash).\n */\nexport function createVersioning(\n\tlevel: VersioningLevel,\n\tinitialValue: unknown,\n\topts?: VersioningOptions,\n): NodeVersionInfo {\n\tconst id = opts?.id ?? randomUUID();\n\tif (level === 0) {\n\t\treturn { id, version: 0 } satisfies V0;\n\t}\n\tconst hash = opts?.hash ?? defaultHash;\n\tconst cid = hash(initialValue);\n\treturn { id, version: 0, cid, prev: null } satisfies V1;\n}\n\n// ---------------------------------------------------------------------------\n// Advance\n// ---------------------------------------------------------------------------\n\n/**\n * Advance versioning state after a DATA emission (value changed).\n *\n * Mutates `info` in place for performance (called on every DATA).\n * Only call when the cached value has actually changed (not on RESOLVED).\n *\n * @param info - The node's current versioning state.\n * @param newValue - The new cached value.\n * @param hashFn - Hash function (only used for V1).\n */\nexport function advanceVersion(info: NodeVersionInfo, newValue: unknown, hashFn: HashFn): void {\n\tinfo.version += 1;\n\tif (\"cid\" in info) {\n\t\t(info as V1).prev = (info as V1).cid;\n\t\t(info as V1).cid = hashFn(newValue);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Guards\n// ---------------------------------------------------------------------------\n\n/** Type guard: is this V1 versioning info? */\nexport function isV1(info: NodeVersionInfo): info is V1 {\n\treturn \"cid\" in info;\n}\n","/**\n * `NodeBase` — abstract class implementing the {@link Node} protocol with\n * lifecycle machinery shared between {@link NodeImpl} (static deps) and\n * {@link DynamicNodeImpl} (runtime-tracked deps).\n *\n * **Responsibilities (shared):**\n * - Identity (name, describeKind, meta, guard, versioning)\n * - Cache + status lifecycle (`_cached`, `_status`, `_terminal`)\n * - Sink storage (null / single / Set fast paths)\n * - `subscribe()` with START handshake + first-subscriber activation\n * - `_downInternal` → `_downToSinks` delivery pipeline (via `downWithBatch`)\n * - `_downAutoValue` (value → protocol framing with equals)\n * - `_handleLocalLifecycle` (cached/status/terminal updates + meta propagation)\n *\n * **Subclass hooks (abstract):**\n * - `_onActivate()` — called when sinkCount transitions 0 → 1\n * - `_doDeactivate()` — cleanup on deactivation (at-most-once, guarded by `_onDeactivate`)\n * - `up()` / `unsubscribe()` / `_upInternal()` — dep-iteration specifics\n * - `_createMetaNode()` — meta companion factory (avoids circular imports)\n *\n * See GRAPHREFLY-SPEC §2 and COMPOSITION-GUIDE §1 for protocol contracts.\n */\n\nimport type { Actor } from \"./actor.js\";\nimport { normalizeActor } from \"./actor.js\";\nimport { downWithBatch } from \"./batch.js\";\nimport { wallClockNs } from \"./clock.js\";\nimport type { GuardAction, NodeGuard } from \"./guard.js\";\nimport { GuardDenied } from \"./guard.js\";\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tINVALIDATE,\n\ttype Message,\n\ttype Messages,\n\tpropagatesToMeta,\n\tRESOLVED,\n\tSTART,\n\tTEARDOWN,\n} from \"./messages.js\";\nimport type { HashFn, NodeVersionInfo, VersioningLevel } from \"./versioning.js\";\nimport { advanceVersion, createVersioning, defaultHash } from \"./versioning.js\";\n\n// ---------------------------------------------------------------------------\n// Symbols\n// ---------------------------------------------------------------------------\n\n/**\n * Internal sentinel value: \"no cached value has been set or emitted.\"\n * Used instead of `undefined` so that `undefined` can be a valid emitted value.\n */\nexport const NO_VALUE: unique symbol = Symbol.for(\"graphrefly/NO_VALUE\");\n\n/**\n * Branded symbol that marks a {@link CleanupResult} wrapper — prevents\n * duck-type collisions with domain objects that happen to have a `cleanup`\n * property.\n */\nexport const CLEANUP_RESULT: unique symbol = Symbol.for(\"graphrefly/CLEANUP_RESULT\");\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Lifecycle status of a node (GRAPHREFLY-SPEC §2.2). */\nexport type NodeStatus =\n\t| \"disconnected\"\n\t| \"pending\"\n\t| \"dirty\"\n\t| \"settled\"\n\t| \"resolved\"\n\t| \"completed\"\n\t| \"errored\";\n\n/** Callback that receives downstream message batches. */\nexport type NodeSink = (messages: Messages) => void;\n\n/** Imperative actions available inside a node's compute function. */\nexport interface NodeActions {\n\t/** Emit raw messages downstream. */\n\tdown(messages: Messages): void;\n\t/** Emit a single value (auto-wraps in DIRTY/DATA or DIRTY/RESOLVED). */\n\temit(value: unknown): void;\n\t/** Send messages upstream toward sources. */\n\tup(messages: Messages): void;\n}\n\n/**\n * Callback for intercepting messages before the default dispatch (§2.6).\n *\n * Called for every message from every dep. Return `true` to consume\n * (skip default handling), or `false` to let default dispatch run.\n */\nexport type OnMessageHandler = (msg: Message, depIndex: number, actions: NodeActions) => boolean;\n\n/**\n * Internal inspector hook (opt-in): emits dependency message and run events\n * for graph-level observability features (`observe(..., { causal|derived })`).\n */\nexport type NodeInspectorHookEvent =\n\t| { kind: \"dep_message\"; depIndex: number; message: Message }\n\t| { kind: \"run\"; depValues: readonly unknown[] };\n\nexport type NodeInspectorHook = (event: NodeInspectorHookEvent) => void;\n\n/** Explicit describe `type` for {@link Graph.describe} (GRAPHREFLY-SPEC Appendix B). */\nexport type NodeDescribeKind = \"state\" | \"derived\" | \"producer\" | \"operator\" | \"effect\";\n\n/** Options accepted by every node constructor. */\nexport interface NodeOptions {\n\tname?: string;\n\t/**\n\t * Overrides inferred `type` in describe output. Sugar constructors set this;\n\t * omit to infer from deps / fn / manual emit usage.\n\t */\n\tdescribeKind?: NodeDescribeKind;\n\t/** Equality check for RESOLVED detection. Defaults to `Object.is`. */\n\tequals?: (a: unknown, b: unknown) => boolean;\n\tinitial?: unknown;\n\t/**\n\t * Each key becomes an independently subscribable companion node.\n\t * Meta nodes outlive the parent's subscription lifecycle: when all sinks\n\t * unsubscribe the parent deactivates but meta nodes stay alive.\n\t * Send `[[TEARDOWN]]` to the parent to release meta node resources.\n\t */\n\tmeta?: Record<string, unknown>;\n\t/** Allow fresh subscriptions after COMPLETE/ERROR. */\n\tresubscribable?: boolean;\n\t/**\n\t * Invoked when a new {@link Node.subscribe} clears a terminal state on a\n\t * resubscribable node — reset operator-local counters/accumulators here.\n\t */\n\tonResubscribe?: () => void;\n\t/** Clear cached value on TEARDOWN. */\n\tresetOnTeardown?: boolean;\n\t/**\n\t * When `true` (default), auto-emit `[[COMPLETE]]` when all deps complete\n\t * (spec §1.3.5). Set `false` for derived/operator nodes that should not\n\t * auto-complete.\n\t */\n\tcompleteWhenDepsComplete?: boolean;\n\t/**\n\t * Intercept messages before the default dispatch (spec §2.6).\n\t *\n\t * Return `true` to consume the message (skip default handling),\n\t * or `false` to let the default dispatch run.\n\t */\n\tonMessage?: OnMessageHandler;\n\t/**\n\t * ABAC: `(actor, action) => boolean`. `write` applies to both {@link Node.down} and {@link Node.up}.\n\t * Companion {@link NodeOptions.meta | meta} nodes inherit this guard from the primary.\n\t */\n\tguard?: NodeGuard;\n\t/** Opt-in versioning level (GRAPHREFLY-SPEC §7). */\n\tversioning?: VersioningLevel;\n\t/** Override auto-generated versioning id. */\n\tversioningId?: string;\n\t/** Custom hash function for V1 cid computation. */\n\tversioningHash?: HashFn;\n}\n\n/** Actor/delivery context for {@link Node.down} and {@link Node.up}. */\nexport type NodeTransportOptions = {\n\tactor?: Actor;\n\t/**\n\t * When `true`, skips guard checks (reactive internals, graph lifecycle TEARDOWN, etc.).\n\t * Not for untrusted call sites.\n\t */\n\tinternal?: boolean;\n\t/**\n\t * `signal` for {@link Graph.signal} deliveries; default `write` for {@link Graph.set}\n\t * and direct `down`.\n\t */\n\tdelivery?: \"write\" | \"signal\";\n};\n\n/** Optional hints passed to {@link Node.subscribe}. */\nexport interface SubscribeHints {\n\t/**\n\t * Subscriber has exactly one dep with `fn` — the source may skip DIRTY\n\t * dispatch when this is the sole subscriber. The subscriber synthesizes\n\t * dirty state locally.\n\t */\n\tsingleDep?: boolean;\n\t/**\n\t * Actor to check against the node's `observe` guard. When set,\n\t * `subscribe()` throws {@link GuardDenied} if the actor is not permitted\n\t * to observe this node.\n\t */\n\tactor?: Actor;\n}\n\n/** A reactive node in the GraphReFly protocol. */\nexport interface Node<T = unknown> {\n\treadonly name?: string;\n\treadonly status: NodeStatus;\n\treadonly meta: Record<string, Node>;\n\t/** Returns the current cached value. */\n\tget(): T | undefined;\n\t/** Push messages downstream. */\n\tdown(messages: Messages, options?: NodeTransportOptions): void;\n\t/**\n\t * Registers a sink to receive downstream messages.\n\t *\n\t * @param sink - Callback receiving message batches.\n\t * @param hints - Optional optimization hints (e.g. `{ singleDep: true }`).\n\t * @returns An unsubscribe function (idempotent).\n\t */\n\tsubscribe(sink: NodeSink, hints?: SubscribeHints): () => void;\n\t/** Send messages upstream (present on nodes with deps). */\n\tup?: (messages: Messages, options?: NodeTransportOptions) => void;\n\t/** Disconnect from upstream deps (present on nodes with deps). */\n\tunsubscribe?: () => void;\n\t/** Last successful guarded `down` / `up` (not set for `internal` deliveries). */\n\treadonly lastMutation?: Readonly<{ actor: Actor; timestamp_ns: number }>;\n\t/** Whether `actor` may {@link Graph.observe} this node. */\n\tallowsObserve(actor: Actor): boolean;\n\t/** Whether a {@link NodeOptions.guard | guard} is installed. */\n\thasGuard(): boolean;\n\t/** Versioning info (GRAPHREFLY-SPEC §7). `undefined` when versioning is not enabled. */\n\treadonly v: Readonly<NodeVersionInfo> | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Cleanup result wrapper\n// ---------------------------------------------------------------------------\n\n/**\n * Explicit cleanup wrapper. When a node fn returns `{ cleanup, value? }`,\n * `cleanup` is registered as the teardown/recompute cleanup and `value`\n * (if present) is emitted as data. This avoids the ambiguity where returning\n * a plain function is silently consumed as cleanup instead of emitted as data.\n */\nexport type CleanupResult<T = unknown> = {\n\treadonly [CLEANUP_RESULT]: true;\n\tcleanup: () => void;\n\tvalue?: T;\n};\n\n/** Create a branded {@link CleanupResult}. */\nexport function cleanupResult<T>(cleanup: () => void): CleanupResult<T>;\nexport function cleanupResult<T>(cleanup: () => void, value: T): CleanupResult<T>;\nexport function cleanupResult<T>(cleanup: () => void, ...args: [] | [T]): CleanupResult<T> {\n\tconst r: CleanupResult<T> = { [CLEANUP_RESULT]: true, cleanup };\n\tif (args.length > 0) r.value = args[0];\n\treturn r;\n}\n\nexport const isCleanupResult = (value: unknown): value is CleanupResult =>\n\ttypeof value === \"object\" && value !== null && CLEANUP_RESULT in value;\n\nexport const isCleanupFn = (value: unknown): value is () => void => typeof value === \"function\";\n\n// ---------------------------------------------------------------------------\n// Status transitions\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the post-message status for `status` after processing `msg`.\n * START is informational and does not transition status.\n */\nexport function statusAfterMessage(status: NodeStatus, msg: Message): NodeStatus {\n\tconst t = msg[0];\n\tif (t === DIRTY) return \"dirty\";\n\tif (t === DATA) return \"settled\";\n\tif (t === RESOLVED) return \"resolved\";\n\tif (t === COMPLETE) return \"completed\";\n\tif (t === ERROR) return \"errored\";\n\tif (t === INVALIDATE) return \"dirty\";\n\tif (t === TEARDOWN) return \"disconnected\";\n\treturn status;\n}\n\n// ---------------------------------------------------------------------------\n// BitSet — dep mask helper (int fast path for ≤31 deps, array for more)\n// ---------------------------------------------------------------------------\n\n/**\n * Dep settlement tracker. IMPORTANT: `covers()` requires both operands to be\n * the same concrete type (both IntBitSet or both ArrayBitSet). Within a node,\n * all masks share the same `createBitSet(deps.length)` factory, so this is\n * always satisfied.\n */\nexport interface BitSet {\n\tset(index: number): void;\n\tclear(index: number): void;\n\thas(index: number): boolean;\n\t/** True when all bits in `other` are also set in `this`. */\n\tcovers(other: BitSet): boolean;\n\t/** True when at least one bit is set. */\n\tany(): boolean;\n\treset(): void;\n\t/** Set all bits in [0, size) — used for the pre-set-dirty wave trick. */\n\tsetAll(): void;\n}\n\nfunction createIntBitSet(size: number): BitSet {\n\tconst fullMask = size >= 32 ? -1 : ~(-1 << size);\n\tlet bits = 0;\n\treturn {\n\t\tset(i: number) {\n\t\t\tbits |= 1 << i;\n\t\t},\n\t\tclear(i: number) {\n\t\t\tbits &= ~(1 << i);\n\t\t},\n\t\thas(i: number) {\n\t\t\treturn (bits & (1 << i)) !== 0;\n\t\t},\n\t\tcovers(other: BitSet) {\n\t\t\tconst otherBits = (other as unknown as { _bits(): number })._bits();\n\t\t\treturn (bits & otherBits) === otherBits;\n\t\t},\n\t\tany() {\n\t\t\treturn bits !== 0;\n\t\t},\n\t\treset() {\n\t\t\tbits = 0;\n\t\t},\n\t\tsetAll() {\n\t\t\tbits = fullMask;\n\t\t},\n\t\t_bits() {\n\t\t\treturn bits;\n\t\t},\n\t} as BitSet & { _bits(): number };\n}\n\nfunction createArrayBitSet(size: number): BitSet {\n\tconst words = new Uint32Array(Math.ceil(size / 32));\n\tconst lastBits = size % 32;\n\tconst lastWordMask = lastBits === 0 ? 0xffffffff : ((1 << lastBits) - 1) >>> 0;\n\treturn {\n\t\tset(i: number) {\n\t\t\twords[i >>> 5] |= 1 << (i & 31);\n\t\t},\n\t\tclear(i: number) {\n\t\t\twords[i >>> 5] &= ~(1 << (i & 31));\n\t\t},\n\t\thas(i: number) {\n\t\t\treturn (words[i >>> 5] & (1 << (i & 31))) !== 0;\n\t\t},\n\t\tcovers(other: BitSet) {\n\t\t\tconst ow = (other as unknown as { _words: Uint32Array })._words;\n\t\t\tfor (let w = 0; w < words.length; w++) {\n\t\t\t\tif ((words[w] & ow[w]) >>> 0 !== ow[w]) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\tany() {\n\t\t\tfor (let w = 0; w < words.length; w++) {\n\t\t\t\tif (words[w] !== 0) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\treset() {\n\t\t\twords.fill(0);\n\t\t},\n\t\tsetAll() {\n\t\t\tfor (let w = 0; w < words.length - 1; w++) words[w] = 0xffffffff;\n\t\t\tif (words.length > 0) words[words.length - 1] = lastWordMask;\n\t\t},\n\t\t_words: words,\n\t} as unknown as BitSet;\n}\n\n/** Create a BitSet sized for `size` bits (≤31 uses fast int path). */\nexport function createBitSet(size: number): BitSet {\n\treturn size <= 31 ? createIntBitSet(size) : createArrayBitSet(size);\n}\n\n// ---------------------------------------------------------------------------\n// NodeBase\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract base class for every node in the graph. Both {@link NodeImpl}\n * (static deps) and {@link DynamicNodeImpl} (runtime-tracked deps) extend\n * this to share subscribe/sink/lifecycle machinery.\n *\n * Invariants (see GRAPHREFLY-SPEC §2.2):\n * - `_sinkCount` always reflects the size of `_sinks`.\n * - `_cached === NO_VALUE` iff the node has never produced a value (SENTINEL).\n * - `_terminal` is set exactly once (per subscription cycle for resubscribable).\n * - `_onActivate` runs exactly once per activation cycle; `_doDeactivate`\n * runs at most once per deactivation (guarded by `_active` flag).\n *\n * ROM/RAM rule (GRAPHREFLY-SPEC §2.2): state nodes (no fn) preserve `_cached`\n * across disconnect — intrinsic, non-volatile. Compute nodes (derived,\n * producer, dynamic) clear `_cached` on disconnect in their subclass\n * `_doDeactivate` — their value is a function of live subscriptions.\n */\nexport abstract class NodeBase<T = unknown> implements Node<T> {\n\t// --- Identity (set once) ---\n\tprotected readonly _optsName: string | undefined;\n\tprivate _registryName: string | undefined;\n\t/** @internal Read by `describeNode` before inference. */\n\treadonly _describeKind: NodeDescribeKind | undefined;\n\treadonly meta: Record<string, Node>;\n\n\t// --- Options ---\n\tprotected readonly _equals: (a: unknown, b: unknown) => boolean;\n\tprotected readonly _resubscribable: boolean;\n\tprotected readonly _resetOnTeardown: boolean;\n\tprotected readonly _onResubscribe: (() => void) | undefined;\n\tprotected readonly _onMessage: OnMessageHandler | undefined;\n\t/** @internal Read by `describeNode` for `accessHintForGuard`. */\n\treadonly _guard: NodeGuard | undefined;\n\t/** @internal Subclasses update this through {@link _recordMutation}. */\n\tprotected _lastMutation: { actor: Actor; timestamp_ns: number } | undefined;\n\n\t// --- Versioning ---\n\tprotected _hashFn: HashFn;\n\tprivate _versioning: NodeVersionInfo | undefined;\n\n\t// --- Lifecycle state ---\n\t/** @internal Read by `describeNode` and `graph.ts`. */\n\t_cached: T | typeof NO_VALUE;\n\t/** @internal Read externally via `get status()`. */\n\t_status: NodeStatus;\n\tprotected _terminal = false;\n\tprivate _active = false;\n\n\t// --- Sink storage ---\n\t/** @internal Read by `graph/profile.ts` for subscriber counts. */\n\t_sinkCount = 0;\n\tprotected _singleDepSinkCount = 0;\n\tprotected _singleDepSinks = new WeakSet<NodeSink>();\n\tprotected _sinks: NodeSink | Set<NodeSink> | null = null;\n\n\t// --- Actions + bound helpers ---\n\tprotected readonly _actions: NodeActions;\n\tprotected readonly _boundDownToSinks: (messages: Messages) => void;\n\n\t// --- Inspector hook (Graph observability) ---\n\tprivate _inspectorHook: NodeInspectorHook | undefined;\n\n\tconstructor(opts: NodeOptions) {\n\t\tthis._optsName = opts.name;\n\t\tthis._describeKind = opts.describeKind;\n\t\tthis._equals = opts.equals ?? Object.is;\n\t\tthis._resubscribable = opts.resubscribable ?? false;\n\t\tthis._resetOnTeardown = opts.resetOnTeardown ?? false;\n\t\tthis._onResubscribe = opts.onResubscribe;\n\t\tthis._onMessage = opts.onMessage;\n\t\tthis._guard = opts.guard;\n\n\t\t// Cache pre-populated from `initial`, or SENTINEL.\n\t\tthis._cached = \"initial\" in opts ? (opts.initial as T) : NO_VALUE;\n\t\t// Initial status: state with value starts settled, otherwise disconnected.\n\t\tthis._status = \"disconnected\";\n\n\t\t// Versioning (GRAPHREFLY-SPEC §7)\n\t\tthis._hashFn = opts.versioningHash ?? defaultHash;\n\t\tthis._versioning =\n\t\t\topts.versioning != null\n\t\t\t\t? createVersioning(opts.versioning, this._cached === NO_VALUE ? undefined : this._cached, {\n\t\t\t\t\t\tid: opts.versioningId,\n\t\t\t\t\t\thash: this._hashFn,\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\n\t\t// Build companion meta nodes — dispatched to subclass to avoid\n\t\t// circular imports (node-base → node → node-base).\n\t\tconst meta: Record<string, Node> = {};\n\t\tfor (const [k, v] of Object.entries(opts.meta ?? {})) {\n\t\t\tmeta[k] = this._createMetaNode(k, v, opts);\n\t\t}\n\t\tObject.freeze(meta);\n\t\tthis.meta = meta;\n\n\t\t// Actions captured once — references `this` via an arrow/closure so\n\t\t// subclass hooks (e.g. `_onManualEmit`) fire on the right instance.\n\t\tconst self = this;\n\t\tthis._actions = {\n\t\t\tdown(messages): void {\n\t\t\t\tself._onManualEmit();\n\t\t\t\tself._downInternal(messages);\n\t\t\t},\n\t\t\temit(value): void {\n\t\t\t\tself._onManualEmit();\n\t\t\t\tself._downAutoValue(value);\n\t\t\t},\n\t\t\tup(messages): void {\n\t\t\t\tself._upInternal(messages);\n\t\t\t},\n\t\t};\n\n\t\tthis._boundDownToSinks = this._downToSinks.bind(this);\n\t}\n\n\t/**\n\t * Subclass hook invoked by `actions.down` / `actions.emit`. Default no-op;\n\t * {@link NodeImpl} overrides to set `_manualEmitUsed`.\n\t */\n\tprotected _onManualEmit(): void {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Create a companion meta node. Called from the base constructor; must\n\t * not touch subclass fields that haven't been initialized yet (safe to\n\t * read from `opts`).\n\t */\n\tprotected abstract _createMetaNode(key: string, initialValue: unknown, opts: NodeOptions): Node;\n\n\t// --- Identity getters ---\n\n\tget name(): string | undefined {\n\t\treturn this._registryName ?? this._optsName;\n\t}\n\n\t/** @internal Assigned by `Graph.add` when registered without an options `name`. */\n\t_assignRegistryName(localName: string): void {\n\t\tif (this._optsName !== undefined || this._registryName !== undefined) return;\n\t\tthis._registryName = localName;\n\t}\n\n\t/**\n\t * @internal Attach/remove inspector hook for graph-level observability.\n\t * Returns a disposer that restores the previous hook.\n\t */\n\t_setInspectorHook(hook?: NodeInspectorHook): () => void {\n\t\tconst prev = this._inspectorHook;\n\t\tthis._inspectorHook = hook;\n\t\treturn () => {\n\t\t\tif (this._inspectorHook === hook) {\n\t\t\t\tthis._inspectorHook = prev;\n\t\t\t}\n\t\t};\n\t}\n\n\t/** @internal Used by subclasses to surface inspector events. */\n\tprotected _emitInspectorHook(event: NodeInspectorHookEvent): void {\n\t\tthis._inspectorHook?.(event);\n\t}\n\n\tget status(): NodeStatus {\n\t\treturn this._status;\n\t}\n\n\tget lastMutation(): Readonly<{ actor: Actor; timestamp_ns: number }> | undefined {\n\t\treturn this._lastMutation;\n\t}\n\n\tget v(): Readonly<NodeVersionInfo> | undefined {\n\t\treturn this._versioning;\n\t}\n\n\t/** @internal Used by `Graph.setVersioning` to retroactively apply versioning. */\n\t_applyVersioning(level: VersioningLevel, opts?: { id?: string; hash?: HashFn }): void {\n\t\tif (this._versioning != null) return;\n\t\tthis._hashFn = opts?.hash ?? this._hashFn;\n\t\tthis._versioning = createVersioning(\n\t\t\tlevel,\n\t\t\tthis._cached === NO_VALUE ? undefined : this._cached,\n\t\t\t{\n\t\t\t\tid: opts?.id,\n\t\t\t\thash: this._hashFn,\n\t\t\t},\n\t\t);\n\t}\n\n\thasGuard(): boolean {\n\t\treturn this._guard != null;\n\t}\n\n\tallowsObserve(actor: Actor): boolean {\n\t\tif (this._guard == null) return true;\n\t\treturn this._guard(normalizeActor(actor), \"observe\");\n\t}\n\n\t// --- Public transport ---\n\n\tget(): T | undefined {\n\t\treturn this._cached === NO_VALUE ? undefined : this._cached;\n\t}\n\n\tdown(messages: Messages, options?: NodeTransportOptions): void {\n\t\tif (messages.length === 0) return;\n\t\tif (!options?.internal && this._guard != null) {\n\t\t\tconst actor = normalizeActor(options?.actor);\n\t\t\tconst delivery = options?.delivery ?? \"write\";\n\t\t\tconst action: GuardAction = delivery === \"signal\" ? \"signal\" : \"write\";\n\t\t\tif (!this._guard(actor, action)) {\n\t\t\t\tthrow new GuardDenied({ actor, action, nodeName: this.name });\n\t\t\t}\n\t\t\tthis._recordMutation(actor);\n\t\t}\n\t\tthis._downInternal(messages);\n\t}\n\n\t/** @internal Record a successful guarded mutation (called by `down` and subclass `up`). */\n\tprotected _recordMutation(actor: Actor): void {\n\t\tthis._lastMutation = { actor, timestamp_ns: wallClockNs() };\n\t}\n\n\t/** Abstract — subclasses forward messages to dependencies (or no-op for sources). */\n\tabstract up(messages: Messages, options?: NodeTransportOptions): void;\n\n\t/** Abstract — subclasses release upstream subscriptions (or no-op for sources). */\n\tabstract unsubscribe(): void;\n\n\t/** Internal upstream-send used by `actions.up`. */\n\tprotected abstract _upInternal(messages: Messages): void;\n\n\t/** Called when `_sinkCount` transitions 0 → 1. */\n\tprotected abstract _onActivate(): void;\n\n\t/**\n\t * At-most-once deactivation guard. Both TEARDOWN (eager) and\n\t * unsubscribe-body (lazy) call this. The `_active` flag ensures\n\t * `_doDeactivate` runs exactly once per activation cycle.\n\t */\n\tprotected _onDeactivate(): void {\n\t\tif (!this._active) return;\n\t\tthis._active = false;\n\t\tthis._doDeactivate();\n\t}\n\n\t/** Subclass hook: cleanup on deactivation (called at most once). */\n\tprotected abstract _doDeactivate(): void;\n\n\t// --- Subscribe (uniform across node shapes) ---\n\n\tsubscribe(sink: NodeSink, hints?: SubscribeHints): () => void {\n\t\tif (hints?.actor != null && this._guard != null) {\n\t\t\tconst actor = normalizeActor(hints.actor);\n\t\t\tif (!this._guard(actor, \"observe\")) {\n\t\t\t\tthrow new GuardDenied({ actor, action: \"observe\", nodeName: this.name });\n\t\t\t}\n\t\t}\n\n\t\t// Resubscribable terminal reset — clear terminal + cache, fire hook.\n\t\tif (this._terminal && this._resubscribable) {\n\t\t\tthis._terminal = false;\n\t\t\tthis._cached = NO_VALUE;\n\t\t\tthis._status = \"disconnected\";\n\t\t\tthis._onResubscribe?.();\n\t\t}\n\n\t\tthis._sinkCount += 1;\n\t\tif (hints?.singleDep) {\n\t\t\tthis._singleDepSinkCount += 1;\n\t\t\tthis._singleDepSinks.add(sink);\n\t\t}\n\n\t\t// §2.2 START handshake — delivered BEFORE registering sink in\n\t\t// `_sinks` so that re-entrant `_downToSinks` calls during\n\t\t// activation cannot reach the new sink before it receives START\n\t\t// (spec §1.3.8: START precedes any other message on a subscription).\n\t\t//\n\t\t// Wire shape:\n\t\t// SENTINEL cache → [[START]]\n\t\t// cached value → [[START], [DATA, cached]]\n\t\t//\n\t\t// DIRTY is intentionally NOT part of the handshake: nothing is\n\t\t// transitioning here, we're simply delivering the current state.\n\t\t// Downstream derived nodes mark both dirty+settled on DATA-without-\n\t\t// prior-DIRTY (spec §1.3.1 compat path), so wave tracking still\n\t\t// works without polluting observers with a spurious DIRTY.\n\t\t// §2.2 handshake: START (+ cached DATA if available). Terminal nodes\n\t\t// skip entirely — absence of START tells the subscriber the stream is\n\t\t// over (spec §2.2: \"absence of START means the subscription was never\n\t\t// established or the node is terminal\"). This is intentional: terminal\n\t\t// nodes release resources; replaying COMPLETE/ERROR to late subscribers\n\t\t// would require retaining the terminal cause indefinitely.\n\t\tif (!this._terminal) {\n\t\t\tconst startMessages: Messages =\n\t\t\t\tthis._cached === NO_VALUE ? [[START]] : [[START], [DATA, this._cached]];\n\t\t\tdownWithBatch(sink, startMessages);\n\t\t}\n\n\t\t// Register sink in the singleton / set storage — AFTER START so\n\t\t// re-entrant `_downToSinks` during `_onActivate` can reach it.\n\t\tif (this._sinks == null) {\n\t\t\tthis._sinks = sink;\n\t\t} else if (typeof this._sinks === \"function\") {\n\t\t\tthis._sinks = new Set<NodeSink>([this._sinks, sink]);\n\t\t} else {\n\t\t\tthis._sinks.add(sink);\n\t\t}\n\n\t\t// First subscriber triggers activation. May cause fn to run and emit\n\t\t// via `_downInternal` → `_downToSinks` to the new sink.\n\t\tif (this._sinkCount === 1 && !this._terminal) {\n\t\t\tthis._active = true;\n\t\t\tthis._onActivate();\n\t\t}\n\n\t\t// If activation did not produce a value (compute node blocked on\n\t\t// SENTINEL deps, or a source with no initial), surface \"pending\".\n\t\tif (!this._terminal && this._status === \"disconnected\" && this._cached === NO_VALUE) {\n\t\t\tthis._status = \"pending\";\n\t\t}\n\n\t\tlet removed = false;\n\t\treturn () => {\n\t\t\tif (removed) return;\n\t\t\tremoved = true;\n\t\t\tthis._sinkCount -= 1;\n\t\t\tif (this._singleDepSinks.has(sink)) {\n\t\t\t\tthis._singleDepSinkCount -= 1;\n\t\t\t\tthis._singleDepSinks.delete(sink);\n\t\t\t}\n\t\t\tif (this._sinks == null) return;\n\t\t\tif (typeof this._sinks === \"function\") {\n\t\t\t\tif (this._sinks === sink) this._sinks = null;\n\t\t\t} else {\n\t\t\t\tthis._sinks.delete(sink);\n\t\t\t\tif (this._sinks.size === 1) {\n\t\t\t\t\tconst [only] = this._sinks;\n\t\t\t\t\tthis._sinks = only;\n\t\t\t\t} else if (this._sinks.size === 0) {\n\t\t\t\t\tthis._sinks = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this._sinks == null) {\n\t\t\t\tthis._onDeactivate();\n\t\t\t}\n\t\t};\n\t}\n\n\t// --- Down pipeline ---\n\n\t/**\n\t * Core outgoing dispatch. Applies terminal filter + local lifecycle\n\t * update, then hands messages to `downWithBatch` for tier-aware delivery.\n\t */\n\tprotected _downInternal(messages: Messages): void {\n\t\tif (messages.length === 0) return;\n\t\tlet sinkMessages = messages;\n\t\tif (this._terminal && !this._resubscribable) {\n\t\t\t// After terminal, only TEARDOWN / INVALIDATE still propagate\n\t\t\t// (so graph teardown and cache-clear still work).\n\t\t\tconst pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);\n\t\t\tif (pass.length === 0) return;\n\t\t\tsinkMessages = pass as Messages;\n\t\t}\n\t\tthis._handleLocalLifecycle(sinkMessages);\n\t\t// Single-dep DIRTY-skip optimization: when this node's only sink is\n\t\t// single-dep AND the batch contains a phase-2 message (DATA/RESOLVED),\n\t\t// strip DIRTY from the outgoing batch. The downstream learns of the\n\t\t// value via the phase-2 message directly.\n\t\tif (this._canSkipDirty()) {\n\t\t\tlet hasPhase2 = false;\n\t\t\tfor (let i = 0; i < sinkMessages.length; i++) {\n\t\t\t\tconst t = sinkMessages[i][0];\n\t\t\t\tif (t === DATA || t === RESOLVED) {\n\t\t\t\t\thasPhase2 = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasPhase2) {\n\t\t\t\tconst filtered: Message[] = [];\n\t\t\t\tfor (let i = 0; i < sinkMessages.length; i++) {\n\t\t\t\t\tif (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);\n\t\t\t\t}\n\t\t\t\tif (filtered.length > 0) {\n\t\t\t\t\tdownWithBatch(this._boundDownToSinks, filtered);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tdownWithBatch(this._boundDownToSinks, sinkMessages);\n\t}\n\n\tprotected _canSkipDirty(): boolean {\n\t\treturn this._sinkCount === 1 && this._singleDepSinkCount === 1;\n\t}\n\n\tprotected _downToSinks(messages: Messages): void {\n\t\tif (this._sinks == null) return;\n\t\tif (typeof this._sinks === \"function\") {\n\t\t\tthis._sinks(messages);\n\t\t\treturn;\n\t\t}\n\t\t// Snapshot: a sink callback may unsubscribe itself or others mid-iteration.\n\t\t// Iterating the live Set would skip not-yet-visited sinks that were removed.\n\t\tconst snapshot = [...this._sinks];\n\t\tfor (const sink of snapshot) {\n\t\t\tsink(messages);\n\t\t}\n\t}\n\n\t/**\n\t * Update `_cached`, `_status`, `_terminal` from message batch before\n\t * delivery. Subclass hooks `_onInvalidate` / `_onTeardown` let\n\t * {@link NodeImpl} clear `_lastDepValues` and invoke cleanup fns.\n\t */\n\tprotected _handleLocalLifecycle(messages: Messages): void {\n\t\tfor (const m of messages) {\n\t\t\tconst t = m[0];\n\t\t\tif (t === DATA) {\n\t\t\t\tif (m.length < 2) {\n\t\t\t\t\t// §1.2: bare [DATA] without payload is a protocol violation.\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis._cached = m[1] as T;\n\t\t\t\tif (this._versioning != null) {\n\t\t\t\t\tadvanceVersion(this._versioning, m[1], this._hashFn);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (t === INVALIDATE) {\n\t\t\t\tthis._onInvalidate();\n\t\t\t\tthis._cached = NO_VALUE;\n\t\t\t}\n\t\t\tthis._status = statusAfterMessage(this._status, m);\n\t\t\tif (t === COMPLETE || t === ERROR) {\n\t\t\t\tthis._terminal = true;\n\t\t\t}\n\t\t\tif (t === TEARDOWN) {\n\t\t\t\tif (this._resetOnTeardown) {\n\t\t\t\t\tthis._cached = NO_VALUE;\n\t\t\t\t}\n\t\t\t\tthis._onTeardown();\n\t\t\t\ttry {\n\t\t\t\t\tthis._propagateToMeta(t);\n\t\t\t\t} finally {\n\t\t\t\t\t// Force upstream disconnect immediately — don't wait for\n\t\t\t\t\t// downstream to unsubscribe. _onDeactivate's _active\n\t\t\t\t\t// guard ensures _doDeactivate runs at most once.\n\t\t\t\t\tthis._onDeactivate();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (t !== TEARDOWN && propagatesToMeta(t)) {\n\t\t\t\tthis._propagateToMeta(t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Subclass hook: invoked when INVALIDATE arrives (before `_cached` is\n\t * cleared). {@link NodeImpl} uses this to run the fn cleanup fn and\n\t * drop `_lastDepValues` so the next wave re-runs fn.\n\t */\n\tprotected _onInvalidate(): void {\n\t\t/* no-op default */\n\t}\n\n\t/**\n\t * Subclass hook: invoked when TEARDOWN arrives, before `_onDeactivate`.\n\t * {@link NodeImpl} uses this to run any pending cleanup fn.\n\t */\n\tprotected _onTeardown(): void {\n\t\t/* no-op default */\n\t}\n\n\t/** Forward a signal to all companion meta nodes (best-effort). */\n\tprotected _propagateToMeta(t: symbol): void {\n\t\tfor (const metaNode of Object.values(this.meta)) {\n\t\t\ttry {\n\t\t\t\t// Direct access through NodeBase — meta nodes are always NodeBase instances.\n\t\t\t\t(metaNode as NodeBase)._downInternal([[t]]);\n\t\t\t} catch {\n\t\t\t\t/* best-effort — other meta nodes still receive the signal */\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Frame a computed value into the right protocol messages and dispatch\n\t * via `_downInternal`. Used by `_runFn` and `actions.emit`.\n\t */\n\tprotected _downAutoValue(value: unknown): void {\n\t\tconst wasDirty = this._status === \"dirty\";\n\t\tlet unchanged: boolean;\n\t\ttry {\n\t\t\tunchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);\n\t\t} catch (eqErr) {\n\t\t\tconst eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);\n\t\t\tconst wrapped = new Error(`Node \"${this.name}\": equals threw: ${eqMsg}`, {\n\t\t\t\tcause: eqErr,\n\t\t\t});\n\t\t\tthis._downInternal([[ERROR, wrapped]]);\n\t\t\treturn;\n\t\t}\n\t\tif (unchanged) {\n\t\t\tthis._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);\n\t\t\treturn;\n\t\t}\n\t\tthis._downInternal(wasDirty ? [[DATA, value]] : [[DIRTY], [DATA, value]]);\n\t}\n}\n","/**\n * `NodeImpl` — the canonical node primitive for static (compile-time known)\n * dependency graphs. Covers state, producer, derived, effect, operator, and\n * passthrough shapes from a single class.\n *\n * Lifecycle machinery (subscribe + START handshake + `_downInternal` pipeline)\n * lives in {@link NodeBase}. This file only adds:\n * - Dep-wave tracking via pre-set dirty masks (first run and subsequent waves\n * share the same code path — see `_connectUpstream` + `_handleDepMessages`)\n * - `_runFn` with identity-skip optimization on `_lastDepValues`\n * - Producer start/stop tied to sink count\n * - ROM/RAM cache semantics: compute nodes clear `_cached` on disconnect,\n * state sources preserve it (see `_onDeactivate`).\n *\n * See GRAPHREFLY-SPEC §§2.1–2.8 and COMPOSITION-GUIDE §§1, 9.\n */\n\nimport { normalizeActor } from \"./actor.js\";\nimport { GuardDenied } from \"./guard.js\";\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tINVALIDATE,\n\ttype Messages,\n\tmessageTier,\n\tPAUSE,\n\tRESOLVED,\n\tRESUME,\n\tSTART,\n\tTEARDOWN,\n} from \"./messages.js\";\nimport {\n\ttype BitSet,\n\tcreateBitSet,\n\tisCleanupFn,\n\tisCleanupResult,\n\tNO_VALUE,\n\ttype Node,\n\ttype NodeActions,\n\tNodeBase,\n\ttype NodeOptions,\n\ttype NodeTransportOptions,\n\ttype SubscribeHints,\n} from \"./node-base.js\";\n\n// Re-exports so downstream files keep importing from \"./node.js\".\nexport {\n\tCLEANUP_RESULT,\n\ttype CleanupResult,\n\tcleanupResult,\n\tNO_VALUE,\n\ttype Node,\n\ttype NodeActions,\n\ttype NodeDescribeKind,\n\ttype NodeInspectorHook,\n\ttype NodeInspectorHookEvent,\n\ttype NodeOptions,\n\ttype NodeSink,\n\ttype NodeStatus,\n\ttype NodeTransportOptions,\n\ttype OnMessageHandler,\n\ttype SubscribeHints,\n} from \"./node-base.js\";\n\n/**\n * Compute function passed to `node(deps, fn, opts?)`.\n *\n * @returns A value to emit, `undefined` to skip emission, or a cleanup\n * function invoked before the next run or on teardown.\n */\nexport type NodeFn<T = unknown> = (\n\tdeps: readonly unknown[],\n\tactions: NodeActions,\n) => T | undefined | (() => void);\n\n// ---------------------------------------------------------------------------\n// NodeImpl\n// ---------------------------------------------------------------------------\n\n/**\n * Class-based implementation of the {@link Node} interface for static deps.\n *\n * All internal state is exposed as package-visible fields so introspection\n * (e.g. `describeNode`) can read them via `instanceof NodeImpl`.\n */\nexport class NodeImpl<T = unknown> extends NodeBase<T> {\n\t// --- Dep configuration (set once) ---\n\t_deps: readonly Node[];\n\t_fn: NodeFn<T> | undefined;\n\t_opts: NodeOptions;\n\t_hasDeps: boolean;\n\t_isSingleDep: boolean;\n\t_autoComplete: boolean;\n\n\t// --- Wave tracking masks ---\n\tprivate _depDirtyMask: BitSet;\n\tprivate _depSettledMask: BitSet;\n\tprivate _depCompleteMask: BitSet;\n\tprivate _allDepsCompleteMask: BitSet;\n\n\t// --- Identity-skip optimization ---\n\tprivate _lastDepValues: readonly unknown[] | undefined;\n\tprivate _cleanup: (() => void) | undefined;\n\n\t// --- Upstream bookkeeping ---\n\tprivate _upstreamUnsubs: Array<() => void> = [];\n\n\t// --- Fn behavior flag ---\n\t/** @internal Read by `describeNode` to infer `\"operator\"` label. */\n\t_manualEmitUsed = false;\n\n\tconstructor(deps: readonly Node[], fn: NodeFn<T> | undefined, opts: NodeOptions) {\n\t\tsuper(opts);\n\t\tthis._deps = deps;\n\t\tthis._fn = fn;\n\t\tthis._opts = opts;\n\t\tthis._hasDeps = deps.length > 0;\n\t\tthis._isSingleDep = deps.length === 1 && fn != null;\n\t\tthis._autoComplete = opts.completeWhenDepsComplete ?? true;\n\n\t\t// State-with-initial-value starts `settled`; everything else starts\n\t\t// `disconnected` and flips to `pending` at first subscribe if fn\n\t\t// hasn't produced a value yet.\n\t\tif (!this._hasDeps && fn == null && this._cached !== NO_VALUE) {\n\t\t\tthis._status = \"settled\";\n\t\t}\n\n\t\tthis._depDirtyMask = createBitSet(deps.length);\n\t\tthis._depSettledMask = createBitSet(deps.length);\n\t\tthis._depCompleteMask = createBitSet(deps.length);\n\t\tthis._allDepsCompleteMask = createBitSet(deps.length);\n\t\tfor (let i = 0; i < deps.length; i++) this._allDepsCompleteMask.set(i);\n\n\t\t// Bind commonly detached protocol methods.\n\t\tthis.down = this.down.bind(this);\n\t\tthis.up = this.up.bind(this);\n\t}\n\n\t// --- Meta node factory (called from base constructor) ---\n\n\tprotected _createMetaNode(key: string, initialValue: unknown, opts: NodeOptions): Node {\n\t\treturn node({\n\t\t\tinitial: initialValue,\n\t\t\tname: `${opts.name ?? \"node\"}:meta:${key}`,\n\t\t\tdescribeKind: \"state\",\n\t\t\t...(opts.guard != null ? { guard: opts.guard } : {}),\n\t\t});\n\t}\n\n\t// --- Manual emit tracker (set by actions.down / actions.emit) ---\n\n\tprotected override _onManualEmit(): void {\n\t\tthis._manualEmitUsed = true;\n\t}\n\n\t// --- Up / unsubscribe ---\n\n\tup(messages: Messages, options?: NodeTransportOptions): void {\n\t\tif (!this._hasDeps) return;\n\t\tif (!options?.internal && this._guard != null) {\n\t\t\tconst actor = normalizeActor(options?.actor);\n\t\t\tif (!this._guard(actor, \"write\")) {\n\t\t\t\tthrow new GuardDenied({ actor, action: \"write\", nodeName: this.name });\n\t\t\t}\n\t\t\tthis._recordMutation(actor);\n\t\t}\n\t\tfor (const dep of this._deps) {\n\t\t\tif (options === undefined) {\n\t\t\t\tdep.up?.(messages);\n\t\t\t} else {\n\t\t\t\tdep.up?.(messages, options);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected _upInternal(messages: Messages): void {\n\t\tif (!this._hasDeps) return;\n\t\tfor (const dep of this._deps) {\n\t\t\tdep.up?.(messages, { internal: true });\n\t\t}\n\t}\n\n\tunsubscribe(): void {\n\t\tif (!this._hasDeps) return;\n\t\tthis._disconnectUpstream();\n\t}\n\n\t// --- Activation (first-subscriber / last-subscriber hooks) ---\n\n\tprotected _onActivate(): void {\n\t\tif (this._hasDeps) {\n\t\t\tthis._connectUpstream();\n\t\t\treturn;\n\t\t}\n\t\tif (this._fn) {\n\t\t\tthis._runFn();\n\t\t\treturn;\n\t\t}\n\t\t// Pure state node: no-op. Cached value (if any) is already delivered\n\t\t// through the subscribe-time START handshake.\n\t}\n\n\tprotected _doDeactivate(): void {\n\t\t// Release upstream subscriptions (for compute nodes with deps).\n\t\tthis._disconnectUpstream();\n\t\t// Flush pending cleanup fn (producers + derived both use `_cleanup`).\n\t\tconst cleanup = this._cleanup;\n\t\tthis._cleanup = undefined;\n\t\tcleanup?.();\n\n\t\t// ROM/RAM rule (GRAPHREFLY-SPEC §2.2): compute nodes (anything with\n\t\t// `fn`) clear their cache on disconnect because their value is a\n\t\t// function of live subscriptions. State nodes (no fn) preserve\n\t\t// `_cached` — runtime writes survive across disconnect.\n\t\tif (this._fn != null) {\n\t\t\tthis._cached = NO_VALUE;\n\t\t\tthis._lastDepValues = undefined;\n\t\t}\n\n\t\t// Status: only transition to \"disconnected\" for compute nodes.\n\t\t// Pure state nodes remain in whatever status they were (usually\n\t\t// \"settled\" if they hold a value).\n\t\tif (this._hasDeps || this._fn != null) {\n\t\t\tthis._status = \"disconnected\";\n\t\t}\n\t}\n\n\t// --- INVALIDATE / TEARDOWN hooks (clear fn state) ---\n\n\tprotected override _onInvalidate(): void {\n\t\tconst cleanup = this._cleanup;\n\t\tthis._cleanup = undefined;\n\t\tcleanup?.();\n\t\tthis._lastDepValues = undefined;\n\t}\n\n\tprotected override _onTeardown(): void {\n\t\tconst cleanup = this._cleanup;\n\t\tthis._cleanup = undefined;\n\t\tcleanup?.();\n\t}\n\n\t// --- Upstream connect / disconnect ---\n\n\tprivate _connectUpstream(): void {\n\t\tif (!this._hasDeps) return;\n\t\tif (this._upstreamUnsubs.length > 0) return; // already connected\n\n\t\t// Pre-set dirty mask to all-ones — wave completes when every dep\n\t\t// has settled at least once (spec §2.7, first-run gating).\n\t\tthis._depDirtyMask.setAll();\n\t\tthis._depSettledMask.reset();\n\t\tthis._depCompleteMask.reset();\n\t\t// Stay in \"disconnected\" until fn runs and updates status via\n\t\t// `_handleLocalLifecycle`. `subscribe()` will flip to \"pending\"\n\t\t// if fn did not run before returning.\n\n\t\tconst depValuesBefore = this._lastDepValues;\n\t\tconst subHints: SubscribeHints | undefined = this._isSingleDep\n\t\t\t? { singleDep: true }\n\t\t\t: undefined;\n\t\tfor (let i = 0; i < this._deps.length; i += 1) {\n\t\t\tconst dep = this._deps[i];\n\t\t\tthis._upstreamUnsubs.push(\n\t\t\t\tdep.subscribe((msgs) => this._handleDepMessages(i, msgs), subHints),\n\t\t\t);\n\t\t}\n\t\t// Fallback for `onMessage`-driven operators: if the subscribe loop\n\t\t// did not drive a wave completion (e.g. `switchMap`, `sample` —\n\t\t// operators whose `onMessage` consumes every dep message without\n\t\t// letting the mask-based wave progress), still run fn once so the\n\t\t// operator can initialize side-effect state (inner subscriptions,\n\t\t// cleanup fns, etc.). The fn-is-identity-check guards against\n\t\t// double runs when the normal wave path already fired.\n\t\tif (this._fn && this._onMessage && !this._terminal && this._lastDepValues === depValuesBefore) {\n\t\t\tthis._runFn();\n\t\t}\n\t}\n\n\tprivate _disconnectUpstream(): void {\n\t\tif (this._upstreamUnsubs.length === 0) return;\n\t\tfor (const unsub of this._upstreamUnsubs.splice(0)) {\n\t\t\tunsub();\n\t\t}\n\t\tthis._depDirtyMask.reset();\n\t\tthis._depSettledMask.reset();\n\t\tthis._depCompleteMask.reset();\n\t}\n\n\t// --- Wave handling ---\n\n\tprivate _handleDepMessages(index: number, messages: Messages): void {\n\t\tfor (const msg of messages) {\n\t\t\tthis._emitInspectorHook({ kind: \"dep_message\", depIndex: index, message: msg });\n\t\t\tconst t = msg[0];\n\t\t\t// User-defined message handler gets first look (spec §2.6).\n\t\t\tif (this._onMessage) {\n\t\t\t\ttry {\n\t\t\t\t\tconst consumed = this._onMessage(msg, index, this._actions);\n\t\t\t\t\tif (consumed) {\n\t\t\t\t\t\t// Clear the dep's pre-set dirty bit when onMessage\n\t\t\t\t\t\t// consumes a protocol-significant message:\n\t\t\t\t\t\t// - START: dep is fully user-managed (takeUntil notifier)\n\t\t\t\t\t\t// - DATA/RESOLVED: dep delivered real data via onMessage\n\t\t\t\t\t\t// instead of the normal wave path\n\t\t\t\t\t\t// This ensures the first-run gate tracks dep readiness\n\t\t\t\t\t\t// correctly even when onMessage bypasses wave tracking.\n\t\t\t\t\t\tif (t === START || t === DATA || t === RESOLVED) {\n\t\t\t\t\t\t\tthis._depDirtyMask.clear(index);\n\t\t\t\t\t\t\tif (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {\n\t\t\t\t\t\t\t\tthis._depDirtyMask.reset();\n\t\t\t\t\t\t\t\tthis._depSettledMask.reset();\n\t\t\t\t\t\t\t\tthis._runFn();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst errMsg = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tconst wrapped = new Error(`Node \"${this.name}\": onMessage threw: ${errMsg}`, {\n\t\t\t\t\t\tcause: err,\n\t\t\t\t\t});\n\t\t\t\t\tthis._downInternal([[ERROR, wrapped]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Tier-0 (START) from a dep is informational — it's the dep's\n\t\t\t// subscribe handshake. It carries no wave-state implication: the\n\t\t\t// paired DATA (if present) is handled by the DATA branch.\n\t\t\tif (messageTier(t) < 1) continue;\n\n\t\t\tif (!this._fn) {\n\t\t\t\t// Passthrough: forward everything except COMPLETE when multi-dep\n\t\t\t\t// (multi-dep passthrough waits for all deps to complete, §1.3.5).\n\t\t\t\tif (t === COMPLETE && this._deps.length > 1) {\n\t\t\t\t\tthis._depCompleteMask.set(index);\n\t\t\t\t\tthis._maybeCompleteFromDeps();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis._downInternal([msg]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (t === DIRTY) {\n\t\t\t\tthis._onDepDirty(index);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === DATA || t === RESOLVED) {\n\t\t\t\tthis._onDepSettled(index);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === COMPLETE) {\n\t\t\t\tthis._depCompleteMask.set(index);\n\t\t\t\tthis._depDirtyMask.clear(index);\n\t\t\t\tthis._depSettledMask.clear(index);\n\t\t\t\tif (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {\n\t\t\t\t\tthis._depDirtyMask.reset();\n\t\t\t\t\tthis._depSettledMask.reset();\n\t\t\t\t\tthis._runFn();\n\t\t\t\t} else if (!this._depDirtyMask.any() && this._status === \"dirty\") {\n\t\t\t\t\t// D2: dep went DIRTY→COMPLETE without DATA — the node is\n\t\t\t\t\t// stuck in \"dirty\" with no pending wave to resolve it.\n\t\t\t\t\t// Recompute so downstream sees RESOLVED (unchanged) or\n\t\t\t\t\t// DATA (changed).\n\t\t\t\t\tthis._depSettledMask.reset();\n\t\t\t\t\tthis._runFn();\n\t\t\t\t}\n\t\t\t\tthis._maybeCompleteFromDeps();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === ERROR) {\n\t\t\t\tthis._downInternal([msg]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === INVALIDATE || t === TEARDOWN || t === PAUSE || t === RESUME) {\n\t\t\t\tthis._downInternal([msg]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Forward unknown message types.\n\t\t\tthis._downInternal([msg]);\n\t\t}\n\t}\n\n\tprivate _onDepDirty(index: number): void {\n\t\t// Track wave transition: propagate DIRTY to our downstream ONLY on\n\t\t// the first dirty of a wave. During the initial wave, the dirty mask\n\t\t// is pre-set to all-ones by `_connectUpstream` so this branch never\n\t\t// fires — our own `_downAutoValue` emits `[[DIRTY],[DATA,v]]` to\n\t\t// downstream when fn completes.\n\t\tconst wasDirty = this._depDirtyMask.has(index);\n\t\tthis._depDirtyMask.set(index);\n\t\tthis._depSettledMask.clear(index);\n\t\tif (!wasDirty) {\n\t\t\tthis._downInternal([[DIRTY]]);\n\t\t}\n\t}\n\n\tprivate _onDepSettled(index: number): void {\n\t\t// Ensure dep is marked dirty. If the dep settled without a prior\n\t\t// DIRTY (spec §1.3.1 compat path for raw external sources), route\n\t\t// through `_onDepDirty` so that our own downstream sees DIRTY\n\t\t// before the resulting DATA/RESOLVED — preserving the two-phase\n\t\t// invariant from our node's perspective.\n\t\tif (!this._depDirtyMask.has(index)) {\n\t\t\tthis._onDepDirty(index);\n\t\t}\n\t\tthis._depSettledMask.set(index);\n\t\tif (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {\n\t\t\tthis._depDirtyMask.reset();\n\t\t\tthis._depSettledMask.reset();\n\t\t\tthis._runFn();\n\t\t}\n\t}\n\n\tprivate _maybeCompleteFromDeps(): void {\n\t\tif (\n\t\t\tthis._autoComplete &&\n\t\t\tthis._deps.length > 0 &&\n\t\t\tthis._depCompleteMask.covers(this._allDepsCompleteMask)\n\t\t) {\n\t\t\tthis._downInternal([[COMPLETE]]);\n\t\t}\n\t}\n\n\t// --- Fn execution ---\n\n\tprivate _runFn(): void {\n\t\tif (!this._fn) return;\n\t\tif (this._terminal && !this._resubscribable) return;\n\n\t\ttry {\n\t\t\tconst n = this._deps.length;\n\t\t\tconst depValues = new Array(n);\n\t\t\tfor (let i = 0; i < n; i++) depValues[i] = this._deps[i].get();\n\n\t\t\t// Identity skip BEFORE cleanup: if all dep values are unchanged,\n\t\t\t// skip cleanup+fn so effect nodes don't teardown/restart on no-op.\n\t\t\tconst prev = this._lastDepValues;\n\t\t\tif (n > 0 && prev != null && prev.length === n) {\n\t\t\t\tlet allSame = true;\n\t\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\t\tif (!Object.is(depValues[i], prev[i])) {\n\t\t\t\t\t\tallSame = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (allSame) {\n\t\t\t\t\tif (this._status === \"dirty\") {\n\t\t\t\t\t\tthis._downInternal([[RESOLVED]]);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst prevCleanup = this._cleanup;\n\t\t\tthis._cleanup = undefined;\n\t\t\tprevCleanup?.();\n\n\t\t\tthis._manualEmitUsed = false;\n\t\t\tthis._lastDepValues = depValues;\n\t\t\tthis._emitInspectorHook({ kind: \"run\", depValues });\n\n\t\t\tconst out = this._fn(depValues, this._actions);\n\n\t\t\t// Explicit cleanup wrapper: { cleanup, value? }\n\t\t\tif (isCleanupResult(out)) {\n\t\t\t\tthis._cleanup = out.cleanup;\n\t\t\t\tif (this._manualEmitUsed) return;\n\t\t\t\tif (\"value\" in out) {\n\t\t\t\t\tthis._downAutoValue(out.value);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Legacy: plain function return → cleanup.\n\t\t\tif (isCleanupFn(out)) {\n\t\t\t\tthis._cleanup = out;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (this._manualEmitUsed) return;\n\t\t\tif (out === undefined) return;\n\t\t\tthis._downAutoValue(out);\n\t\t} catch (err) {\n\t\t\tconst errMsg = err instanceof Error ? err.message : String(err);\n\t\t\tconst wrapped = new Error(`Node \"${this.name}\": fn threw: ${errMsg}`, { cause: err });\n\t\t\tthis._downInternal([[ERROR, wrapped]]);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nconst isNodeArray = (value: unknown): value is readonly Node[] => Array.isArray(value);\n\nconst isNodeOptions = (value: unknown): value is NodeOptions =>\n\ttypeof value === \"object\" && value != null && !Array.isArray(value);\n\n/**\n * Creates a reactive {@link Node} — the single GraphReFly primitive (§2).\n *\n * Typical shapes: `node([])` / `node([], opts)` for a manual source;\n * `node(producerFn, opts)` for a producer; `node(deps, computeFn, opts)` for\n * derived nodes and operators.\n *\n * @param depsOrFn - Dependency nodes, a {@link NodeFn} (producer), or {@link NodeOptions} alone.\n * @param fnOrOpts - With deps: compute function or options. Omitted for producer-only form.\n * @param optsArg - Options when both `deps` and `fn` are provided.\n * @returns `Node<T>` — lazy until subscribed.\n *\n * @remarks\n * **Protocol:** START handshake, DIRTY / DATA / RESOLVED ordering, completion,\n * and batch deferral follow `~/src/graphrefly/GRAPHREFLY-SPEC.md`.\n *\n * **`equals` and mutable values:** The default `Object.is` identity check is\n * correct for the common immutable-value case. If your node produces mutable\n * objects, provide a custom `equals` — otherwise `Object.is` always returns\n * `true` for the same reference and the node emits `RESOLVED` instead of `DATA`.\n *\n * **ROM/RAM (§2.2):** State nodes (no fn) preserve their cache across\n * disconnect — runtime writes survive. Compute nodes (derived, producer)\n * clear their cache on disconnect; reconnect re-runs fn.\n *\n * @example\n * ```ts\n * import { node, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const a = state(1);\n * const b = node([a], ([x]) => (x as number) + 1);\n * ```\n *\n * @category core\n */\nexport function node<T = unknown>(\n\tdepsOrFn?: readonly Node[] | NodeFn<T> | NodeOptions,\n\tfnOrOpts?: NodeFn<T> | NodeOptions,\n\toptsArg?: NodeOptions,\n): Node<T> {\n\tconst deps: readonly Node[] = isNodeArray(depsOrFn) ? depsOrFn : [];\n\tconst fn: NodeFn<T> | undefined =\n\t\ttypeof depsOrFn === \"function\"\n\t\t\t? depsOrFn\n\t\t\t: typeof fnOrOpts === \"function\"\n\t\t\t\t? fnOrOpts\n\t\t\t\t: undefined;\n\tlet opts: NodeOptions = {};\n\tif (isNodeArray(depsOrFn)) {\n\t\topts = (isNodeOptions(fnOrOpts) ? fnOrOpts : optsArg) ?? {};\n\t} else if (isNodeOptions(depsOrFn)) {\n\t\topts = depsOrFn;\n\t} else {\n\t\topts = (isNodeOptions(fnOrOpts) ? fnOrOpts : optsArg) ?? {};\n\t}\n\n\treturn new NodeImpl<T>(deps, fn, opts);\n}\n","/**\n * bridge — graph-visible message forwarding between two nodes.\n *\n * Replaces ad-hoc `subscribe()` bridges that bypass graph topology.\n * The returned node is an effect that intercepts messages from `from`\n * and forwards them to `to.down()`. Register it with `graph.add()` to\n * make the bridge visible in `describe()` and `snapshot()`.\n *\n * **Upstream path:** The bridge node has `from` as its dep, so anything\n * downstream of the bridge that calls `up()` naturally reaches `from`.\n * If `to` is used as a dep by other nodes and those nodes send `up()`,\n * the messages reach `to`'s deps (not `from`). For full upstream relay\n * across the bridge boundary, wire the bridge as a dep of `to`'s\n * consumers or use `graph.connect()`.\n *\n * **ABAC / guards:** `to.down()` is called without `NodeTransportOptions`,\n * so any ABAC guard on `to` receives `actor = undefined`. Upstream (`up()`)\n * messages propagate through the dep chain the same way — no actor is\n * injected on either path. Both paths are intentionally unguarded; if `to`\n * requires a specific actor, provide a guarded wrapper node and bridge to\n * that instead.\n *\n * **Default forwarding:** All standard message types are forwarded by\n * default, including TEARDOWN, PAUSE, RESUME, and INVALIDATE. Use the\n * `down` option to restrict which types are forwarded. Callers that need\n * to exclude TEARDOWN (e.g. inter-stage wiring in `funnel()`) pass an\n * explicit `down` array without TEARDOWN.\n *\n * @module\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tINVALIDATE,\n\ttype Message,\n\tPAUSE,\n\tRESOLVED,\n\tRESUME,\n\tTEARDOWN,\n} from \"./messages.js\";\nimport { type Node, type NodeActions, node, type OnMessageHandler } from \"./node.js\";\n\n/** Options for {@link bridge}. */\nexport type BridgeOptions = {\n\t/** Node name (for graph registration / describe). */\n\tname?: string;\n\t/**\n\t * Standard message types to forward downstream. Default: all standard\n\t * types. Unknown (non-standard) types always forward per spec §1.3.6\n\t * regardless of this option.\n\t */\n\tdown?: readonly symbol[];\n};\n\n/** All standard types forwarded by default. Export for callers that\n * need to customize (e.g. exclude TEARDOWN). */\nexport const DEFAULT_DOWN: readonly symbol[] = [\n\tDATA,\n\tDIRTY,\n\tRESOLVED,\n\tCOMPLETE,\n\tERROR,\n\tTEARDOWN,\n\tPAUSE,\n\tRESUME,\n\tINVALIDATE,\n];\n\n/**\n * All standard message types the bridge understands. Types outside this set\n * are \"unknown\" and must always be forwarded (spec §1.3.6).\n */\nconst STANDARD_TYPES = new Set<symbol>([\n\tDATA,\n\tDIRTY,\n\tRESOLVED,\n\tCOMPLETE,\n\tERROR,\n\tTEARDOWN,\n\tPAUSE,\n\tRESUME,\n\tINVALIDATE,\n]);\n\n/**\n * Create a graph-visible bridge node that forwards messages from `from` to `to`.\n *\n * The bridge is a real node (effect) — it shows up in `describe()`, participates\n * in two-phase push, and cleans up on TEARDOWN. Register it via `graph.add()`\n * to make it part of the graph topology.\n *\n * **Unknown message types** (custom domain signals not in the standard protocol\n * set) are always forwarded to `to`, regardless of the `down` option. This\n * satisfies spec §1.3.6 (\"unknown types forward unchanged\").\n *\n * **COMPLETE / ERROR**: when forwarded, the bridge also transitions to terminal\n * state so graph-wide completion detection works correctly.\n *\n * @param from - Source node to observe.\n * @param to - Target node to forward messages to via `to.down()`.\n * @param opts - Optional configuration.\n * @returns A bridge effect node. Add it to a graph with `graph.add(name, bridge(...))`.\n *\n * @example\n * ```ts\n * import { bridge, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const a = state(0);\n * const b = state(0);\n * const br = bridge(a, b, { name: \"__bridge_a_b\" });\n * graph.add(\"__bridge_a_b\", br);\n * // Now a's messages flow to b, visible in describe()\n * ```\n *\n * @category core\n */\nexport function bridge<T = unknown>(from: Node<T>, to: Node, opts?: BridgeOptions): Node<unknown> {\n\tconst allowedDown = new Set(opts?.down ?? DEFAULT_DOWN);\n\n\tconst onMessage: OnMessageHandler = (\n\t\tmsg: Message,\n\t\t_depIndex: number,\n\t\t_actions: NodeActions,\n\t): boolean => {\n\t\tconst type = msg[0];\n\n\t\t// Unknown types (custom domain signals) always forward — spec §1.3.6.\n\t\tif (!STANDARD_TYPES.has(type)) {\n\t\t\tto.down([msg]);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Terminal types: always transition the bridge to terminal state\n\t\t// (return false → default dispatch). Only forward to `to` if allowed.\n\t\tif (type === COMPLETE || type === ERROR) {\n\t\t\tif (allowedDown.has(type)) {\n\t\t\t\tto.down([msg]);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Known type, not in allowedDown — consume without forwarding.\n\t\tif (!allowedDown.has(type)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Forward the message to the target.\n\t\tto.down([msg]);\n\t\treturn true;\n\t};\n\n\treturn node([from as Node], undefined, {\n\t\tname: opts?.name,\n\t\tdescribeKind: \"effect\",\n\t\tonMessage,\n\t\tmeta: { _internal: true },\n\t});\n}\n","/**\n * `dynamicNode` — runtime dep tracking with diamond resolution (Phase 0.3b).\n *\n * Unlike `node()` where deps are fixed at construction, `dynamicNode`\n * discovers deps at runtime via a tracking `get()` proxy. After each\n * recompute, deps are diffed: new deps are connected, removed deps are\n * disconnected, and bitmasks are rebuilt.\n *\n * Shares subscribe / sink / lifecycle machinery with {@link NodeImpl} via\n * {@link NodeBase}. The only things that diverge from static nodes:\n * - deps are discovered inside `_runFn` via a tracking `get` proxy\n * - `_rewire` installs subscriptions lazily during `_runFn`\n * - during rewire, new dep messages are **buffered** (option C from the\n * Apr-2026 refactor); after rewire we scan the buffer for DATA values\n * that differ from what fn tracked, and re-run fn if any found\n * (bounded by `MAX_RERUN`)\n */\n\nimport { normalizeActor } from \"./actor.js\";\nimport { GuardDenied } from \"./guard.js\";\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tINVALIDATE,\n\ttype Messages,\n\tmessageTier,\n\tPAUSE,\n\tRESOLVED,\n\tRESUME,\n\tTEARDOWN,\n} from \"./messages.js\";\nimport { node as createNode } from \"./node.js\";\nimport {\n\tNO_VALUE,\n\ttype Node,\n\tNodeBase,\n\ttype NodeOptions,\n\ttype NodeTransportOptions,\n} from \"./node-base.js\";\n\n/**\n * The tracking `get` function passed to `dynamicNode`'s compute function.\n * Each call to `get(dep)` reads the dep's current value and records it as a dependency.\n */\nexport type DynGet = <V>(dep: Node<V>) => V | undefined;\n\n/**\n * Compute function for `dynamicNode`. Receives a tracking `get` proxy.\n * Deps are discovered by which nodes are passed to `get()` during execution.\n */\nexport type DynamicNodeFn<T> = (get: DynGet) => T;\n\n/** Options for `dynamicNode`. */\nexport type DynamicNodeOptions = Pick<\n\tNodeOptions,\n\t| \"name\"\n\t| \"equals\"\n\t| \"meta\"\n\t| \"resubscribable\"\n\t| \"resetOnTeardown\"\n\t| \"guard\"\n\t| \"onMessage\"\n\t| \"onResubscribe\"\n\t| \"completeWhenDepsComplete\"\n\t| \"describeKind\"\n>;\n\n/** Bounded rerun cap for dynamic fn activation loop (lazy-dep stabilization). */\nconst MAX_RERUN = 16;\n\n/**\n * Creates a node with runtime dep tracking. Deps are discovered each time the\n * compute function runs by tracking which nodes are passed to the `get()` proxy.\n *\n * After each recompute:\n * - New deps (not in previous set) are subscribed\n * - Removed deps (not in current set) are unsubscribed\n * - Kept deps retain their existing subscriptions\n *\n * The node participates in diamond resolution via the pre-set dirty mask\n * (shared with {@link NodeImpl}).\n *\n * **Lazy-dep composition:** when a tracked dep is itself a lazy compute node\n * whose first subscribe causes a fresh value to arrive, the rewire buffer\n * detects the discrepancy and re-runs fn once so it observes the real value.\n * Capped at {@link MAX_RERUN} iterations.\n *\n * @param fn - Compute function receiving a tracking `get` proxy.\n * @param opts - Optional configuration.\n * @returns `Node<T>` with dynamic dep tracking.\n *\n * @example\n * ```ts\n * import { dynamicNode, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const cond = state(true);\n * const a = state(1);\n * const b = state(2);\n *\n * const d = dynamicNode((get) => {\n * const useA = get(cond);\n * return useA ? get(a) : get(b);\n * });\n * ```\n *\n * @category core\n */\nexport function dynamicNode<T = unknown>(fn: DynamicNodeFn<T>, opts?: DynamicNodeOptions): Node<T> {\n\treturn new DynamicNodeImpl<T>(fn, opts ?? {});\n}\n\n/** Pending buffer entry during `_rewire`. */\ntype PendingEntry = { index: number; msgs: Messages };\n\n/** @internal — exported for {@link describeNode} `instanceof` check. */\nexport class DynamicNodeImpl<T = unknown> extends NodeBase<T> {\n\tprivate readonly _fn: DynamicNodeFn<T>;\n\tprivate readonly _autoComplete: boolean;\n\n\t// Dynamic deps tracking\n\t/** @internal Read by `describeNode`. */\n\t_deps: Node[] = [];\n\tprivate _depUnsubs: Array<() => void> = [];\n\tprivate _depIndexMap = new Map<Node, number>();\n\tprivate _depDirtyBits = new Set<number>();\n\tprivate _depSettledBits = new Set<number>();\n\tprivate _depCompleteBits = new Set<number>();\n\n\t// Execution state\n\tprivate _running = false;\n\tprivate _rewiring = false;\n\tprivate _bufferedDepMessages: PendingEntry[] = [];\n\tprivate _trackedValues: Map<Node, unknown> = new Map();\n\tprivate _rerunCount = 0;\n\n\tconstructor(fn: DynamicNodeFn<T>, opts: DynamicNodeOptions) {\n\t\tsuper(opts);\n\t\tthis._fn = fn;\n\t\tthis._autoComplete = opts.completeWhenDepsComplete ?? true;\n\n\t\t// Bind commonly detached protocol methods.\n\t\tthis.down = this.down.bind(this);\n\t\tthis.up = this.up.bind(this);\n\t}\n\n\tprotected _createMetaNode(key: string, initialValue: unknown, opts: NodeOptions): Node {\n\t\treturn createNode({\n\t\t\tinitial: initialValue,\n\t\t\tname: `${opts.name ?? \"dynamicNode\"}:meta:${key}`,\n\t\t\tdescribeKind: \"state\",\n\t\t\t...(opts.guard != null ? { guard: opts.guard } : {}),\n\t\t});\n\t}\n\n\t/** Versioning not supported on DynamicNodeImpl (override base). */\n\toverride get v(): undefined {\n\t\treturn undefined;\n\t}\n\n\t// --- Up / unsubscribe ---\n\n\tup(messages: Messages, options?: NodeTransportOptions): void {\n\t\tif (this._deps.length === 0) return;\n\t\tif (!options?.internal && this._guard != null) {\n\t\t\tconst actor = normalizeActor(options?.actor);\n\t\t\tif (!this._guard(actor, \"write\")) {\n\t\t\t\tthrow new GuardDenied({ actor, action: \"write\", nodeName: this.name });\n\t\t\t}\n\t\t\tthis._recordMutation(actor);\n\t\t}\n\t\tfor (const dep of this._deps) {\n\t\t\tdep.up?.(messages, options);\n\t\t}\n\t}\n\n\tprotected _upInternal(messages: Messages): void {\n\t\tfor (const dep of this._deps) {\n\t\t\tdep.up?.(messages, { internal: true });\n\t\t}\n\t}\n\n\tunsubscribe(): void {\n\t\tthis._disconnect();\n\t}\n\n\t// --- Activation hooks ---\n\n\tprotected _onActivate(): void {\n\t\tthis._runFn();\n\t}\n\n\tprotected _doDeactivate(): void {\n\t\tthis._disconnect();\n\t}\n\n\tprivate _disconnect(): void {\n\t\tfor (const unsub of this._depUnsubs) unsub();\n\t\tthis._depUnsubs = [];\n\t\tthis._deps = [];\n\t\tthis._depIndexMap.clear();\n\t\tthis._depDirtyBits.clear();\n\t\tthis._depSettledBits.clear();\n\t\tthis._depCompleteBits.clear();\n\n\t\t// ROM/RAM rule (GRAPHREFLY-SPEC §2.2): compute node clears cache on\n\t\t// disconnect — dynamic nodes are always compute nodes.\n\t\tthis._cached = NO_VALUE;\n\t\tthis._status = \"disconnected\";\n\t}\n\n\t// --- Fn execution with rewire buffer ---\n\n\tprivate _runFn(): void {\n\t\tif (this._terminal && !this._resubscribable) return;\n\t\tif (this._running) return;\n\n\t\tthis._running = true;\n\t\tthis._rerunCount = 0;\n\t\tlet result: T | undefined;\n\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\t// --- Phase 1: execute fn with tracking `get`. ---\n\t\t\t\tconst trackedDeps: Node[] = [];\n\t\t\t\tconst trackedValuesMap = new Map<Node, unknown>();\n\t\t\t\tconst trackedSet = new Set<Node>();\n\n\t\t\t\tconst get: DynGet = <V>(dep: Node<V>): V | undefined => {\n\t\t\t\t\tif (!trackedSet.has(dep)) {\n\t\t\t\t\t\ttrackedSet.add(dep);\n\t\t\t\t\t\ttrackedDeps.push(dep);\n\t\t\t\t\t\t// Spec §2.2: `get()` never triggers computation. If the\n\t\t\t\t\t\t// dep is a disconnected lazy node, this returns\n\t\t\t\t\t\t// undefined — the rewire buffer will detect the\n\t\t\t\t\t\t// discrepancy once the lazy dep's real value arrives\n\t\t\t\t\t\t// via subscribe-time push.\n\t\t\t\t\t\ttrackedValuesMap.set(dep, dep.get());\n\t\t\t\t\t}\n\t\t\t\t\treturn dep.get() as V | undefined;\n\t\t\t\t};\n\n\t\t\t\tthis._trackedValues = trackedValuesMap;\n\n\t\t\t\t// Collect dep values for inspector hook\n\t\t\t\tconst depValues: unknown[] = [];\n\t\t\t\tfor (const dep of this._deps) depValues.push(dep.get());\n\t\t\t\tthis._emitInspectorHook({ kind: \"run\", depValues });\n\n\t\t\t\ttry {\n\t\t\t\t\tresult = this._fn(get);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst errMsg = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tconst wrapped = new Error(`Node \"${this.name}\": fn threw: ${errMsg}`, {\n\t\t\t\t\t\tcause: err,\n\t\t\t\t\t});\n\t\t\t\t\tthis._downInternal([[ERROR, wrapped]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// --- Phase 2: rewire. ---\n\t\t\t\tthis._rewiring = true;\n\t\t\t\tthis._bufferedDepMessages = [];\n\t\t\t\ttry {\n\t\t\t\t\tthis._rewire(trackedDeps);\n\t\t\t\t} finally {\n\t\t\t\t\tthis._rewiring = false;\n\t\t\t\t}\n\n\t\t\t\t// --- Phase 3: scan buffer for discrepancies that require re-run. ---\n\t\t\t\tlet needsRerun = false;\n\t\t\t\tfor (const entry of this._bufferedDepMessages) {\n\t\t\t\t\tfor (const msg of entry.msgs) {\n\t\t\t\t\t\tif (msg[0] === DATA) {\n\t\t\t\t\t\t\tconst dep = this._deps[entry.index];\n\t\t\t\t\t\t\tconst trackedValue = dep != null ? this._trackedValues.get(dep) : undefined;\n\t\t\t\t\t\t\tconst actualValue = msg[1];\n\t\t\t\t\t\t\tif (!this._equals(trackedValue, actualValue)) {\n\t\t\t\t\t\t\t\tneedsRerun = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (needsRerun) break;\n\t\t\t\t}\n\n\t\t\t\tif (needsRerun) {\n\t\t\t\t\tthis._rerunCount += 1;\n\t\t\t\t\tif (this._rerunCount > MAX_RERUN) {\n\t\t\t\t\t\tthis._bufferedDepMessages = [];\n\t\t\t\t\t\tthis._downInternal([\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tERROR,\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`dynamicNode \"${this.name ?? \"anonymous\"}\": rewire did not stabilize within ${MAX_RERUN} iterations`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// Discard this iteration's buffer and loop.\n\t\t\t\t\tthis._bufferedDepMessages = [];\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// --- Phase 4: drain buffer — update masks only, no _runFn. ---\n\t\t\t\t//\n\t\t\t\t// Buffered activation-cascade messages are already reflected\n\t\t\t\t// in `_trackedValues` (fn saw the same values). We update\n\t\t\t\t// the wave masks so subsequent dep updates start from a\n\t\t\t\t// consistent state, then clear both masks so the node\n\t\t\t\t// enters post-rewire with clean wave tracking.\n\t\t\t\tconst drain = this._bufferedDepMessages;\n\t\t\t\tthis._bufferedDepMessages = [];\n\t\t\t\tfor (const entry of drain) {\n\t\t\t\t\tfor (const msg of entry.msgs) {\n\t\t\t\t\t\tthis._updateMasksForMessage(entry.index, msg);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Clear wave masks — the activation cascade is fully\n\t\t\t\t// consumed; subsequent dep updates should start fresh.\n\t\t\t\tthis._depDirtyBits.clear();\n\t\t\t\tthis._depSettledBits.clear();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._running = false;\n\t\t}\n\n\t\tthis._downAutoValue(result);\n\t}\n\n\tprivate _rewire(newDeps: Node[]): void {\n\t\tconst oldMap = this._depIndexMap;\n\t\tconst newMap = new Map<Node, number>();\n\t\tconst newUnsubs: Array<() => void> = [];\n\n\t\t// Subscribe to new deps (or reuse existing subscriptions).\n\t\tfor (let i = 0; i < newDeps.length; i++) {\n\t\t\tconst dep = newDeps[i];\n\t\t\tnewMap.set(dep, i);\n\t\t\tconst oldIdx = oldMap.get(dep);\n\t\t\tif (oldIdx !== undefined) {\n\t\t\t\t// Kept dep — reuse subscription.\n\t\t\t\tnewUnsubs.push(this._depUnsubs[oldIdx]);\n\t\t\t\tthis._depUnsubs[oldIdx] = () => {};\n\t\t\t} else {\n\t\t\t\t// New dep — subscribe. Its subscribe-time handshake +\n\t\t\t\t// activation cascade will land in `_handleDepMessages`,\n\t\t\t\t// which routes to the buffer while `_rewiring` is true.\n\t\t\t\tconst idx = i;\n\t\t\t\tconst unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));\n\t\t\t\tnewUnsubs.push(unsub);\n\t\t\t}\n\t\t}\n\n\t\t// Disconnect removed deps.\n\t\tfor (const [dep, oldIdx] of oldMap) {\n\t\t\tif (!newMap.has(dep)) {\n\t\t\t\tthis._depUnsubs[oldIdx]();\n\t\t\t}\n\t\t}\n\n\t\tthis._deps = newDeps;\n\t\tthis._depUnsubs = newUnsubs;\n\t\tthis._depIndexMap = newMap;\n\t\tthis._depDirtyBits.clear();\n\t\tthis._depSettledBits.clear();\n\n\t\t// Preserve complete bits for deps that are still present (re-indexed).\n\t\tconst newCompleteBits = new Set<number>();\n\t\tfor (const oldIdx of this._depCompleteBits) {\n\t\t\tfor (const [dep, idx] of oldMap) {\n\t\t\t\tif (idx === oldIdx && newMap.has(dep)) {\n\t\t\t\t\tnewCompleteBits.add(newMap.get(dep)!);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis._depCompleteBits = newCompleteBits;\n\t}\n\n\t// --- Dep message handling ---\n\n\tprivate _handleDepMessages(index: number, messages: Messages): void {\n\t\t// During rewire, buffer messages for post-rewire scan.\n\t\tif (this._rewiring) {\n\t\t\tthis._bufferedDepMessages.push({ index, msgs: messages });\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const msg of messages) {\n\t\t\tthis._emitInspectorHook({ kind: \"dep_message\", depIndex: index, message: msg });\n\t\t\tconst t = msg[0];\n\n\t\t\t// Note: unlike NodeImpl, we do NOT clear dirty bits when onMessage\n\t\t\t// consumes START. NodeImpl pre-sets all dirty bits in _connectUpstream\n\t\t\t// and must clear them for consumed-START deps (notifier pattern).\n\t\t\t// DynamicNodeImpl never pre-sets dirty bits — deps are discovered\n\t\t\t// lazily during _runFn — so there are no bits to clear.\n\t\t\tif (this._onMessage) {\n\t\t\t\ttry {\n\t\t\t\t\tif (this._onMessage(msg, index, this._actions)) continue;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst errMsg = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tconst wrapped = new Error(`Node \"${this.name}\": onMessage threw: ${errMsg}`, {\n\t\t\t\t\t\tcause: err,\n\t\t\t\t\t});\n\t\t\t\t\tthis._downInternal([[ERROR, wrapped]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Tier-0 (START) from a dep is informational — see NodeImpl for rationale.\n\t\t\tif (messageTier(t) < 1) continue;\n\n\t\t\tif (t === DIRTY) {\n\t\t\t\tconst wasEmpty = this._depDirtyBits.size === 0;\n\t\t\t\tthis._depDirtyBits.add(index);\n\t\t\t\tthis._depSettledBits.delete(index);\n\t\t\t\tif (wasEmpty) {\n\t\t\t\t\tthis._downInternal([[DIRTY]]);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === DATA || t === RESOLVED) {\n\t\t\t\tif (!this._depDirtyBits.has(index)) {\n\t\t\t\t\t// DATA-without-prior-DIRTY — propagate DIRTY for the two-phase\n\t\t\t\t\t// invariant (§1.3.1 compat path).\n\t\t\t\t\tconst wasEmpty = this._depDirtyBits.size === 0;\n\t\t\t\t\tthis._depDirtyBits.add(index);\n\t\t\t\t\tif (wasEmpty) {\n\t\t\t\t\t\tthis._downInternal([[DIRTY]]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis._depSettledBits.add(index);\n\t\t\t\tif (this._allDirtySettled()) {\n\t\t\t\t\tthis._depDirtyBits.clear();\n\t\t\t\t\tthis._depSettledBits.clear();\n\t\t\t\t\tif (!this._running) {\n\t\t\t\t\t\t// Identity check against the values fn already saw — if\n\t\t\t\t\t\t// nothing has actually changed, skip the run. This\n\t\t\t\t\t\t// guards against deferred handshake DATA (arriving\n\t\t\t\t\t\t// after `_rewire` finishes inside an open batch)\n\t\t\t\t\t\t// from triggering a redundant re-run.\n\t\t\t\t\t\tif (this._depValuesDifferFromTracked()) {\n\t\t\t\t\t\t\tthis._runFn();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === COMPLETE) {\n\t\t\t\tthis._depCompleteBits.add(index);\n\t\t\t\tthis._depDirtyBits.delete(index);\n\t\t\t\tthis._depSettledBits.delete(index);\n\t\t\t\tif (this._allDirtySettled()) {\n\t\t\t\t\tthis._depDirtyBits.clear();\n\t\t\t\t\tthis._depSettledBits.clear();\n\t\t\t\t\tif (!this._running) this._runFn();\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tthis._autoComplete &&\n\t\t\t\t\tthis._depCompleteBits.size >= this._deps.length &&\n\t\t\t\t\tthis._deps.length > 0\n\t\t\t\t) {\n\t\t\t\t\tthis._downInternal([[COMPLETE]]);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === ERROR) {\n\t\t\t\tthis._downInternal([msg]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t === INVALIDATE || t === TEARDOWN || t === PAUSE || t === RESUME) {\n\t\t\t\tthis._downInternal([msg]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis._downInternal([msg]);\n\t\t}\n\t}\n\n\t/**\n\t * Update dep masks for a message without triggering `_runFn` — used\n\t * during post-rewire drain so the wave state is consistent with the\n\t * buffered activation cascade without recursing.\n\t */\n\tprivate _updateMasksForMessage(index: number, msg: Messages[number]): void {\n\t\tconst t = msg[0];\n\t\tif (t === DIRTY) {\n\t\t\tthis._depDirtyBits.add(index);\n\t\t\tthis._depSettledBits.delete(index);\n\t\t} else if (t === DATA || t === RESOLVED) {\n\t\t\tthis._depDirtyBits.add(index);\n\t\t\tthis._depSettledBits.add(index);\n\t\t} else if (t === COMPLETE) {\n\t\t\tthis._depCompleteBits.add(index);\n\t\t\tthis._depDirtyBits.delete(index);\n\t\t\tthis._depSettledBits.delete(index);\n\t\t}\n\t}\n\n\tprivate _allDirtySettled(): boolean {\n\t\tif (this._depDirtyBits.size === 0) return false;\n\t\tfor (const idx of this._depDirtyBits) {\n\t\t\tif (!this._depSettledBits.has(idx)) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * True if any current dep value differs from what the last `_runFn`\n\t * saw via `get()`. Used to suppress redundant re-runs when deferred\n\t * handshake messages arrive after `_rewire` for a dep whose value\n\t * already matches `_trackedValues`.\n\t */\n\tprivate _depValuesDifferFromTracked(): boolean {\n\t\tfor (const dep of this._deps) {\n\t\t\tconst current = dep.get();\n\t\t\tconst tracked = this._trackedValues.get(dep);\n\t\t\tif (!this._equals(current, tracked)) return true;\n\t\t}\n\t\treturn false;\n\t}\n}\n","import type { Actor } from \"./actor.js\";\nimport { DynamicNodeImpl } from \"./dynamic-node.js\";\nimport { accessHintForGuard } from \"./guard.js\";\nimport { NO_VALUE, type Node, NodeImpl } from \"./node.js\";\n\n/** JSON-shaped slice of a node for Phase 1 `Graph.describe()` (GRAPHREFLY-SPEC §3.6, Appendix B). */\nexport type DescribeNodeOutput = {\n\ttype: \"state\" | \"derived\" | \"producer\" | \"operator\" | \"effect\";\n\tstatus?: Node[\"status\"];\n\tdeps: string[];\n\tmeta?: Record<string, unknown>;\n\tname?: string;\n\tvalue?: unknown;\n\t/** True when the node has never received or been initialized with a value (cache holds SENTINEL). */\n\tsentinel?: boolean;\n\t/** Node versioning info (GRAPHREFLY-SPEC §7). Present only when versioning is enabled. */\n\tv?: { id: string; version: number; cid?: string; prev?: string | null };\n\t/** Guard info (full detail). */\n\tguard?: string;\n\t/** Last mutation attribution (full detail). */\n\tlastMutation?: Readonly<{ actor: Actor; timestamp_ns: number }>;\n};\n\n/**\n * Detail level for `describe()` progressive disclosure (Phase 3.3b).\n * - `\"minimal\"` — type + deps only (default). LLM-friendly.\n * - `\"standard\"` — type, status, value, deps, meta, versioning (`v`).\n * - `\"full\"` — standard + guard, lastMutation.\n */\nexport type DescribeDetail = \"minimal\" | \"standard\" | \"full\";\n\n/**\n * Valid field names for `describe({ fields: [...] })` (Phase 3.3b).\n * Dotted paths like `\"meta.label\"` select specific meta keys.\n */\nexport type DescribeField =\n\t| \"type\"\n\t| \"status\"\n\t| \"value\"\n\t| \"deps\"\n\t| \"meta\"\n\t| \"v\"\n\t| \"guard\"\n\t| \"lastMutation\"\n\t| `meta.${string}`;\n\n/** Resolve which fields to include based on detail level or explicit field list. */\nexport function resolveDescribeFields(\n\tdetail?: DescribeDetail,\n\tfields?: readonly DescribeField[],\n): Set<string> | null {\n\t// Explicit fields override detail level\n\tif (fields != null && fields.length > 0) return new Set(fields);\n\tswitch (detail) {\n\t\tcase \"standard\":\n\t\t\treturn new Set([\"type\", \"status\", \"value\", \"deps\", \"meta\", \"v\"]);\n\t\tcase \"full\":\n\t\t\treturn null; // null = include everything\n\t\tdefault:\n\t\t\treturn new Set([\"type\", \"deps\"]);\n\t}\n}\n\nfunction inferDescribeType(n: NodeImpl): DescribeNodeOutput[\"type\"] {\n\tif (n._describeKind != null) return n._describeKind;\n\tif (!n._hasDeps) return n._fn != null ? \"producer\" : \"state\";\n\tif (n._fn == null) return \"derived\";\n\tif (n._manualEmitUsed) return \"operator\";\n\treturn \"derived\";\n}\n\n/**\n * Reads the current cached value of every companion meta field on a node,\n * suitable for merging into `describe()`-style JSON (GRAPHREFLY-SPEC §2.3, §3.6).\n *\n * @remarks\n * Values come from {@link Node.get}, which returns the **last settled** cache.\n * If a meta field is in `\"dirty\"` status (DIRTY received, DATA pending), the\n * snapshot contains the *previous* value — check `node.meta[key].status` when\n * freshness matters. Avoid calling mid-batch for the same reason.\n *\n * Meta nodes are **not** terminated when their parent receives COMPLETE or\n * ERROR — they remain writable so callers can record post-mortem metadata\n * (e.g. `meta.error`). They *are* torn down when the parent receives TEARDOWN.\n *\n * @param node - The node whose meta fields to snapshot.\n * @returns Plain object of `{ key: value }` pairs (empty if no meta defined).\n * Keys whose companion node's {@link Node.get} throws are omitted.\n *\n * @example\n * ```ts\n * import { core } from \"@graphrefly/graphrefly-ts\";\n *\n * const n = core.node({ initial: 0, meta: { tag: \"a\" } });\n * core.metaSnapshot(n); // { tag: \"a\" }\n * ```\n */\n/** @internal Used by {@link describeNode} — not part of the public API. */\nexport function metaSnapshot(node: Node): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(node.meta)) {\n\t\ttry {\n\t\t\tout[key] = child.get();\n\t\t} catch {\n\t\t\t/* omit key — describe tooling still gets other fields */\n\t\t}\n\t}\n\treturn out;\n}\n\n/**\n * Builds a single-node slice of `Graph.describe()` JSON (structure + `meta` snapshot).\n * Parity with graphrefly-py `describe_node`.\n *\n * `type` is inferred from factory configuration, optional `describeKind` in node options,\n * and the last `manualEmitUsed` hint (operator vs derived). {@link effect} sets\n * `describeKind: \"effect\"`. Nodes not created by {@link node} fall back to `type: \"state\"` and empty `deps`.\n *\n * @param node - Any `Node` to introspect.\n * @returns `DescribeNodeOutput` suitable for merging into graph describe maps.\n *\n * @example\n * ```ts\n * import { describeNode, state } from \"@graphrefly/graphrefly-ts\";\n *\n * describeNode(state(0));\n * ```\n */\n/**\n * Builds a single-node slice for `Graph.describe()`.\n *\n * @param node - Node to introspect.\n * @param includeFields - Set of fields to include, or `null` for all. When omitted, all fields are included (legacy behavior).\n */\n/** @internal Used by {@link Graph.describe} — not part of the public API. */\nexport function describeNode(node: Node, includeFields?: Set<string> | null): DescribeNodeOutput {\n\tconst all = includeFields == null; // null or undefined → include everything\n\n\t// Specific meta keys requested via dotted paths (e.g. \"meta.label\")\n\tconst metaKeys: string[] | null =\n\t\t!all && includeFields != null\n\t\t\t? [...includeFields].filter((f) => f.startsWith(\"meta.\")).map((f) => f.slice(5))\n\t\t\t: null;\n\tconst wantsMeta = all || includeFields!.has(\"meta\") || (metaKeys != null && metaKeys.length > 0);\n\n\tlet type: DescribeNodeOutput[\"type\"] = \"state\";\n\tlet deps: string[] = [];\n\n\tif (node instanceof NodeImpl) {\n\t\ttype = inferDescribeType(node);\n\t\tdeps = node._deps.map((d) => d.name ?? \"\");\n\t} else if (node instanceof DynamicNodeImpl) {\n\t\ttype = node._describeKind ?? \"derived\";\n\t\tdeps = [];\n\t}\n\n\tconst out: DescribeNodeOutput = { type, deps };\n\n\t// status\n\tif (all || includeFields!.has(\"status\")) {\n\t\tout.status = node.status;\n\t}\n\n\t// Resolve guard once — used by both meta.access hint and standalone guard field\n\tconst guard =\n\t\t(node instanceof NodeImpl && node._guard) ||\n\t\t(node instanceof DynamicNodeImpl && node._guard) ||\n\t\tundefined;\n\n\t// meta\n\tif (wantsMeta) {\n\t\tconst rawMeta: Record<string, unknown> = { ...metaSnapshot(node) };\n\t\tif (guard != null && rawMeta.access === undefined) {\n\t\t\trawMeta.access = accessHintForGuard(guard);\n\t\t}\n\n\t\tif (metaKeys != null && metaKeys.length > 0 && !includeFields!.has(\"meta\")) {\n\t\t\t// Only specific meta keys\n\t\t\tconst filtered: Record<string, unknown> = {};\n\t\t\tfor (const k of metaKeys) {\n\t\t\t\tif (k in rawMeta) filtered[k] = rawMeta[k];\n\t\t\t}\n\t\t\tout.meta = filtered;\n\t\t} else {\n\t\t\tout.meta = rawMeta;\n\t\t}\n\t}\n\n\t// name (always include when present — it's identity, not detail)\n\tif (node.name != null) {\n\t\tout.name = node.name;\n\t}\n\n\t// value + sentinel indicator\n\tif (all || includeFields!.has(\"value\")) {\n\t\tconst isSentinel =\n\t\t\t(node instanceof NodeImpl && node._cached === NO_VALUE) ||\n\t\t\t(node instanceof DynamicNodeImpl && node._cached === NO_VALUE);\n\t\tif (isSentinel) {\n\t\t\tout.sentinel = true;\n\t\t}\n\t\ttry {\n\t\t\tout.value = node.get();\n\t\t} catch {\n\t\t\t/* omit value */\n\t\t}\n\t}\n\n\t// Versioning (GRAPHREFLY-SPEC §7)\n\tif ((all || includeFields!.has(\"v\")) && node.v != null) {\n\t\tconst vInfo: DescribeNodeOutput[\"v\"] = { id: node.v.id, version: node.v.version };\n\t\tif (\"cid\" in node.v) {\n\t\t\tvInfo!.cid = (node.v as { cid: string }).cid;\n\t\t\tvInfo!.prev = (node.v as { prev: string | null }).prev;\n\t\t}\n\t\tout.v = vInfo;\n\t}\n\n\t// Guard info (full detail)\n\tif (all || includeFields!.has(\"guard\")) {\n\t\tif (guard != null) {\n\t\t\tout.guard = accessHintForGuard(guard);\n\t\t}\n\t}\n\n\t// Last mutation attribution (full detail)\n\tif (all || includeFields!.has(\"lastMutation\")) {\n\t\tif (node.lastMutation != null) {\n\t\t\tout.lastMutation = node.lastMutation;\n\t\t}\n\t}\n\n\treturn out;\n}\n","import { type Node, type NodeFn, type NodeOptions, node } from \"./node.js\";\n\n/**\n * Creates a manual source with no upstream deps. Emit values with {@link Node.down}.\n *\n * Spec: `state(initial, opts?)` is `node([], { initial, ...opts })` (GRAPHREFLY-SPEC §2.7).\n *\n * @param initial - Initial cached value. Because `initial` is provided, `equals` is\n * called on the first {@link Node.down | down()} emission — if the value matches\n * `initial`, the node emits `RESOLVED` instead of `DATA` (spec §2.5).\n * @param opts - Optional {@link NodeOptions} (excluding `initial`).\n * @returns `Node<T>` - Stateful node you drive imperatively.\n *\n * @example\n * ```ts\n * import { DATA, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const n = state(0);\n * n.down([[DATA, 1]]);\n * ```\n *\n * @category core\n */\nexport function state<T>(initial: T, opts?: Omit<NodeOptions, \"initial\">): Node<T> {\n\treturn node<T>([], { ...opts, initial });\n}\n\n/**\n * Creates a producer node with no deps; `fn` runs when the first subscriber connects.\n *\n * @param fn - Receives deps (empty) and {@link NodeActions}; use `emit` / `down` to push.\n * @param opts - Optional {@link NodeOptions}.\n * @returns `Node<T>` - Producer node.\n *\n * @example\n * ```ts\n * import { producer } from \"@graphrefly/graphrefly-ts\";\n *\n * const tick = producer((_d, a) => {\n * a.emit(1);\n * });\n * ```\n *\n * @category core\n */\nexport function producer<T = unknown>(fn: NodeFn<T>, opts?: NodeOptions): Node<T> {\n\treturn node<T>(fn, { describeKind: \"producer\", ...opts });\n}\n\n/**\n * Creates a derived node from dependencies and a compute function (same primitive as operators).\n *\n * @param deps - Upstream nodes.\n * @param fn - Compute function; return value is emitted, or use `actions` explicitly.\n * @param opts - Optional {@link NodeOptions}.\n * @returns `Node<T>` - Derived node.\n *\n * @example\n * ```ts\n * import { derived, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const a = state(1);\n * const b = derived([a], ([x]) => (x as number) * 2);\n * ```\n *\n * @category core\n */\nexport function derived<T = unknown>(\n\tdeps: readonly Node[],\n\tfn: NodeFn<T>,\n\topts?: NodeOptions,\n): Node<T> {\n\treturn node<T>(deps, fn, { describeKind: \"derived\", ...opts });\n}\n\n/**\n * Runs a side-effect when deps settle; return value is not auto-emitted.\n *\n * @param deps - Nodes to watch.\n * @param fn - Side-effect body.\n * @returns `Node<unknown>` - Effect node.\n *\n * @example\n * ```ts\n * import { effect, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const n = state(1);\n * effect([n], ([v]) => {\n * console.log(v);\n * });\n * ```\n *\n * @category core\n */\nexport function effect(\n\tdeps: readonly Node[],\n\tfn: NodeFn<unknown>,\n\topts?: NodeOptions,\n): Node<unknown> {\n\treturn node(deps, fn, { describeKind: \"effect\", ...opts });\n}\n\n/** Unary transform used by {@link pipe} (typically returns a new node wrapping `n`). */\nexport type PipeOperator = (n: Node) => Node;\n\n/**\n * Composes unary operators left-to-right; returns the final node. Does not register a {@link Graph}.\n *\n * @param source - Starting node.\n * @param ops - Each operator maps `Node` to `Node` (curried operators from `extra` use a factory pattern — wrap or use direct calls).\n * @returns `Node` - Result of the last operator.\n *\n * @example\n * ```ts\n * import { filter, map, pipe, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const src = state(1);\n * const out = pipe(\n * src,\n * (n) => map(n, (x) => x + 1),\n * (n) => filter(n, (x) => x > 0),\n * );\n * ```\n *\n * @category core\n */\nexport function pipe(source: Node, ...ops: PipeOperator[]): Node {\n\tlet current = source;\n\tfor (const op of ops) {\n\t\tcurrent = op(current);\n\t}\n\treturn current;\n}\n","/**\n * Creates a resettable deadline timer for internal timeout, retry, and rate-limiting use.\n *\n * @remarks **Centralised primitive:** wraps `setTimeout`/`clearTimeout` with a generation guard\n * so that stale callbacks never fire after `cancel()` or a new `start()`.\n *\n * @remarks **Spec §5.10 exception:** resilience operators (`timeout`, `retry`, `rateLimiter`)\n * need raw timers — `fromTimer` creates a new Node per reset, which is too heavy here.\n *\n * @example\n * ```ts\n * import { ResettableTimer } from \"@graphrefly/graphrefly-ts\";\n *\n * const timer = new ResettableTimer();\n * timer.start(1000, () => console.log(\"fired\"));\n * timer.cancel(); // cancels before firing\n * timer.start(500, () => console.log(\"new deadline\"));\n * console.log(timer.pending); // true\n * ```\n *\n * @category core\n */\nexport class ResettableTimer {\n\tprivate _timer: ReturnType<typeof setTimeout> | undefined;\n\tprivate _gen = 0;\n\n\t/** Schedule callback after delayMs. Cancels any pending timer. */\n\tstart(delayMs: number, callback: () => void): void {\n\t\tthis.cancel();\n\t\tthis._gen += 1;\n\t\tconst gen = this._gen;\n\t\tthis._timer = setTimeout(() => {\n\t\t\tthis._timer = undefined;\n\t\t\tif (gen !== this._gen) return;\n\t\t\tcallback();\n\t\t}, delayMs);\n\t}\n\n\t/** Cancel the pending timer (if any). */\n\tcancel(): void {\n\t\tif (this._timer !== undefined) {\n\t\t\tclearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/** Whether a timer is currently pending. */\n\tget pending(): boolean {\n\t\treturn this._timer !== undefined;\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/index.ts","../../src/core/actor.ts","../../src/core/batch.ts","../../src/core/clock.ts","../../src/core/messages.ts","../../src/core/config.ts","../../src/core/guard.ts","../../src/graph/codec.ts","../../src/core/versioning.ts","../../src/core/node.ts","../../src/core/meta.ts","../../src/core/sugar.ts"],"sourcesContent":["/**\n * Core layer: message protocol, node primitive, lifecycle (Phase 0).\n */\nexport * from \"./actor.js\";\nexport { batch, downWithBatch, isBatching } from \"./batch.js\";\nexport { monotonicNs, wallClockNs } from \"./clock.js\";\nexport {\n\ttype GlobalInspectorEvent,\n\ttype GlobalInspectorHook,\n\tGraphReFlyConfig,\n\ttype MessageContext,\n\ttype NodeActions,\n\ttype NodeCtx,\n\ttype OnMessageHandler,\n\ttype OnSubscribeHandler,\n\tregisterBuiltins,\n\ttype SubscribeContext,\n} from \"./config.js\";\nexport * from \"./guard.js\";\nexport {\n\tCOMPLETE,\n\tCOMPLETE_MSG,\n\tCOMPLETE_ONLY_BATCH,\n\tDATA,\n\tDIRTY,\n\tDIRTY_MSG,\n\tDIRTY_ONLY_BATCH,\n\tERROR,\n\tINVALIDATE,\n\tINVALIDATE_MSG,\n\tINVALIDATE_ONLY_BATCH,\n\ttype Message,\n\ttype Messages,\n\ttype MessageTypeRegistration,\n\ttype MessageTypeRegistrationInput,\n\tPAUSE,\n\tRESOLVED,\n\tRESOLVED_MSG,\n\tRESOLVED_ONLY_BATCH,\n\tRESUME,\n\tSTART,\n\tSTART_MSG,\n\tTEARDOWN,\n\tTEARDOWN_MSG,\n\tTEARDOWN_ONLY_BATCH,\n} from \"./messages.js\";\nexport {\n\ttype DescribeDetail,\n\ttype DescribeField,\n\ttype DescribeNodeOutput,\n\tresolveDescribeFields,\n} from \"./meta.js\";\nexport {\n\tconfigure,\n\ttype DepRecord,\n\tdefaultConfig,\n\ttype FnCtx,\n\ttype Node,\n\ttype NodeDescribeKind,\n\ttype NodeFn,\n\ttype NodeFnCleanup,\n\tNodeImpl,\n\ttype NodeInspectorHook,\n\ttype NodeInspectorHookEvent,\n\ttype NodeOptions,\n\ttype NodeSink,\n\ttype NodeStatus,\n\ttype NodeTransportOptions,\n\tnode,\n} from \"./node.js\";\nexport {\n\ttype AutoTrackOptions,\n\tautoTrackNode,\n\ttype DerivedFn,\n\ttype DynamicFn,\n\tderived,\n\tdynamicNode,\n\ttype EffectFn,\n\teffect,\n\ttype PipeOperator,\n\ttype ProducerFn,\n\tpipe,\n\tproducer,\n\tstate,\n\ttype TrackFn,\n} from \"./sugar.js\";\nexport {\n\tadvanceVersion,\n\tcreateVersioning,\n\tdefaultHash,\n\ttype HashFn,\n\tisV1,\n\ttype NodeVersionInfo,\n\ttype V0,\n\ttype V1,\n\ttype VersioningLevel,\n\ttype VersioningOptions,\n} from \"./versioning.js\";\n","/**\n * Who is performing an operation (attribution + ABAC input).\n *\n * @see GRAPHREFLY-SPEC — roadmap Phase 1.5 (Actor & Guard).\n */\nexport type Actor = {\n\ttype: \"human\" | \"llm\" | \"wallet\" | \"system\" | string;\n\tid: string;\n} & Record<string, unknown>;\n\n/** Default actor when none is passed ({@link normalizeActor}). */\nexport const DEFAULT_ACTOR: Actor = { type: \"system\", id: \"\" };\n\n/**\n * Fills missing `type` / `id` on an actor and returns {@link DEFAULT_ACTOR} when input is undefined.\n *\n * @param actor - Optional partial actor from a transport hint.\n * @returns A normalized `Actor` safe to pass to guards and graph APIs.\n *\n * @example\n * ```ts\n * import { normalizeActor } from \"@graphrefly/graphrefly-ts\";\n *\n * normalizeActor({ type: \"human\", id: \"u1\" });\n * ```\n */\nexport function normalizeActor(actor?: Actor): Actor {\n\tif (actor == null) return DEFAULT_ACTOR;\n\tconst { type, id, ...rest } = actor;\n\treturn {\n\t\ttype: type ?? \"system\",\n\t\tid: id ?? \"\",\n\t\t...rest,\n\t} as Actor;\n}\n","/**\n * Batch deferral for tier-3+ messages, plus per-node emit coalescing inside\n * explicit `batch()` scopes.\n *\n * §1.3.7 — Inside a batch, tier 0–2 signals propagate immediately. Tier 3\n * (DATA/RESOLVED), tier 4 (COMPLETE/ERROR), and tier 5 (TEARDOWN) are queued\n * and drained in ascending phase order after the outermost `batch()` callback\n * returns.\n *\n * **Per-node emit coalescing (Bug 2 fix, 2026-04-17).** Inside an explicit\n * `batch()` scope, consecutive emissions from the same node accumulate in\n * `NodeImpl._batchPendingMessages` instead of each producing a separate\n * downstream wave. At batch end, each node flushes its accumulated messages\n * as ONE multi-message `downWithBatch` call — K `.emit()`s to the same\n * source collapse to K DIRTYs in one tier-1 sink call + K DATAs in one\n * tier-3 sink call. Downstream nodes' fns run once per wave with the full\n * `batchData` (dep's `dataBatch` accumulates all K values, fn sees\n * `[[v1, v2, ..., vK]]`). Resolves the K+1 fan-in over-fire.\n *\n * Outside batch — and during drain (where `flushInProgress` is true but\n * `batchDepth` is 0) — coalescing does NOT apply: each emit goes through\n * its own `downWithBatch` call and produces its own wave.\n *\n * **Phase vocabulary:**\n * - Phase 1 = tiers 0–2 — immediate, never queued.\n * - Phase 2 = tier 3 — {@link drainPhase2}. Value settlements.\n * - Phase 3 = tier 4 — {@link drainPhase3}. Terminal signals.\n * - Phase 4 = tier 5 — {@link drainPhase4}. TEARDOWN (unified deferral).\n *\n * Drain rule: fire any pending flush hooks first, then the lowest non-empty\n * phase. Re-enqueues during drain (and hooks registered by reentrant batches\n * inside subscriber callbacks) bump the loop back to the top so newly-added\n * hooks and closures get processed.\n *\n * **Pre-sorted input invariant.** `downWithBatch` assumes `messages` is\n * already sorted in ascending tier order (produced by `_frameBatch` in\n * `node.ts`). The walker exploits monotonicity for a single O(n) pass and\n * slices at phase boundaries without re-sorting. Flushed multi-emit batches\n * re-run `_frameBatch` to restore monotonicity (the per-emit framings\n * accumulate in interleaved order: `[DIRTY, DATA, DIRTY, DATA, ...]`).\n */\n\nimport type { Messages } from \"./messages.js\";\n\nconst MAX_DRAIN_ITERATIONS = 1000;\n\nlet batchDepth = 0;\nlet flushInProgress = false;\n\n/** Tier 3 (DATA/RESOLVED) deferral queue — drained first. */\nconst drainPhase2: Array<() => void> = [];\n/** Tier 4 (COMPLETE/ERROR) deferral queue — drained after phase 2. */\nconst drainPhase3: Array<() => void> = [];\n/** Tier 5 (TEARDOWN) deferral queue — drained last. */\nconst drainPhase4: Array<() => void> = [];\n\n/**\n * Per-batch flush hooks. Each hook is registered by a node the first time\n * it accumulates an emission inside an explicit `batch()` scope (Bug 2 —\n * per-node emit coalescing). Hooks fire at the head of `drainPending`,\n * before the standard tier-3/4/5 drain queues — they call `downWithBatch`\n * with the node's accumulated multi-message batch, which enqueues the\n * tier-3+ portion into `drainPhase2/3/4` for the standard loop.\n *\n * On a `batch()` throw, hooks still fire so each node clears its pending\n * state (they're idempotent — the side-effects are wiped because the\n * drainPhase queues that they enqueue into are cleared in the same finally\n * block).\n */\nconst flushHooks: Array<() => void> = [];\n\n/**\n * Returns whether the current call stack is inside a batch scope **or** while\n * a deferred drain is in progress. Nested `downWithBatch` calls during drain\n * still defer (they bump the drain loop).\n */\nexport function isBatching(): boolean {\n\treturn batchDepth > 0 || flushInProgress;\n}\n\n/**\n * Returns whether the current call stack is inside an **explicit** `batch()`\n * scope. Excludes `flushInProgress` — i.e. emissions that happen during a\n * drain (e.g. inside a fn callback) are NOT explicitly batched and should\n * not trigger per-node coalescing (Bug 2).\n */\nexport function isExplicitlyBatching(): boolean {\n\treturn batchDepth > 0;\n}\n\n/**\n * Register a hook to fire at the head of the next `drainPending`. Used by\n * `NodeImpl._emit` to flush its per-batch accumulator (Bug 2). If called\n * outside an explicit batch the hook fires immediately, since there's no\n * drain coming.\n */\nexport function registerBatchFlushHook(hook: () => void): void {\n\tif (batchDepth > 0) {\n\t\tflushHooks.push(hook);\n\t} else {\n\t\thook();\n\t}\n}\n\n/**\n * Runs `fn` inside a batch scope. Nested `batch()` calls share one deferral\n * queue. If `fn` throws, deferred work for the outer frame is discarded\n * (unless a drain is already in progress — cross-language decision A4).\n */\nexport function batch(fn: () => void): void {\n\tbatchDepth += 1;\n\tlet threw = false;\n\ttry {\n\t\tfn();\n\t} catch (e) {\n\t\tthrew = true;\n\t\tthrow e;\n\t} finally {\n\t\tbatchDepth -= 1;\n\t\tif (batchDepth === 0) {\n\t\t\tif (threw) {\n\t\t\t\tif (!flushInProgress) {\n\t\t\t\t\t// Fire any per-node flush hooks so nodes clear their\n\t\t\t\t\t// pending state. The downWithBatch calls those hooks\n\t\t\t\t\t// make enqueue into drainPhase queues — those queues\n\t\t\t\t\t// are cleared right after, so the side-effects are\n\t\t\t\t\t// wiped. Net result: clean node state, no delivery.\n\t\t\t\t\tconst hooks = flushHooks.splice(0);\n\t\t\t\t\tfor (const h of hooks) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\th();\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* best-effort */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdrainPhase2.length = 0;\n\t\t\t\t\tdrainPhase3.length = 0;\n\t\t\t\t\tdrainPhase4.length = 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdrainPending();\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction drainPending(): void {\n\tconst ownsFlush = !flushInProgress;\n\tif (ownsFlush) flushInProgress = true;\n\n\tconst errors: unknown[] = [];\n\n\tlet iterations = 0;\n\ttry {\n\t\t// Loop while EITHER tier-3+ deferred work OR pending flush hooks exist.\n\t\t// Hooks can be re-registered mid-drain by reentrant `batch()` calls\n\t\t// inside subscriber callbacks; checking `flushHooks` each iteration\n\t\t// (not just before the loop) ensures those late hooks fire too.\n\t\twhile (\n\t\t\tdrainPhase2.length > 0 ||\n\t\t\tdrainPhase3.length > 0 ||\n\t\t\tdrainPhase4.length > 0 ||\n\t\t\t(ownsFlush && flushHooks.length > 0)\n\t\t) {\n\t\t\t// Fire any pending flush hooks FIRST so their downWithBatch calls\n\t\t\t// enqueue tier-3+ work into drainPhase before we process it.\n\t\t\tif (ownsFlush && flushHooks.length > 0) {\n\t\t\t\tconst hooks = flushHooks.splice(0);\n\t\t\t\tfor (const h of hooks) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\th();\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\terrors.push(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue; // restart loop — hooks may have enqueued tier-3+ work or more hooks\n\t\t\t}\n\t\t\titerations += 1;\n\t\t\tif (iterations > MAX_DRAIN_ITERATIONS) {\n\t\t\t\tdrainPhase2.length = 0;\n\t\t\t\tdrainPhase3.length = 0;\n\t\t\t\tdrainPhase4.length = 0;\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`batch drain exceeded ${MAX_DRAIN_ITERATIONS} iterations — likely a reactive cycle`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Always drain the lowest non-empty phase. Re-enqueues at any level\n\t\t\t// cause the next iteration to restart from phase 2 if needed.\n\t\t\tconst queue =\n\t\t\t\tdrainPhase2.length > 0 ? drainPhase2 : drainPhase3.length > 0 ? drainPhase3 : drainPhase4;\n\t\t\tconst ops = queue.splice(0);\n\t\t\tfor (const run of ops) {\n\t\t\t\ttry {\n\t\t\t\t\trun();\n\t\t\t\t} catch (e) {\n\t\t\t\t\terrors.push(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} finally {\n\t\tif (ownsFlush) flushInProgress = false;\n\t}\n\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) {\n\t\tthrow new AggregateError(errors, \"batch drain: multiple callbacks threw\");\n\t}\n}\n\n/**\n * Deliver pre-sorted messages through `sink` with tier-based deferral applied.\n *\n * `messages` MUST be in ascending tier order (produced by `_frameBatch` in\n * `node.ts`); the walker exploits that invariant to find phase cuts in one\n * pass without re-sorting.\n *\n * Behavior:\n * - Tier 0–2 — delivered synchronously.\n * - Tier 3 — deferred to {@link drainPhase2} when batching, else synchronous.\n * - Tier 4 — deferred to {@link drainPhase3} when batching, else synchronous.\n * - Tier 5 — deferred to {@link drainPhase4} when batching, else synchronous.\n *\n * Tier-classification uses the caller-supplied `tierOf` so that batch stays\n * decoupled from `GraphReFlyConfig`. NodeImpl passes `config.tierOf` (a\n * pre-bound closure built once in the config constructor) at the emit site;\n * alternate configs can pass their own lookup.\n */\nexport function downWithBatch(\n\tsink: (messages: Messages) => void,\n\tmessages: Messages,\n\ttierOf: (t: symbol) => number,\n): void {\n\tif (messages.length === 0) return;\n\n\t// Fast path: single message (hot in propagation).\n\tif (messages.length === 1) {\n\t\tconst tier = tierOf(messages[0][0]);\n\t\tif (tier < 3 || !isBatching()) {\n\t\t\tsink(messages);\n\t\t\treturn;\n\t\t}\n\t\tconst queue = tier >= 5 ? drainPhase4 : tier === 4 ? drainPhase3 : drainPhase2;\n\t\tqueue.push(() => sink(messages));\n\t\treturn;\n\t}\n\n\t// Multi-message: walk once over pre-sorted input, find phase cuts.\n\t// Monotone tier order means `phase2Start <= phase3Start <= phase4Start`.\n\tconst n = messages.length;\n\tlet phase2Start = n;\n\tlet phase3Start = n;\n\tlet phase4Start = n;\n\n\tlet i = 0;\n\twhile (i < n && tierOf(messages[i][0]) < 3) i++;\n\tphase2Start = i;\n\twhile (i < n && tierOf(messages[i][0]) === 3) i++;\n\tphase3Start = i;\n\twhile (i < n && tierOf(messages[i][0]) === 4) i++;\n\tphase4Start = i;\n\t// Anything from phase4Start..n has tier >= 5.\n\n\tconst batching = isBatching();\n\n\tif (phase2Start > 0) {\n\t\t// Immediate tier 0–2 region.\n\t\tconst immediate = messages.slice(0, phase2Start);\n\t\tsink(immediate);\n\t}\n\n\tif (phase3Start > phase2Start) {\n\t\tconst phase2 = messages.slice(phase2Start, phase3Start);\n\t\tif (batching) drainPhase2.push(() => sink(phase2));\n\t\telse sink(phase2);\n\t}\n\n\tif (phase4Start > phase3Start) {\n\t\tconst phase3 = messages.slice(phase3Start, phase4Start);\n\t\tif (batching) drainPhase3.push(() => sink(phase3));\n\t\telse sink(phase3);\n\t}\n\n\tif (n > phase4Start) {\n\t\tconst phase4 = messages.slice(phase4Start, n);\n\t\tif (batching) drainPhase4.push(() => sink(phase4));\n\t\telse sink(phase4);\n\t}\n}\n","/**\n * Centralised timestamp utilities.\n *\n * Convention: all graphrefly-ts timestamps use nanoseconds (`_ns` suffix).\n *\n * - {@link monotonicNs} — monotonic clock (ordering, durations, timeline events).\n * - {@link wallClockNs} — wall-clock (mutation attribution, cron emission).\n *\n * **Precision limits (JS platform):**\n *\n * - `monotonicNs`: effective ~microsecond precision. `performance.now()` returns\n * milliseconds with ~5µs resolution; the last 3 digits of the nanosecond value\n * are always zero. Python's `time.monotonic_ns()` gives true nanoseconds.\n *\n * - `wallClockNs`: ~256ns precision loss at current epoch. `Date.now() * 1e6`\n * produces values around 1.8×10¹⁸ which exceed IEEE 754's 2⁵³ safe integer\n * limit. Python's `time.time_ns()` (arbitrary-precision `int`) has no loss.\n * In practice this is irrelevant — JS is single-threaded, so sub-microsecond\n * timestamp collisions cannot occur.\n */\n\n/** Monotonic nanosecond timestamp via `performance.now()`. */\nexport function monotonicNs(): number {\n\treturn Math.trunc(performance.now() * 1_000_000);\n}\n\n/** Wall-clock nanosecond timestamp via `Date.now()`. */\nexport function wallClockNs(): number {\n\treturn Date.now() * 1_000_000;\n}\n","/**\n * GraphReFly message protocol — §1 `~/src/graphrefly/GRAPHREFLY-SPEC.md`.\n * Emissions are always `[[Type, Data?], ...]` (no single-tuple shorthand).\n *\n * This file is protocol-pure:\n * - Message type symbols (10 built-ins).\n * - `Message` / `Messages` tuple types.\n * - `MessageTypeRegistration` interface (shape of a registry entry).\n *\n * It does NOT own the registry, tier lookups, or any cross-cutting singleton\n * state — that lives in `GraphReFlyConfig` (see `config.ts`) so custom\n * protocols can build isolated instances. Import this module when you need\n * the symbol constants or the tuple types; import `config.ts` when you need\n * tier / wire-crossing / registry lookups.\n */\n\n/** Subscribe-time handshake. Delivered to each new sink at the top of `subscribe()`. Tier 0. */\nexport const START = Symbol.for(\"graphrefly/START\");\n/** Value delivery (`DATA`, value). Tier 3 — deferred inside `batch()`. */\nexport const DATA = Symbol.for(\"graphrefly/DATA\");\n/** Phase 1: value about to change. Tier 1 — immediate. */\nexport const DIRTY = Symbol.for(\"graphrefly/DIRTY\");\n/** Phase 2: dirty pass completed, value unchanged. Tier 3 — deferred inside `batch()`. */\nexport const RESOLVED = Symbol.for(\"graphrefly/RESOLVED\");\n/** Clear cached state; do not auto-emit. Tier 1 — immediate. */\nexport const INVALIDATE = Symbol.for(\"graphrefly/INVALIDATE\");\n/** Suspend activity. Tier 2 — immediate. */\nexport const PAUSE = Symbol.for(\"graphrefly/PAUSE\");\n/** Resume after pause. Tier 2 — immediate. */\nexport const RESUME = Symbol.for(\"graphrefly/RESUME\");\n/** Permanent cleanup. Tier 5 — deferred to batch phase 4. */\nexport const TEARDOWN = Symbol.for(\"graphrefly/TEARDOWN\");\n/** Clean termination. Tier 4 — deferred to batch phase 3. */\nexport const COMPLETE = Symbol.for(\"graphrefly/COMPLETE\");\n/** Error termination. Tier 4 — deferred to batch phase 3. */\nexport const ERROR = Symbol.for(\"graphrefly/ERROR\");\n\n/** One protocol tuple: `[Type, optional payload]`. */\nexport type Message = readonly [symbol, unknown?];\n\n/** A batch of tuples — the wire shape for `node.down()` / `node.up()`. */\nexport type Messages = readonly Message[];\n\n// ---------------------------------------------------------------------------\n// Interned singletons for payload-free tuples\n// ---------------------------------------------------------------------------\n//\n// Every emission path used to allocate fresh `[[DIRTY]]`, `[[RESOLVED]]`,\n// etc. — two arrays per emit in the hot path. Since none of these tuples\n// carry a payload, one frozen instance is indistinguishable from a\n// fresh one. We intern them at module load and reuse forever. Only\n// `[DATA, v]`, `[ERROR, e]`, `[PAUSE, lockId]`, `[RESUME, lockId]` still\n// allocate per-call because they carry payloads.\n//\n// Downstream code MUST treat these as immutable. `Object.freeze` makes\n// accidental mutation throw in strict mode. Do not `push`, splice, or\n// otherwise mutate a Messages array that came from one of these.\n\n/** Singleton `[DIRTY]` tuple — payload-free, interned. */\nexport const DIRTY_MSG: Message = Object.freeze([DIRTY]) as Message;\n/** Singleton `[RESOLVED]` tuple — payload-free, interned. */\nexport const RESOLVED_MSG: Message = Object.freeze([RESOLVED]) as Message;\n/** Singleton `[INVALIDATE]` tuple — payload-free, interned. */\nexport const INVALIDATE_MSG: Message = Object.freeze([INVALIDATE]) as Message;\n/** Singleton `[START]` tuple — payload-free, interned. */\nexport const START_MSG: Message = Object.freeze([START]) as Message;\n/** Singleton `[COMPLETE]` tuple — payload-free, interned. */\nexport const COMPLETE_MSG: Message = Object.freeze([COMPLETE]) as Message;\n/** Singleton `[TEARDOWN]` tuple — payload-free, interned. */\nexport const TEARDOWN_MSG: Message = Object.freeze([TEARDOWN]) as Message;\n\n/** Pre-wrapped `[[DIRTY]]` for `_emit([DIRTY_ONLY_BATCH])`-style callers. */\nexport const DIRTY_ONLY_BATCH: Messages = Object.freeze([DIRTY_MSG]) as Messages;\n/** Pre-wrapped `[[RESOLVED]]`. */\nexport const RESOLVED_ONLY_BATCH: Messages = Object.freeze([RESOLVED_MSG]) as Messages;\n/** Pre-wrapped `[[INVALIDATE]]`. */\nexport const INVALIDATE_ONLY_BATCH: Messages = Object.freeze([INVALIDATE_MSG]) as Messages;\n/** Pre-wrapped `[[COMPLETE]]`. */\nexport const COMPLETE_ONLY_BATCH: Messages = Object.freeze([COMPLETE_MSG]) as Messages;\n/** Pre-wrapped `[[TEARDOWN]]`. */\nexport const TEARDOWN_ONLY_BATCH: Messages = Object.freeze([TEARDOWN_MSG]) as Messages;\n\n// ---------------------------------------------------------------------------\n// Registry entry shape\n// ---------------------------------------------------------------------------\n\n/**\n * Per-type record stored in a {@link GraphReFlyConfig}'s registry.\n *\n * - `tier` — signal tier (0–5 built-in; custom tiers allowed but should fit\n * the phase model used by `batch.ts`).\n * - `wireCrossing` — when `true`, forwarded across SSE/WebSocket/worker\n * adapters. Defaults to `tier >= 3` if omitted in registration input.\n * - `metaPassthrough` — when `false`, this message type is filtered out of\n * `Graph.signal` deliveries to meta companion nodes (spec §2.3). Meta\n * companions still receive everything via their primary's own cascade.\n * Defaults to `true` (meta receives the message).\n */\nexport interface MessageTypeRegistration {\n\ttier: number;\n\twireCrossing: boolean;\n\tmetaPassthrough: boolean;\n}\n\n/**\n * Input accepted by {@link GraphReFlyConfig.registerMessageType}. Only `tier`\n * is required; `wireCrossing` defaults to `tier >= 3`; `metaPassthrough`\n * defaults to `true`.\n */\nexport interface MessageTypeRegistrationInput {\n\ttier: number;\n\twireCrossing?: boolean;\n\tmetaPassthrough?: boolean;\n}\n","/**\n * Singleton protocol config. Holds the message-type registry, the\n * `onMessage` / `onSubscribe` hooks, versioning defaults, and the freeze flag.\n *\n * Layering: this file is protocol-pure. It imports only from `messages.ts`\n * and declares opaque type shapes for handlers — the concrete default\n * implementations and the `defaultConfig` instance live in `node.ts` so that\n * handler bodies can touch `NodeImpl` internals without creating a cycle.\n *\n * Two access paths:\n * 1. **Default instance** (`defaultConfig` in `node.ts`) — use\n * `configure((cfg) => ...)` at app startup; every node implicitly binds to it.\n * 2. **Isolated instance** (`new GraphReFlyConfig(...)`) — pass via\n * `opts.config` for test isolation or custom protocol stacks.\n *\n * A config **freezes on first getter read** of any hook (`onMessage`,\n * `onSubscribe`). `NodeImpl`'s constructor intentionally touches one of these\n * on first use so configuration cannot drift once nodes exist.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tINVALIDATE,\n\ttype Message,\n\ttype Messages,\n\ttype MessageTypeRegistration,\n\ttype MessageTypeRegistrationInput,\n\tPAUSE,\n\tRESOLVED,\n\tRESUME,\n\tSTART,\n\tTEARDOWN,\n} from \"./messages.js\";\nimport type { HashFn, VersioningLevel } from \"./versioning.js\";\n\n// ---------------------------------------------------------------------------\n// Handler type shapes\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal node surface visible to default handlers. Concrete `NodeImpl`\n * implements this plus a large set of package-private fields; handlers that\n * need the richer surface cast to the concrete type in `node.ts`.\n */\nexport interface NodeCtx {\n\treadonly name?: string;\n\treadonly status: string;\n\treadonly cache: unknown;\n}\n\n/** Imperative actions available inside a node's compute function (§5). */\nexport interface NodeActions {\n\t/**\n\t * Sugar for `down([[DATA, value]])`. One call = one wave with a\n\t * single DATA payload. The emit pipeline auto-prefixes `[DIRTY]`,\n\t * runs equals substitution against the live cache, and dispatches\n\t * to sinks with phase deferral. Diamond-safe by construction.\n\t */\n\temit(value: unknown): void;\n\t/**\n\t * Send one or more messages downstream. Accepts either a single\n\t * {@link Message} tuple or a {@link Messages} array of tuples. One\n\t * call = one wave: the emit pipeline tier-sorts the input,\n\t * auto-prefixes `[DIRTY]` when a tier-3 payload is present and the\n\t * node isn't already dirty, runs equals substitution, then\n\t * dispatches. Multiple calls produce multiple waves.\n\t */\n\tdown(messageOrMessages: Message | Messages): void;\n\t/**\n\t * Send one or more messages upstream. Accepts the same shapes as\n\t * {@link down}. Tier 3 (DATA/RESOLVED) and tier 4 (COMPLETE/ERROR)\n\t * are downstream-only and will throw — up is for DIRTY, INVALIDATE,\n\t * PAUSE, RESUME, and TEARDOWN only. No cache advance, no equals,\n\t * no framing — a plain forward to every dep.\n\t */\n\tup(messageOrMessages: Message | Messages): void;\n}\n\n/**\n * Message-flow context passed to {@link OnMessageHandler}.\n *\n * - `\"down-in\"` — message arriving from a dep (identified by `depIndex`).\n * - `\"up-in\"` — message arriving from a sink.\n */\nexport type MessageContext = { direction: \"down-in\"; depIndex: number } | { direction: \"up-in\" };\n\n/**\n * Per-sink context passed to {@link OnSubscribeHandler}.\n */\nexport interface SubscribeContext {\n\t/** Post-subscribe sink count. `1` means first subscriber after 0. */\n\tsinkCount: number;\n\t/** True when this subscribe cleared a resubscribable terminal state. */\n\tafterTerminalReset: boolean;\n}\n\n/**\n * Singleton message interceptor. Called for every message in either direction\n * before the default per-tier dispatch runs. Return `\"consume\"` to suppress\n * default handling.\n */\nexport type OnMessageHandler = (\n\tnode: NodeCtx,\n\tmsg: Message,\n\tctx: MessageContext,\n\tactions: NodeActions,\n) => \"consume\" | undefined;\n\n/**\n * Singleton subscribe ceremony. Fires for every sink subscribe on every node.\n * Default implementation emits the START handshake (+ cached DATA when\n * present) to the new sink. Return a cleanup function to run on unsubscribe.\n */\nexport type OnSubscribeHandler = (\n\tnode: NodeCtx,\n\tsink: (messages: Messages) => void,\n\tctx: SubscribeContext,\n\tactions: NodeActions,\n) => (() => void) | undefined;\n\n/**\n * Event payload for {@link GlobalInspectorHook}. One event fires per outgoing\n * message batch from any node bound to the config.\n *\n * - `kind: \"emit\"` — fires after equals substitution (`finalMessages`) and\n * before sink dispatch. `messages` is the exact batch sinks see.\n */\nexport type GlobalInspectorEvent = {\n\tkind: \"emit\";\n\tnode: NodeCtx;\n\tmessages: Messages;\n};\n\n/**\n * Process-global observability hook for full-graph tracing\n * (Redux-DevTools-style action history). Fires from every node's `_emit`\n * waist whenever {@link GraphReFlyConfig.inspectorEnabled} is `true`.\n *\n * Distinct from per-node `_setInspectorHook` (which is the dep-message /\n * fn-run causal trace used by `Graph.observe(path, { causal, derived })`).\n * Use the global hook to record every emission across every node — useful for\n * time-travel debuggers, tracers, and replay tooling. Use the per-node hook\n * to attribute a single subscribed path's wave to its driving deps.\n *\n * Errors thrown from the hook are swallowed — instrumentation must not break\n * the data plane.\n */\nexport type GlobalInspectorHook = (event: GlobalInspectorEvent) => void;\n\n// ---------------------------------------------------------------------------\n// GraphReFlyConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Singleton protocol config.\n *\n * A config freezes on first getter read of any hook. After freeze, any\n * attempt to mutate (register a message type, set a hook) throws.\n */\nexport class GraphReFlyConfig {\n\tprivate _messageTypes = new Map<symbol, MessageTypeRegistration>();\n\tprivate _codecs = new Map<string, { readonly name: string; readonly version: number }>();\n\tprivate _onMessage: OnMessageHandler;\n\tprivate _onSubscribe: OnSubscribeHandler;\n\tprivate _defaultVersioning: VersioningLevel | undefined;\n\tprivate _defaultHashFn: HashFn | undefined;\n\tprivate _inspectorEnabled: boolean = !(\n\t\ttypeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\"\n\t);\n\tprivate _globalInspector?: GlobalInspectorHook;\n\tprivate _frozen = false;\n\n\t/**\n\t * Pre-bound tier lookup — shared by every node bound to this config. Since\n\t * the registry is frozen on first hook access, this closure can be built\n\t * once in the constructor and handed directly to `downWithBatch` /\n\t * `_frameBatch` paths without per-node or per-emission `.bind(config)`\n\t * allocation.\n\t */\n\treadonly tierOf: (t: symbol) => number;\n\n\tconstructor(init: {\n\t\tonMessage: OnMessageHandler;\n\t\tonSubscribe: OnSubscribeHandler;\n\t\tdefaultVersioning?: VersioningLevel;\n\t\tdefaultHashFn?: HashFn;\n\t}) {\n\t\tthis._onMessage = init.onMessage;\n\t\tthis._onSubscribe = init.onSubscribe;\n\t\tthis._defaultVersioning = init.defaultVersioning;\n\t\tthis._defaultHashFn = init.defaultHashFn;\n\t\t// Captured once. Calls back into `this._messageTypes` — still returns\n\t\t// the current registration, but post-freeze the registry is immutable\n\t\t// so the closure is effectively constant.\n\t\tthis.tierOf = (t: symbol): number => {\n\t\t\tconst reg = this._messageTypes.get(t);\n\t\t\treturn reg != null ? reg.tier : 1;\n\t\t};\n\t}\n\n\t// --- Hook getters (freeze on read) ---\n\n\tget onMessage(): OnMessageHandler {\n\t\tthis._frozen = true;\n\t\treturn this._onMessage;\n\t}\n\n\tget onSubscribe(): OnSubscribeHandler {\n\t\tthis._frozen = true;\n\t\treturn this._onSubscribe;\n\t}\n\n\t// --- Hook setters (throw when frozen) ---\n\n\tset onMessage(v: OnMessageHandler) {\n\t\tthis._assertUnfrozen();\n\t\tthis._onMessage = v;\n\t}\n\n\tset onSubscribe(v: OnSubscribeHandler) {\n\t\tthis._assertUnfrozen();\n\t\tthis._onSubscribe = v;\n\t}\n\n\t/**\n\t * Default versioning level applied to every node bound to this config,\n\t * unless the node's own `opts.versioning` provides an explicit override.\n\t * Setting this is only allowed before the config freezes (i.e., before\n\t * the first node is created) so every node in the graph sees a\n\t * consistent starting level. Individual nodes can still opt into a\n\t * higher level via `opts.versioning`, or post-hoc via\n\t * `NodeImpl._applyVersioning(level)` when the node is quiescent.\n\t *\n\t * v0 is the minimum opt-in — unversioned nodes (`undefined`) skip\n\t * the version counter entirely. v1 adds content-addressed cid.\n\t * Future levels (v2, v3) are reserved for linked-history and\n\t * cryptographic attestation extensions.\n\t */\n\tget defaultVersioning(): VersioningLevel | undefined {\n\t\treturn this._defaultVersioning;\n\t}\n\tset defaultVersioning(v: VersioningLevel | undefined) {\n\t\tthis._assertUnfrozen();\n\t\tthis._defaultVersioning = v;\n\t}\n\n\t/**\n\t * Default content-hash function applied to every versioned node bound\n\t * to this config, unless the node's own `opts.versioningHash` provides\n\t * an explicit override. Use this when a graph needs a non-default hash\n\t * — e.g., swap the vendored sync SHA-256 for a faster non-crypto hash\n\t * (xxHash, FNV-1a) in hot-path workloads, or a stronger hash when\n\t * versioning v1 cids are used as audit anchors.\n\t *\n\t * Only settable before the config freezes. Individual nodes can still\n\t * override via `opts.versioningHash`.\n\t */\n\tget defaultHashFn(): HashFn | undefined {\n\t\treturn this._defaultHashFn;\n\t}\n\tset defaultHashFn(v: HashFn | undefined) {\n\t\tthis._assertUnfrozen();\n\t\tthis._defaultHashFn = v;\n\t}\n\n\t/**\n\t * When `false`, structured observation options (`causal`, `timeline`)\n\t * and `Graph.trace()` writes are no-ops. Raw `Graph.observe()` always\n\t * works. Default: `true` outside production (`NODE_ENV !== \"production\"`).\n\t *\n\t * Settable at any time — inspector gating is an operational concern, not\n\t * a protocol invariant, so it does NOT require freeze before node creation.\n\t */\n\tget inspectorEnabled(): boolean {\n\t\treturn this._inspectorEnabled;\n\t}\n\tset inspectorEnabled(v: boolean) {\n\t\tthis._inspectorEnabled = v;\n\t}\n\n\t/**\n\t * Process-global observability hook (Redux-DevTools-style full-graph\n\t * tracer). Fires once per outgoing batch from every node bound to this\n\t * config, gated by {@link inspectorEnabled}. See {@link GlobalInspectorHook}.\n\t *\n\t * Settable at any time — like {@link inspectorEnabled} this is operational,\n\t * not protocol-shaping, so it does NOT trigger config freeze.\n\t */\n\tget globalInspector(): GlobalInspectorHook | undefined {\n\t\treturn this._globalInspector;\n\t}\n\tset globalInspector(v: GlobalInspectorHook | undefined) {\n\t\tthis._globalInspector = v;\n\t}\n\n\t// --- Registry (writes require unfrozen; reads are free lookups) ---\n\n\t/**\n\t * Register a custom message type. Must be called before any node that\n\t * uses this config has been created — otherwise throws. Default\n\t * `wireCrossing` is `tier >= 3`.\n\t */\n\tregisterMessageType(t: symbol, input: MessageTypeRegistrationInput): this {\n\t\tthis._assertUnfrozen();\n\t\tthis._messageTypes.set(t, {\n\t\t\ttier: input.tier,\n\t\t\twireCrossing: input.wireCrossing ?? input.tier >= 3,\n\t\t\tmetaPassthrough: input.metaPassthrough ?? true,\n\t\t});\n\t\treturn this;\n\t}\n\n\t/** Tier for `t`. Unknown types default to tier 1 (immediate, after START). */\n\tmessageTier(t: symbol): number {\n\t\tconst reg = this._messageTypes.get(t);\n\t\treturn reg != null ? reg.tier : 1;\n\t}\n\n\t/**\n\t * Whether `t` is registered as wire-crossing. Unknown types default to\n\t * `true` (spec §1.3.6 forward-compat — unknowns cross the wire).\n\t */\n\tisWireCrossing(t: symbol): boolean {\n\t\tconst reg = this._messageTypes.get(t);\n\t\treturn reg != null ? reg.wireCrossing : true;\n\t}\n\n\t/** Convenience inverse of {@link isWireCrossing}. */\n\tisLocalOnly(t: symbol): boolean {\n\t\treturn !this.isWireCrossing(t);\n\t}\n\n\t/**\n\t * Whether `t` is forwarded to meta companions by `Graph.signal`. Defaults\n\t * to `true` for unknowns (forward-compat — new types pass through meta by\n\t * default; opt-in filter via `registerMessageType({metaPassthrough: false})`).\n\t */\n\tisMetaPassthrough(t: symbol): boolean {\n\t\tconst reg = this._messageTypes.get(t);\n\t\treturn reg != null ? reg.metaPassthrough : true;\n\t}\n\n\t/** Whether `t` is a registered (built-in or custom) type. */\n\tisKnownMessageType(t: symbol): boolean {\n\t\treturn this._messageTypes.has(t);\n\t}\n\n\t// --- Codec registry (writes require unfrozen; reads are free lookups) ---\n\n\t/**\n\t * Register a graph codec by `codec.name`. Used by the envelope-based\n\t * `graph.snapshot({format: \"bytes\", codec: name})` path and\n\t * `Graph.decode(bytes)` auto-dispatch. Must be called before any node\n\t * bound to this config is created — otherwise throws.\n\t *\n\t * Re-registering the same name overwrites, so user codecs can shadow\n\t * built-in ones before freeze (e.g., to swap a zstd-wrapped dag-cbor in\n\t * for `\"dag-cbor\"`).\n\t */\n\tregisterCodec<T extends { readonly name: string; readonly version: number }>(codec: T): this {\n\t\tthis._assertUnfrozen();\n\t\tthis._codecs.set(codec.name, codec);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Resolve a registered codec by name. Returns `undefined` for unknown\n\t * names. Typed callers cast to their concrete codec interface (e.g.,\n\t * `config.lookupCodec<GraphCodec>(\"json\")`) — this method stays\n\t * layer-pure (no import of graph-layer types into `core/`).\n\t */\n\tlookupCodec<T = { readonly name: string; readonly version: number }>(\n\t\tname: string,\n\t): T | undefined {\n\t\treturn this._codecs.get(name) as T | undefined;\n\t}\n\n\t/** @internal Used by tests and dev tooling — check freeze state without triggering it. */\n\t_isFrozen(): boolean {\n\t\treturn this._frozen;\n\t}\n\n\tprivate _assertUnfrozen(): void {\n\t\tif (this._frozen) {\n\t\t\tthrow new Error(\n\t\t\t\t\"GraphReFlyConfig is frozen: a node has already captured this config. \" +\n\t\t\t\t\t\"Register custom types and set hooks before creating any node.\",\n\t\t\t);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Built-in registration\n// ---------------------------------------------------------------------------\n\n/**\n * Register the 10 built-in message types on a fresh config. Called by\n * `node.ts` when it constructs `defaultConfig` and by test code / advanced\n * users after `new GraphReFlyConfig(...)`.\n */\nexport function registerBuiltins(cfg: GraphReFlyConfig): void {\n\tcfg.registerMessageType(START, { tier: 0, wireCrossing: false });\n\tcfg.registerMessageType(DIRTY, { tier: 1, wireCrossing: false });\n\t// INVALIDATE, COMPLETE, ERROR, TEARDOWN do NOT pass through to meta\n\t// companions via Graph.signal (spec §2.3). Meta still sees them via the\n\t// primary's own down-cascade.\n\tcfg.registerMessageType(INVALIDATE, {\n\t\ttier: 1,\n\t\twireCrossing: false,\n\t\tmetaPassthrough: false,\n\t});\n\tcfg.registerMessageType(PAUSE, { tier: 2, wireCrossing: false });\n\tcfg.registerMessageType(RESUME, { tier: 2, wireCrossing: false });\n\tcfg.registerMessageType(DATA, { tier: 3, wireCrossing: true });\n\tcfg.registerMessageType(RESOLVED, { tier: 3, wireCrossing: true });\n\tcfg.registerMessageType(COMPLETE, {\n\t\ttier: 4,\n\t\twireCrossing: true,\n\t\tmetaPassthrough: false,\n\t});\n\tcfg.registerMessageType(ERROR, {\n\t\ttier: 4,\n\t\twireCrossing: true,\n\t\tmetaPassthrough: false,\n\t});\n\tcfg.registerMessageType(TEARDOWN, {\n\t\ttier: 5,\n\t\twireCrossing: true,\n\t\tmetaPassthrough: false,\n\t});\n}\n","import type { Actor } from \"./actor.js\";\n\n/**\n * Actions checked by {@link NodeGuard}. `write` covers both {@link Node.down} and\n * {@link Node.up} today; finer-grained strings may be added later (e.g. `\"write.data\"`).\n */\nexport type GuardAction = \"write\" | \"signal\" | \"observe\" | (string & {});\n\nexport type NodeGuard = (actor: Actor, action: GuardAction) => boolean;\n\nexport type GuardDeniedDetails = {\n\tactor: Actor;\n\taction: GuardAction;\n\t/** Registry or options name when known */\n\tnodeName?: string;\n};\n\n/**\n * Thrown when a {@link NodeGuard} denies an action for a given actor.\n *\n * Carries the rejected `actor`, `action`, and optional `nodeName` for diagnostic\n * messages and middleware error handling.\n *\n * @example\n * ```ts\n * import { GuardDenied, policy } from \"@graphrefly/graphrefly-ts\";\n *\n * const guard = policy((allow) => { allow(\"observe\"); });\n * try {\n * if (!guard({ type: \"llm\", id: \"agent-1\" }, \"write\")) {\n * throw new GuardDenied(\n * { actor: { type: \"llm\", id: \"agent-1\" }, action: \"write\", nodeName: \"userInput\" },\n * );\n * }\n * } catch (e) {\n * if (e instanceof GuardDenied) console.error(e.action, e.actor.type); // \"write\" \"llm\"\n * }\n * ```\n */\nexport class GuardDenied extends Error {\n\treadonly actor: Actor;\n\treadonly action: GuardAction;\n\treadonly nodeName?: string;\n\n\t/**\n\t * @param details - Actor, action, and optional node name for the denial.\n\t * @param message - Optional override for the default error message.\n\t */\n\tconstructor(details: GuardDeniedDetails, message?: string) {\n\t\tsuper(\n\t\t\tmessage ??\n\t\t\t\t`GuardDenied: action \"${String(details.action)}\" denied for actor type \"${String(details.actor.type)}\"`,\n\t\t);\n\t\tthis.name = \"GuardDenied\";\n\t\tthis.actor = details.actor;\n\t\tthis.action = details.action;\n\t\tthis.nodeName = details.nodeName;\n\t}\n\n\t/** Qualified registry path when known (roadmap diagnostics: same as {@link nodeName}). */\n\tget node(): string | undefined {\n\t\treturn this.nodeName;\n\t}\n}\n\ntype Where = (actor: Actor) => boolean;\n\ntype Rule = {\n\tkind: \"allow\" | \"deny\";\n\tactions: Set<GuardAction>;\n\twhere: Where;\n};\n\nfunction normalizeActions(action: GuardAction | readonly GuardAction[]): GuardAction[] {\n\tif (Array.isArray(action)) {\n\t\treturn [...action];\n\t}\n\treturn [action as GuardAction];\n}\n\nfunction matchesActions(set: Set<GuardAction>, action: GuardAction): boolean {\n\treturn set.has(action) || set.has(\"*\" as GuardAction);\n}\n\nexport type PolicyAllow = (\n\taction: GuardAction | readonly GuardAction[],\n\topts?: { where?: Where },\n) => void;\n\nexport type PolicyDeny = (\n\taction: GuardAction | readonly GuardAction[],\n\topts?: { where?: Where },\n) => void;\n\nexport type PolicyRuleData = {\n\teffect: \"allow\" | \"deny\";\n\taction: GuardAction | readonly GuardAction[];\n\tactorType?: string | readonly string[];\n\tactorId?: string | readonly string[];\n\tclaims?: Record<string, unknown>;\n};\n\n/**\n * Declarative guard builder. Precedence: any matching **deny** blocks even if an allow also matches.\n * If no rule matches, the guard returns `false` (deny-by-default). Aligned with graphrefly-py `policy()`.\n *\n * @param build - Callback that registers `allow(...)` / `deny(...)` rules in order.\n * @returns A `NodeGuard` for use as `node({ guard })`.\n *\n * @example\n * ```ts\n * const guard = policy((allow, deny) => {\n * allow(\"observe\");\n * deny(\"write\", { where: (a) => a.type === \"llm\" });\n * });\n * ```\n */\nexport function policy(build: (allow: PolicyAllow, deny: PolicyDeny) => void): NodeGuard {\n\tconst rules: Rule[] = [];\n\tconst allow: PolicyAllow = (action, opts) => {\n\t\trules.push({\n\t\t\tkind: \"allow\",\n\t\t\tactions: new Set(normalizeActions(action)),\n\t\t\twhere: opts?.where ?? (() => true),\n\t\t});\n\t};\n\tconst deny: PolicyDeny = (action, opts) => {\n\t\trules.push({\n\t\t\tkind: \"deny\",\n\t\t\tactions: new Set(normalizeActions(action)),\n\t\t\twhere: opts?.where ?? (() => true),\n\t\t});\n\t};\n\tbuild(allow, deny);\n\treturn (actor, action) => {\n\t\tlet denied = false;\n\t\tlet allowed = false;\n\t\tfor (const r of rules) {\n\t\t\tif (!matchesActions(r.actions, action)) continue;\n\t\t\tif (!r.where(actor)) continue;\n\t\t\tif (r.kind === \"deny\") {\n\t\t\t\tdenied = true;\n\t\t\t} else {\n\t\t\t\tallowed = true;\n\t\t\t}\n\t\t}\n\t\tif (denied) return false;\n\t\treturn allowed;\n\t};\n}\n\n/**\n * Rebuild a declarative guard from persisted policy data (snapshot-safe).\n *\n * Rules are deny-overrides, same semantics as {@link policy}.\n */\nexport function policyFromRules(rules: readonly PolicyRuleData[]): NodeGuard {\n\treturn policy((allow, deny) => {\n\t\tfor (const rule of rules) {\n\t\t\tconst actorTypes =\n\t\t\t\trule.actorType == null\n\t\t\t\t\t? null\n\t\t\t\t\t: new Set(Array.isArray(rule.actorType) ? rule.actorType : [rule.actorType]);\n\t\t\tconst actorIds =\n\t\t\t\trule.actorId == null\n\t\t\t\t\t? null\n\t\t\t\t\t: new Set(Array.isArray(rule.actorId) ? rule.actorId : [rule.actorId]);\n\t\t\tconst claimEntries = Object.entries(rule.claims ?? {});\n\t\t\tconst where: Where = (actor) => {\n\t\t\t\tif (actorTypes !== null && !actorTypes.has(String(actor.type))) return false;\n\t\t\t\tif (actorIds !== null && !actorIds.has(String(actor.id ?? \"\"))) return false;\n\t\t\t\tfor (const [key, value] of claimEntries) {\n\t\t\t\t\tif ((actor as Record<string, unknown>)[key] !== value) return false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\t\t\tif (rule.effect === \"deny\") {\n\t\t\t\tdeny(rule.action, { where });\n\t\t\t} else {\n\t\t\t\tallow(rule.action, { where });\n\t\t\t}\n\t\t}\n\t});\n}\n\nconst STANDARD_WRITE_TYPES = [\"human\", \"llm\", \"wallet\", \"system\"] as const;\n\n/**\n * Derives a best-effort `meta.access` hint string by probing `guard` with the\n * standard actor types `human`, `llm`, `wallet`, `system` for the `\"write\"` action\n * (roadmap 1.5). Aligned with graphrefly-py `access_hint_for_guard`.\n *\n * @param guard - Guard function to probe (typically from {@link policy}).\n * @returns `\"restricted\"` when no standard type is allowed; `\"both\"` when both\n * `human` and `llm` are allowed (plus optionally `system`); the single allowed\n * type name when only one passes; or a `\"+\"` joined list otherwise.\n *\n * @example\n * ```ts\n * import { policy, accessHintForGuard } from \"@graphrefly/graphrefly-ts\";\n *\n * const guardBoth = policy((allow) => { allow(\"write\"); });\n * accessHintForGuard(guardBoth); // \"both\"\n *\n * const guardHuman = policy((allow) => {\n * allow(\"write\", { where: (a) => a.type === \"human\" });\n * });\n * accessHintForGuard(guardHuman); // \"human\"\n * ```\n */\nexport function accessHintForGuard(guard: NodeGuard): string {\n\tconst allowed = STANDARD_WRITE_TYPES.filter((t) => guard({ type: t, id: \"\" }, \"write\"));\n\tif (allowed.length === 0) return \"restricted\";\n\tif (\n\t\tallowed.includes(\"human\") &&\n\t\tallowed.includes(\"llm\") &&\n\t\tallowed.every((t) => t === \"human\" || t === \"llm\" || t === \"system\")\n\t) {\n\t\treturn \"both\";\n\t}\n\tif (allowed.length === 1) return allowed[0];\n\treturn allowed.join(\"+\");\n}\n","/**\n * GraphCodec — pluggable serialization for graph snapshots (Phase 8.6).\n *\n * Design reference: `archive/docs/SESSION-serialization-memory-footprint.md`\n *\n * The codec interface decouples snapshot format from graph internals.\n * Default is JSON (current behavior). DAG-CBOR and compressed variants\n * ship as optional codecs. FlatBuffers/Arrow for advanced tiers.\n *\n * Tiered representation:\n * HOT — JS objects (live propagation, no codec involved)\n * WARM — DAG-CBOR in-memory buffer (lazy hydration, delta checkpoints)\n * COLD — Arrow/Parquet (bulk storage, ML pipelines, archival)\n * PEEK — FlatBuffers (zero-copy read from dormant graph)\n *\n * Wire-protocol envelope (v1):\n *\n * [envelope_v=1: u8][name_len: u8][name: utf8][codec_v: u16 BE][payload: rest]\n *\n * `graph.snapshot({format: \"bytes\", codec: name})` wraps the codec's\n * `encode` output in this envelope; `Graph.decode(bytes)` auto-dispatches\n * via the config's codec registry — no out-of-band content-type needed.\n */\n\nimport type { GraphReFlyConfig } from \"../core/config.js\";\nimport type { GraphCheckpointRecord, GraphPersistSnapshot } from \"./graph.js\";\n\n// ---------------------------------------------------------------------------\n// Core codec interface\n// ---------------------------------------------------------------------------\n\n/**\n * Encode/decode graph snapshots to/from binary.\n *\n * Implementations must be deterministic: `encode(x)` always produces the\n * same bytes for the same input. This is critical for CID computation (V1)\n * and snapshot hash-comparison.\n */\nexport interface GraphCodec {\n\t/** Human-readable name; used as the lookup key in the envelope and config registry. */\n\treadonly name: string;\n\n\t/**\n\t * Codec version. Bumps on breaking wire format changes; `decode` receives\n\t * this via the envelope so codecs can dispatch on historical layouts.\n\t * Must fit in a `u16` (0–65535).\n\t */\n\treadonly version: number;\n\n\t/** MIME-like content type identifier (e.g. `\"application/dag-cbor+zstd\"`). */\n\treadonly contentType: string;\n\n\t/** Encode a snapshot to binary. */\n\tencode(snapshot: GraphPersistSnapshot): Uint8Array;\n\n\t/**\n\t * Decode binary back to a snapshot.\n\t *\n\t * `codecVersion` is the version that produced `buffer` (read from the\n\t * envelope). Omit when the caller is sure of the version (tests, one-shot\n\t * round-trips). Codecs that support multiple historical layouts dispatch\n\t * on this value.\n\t *\n\t * For lazy codecs, this may return a proxy that decodes nodes on access\n\t * (see {@link LazyGraphCodec}).\n\t */\n\tdecode(buffer: Uint8Array, codecVersion?: number): GraphPersistSnapshot;\n}\n\n/**\n * Extended codec that supports lazy (on-demand) node decoding.\n *\n * `decodeLazy` returns a snapshot where `nodes` is a Proxy — individual\n * nodes are decoded only when accessed. This enables near-zero cold-start\n * for large graphs (decode envelope + topology, skip node values until read).\n */\nexport interface LazyGraphCodec extends GraphCodec {\n\t/** Decode envelope and topology; defer node value decoding to access time. */\n\tdecodeLazy(buffer: Uint8Array, codecVersion?: number): GraphPersistSnapshot;\n}\n\n// ---------------------------------------------------------------------------\n// Delta checkpoint types (requires V0 — Phase 6.0)\n// ---------------------------------------------------------------------------\n\n/**\n * WAL entry. Unified with {@link GraphCheckpointRecord} — every record\n * already carries `mode` / `seq` / `timestamp_ns` / `format_version`, so the\n * WAL is just an ordered list of records. `replayWAL` walks this list.\n */\nexport type WALEntry = GraphCheckpointRecord;\n\n// ---------------------------------------------------------------------------\n// Eviction policy (dormant subgraph management)\n// ---------------------------------------------------------------------------\n\n/**\n * Policy for evicting dormant subgraphs to reduce memory.\n *\n * When a subgraph hasn't propagated for `idleTimeoutMs`, it is serialized\n * using the graph's codec and JS objects are released. Re-hydrated on next\n * access (read, propagation, describe).\n */\nexport interface EvictionPolicy {\n\t/** Milliseconds of inactivity before eviction. */\n\tidleTimeoutMs: number;\n\t/** Codec to use for serializing evicted subgraphs (default: graph's codec). */\n\tcodec?: GraphCodec;\n}\n\n/** Metadata about an evicted subgraph, exposed via describe(). */\nexport interface EvictedSubgraphInfo {\n\t/** True if currently evicted (serialized, JS objects released). */\n\tevicted: true;\n\t/** Wall-clock ns of last propagation before eviction. */\n\tlastActiveNs: bigint;\n\t/** Size of serialized buffer in bytes. */\n\tserializedBytes: number;\n\t/** Codec used for serialization. */\n\tcodecName: string;\n}\n\n// ---------------------------------------------------------------------------\n// JSON codec (default — wraps current behavior)\n// ---------------------------------------------------------------------------\n\n/**\n * Default JSON codec. Wraps `JSON.stringify`/`JSON.parse` with deterministic\n * key ordering (matching current `snapshot()` behavior).\n */\nexport const JsonCodec: GraphCodec = {\n\tname: \"json\",\n\tversion: 1,\n\tcontentType: \"application/json\",\n\n\tencode(snapshot: GraphPersistSnapshot): Uint8Array {\n\t\t// Deterministic: snapshot() already sorts keys.\n\t\tconst json = JSON.stringify(snapshot);\n\t\treturn new TextEncoder().encode(json);\n\t},\n\n\tdecode(buffer: Uint8Array, _codecVersion?: number): GraphPersistSnapshot {\n\t\tconst json = new TextDecoder().decode(buffer);\n\t\treturn JSON.parse(json) as GraphPersistSnapshot;\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// DAG-CBOR codec (factory — requires @ipld/dag-cbor DI)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a DAG-CBOR codec.\n *\n * Requires `@ipld/dag-cbor` as a peer dependency. ~40-50% smaller than JSON,\n * deterministic encoding (required for V1 CID), CID links as native type.\n *\n * @example\n * ```ts\n * import * as dagCbor from \"@ipld/dag-cbor\";\n * const codec = createDagCborCodec(dagCbor);\n * config.registerCodec(codec);\n * const bytes = graph.snapshot({ format: \"bytes\", codec: \"dag-cbor\" });\n * ```\n */\nexport function createDagCborCodec(dagCbor: {\n\tencode: (value: unknown) => Uint8Array;\n\tdecode: (bytes: Uint8Array) => unknown;\n}): GraphCodec {\n\treturn {\n\t\tname: \"dag-cbor\",\n\t\tversion: 1,\n\t\tcontentType: \"application/dag-cbor\",\n\t\tencode: (snapshot) => dagCbor.encode(snapshot),\n\t\tdecode: (buffer, _codecVersion) => dagCbor.decode(buffer) as GraphPersistSnapshot,\n\t};\n}\n\n/**\n * Create a DAG-CBOR + zstd codec. ~80-90% smaller than JSON.\n *\n * Requires `@ipld/dag-cbor` and a zstd implementation (e.g. `fzstd` for\n * browser, `node:zlib` for Node.js).\n *\n * @example\n * ```ts\n * import * as dagCbor from \"@ipld/dag-cbor\";\n * import { compressSync, decompressSync } from \"fzstd\";\n * const codec = createDagCborZstdCodec(dagCbor, { compressSync, decompressSync });\n * config.registerCodec(codec);\n * ```\n */\nexport function createDagCborZstdCodec(\n\tdagCbor: {\n\t\tencode: (value: unknown) => Uint8Array;\n\t\tdecode: (bytes: Uint8Array) => unknown;\n\t},\n\tzstd: {\n\t\tcompressSync: (data: Uint8Array) => Uint8Array;\n\t\tdecompressSync: (data: Uint8Array) => Uint8Array;\n\t},\n): GraphCodec {\n\treturn {\n\t\tname: \"dag-cbor-zstd\",\n\t\tversion: 1,\n\t\tcontentType: \"application/dag-cbor+zstd\",\n\t\tencode: (snapshot) => zstd.compressSync(dagCbor.encode(snapshot)),\n\t\tdecode: (buffer, _codecVersion) =>\n\t\t\tdagCbor.decode(zstd.decompressSync(buffer)) as GraphPersistSnapshot,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Envelope (v1) — self-describing codec metadata prepended to payload bytes\n// ---------------------------------------------------------------------------\n\n/** Current envelope format version. Bump on breaking layout changes. */\nexport const ENVELOPE_VERSION = 1;\n\nconst ENVELOPE_MIN_LEN = 4; // env_v(1) + name_len(1) + codec_v(2) + name/payload(≥0)\n\n/**\n * Prepend the v1 envelope to `payload` identifying `codec`. The resulting\n * bytes are self-describing — any caller with access to the registering\n * {@link GraphReFlyConfig} can {@link decodeEnvelope} without knowing the\n * codec up front.\n *\n * Layout:\n * `[envelope_v=1: u8][name_len: u8][name: utf8][codec_v: u16 BE][payload: rest]`\n *\n * @throws If `codec.name` encodes to more than 255 UTF-8 bytes or\n * `codec.version` doesn't fit in a u16.\n */\nexport function encodeEnvelope(\n\tcodec: Pick<GraphCodec, \"name\" | \"version\">,\n\tpayload: Uint8Array,\n): Uint8Array {\n\tconst nameBytes = new TextEncoder().encode(codec.name);\n\tif (nameBytes.length === 0 || nameBytes.length > 255) {\n\t\tthrow new Error(\n\t\t\t`encodeEnvelope: codec name \"${codec.name}\" encodes to ${nameBytes.length} bytes (must be 1–255)`,\n\t\t);\n\t}\n\tconst cv = codec.version;\n\tif (!Number.isInteger(cv) || cv < 0 || cv > 0xffff) {\n\t\tthrow new Error(\n\t\t\t`encodeEnvelope: codec.version ${cv} out of u16 range (expected integer 0–65535)`,\n\t\t);\n\t}\n\t// Guard against RangeError-on-alloc for very large payloads — we need\n\t// `Number.isSafeInteger` math on `1 + 1 + nameBytes.length + 2 +\n\t// payload.length` and the resulting Uint8Array must fit within the\n\t// platform limit (2³² − 1 bytes on 64-bit JS engines).\n\tconst totalLen = 1 + 1 + nameBytes.length + 2 + payload.length;\n\tif (totalLen > 0xffffffff) {\n\t\tthrow new Error(\n\t\t\t`encodeEnvelope: total envelope size ${totalLen} exceeds 2^32-1 bytes (payload ${payload.length} bytes)`,\n\t\t);\n\t}\n\tconst out = new Uint8Array(totalLen);\n\tlet i = 0;\n\tout[i++] = ENVELOPE_VERSION;\n\tout[i++] = nameBytes.length;\n\tout.set(nameBytes, i);\n\ti += nameBytes.length;\n\tout[i++] = (cv >>> 8) & 0xff;\n\tout[i++] = cv & 0xff;\n\tout.set(payload, i);\n\treturn out;\n}\n\n/**\n * Inverse of {@link encodeEnvelope}. Reads the header, resolves the codec\n * via `config.lookupCodec(name)`, and returns the codec + its version + the\n * inner payload slice. The caller feeds `payload` to `codec.decode(payload,\n * codecVersion)` — or uses {@link Graph.decode} which does both steps.\n *\n * @throws If the envelope is truncated, the version is unsupported, or the\n * named codec isn't registered on `config`.\n */\nexport function decodeEnvelope(\n\tbytes: Uint8Array,\n\tconfig: GraphReFlyConfig,\n): { codec: GraphCodec; codecVersion: number; payload: Uint8Array } {\n\tif (bytes.length < ENVELOPE_MIN_LEN) {\n\t\tthrow new Error(`decodeEnvelope: bytes too short (${bytes.length} < ${ENVELOPE_MIN_LEN})`);\n\t}\n\tlet i = 0;\n\tconst envVersion = bytes[i++]!;\n\tif (envVersion !== ENVELOPE_VERSION) {\n\t\tthrow new Error(\n\t\t\t`decodeEnvelope: unsupported envelope version ${envVersion} (expected ${ENVELOPE_VERSION})`,\n\t\t);\n\t}\n\tconst nameLen = bytes[i++]!;\n\tif (nameLen === 0) {\n\t\tthrow new Error(\"decodeEnvelope: name_len must be >= 1\");\n\t}\n\tif (i + nameLen + 2 > bytes.length) {\n\t\tthrow new Error(\n\t\t\t`decodeEnvelope: envelope truncated (need ${i + nameLen + 2} bytes, have ${bytes.length})`,\n\t\t);\n\t}\n\tconst name = new TextDecoder().decode(bytes.subarray(i, i + nameLen));\n\ti += nameLen;\n\tconst codecVersion = ((bytes[i]! << 8) | bytes[i + 1]!) >>> 0;\n\ti += 2;\n\tconst payload = bytes.subarray(i);\n\tconst codec = config.lookupCodec<GraphCodec>(name);\n\tif (codec == null) {\n\t\tthrow new Error(\n\t\t\t`decodeEnvelope: codec \"${name}\" not registered (envelope codec_v=${codecVersion})`,\n\t\t);\n\t}\n\treturn { codec, codecVersion, payload };\n}\n\n/**\n * Register the built-in {@link JsonCodec} on a config. Called once on\n * `defaultConfig` at module load so `graph.snapshot({format: \"bytes\", codec:\n * \"json\"})` works out of the box. Test / isolated configs should call this\n * manually before the first node is created.\n */\nexport function registerBuiltinCodecs(config: GraphReFlyConfig): void {\n\tconfig.registerCodec(JsonCodec);\n}\n\n// ---------------------------------------------------------------------------\n// WAL helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Reconstruct a snapshot from a WAL (sequence of {@link GraphCheckpointRecord}s).\n *\n * - Must start with a `\"full\"` record carrying a baseline snapshot — that's\n * the anchor {@link Graph.attachStorage} always emits on the first flush\n * of any tier (and every `compactEvery`-th flush thereafter).\n * - Subsequent `\"full\"` entries (compaction points) **replace** the result\n * wholesale.\n * - `\"diff\"` entries roll forward by applying the structural diff —\n * added nodes (via `nodesAddedFull`), removed nodes, and changed fields\n * are reflected into the accumulated snapshot.\n *\n * Validates monotonic `seq` progression across entries.\n */\nexport function replayWAL(entries: readonly WALEntry[]): GraphPersistSnapshot {\n\tif (entries.length === 0) {\n\t\tthrow new Error(\"WAL is empty — need at least one full snapshot\");\n\t}\n\n\tconst first = entries[0]!;\n\tif (first.mode !== \"full\") {\n\t\tthrow new Error(\"WAL must start with a full record carrying a baseline snapshot\");\n\t}\n\n\tlet result: GraphPersistSnapshot = JSON.parse(JSON.stringify(first.snapshot));\n\tlet prevSeq: number = first.seq;\n\n\tfor (let i = 1; i < entries.length; i++) {\n\t\tconst entry = entries[i]!;\n\t\tif (entry.seq <= prevSeq) {\n\t\t\tthrow new Error(\n\t\t\t\t`WAL chain broken at index ${i}: seq=${entry.seq} must exceed prev seq=${prevSeq}`,\n\t\t\t);\n\t\t}\n\n\t\tif (entry.mode === \"full\") {\n\t\t\t// Replace baseline wholesale (Unit 23 D fix — Object.assign left\n\t\t\t// stale keys from pre-compact state visible).\n\t\t\tresult = JSON.parse(JSON.stringify(entry.snapshot));\n\t\t\tprevSeq = entry.seq;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// mode === \"diff\": apply structural diff to the accumulated snapshot.\n\t\tconst diff = entry.diff;\n\t\t// Apply removes first so a path reused across a remove+add in a single\n\t\t// diff lands on the `nodesAddedFull` slice rather than being wiped.\n\t\tfor (const path of diff.nodesRemoved) {\n\t\t\tdelete result.nodes[path];\n\t\t}\n\t\t// Reinstate added nodes from the full-slice payload carried by\n\t\t// GraphWALDiff. Deep-clone so later mutations don't alias the WAL\n\t\t// entry's source slice.\n\t\tconst addedFull = diff.nodesAddedFull;\n\t\tif (addedFull != null) {\n\t\t\tfor (const [path, slice] of Object.entries(addedFull)) {\n\t\t\t\tresult.nodes[path] = JSON.parse(JSON.stringify(slice));\n\t\t\t}\n\t\t}\n\t\tfor (const change of diff.nodesChanged) {\n\t\t\tconst existing = result.nodes[change.path];\n\t\t\tif (existing == null) continue;\n\t\t\t(existing as Record<string, unknown>)[change.field] = change.to;\n\t\t}\n\n\t\t// Edges are derived from node `_deps` at restore time (Unit 7) — no\n\t\t// separate edge-patch pass.\n\t\tprevSeq = entry.seq;\n\t}\n\n\treturn result;\n}\n","/**\n * Node versioning — GRAPHREFLY-SPEC §7.\n *\n * Progressive, optional versioning for node identity and change tracking.\n *\n * - **V0**: `id` + `version` — identity & change detection (~16 bytes overhead)\n * - **V1**: + `cid` + `prev` — content addressing & linked history (~60 bytes overhead)\n *\n * **Lifecycle notes:**\n * - Version advances only on DATA (not RESOLVED, INVALIDATE, or TEARDOWN).\n * - `resetOnTeardown` clears the cached value but does NOT reset versioning state.\n * After teardown, `v.cid` still reflects the last DATA value, not the cleared cache.\n * The invariant `hash(node.cache) === v.cid` only holds in `settled`/`resolved` status.\n * - Resubscribable nodes preserve versioning across subscription lifetimes (monotonic counter).\n */\n\n// Runtime-agnostic — no `node:crypto` import. `randomUUID` comes from Web\n// Crypto (`globalThis.crypto.randomUUID()`), available in Node 14.17+,\n// browsers, Deno, Bun, and Cloudflare Workers. The default content hash is a\n// vendored sync SHA-256 (see `sha256Hex` below) so versioning stays callable\n// from any runtime — `crypto.subtle.digest` is async and can't back a\n// synchronous `defaultHash`.\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** V0: identity + monotonic version counter. */\nexport type V0 = {\n\treadonly id: string;\n\tversion: number;\n};\n\n/** V1: V0 + content-addressed identifier + previous cid link. */\nexport type V1 = V0 & {\n\tcid: string;\n\tprev: string | null;\n};\n\n/** Union of all versioning info shapes. */\nexport type NodeVersionInfo = V0 | V1;\n\n/** Supported versioning levels (extensible to 2, 3 later). */\nexport type VersioningLevel = 0 | 1;\n\n/** Function that hashes a value to a hex string (for V1 cid). */\nexport type HashFn = (value: unknown) => string;\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface VersioningOptions {\n\t/** Override auto-generated id. */\n\tid?: string;\n\t/** Custom hash function for V1 cid (default: SHA-256 truncated to 16 hex chars). */\n\thash?: HashFn;\n}\n\n// ---------------------------------------------------------------------------\n// Default hash\n// ---------------------------------------------------------------------------\n\n/**\n * Canonicalize a value for deterministic cross-language hashing.\n *\n * - Integer-valued floats normalize to integer strings (`1.0` → `1`).\n * - `NaN`, `Infinity`, `-Infinity` are rejected (no JSON equivalent).\n * - `undefined` normalizes to `null`.\n * - Object keys are sorted lexicographically.\n *\n * This ensures TS `JSON.stringify` and Python `json.dumps(sort_keys=True)`\n * produce identical output for the same logical value.\n */\nexport function canonicalizeForHash(value: unknown): unknown {\n\tif (value === undefined) return null;\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isFinite(value)) {\n\t\t\tthrow new TypeError(`Cannot hash non-finite number: ${value}`);\n\t\t}\n\t\tif (Number.isInteger(value) && !Number.isSafeInteger(value)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`Cannot hash integer outside safe range (|n| > 2^53-1): ${value}. ` +\n\t\t\t\t\t\"Cross-language cid parity is not guaranteed for unsafe integers.\",\n\t\t\t);\n\t\t}\n\t\treturn value;\n\t}\n\tif (typeof value === \"string\" || typeof value === \"boolean\" || value === null) {\n\t\treturn value;\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.map(canonicalizeForHash);\n\t}\n\tif (typeof value === \"object\" && value !== null) {\n\t\tconst sorted: Record<string, unknown> = {};\n\t\tfor (const k of Object.keys(value as Record<string, unknown>).sort()) {\n\t\t\tsorted[k] = canonicalizeForHash((value as Record<string, unknown>)[k]);\n\t\t}\n\t\treturn sorted;\n\t}\n\t// Fallback: coerce to null (bigint, symbol, function)\n\treturn null;\n}\n\n// SHA-256 round constants (FIPS 180-4).\nconst SHA256_K = /* @__PURE__ */ new Uint32Array([\n\t0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n\t0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n\t0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n\t0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n\t0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n\t0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n\t0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n\t0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst UTF8_ENCODER = /* @__PURE__ */ new TextEncoder();\n\n/**\n * Sync SHA-256 of a UTF-8 string, returned as a lowercase hex digest. Matches\n * Node `crypto.createHash(\"sha256\").update(msg).digest(\"hex\")` byte-for-byte.\n *\n * Runtime-agnostic (no `node:crypto`, no `crypto.subtle`). Small enough to\n * inline rather than pulling a dependency; called only from `defaultHash`,\n * which runs once per DATA on versioned nodes, so per-call allocation is\n * acceptable. Callers that need a faster path override via\n * `NodeOptions.versioningHash`.\n */\nfunction sha256Hex(msg: string): string {\n\tconst bytes = UTF8_ENCODER.encode(msg);\n\tconst msgLen = bytes.length;\n\tconst bitLen = msgLen * 8;\n\t// Pad to multiple of 64: 0x80 byte + zeros + 8-byte big-endian bit length.\n\tconst totalLen = (msgLen + 9 + 63) & ~63;\n\tconst padded = new Uint8Array(totalLen);\n\tpadded.set(bytes);\n\tpadded[msgLen] = 0x80;\n\tconst dv = new DataView(padded.buffer);\n\t// Bit length as big-endian 64-bit int. JS numbers are 53-bit safe, so we\n\t// split into two 32-bit halves; messages up to 2^53 bits are supported.\n\tdv.setUint32(totalLen - 4, bitLen >>> 0, false);\n\tdv.setUint32(totalLen - 8, Math.floor(bitLen / 0x100000000) >>> 0, false);\n\n\t// Initial hash values (first 32 bits of fractional parts of sqrt of first 8 primes).\n\tlet h0 = 0x6a09e667;\n\tlet h1 = 0xbb67ae85;\n\tlet h2 = 0x3c6ef372;\n\tlet h3 = 0xa54ff53a;\n\tlet h4 = 0x510e527f;\n\tlet h5 = 0x9b05688c;\n\tlet h6 = 0x1f83d9ab;\n\tlet h7 = 0x5be0cd19;\n\n\tconst W = new Uint32Array(64);\n\tconst rotr = (x: number, n: number): number => (x >>> n) | (x << (32 - n));\n\n\tfor (let off = 0; off < totalLen; off += 64) {\n\t\tfor (let i = 0; i < 16; i++) W[i] = dv.getUint32(off + i * 4, false);\n\t\tfor (let i = 16; i < 64; i++) {\n\t\t\tconst w15 = W[i - 15];\n\t\t\tconst w2 = W[i - 2];\n\t\t\tconst s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);\n\t\t\tconst s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);\n\t\t\tW[i] = (W[i - 16] + s0 + W[i - 7] + s1) >>> 0;\n\t\t}\n\n\t\tlet a = h0;\n\t\tlet b = h1;\n\t\tlet c = h2;\n\t\tlet d = h3;\n\t\tlet e = h4;\n\t\tlet f = h5;\n\t\tlet g = h6;\n\t\tlet h = h7;\n\n\t\tfor (let i = 0; i < 64; i++) {\n\t\t\tconst S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n\t\t\tconst ch = (e & f) ^ (~e & g);\n\t\t\tconst t1 = (h + S1 + ch + SHA256_K[i] + W[i]) >>> 0;\n\t\t\tconst S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n\t\t\tconst mj = (a & b) ^ (a & c) ^ (b & c);\n\t\t\tconst t2 = (S0 + mj) >>> 0;\n\t\t\th = g;\n\t\t\tg = f;\n\t\t\tf = e;\n\t\t\te = (d + t1) >>> 0;\n\t\t\td = c;\n\t\t\tc = b;\n\t\t\tb = a;\n\t\t\ta = (t1 + t2) >>> 0;\n\t\t}\n\n\t\th0 = (h0 + a) >>> 0;\n\t\th1 = (h1 + b) >>> 0;\n\t\th2 = (h2 + c) >>> 0;\n\t\th3 = (h3 + d) >>> 0;\n\t\th4 = (h4 + e) >>> 0;\n\t\th5 = (h5 + f) >>> 0;\n\t\th6 = (h6 + g) >>> 0;\n\t\th7 = (h7 + h) >>> 0;\n\t}\n\n\tconst toHex = (x: number): string => x.toString(16).padStart(8, \"0\");\n\treturn (\n\t\ttoHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4) + toHex(h5) + toHex(h6) + toHex(h7)\n\t);\n}\n\n/**\n * Default content hash: SHA-256 of deterministic JSON, truncated to 16 hex\n * chars (~64-bit). Uses {@link canonicalizeForHash} for cross-language parity\n * with Python `default_hash`.\n */\nexport function defaultHash(value: unknown): string {\n\tconst canonical = canonicalizeForHash(value ?? null);\n\tconst json = JSON.stringify(canonical);\n\treturn sha256Hex(json).slice(0, 16);\n}\n\n/**\n * Cross-runtime UUID generator. Uses Web Crypto (`globalThis.crypto.randomUUID`)\n * when available. Falls back to a tiny `Math.random`-seeded RFC 4122 v4\n * generator for environments that omit `crypto.randomUUID` — identity only,\n * not cryptographic.\n */\nfunction randomUuid(): string {\n\tconst c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n\tif (c?.randomUUID) return c.randomUUID();\n\t// Fallback (extremely rare — only hits on very old runtimes that expose no\n\t// Web Crypto at all). Not cryptographically strong.\n\tconst r = () =>\n\t\tMath.floor(Math.random() * 0x100000000)\n\t\t\t.toString(16)\n\t\t\t.padStart(8, \"0\");\n\tconst hex = r() + r() + r() + r();\n\treturn (\n\t\t`${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-` +\n\t\t`${((parseInt(hex.slice(16, 17), 16) & 0x3) | 0x8).toString(16)}${hex.slice(17, 20)}-${hex.slice(20, 32)}`\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create initial versioning state for a node.\n *\n * @param level - 0 for V0, 1 for V1.\n * @param initialValue - The node's initial cached value (used for V1 cid).\n * @param opts - Optional overrides (id, hash).\n */\nexport function createVersioning(\n\tlevel: VersioningLevel,\n\tinitialValue: unknown,\n\topts?: VersioningOptions,\n): NodeVersionInfo {\n\tconst id = opts?.id ?? randomUuid();\n\tif (level === 0) {\n\t\treturn { id, version: 0 } satisfies V0;\n\t}\n\tconst hash = opts?.hash ?? defaultHash;\n\tconst cid = hash(initialValue);\n\treturn { id, version: 0, cid, prev: null } satisfies V1;\n}\n\n// ---------------------------------------------------------------------------\n// Advance\n// ---------------------------------------------------------------------------\n\n/**\n * Advance versioning state after a DATA emission (value changed).\n *\n * Mutates `info` in place for performance (called on every DATA).\n * Only call when the cached value has actually changed (not on RESOLVED).\n *\n * @param info - The node's current versioning state.\n * @param newValue - The new cached value.\n * @param hashFn - Hash function (only used for V1).\n */\nexport function advanceVersion(info: NodeVersionInfo, newValue: unknown, hashFn: HashFn): void {\n\tinfo.version += 1;\n\tif (\"cid\" in info) {\n\t\t(info as V1).prev = (info as V1).cid;\n\t\t(info as V1).cid = hashFn(newValue);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Guards\n// ---------------------------------------------------------------------------\n\n/** Type guard: is this V1 versioning info? */\nexport function isV1(info: NodeVersionInfo): info is V1 {\n\treturn \"cid\" in info;\n}\n","/**\n * `NodeImpl` — the single GraphReFly node primitive.\n *\n * Per-dep state lives in a `DepRecord[]` — one entry per declared dep —\n * consolidating subscription cleanup, latest-data tracking, dirty/settled\n * flags, and terminal state into a single structure per dep.\n *\n * This file also owns the default singleton handlers (`defaultOnMessage`,\n * `defaultOnSubscribe`), the `defaultConfig` instance, and the public\n * `configure(...)` entry point. They live here because their bodies touch\n * `NodeImpl` internals; `config.ts` stays NodeImpl-agnostic.\n *\n * See GRAPHREFLY-SPEC §2 and COMPOSITION-GUIDE §1/§9 for the behavior\n * contract. See SESSION-foundation-redesign.md §§1–10 for design history.\n */\n\nimport { registerBuiltinCodecs } from \"../graph/codec.js\";\nimport type { Actor } from \"./actor.js\";\nimport { normalizeActor } from \"./actor.js\";\nimport { downWithBatch, isExplicitlyBatching, registerBatchFlushHook } from \"./batch.js\";\nimport { wallClockNs } from \"./clock.js\";\nimport type {\n\tMessageContext,\n\tNodeActions,\n\tNodeCtx,\n\tOnMessageHandler,\n\tOnSubscribeHandler,\n\tSubscribeContext,\n} from \"./config.js\";\nimport { GraphReFlyConfig, registerBuiltins } from \"./config.js\";\nimport type { GuardAction, NodeGuard } from \"./guard.js\";\nimport { GuardDenied } from \"./guard.js\";\nimport {\n\tCOMPLETE,\n\tCOMPLETE_ONLY_BATCH,\n\tDATA,\n\tDIRTY,\n\tDIRTY_MSG,\n\tDIRTY_ONLY_BATCH,\n\tERROR,\n\tINVALIDATE,\n\tINVALIDATE_ONLY_BATCH,\n\ttype Message,\n\ttype Messages,\n\tPAUSE,\n\tRESOLVED,\n\tRESOLVED_MSG,\n\tRESOLVED_ONLY_BATCH,\n\tRESUME,\n\tSTART,\n\tSTART_MSG,\n\tTEARDOWN,\n\tTEARDOWN_ONLY_BATCH,\n} from \"./messages.js\";\nimport {\n\tadvanceVersion,\n\tcreateVersioning,\n\tdefaultHash,\n\ttype HashFn,\n\ttype NodeVersionInfo,\n\ttype VersioningLevel,\n} from \"./versioning.js\";\n\n// ---------------------------------------------------------------------------\n// Internal sentinel + type helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Placeholder unsubscribe used to mark a dep subscription as \"pending\" during\n * the synchronous window between `dep.unsub = noopUnsub` and the return of\n * `dep.node.subscribe(...)`. Ensures the liveness check `dep.unsub === null`\n * in the subscription callback correctly passes for synchronous push-on-\n * subscribe deliveries, while still blocking stale drainPhase2 closures that\n * fire after deactivation has set `dep.unsub = null`.\n */\nconst noopUnsub: () => void = () => {};\n\n/**\n * Maximum `_pendingRerun` depth before we give up and emit ERROR. Bounds\n * autoTrackNode / `_addDep` discovery loops — a well-formed discovery\n * converges in O(n) total rounds for n deps, so 100 is ample.\n */\nconst MAX_RERUN_DEPTH = 100;\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Lifecycle status of a node (GRAPHREFLY-SPEC §2.2). */\nexport type NodeStatus =\n\t| \"sentinel\"\n\t| \"pending\"\n\t| \"dirty\"\n\t| \"settled\"\n\t| \"resolved\"\n\t| \"completed\"\n\t| \"errored\";\n\n/** Callback that receives downstream message batches. */\nexport type NodeSink = (messages: Messages) => void;\n\n/**\n * Observability hook events fired by a per-node inspector. Used by\n * `Graph.observe(path, { causal, derived })` to build causal traces.\n *\n * - `\"dep_message\"` — fires in `_onDepMessage` before default dispatch,\n * one event per message received from a dep. Includes `depIndex` and\n * the raw `Message` tuple.\n * - `\"run\"` — fires in `_execFn` just before the user fn runs. Includes\n * the per-dep `prevData` snapshot that will be passed to fn.\n */\nexport type NodeInspectorHookEvent =\n\t| { kind: \"dep_message\"; depIndex: number; message: Message }\n\t| {\n\t\t\tkind: \"run\";\n\t\t\tbatchData: readonly (readonly unknown[] | undefined)[];\n\t\t\tprevData: readonly unknown[];\n\t };\n\n/** Callback attached to a node for per-message/per-run inspection. */\nexport type NodeInspectorHook = (event: NodeInspectorHookEvent) => void;\n\n/** Describe `type` for `Graph.describe` (GRAPHREFLY-SPEC Appendix B). */\nexport type NodeDescribeKind = \"state\" | \"derived\" | \"producer\" | \"effect\";\n\n/** Actor/delivery context for {@link Node.down} and {@link Node.up}. */\nexport type NodeTransportOptions = {\n\tactor?: Actor;\n\t/** When `true`, skips guard checks. */\n\tinternal?: boolean;\n\t/** `signal` for `Graph.signal` deliveries; default `write`. */\n\tdelivery?: \"write\" | \"signal\";\n};\n\n/**\n * Cleanup return shape from a node {@link NodeFn}.\n * - `() => void` — fires before the next fn run AND on deactivation (default).\n * - `{ deactivation: () => void }` — fires only on deactivation (persistent\n * resources that should survive across fn re-runs).\n */\nexport type NodeFnCleanup = (() => void) | { deactivation: () => void };\n\n/**\n * Fn-time context exposing per-wave metadata and a per-node persistent\n * scratch pad.\n *\n * - `prevData[i]` — last DATA value from dep `i` as of the END of the\n * previous wave (i.e. the value that was stable before this wave started).\n * Use as the fallback when `data[i]` is `undefined` (not involved) or\n * `[]` (RESOLVED, no new values this wave).\n * `undefined` means dep `i` has never produced DATA (sentinel state).\n * `null` is a valid DATA value. `undefined` is not a valid DATA value —\n * the protocol reserves it as the \"never sent\" sentinel.\n * - `ctx.prevData[i] === undefined` → dep has never produced DATA\n * - `ctx.prevData[i] !== undefined` → last DATA value (may be `null`)\n * - `terminalDeps[i]` — runtime shape:\n * - `undefined` → dep `i` is still live.\n * - `true` → dep `i` sent COMPLETE.\n * - anything else → dep `i` sent ERROR, value is the error payload.\n * Type is `readonly unknown[]` because `true | unknown` collapses to\n * `unknown` anyway; the three states are documented contract, not type.\n * - `store` — mutable bag that persists across fn runs within one activation\n * cycle. Wiped on deactivation and on resubscribable terminal reset.\n */\nexport interface FnCtx {\n\treadonly prevData: readonly unknown[];\n\treadonly terminalDeps: readonly unknown[];\n\treadonly store: Record<string, unknown>;\n}\n\n/**\n * Compute function passed to `node(deps, fn, opts?)`.\n *\n * `data[i]` holds the batch of DATA values received from dep `i` during the\n * current wave. Shape contract:\n * - `undefined` — dep `i` was not involved in this wave (no DIRTY received).\n * - `[]` — dep `i` was involved (dirtied), but settled as RESOLVED (value\n * unchanged). Use `ctx.prevData[i]` to read its last known value from the\n * previous wave.\n * - `[v1, v2, ...]` — dep `i` sent one or more DATA values. `at(-1)` gives\n * the latest; iterate for multi-emission processing.\n *\n * Emission is explicit via `actions.emit(v)` (sugar: equals + framing) or\n * `actions.down(msgs)` (raw). Return a cleanup function (or\n * `{ deactivation }`) to register teardown — any non-cleanup return value\n * is ignored. The `| void` leg lets arrow-block bodies satisfy `NodeFn`\n * without an explicit `return undefined`.\n *\n * Sugar constructors (`derived`, `effect`, `dynamicNode`) unwrap `data[i]`\n * to a single scalar (`at(-1)` with `ctx.prevData[i]` fallback) so their\n * user-facing fn signatures stay unchanged. Use raw `node()` when you need\n * the full batch array.\n */\nexport type NodeFn = (\n\tdata: readonly (readonly unknown[] | undefined)[],\n\tactions: NodeActions,\n\tctx: FnCtx,\n\t// biome-ignore lint/suspicious/noConfusingVoidType: see JSDoc above.\n) => NodeFnCleanup | void;\n\n/** Options accepted by every node constructor. */\nexport interface NodeOptions<T = unknown> {\n\tname?: string;\n\tdescribeKind?: NodeDescribeKind;\n\tequals?: (a: T, b: T) => boolean;\n\t/**\n\t * Pre-populate the cache at construction. `null` is a valid initial value.\n\t * `undefined` is treated as absent (not a valid DATA payload).\n\t */\n\tinitial?: T | null;\n\tmeta?: Record<string, unknown>;\n\tresubscribable?: boolean;\n\tresetOnTeardown?: boolean;\n\t/** Auto-emit `[[COMPLETE]]` when all deps complete. Default `true`. */\n\tcompleteWhenDepsComplete?: boolean;\n\t/**\n\t * Auto-propagate `[[ERROR]]` when any dep errors. Default `true`.\n\t * Set `false` only for rescue/catchError operators that handle errors\n\t * explicitly via `ctx.terminalDeps`.\n\t */\n\terrorWhenDepsError?: boolean;\n\t/**\n\t * Tier-2 PAUSE/RESUME handling.\n\t * - `true` (default): wave completion suppressed while paused; fn fires\n\t * once on RESUME if gate is satisfied.\n\t * - `false`: node ignores PAUSE (sources like timers that must keep running).\n\t * - `\"resumeAll\"`: on RESUME, replay every buffered DATA (future).\n\t */\n\tpausable?: boolean | \"resumeAll\";\n\tguard?: NodeGuard;\n\tversioning?: VersioningLevel;\n\tversioningId?: string;\n\tversioningHash?: HashFn;\n\t/**\n\t * Override the config instance this node binds to. Defaults to\n\t * {@link defaultConfig}. Useful for test isolation and custom protocol\n\t * stacks. The first node that reads any hook on the config freezes it.\n\t */\n\tconfig?: GraphReFlyConfig;\n}\n\n/** A reactive node in the GraphReFly protocol. */\nexport interface Node<T = unknown> {\n\treadonly name?: string;\n\treadonly status: NodeStatus;\n\t/**\n\t * Current cached value. Returns `undefined` when the node is in\n\t * `\"sentinel\"` state (no DATA ever emitted). v5 reserves `undefined`\n\t * globally as the sentinel value — the valid DATA type is `T | null`.\n\t * Therefore `node.cache === undefined` is a valid \"never emitted\" guard.\n\t * `node.status` distinguishes the richer states (`\"sentinel\"`,\n\t * `\"settled\"`, `\"errored\"`, etc.) when you need more than has-value.\n\t */\n\treadonly cache: T | null | undefined;\n\treadonly meta: Record<string, Node>;\n\treadonly lastMutation: Readonly<{ actor: Actor; timestamp_ns: number }> | undefined;\n\treadonly v: Readonly<NodeVersionInfo> | undefined;\n\t/**\n\t * Send one or more messages downstream. Accepts either a single\n\t * {@link Message} tuple (e.g. `node.down([DATA, 42])`) or a\n\t * {@link Messages} array of tuples (e.g.\n\t * `node.down([[DIRTY], [DATA, 42]])`). One call = one wave: the\n\t * emit pipeline tier-sorts the input, auto-prefixes `[DIRTY]` when\n\t * any tier-3 payload is present and the node is not already dirty,\n\t * runs equals substitution against the live cache (§3.5.1), then\n\t * dispatches to sinks with phase deferral.\n\t */\n\tdown(messageOrMessages: Message | Messages, options?: NodeTransportOptions): void;\n\t/**\n\t * Sugar for `down([[DATA, value]])`. One wave with a single DATA\n\t * payload — the pipeline adds the synthetic DIRTY prefix and runs\n\t * equals substitution against the live cache.\n\t */\n\temit(value: T | undefined | null, options?: NodeTransportOptions): void;\n\t/**\n\t * Send one or more messages upstream. Accepts the same shapes as\n\t * {@link down}. Upstream messages are tier <3 + tier 5 only\n\t * (DIRTY, INVALIDATE, PAUSE, RESUME, TEARDOWN); tier-3/4 payloads\n\t * throw — DATA/RESOLVED/COMPLETE/ERROR are downstream-only in this\n\t * protocol. No equals substitution, no cache advance, no DIRTY\n\t * auto-prefix — the up direction just forwards to every dep.\n\t */\n\tup?(messageOrMessages: Message | Messages, options?: NodeTransportOptions): void;\n\tsubscribe(sink: NodeSink, actor?: Actor): () => void;\n\tallowsObserve(actor: Actor): boolean;\n\thasGuard(): boolean;\n}\n\n// ---------------------------------------------------------------------------\n// DepRecord — per-dep state (§8.2)\n// ---------------------------------------------------------------------------\n\n/**\n * Per-dep runtime state. One entry per upstream node.\n *\n * `terminal` is the single terminal-state slot, shaped to match\n * {@link FnCtx.terminalDeps}:\n * - `undefined` — dep is still live.\n * - `true` — dep sent COMPLETE.\n * - anything else — dep sent ERROR with that payload.\n *\n * Edge case: an ERROR carrying an `undefined` payload is indistinguishable\n * from \"live\". Pass meaningful error values (Error objects, domain tags).\n */\nexport interface DepRecord {\n\treadonly node: Node;\n\tunsub: (() => void) | null;\n\t/**\n\t * Last DATA value from this dep as of the end of the previous completed\n\t * wave. `undefined` until dep has produced at least one DATA (sentinel).\n\t * Committed by `_execFn` after snapshotting `ctx.prevData` and before\n\t * `_clearWaveFlags`. `undefined` is reserved as the \"never sent\" sentinel —\n\t * `undefined` is not a valid DATA payload.\n\t */\n\tprevData: unknown;\n\t/** True while awaiting DATA/RESOLVED for the current wave. */\n\tdirty: boolean;\n\t/**\n\t * True if this dep was dirtied in the current wave (set in `_depDirtied`,\n\t * cleared in `_clearWaveFlags`). Distinguishes \"RESOLVED\" (`involvedThisWave\n\t * && dataBatch.length === 0`) from \"not involved\" (`!involvedThisWave`) in\n\t * the `data[i]` batch snapshot passed to fn.\n\t */\n\tinvolvedThisWave: boolean;\n\t/**\n\t * DATA values accumulated from this dep during the current wave.\n\t * Populated by `_depSettledAsData`, cleared by `_clearWaveFlags`.\n\t * Snapshotted (copied) by `_execFn` before `_clearWaveFlags` runs so\n\t * that fn always sees the full wave batch.\n\t */\n\tdataBatch: unknown[];\n\t/** Terminal-state slot — see JSDoc on {@link DepRecord}. */\n\tterminal: unknown;\n}\n\nfunction createDepRecord(n: Node): DepRecord {\n\treturn {\n\t\tnode: n,\n\t\tunsub: null,\n\t\tprevData: undefined,\n\t\tdirty: false,\n\t\tinvolvedThisWave: false,\n\t\tdataBatch: [],\n\t\tterminal: undefined,\n\t};\n}\n\nfunction resetDepRecord(d: DepRecord): void {\n\td.prevData = undefined;\n\td.dirty = false;\n\td.involvedThisWave = false;\n\td.dataBatch.length = 0;\n\td.terminal = undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Normalization helper\n// ---------------------------------------------------------------------------\n\n/**\n * Accept either a single `Message` tuple or a `Messages` array and return\n * a `Messages` array. The discriminator is the type of the first element:\n * a `Message` has a symbol at index 0, while a `Messages` array has a\n * nested array at index 0. This lets `node.down(...)` and `actions.down(...)`\n * take either shape without a wrapper allocation on the common single-msg\n * path.\n */\nfunction normalizeMessages(input: Message | Messages): Messages {\n\tif (input.length === 0) return input as Messages;\n\treturn typeof (input as Message)[0] === \"symbol\" ? [input as Message] : (input as Messages);\n}\n\n// ---------------------------------------------------------------------------\n// Default handlers\n// ---------------------------------------------------------------------------\n\n/**\n * Default {@link OnMessageHandler}. For `\"down-in\"` messages (from a dep),\n * routes to `NodeImpl._onDepMessage`. For `\"up-in\"` messages (from a sink),\n * the up-path is wired directly via `Node.up()` and never passes through\n * onMessage — this hook is reserved for future P5 symmetry.\n */\nconst defaultOnMessage: OnMessageHandler = (\n\tnode: NodeCtx,\n\tmsg: Message,\n\tctx: MessageContext,\n\t_actions: NodeActions,\n): \"consume\" | undefined => {\n\tif (ctx.direction === \"down-in\") {\n\t\t(node as NodeImpl)._onDepMessage(ctx.depIndex, msg);\n\t}\n\t// up-in is currently unused; default is to do nothing.\n\treturn undefined;\n};\n\n/**\n * Default {@link OnSubscribeHandler}. Delivers the subscribe handshake —\n * `[[START]]` for a sentinel cache, or `[[START], [DATA, cached]]` when a\n * value exists. Terminal nodes skip entirely so absence of START tells the\n * sink the stream is over (spec §2.2). Delivered through `downWithBatch` so\n * `subscribe()` inside `batch()` still defers the paired DATA correctly.\n */\nconst defaultOnSubscribe: OnSubscribeHandler = (\n\tnode: NodeCtx,\n\tsink: NodeSink,\n\t_ctx: SubscribeContext,\n\t_actions: NodeActions,\n): (() => void) | undefined => {\n\tconst impl = node as NodeImpl;\n\tif (impl._status === \"completed\" || impl._status === \"errored\") return;\n\tconst cached = impl._cached;\n\tconst initial: Message[] =\n\t\tcached === undefined ? [START_MSG] : [START_MSG, [DATA, cached] as Message];\n\t// When the node is mid-wave (`\"dirty\"`), append a DIRTY so the late\n\t// joiner participates in the in-flight wave. Without this, the next\n\t// DATA/RESOLVED the sink receives lacks the preceding DIRTY required\n\t// by spec §1.3.1 — the emit-side DIRTY auto-prefix is suppressed when\n\t// `_status` is already `\"dirty\"`.\n\tif (impl._status === \"dirty\") initial.push(DIRTY_MSG);\n\tdownWithBatch(sink, initial, impl._config.tierOf);\n};\n\n// ---------------------------------------------------------------------------\n// defaultConfig + configure\n// ---------------------------------------------------------------------------\n\n/**\n * Default {@link GraphReFlyConfig} instance. Every `NodeImpl` constructed\n * without an explicit `opts.config` binds to this instance and freezes it\n * on first hook access.\n */\nexport const defaultConfig = new GraphReFlyConfig({\n\tonMessage: defaultOnMessage,\n\tonSubscribe: defaultOnSubscribe,\n});\nregisterBuiltins(defaultConfig);\nregisterBuiltinCodecs(defaultConfig);\n\n/**\n * Apply configuration to {@link defaultConfig}. Must be called before the\n * first node is created — otherwise throws. Custom message types, hook\n * overrides, etc. go through here at app startup.\n *\n * ```ts\n * configure((cfg) => {\n * cfg.registerMessageType(MY_TYPE, { tier: 3 });\n * cfg.onMessage = (node, msg, ctx, actions) => { ... };\n * });\n * ```\n */\nexport function configure(fn: (cfg: GraphReFlyConfig) => void): void {\n\tif (defaultConfig._isFrozen()) {\n\t\tthrow new Error(\n\t\t\t\"configure() called after a node was created — the default \" +\n\t\t\t\t\"GraphReFlyConfig is frozen. Call configure(...) at application \" +\n\t\t\t\t\"startup, before any node factories run.\",\n\t\t);\n\t}\n\tfn(defaultConfig);\n}\n\n// Re-export the class for advanced callers that want an isolated instance.\nexport { GraphReFlyConfig };\n\n// ---------------------------------------------------------------------------\n// NodeImpl\n// ---------------------------------------------------------------------------\n\n/**\n * Single-class node implementation. Covers state, producer, derived, effect,\n * and passthrough shapes. See `sugar.ts` for ergonomic factories and\n * `dynamicNode()` (sugar-level wrapper around plain `NodeImpl`).\n */\nexport class NodeImpl<T = unknown> implements Node<T> {\n\t// --- Identity ---\n\treadonly _optsName: string | undefined;\n\treadonly _describeKind: NodeDescribeKind | undefined;\n\treadonly meta: Record<string, Node>;\n\t/**\n\t * Cached `Object.keys(meta).length > 0` check. `meta` is frozen at\n\t * construction so this boolean never flips. Used by `_emit` to skip\n\t * the meta TEARDOWN fan-out block allocation on the common \"no meta\"\n\t * hot path.\n\t */\n\treadonly _hasMeta: boolean;\n\n\t// --- Config ---\n\treadonly _config: GraphReFlyConfig;\n\n\t// --- Topology ---\n\t/** Mutable for autoTrackNode / Graph.connect() post-construction dep addition. */\n\t_deps: DepRecord[];\n\t_sinks: NodeSink | Set<NodeSink> | null = null;\n\t_sinkCount = 0;\n\n\t// --- State ---\n\t_cached: T | undefined;\n\t_status: NodeStatus;\n\t_cleanup: NodeFnCleanup | undefined;\n\t_store: Record<string, unknown> = {};\n\t_waveHasNewData = false;\n\t_hasNewTerminal = false;\n\t_hasCalledFnOnce = false;\n\t_paused = false;\n\t_pendingWave = false;\n\t_isExecutingFn = false;\n\t_pendingRerun = false;\n\t_rerunDepth = 0;\n\n\t// --- Settlement counter (A3) ---\n\t/**\n\t * Count of deps currently in `dirty === true`. `_maybeRunFnOnSettlement`\n\t * treats `0` as \"wave settled\" — O(1) check for full dep settlement.\n\t */\n\t_dirtyDepCount = 0;\n\n\t// --- Per-batch emit accumulator (Bug 2: K+1 fan-in fix) ---\n\t/**\n\t * Inside an explicit `batch(() => ...)` scope, every `_emit` accumulates\n\t * its already-framed messages here instead of dispatching synchronously.\n\t * At batch end, `_flushBatchPending` runs (registered via\n\t * `registerBatchFlushHook`) and delivers the whole accumulated batch as\n\t * one `downWithBatch` call — collapsing what would otherwise be K\n\t * separate sink invocations into one. This is the fix for the diamond\n\t * fan-in K+1 over-fire.\n\t *\n\t * `null` outside batch (or after flush). Only ever appended to within\n\t * a single explicit batch lifetime; reset to `null` on flush. State\n\t * updates (cache, version, status) still happen per-emit via\n\t * `_updateState` — only the downstream delivery is coalesced.\n\t */\n\t_batchPendingMessages: Message[] | null = null;\n\n\t// --- PAUSE/RESUME lock tracking (C0) ---\n\t/**\n\t * Set of active pause locks held against this node. Every `[PAUSE, lockId]`\n\t * adds its `lockId` to the set; every `[RESUME, lockId]` removes it.\n\t * `_paused` is a derived quantity: `_pauseLocks.size > 0`. Multi-pauser\n\t * correctness — one controller releasing its lock does NOT resume the\n\t * node while another controller still holds its lock.\n\t */\n\t_pauseLocks: Set<unknown> | null = null;\n\t/**\n\t * Buffered DATA messages held while paused. Only populated when\n\t * `_pausable === \"resumeAll\"` (bufferAll mode). On final lock release\n\t * the buffer is replayed through the node's outgoing pipeline in the\n\t * order received. Non-bufferAll pause mode drops DATA on the floor\n\t * (upstream is expected to honor PAUSE by suppressing production).\n\t */\n\t_pauseBuffer: Message[] | null = null;\n\n\t// --- Options (frozen at construction) ---\n\treadonly _fn: NodeFn | undefined;\n\treadonly _equals: (a: T, b: T) => boolean;\n\treadonly _resubscribable: boolean;\n\treadonly _resetOnTeardown: boolean;\n\treadonly _autoComplete: boolean;\n\treadonly _autoError: boolean;\n\treadonly _pausable: boolean | \"resumeAll\";\n\treadonly _guard: NodeGuard | undefined;\n\t_hashFn: HashFn;\n\t_versioning: NodeVersionInfo | undefined;\n\t/**\n\t * Explicit versioning level, tracked separately from `_versioning` so\n\t * monotonicity checks and future v2/v3 extensions don't rely on the\n\t * fragile `\"cid\" in _versioning` shape discriminator. `undefined` means\n\t * the node has no versioning attached; `0` / `1` / future levels name\n\t * the tier. Mutated in lockstep with `_versioning` by the constructor\n\t * and by `_applyVersioning`.\n\t */\n\t_versioningLevel: VersioningLevel | undefined;\n\n\t// --- ABAC ---\n\t_lastMutation: { actor: Actor; timestamp_ns: number } | undefined;\n\n\t/**\n\t * @internal Per-node inspector hooks for `Graph.observe(path,\n\t * { causal, derived })`. Fires in `_onDepMessage` and `_execFn`.\n\t * Attached via `_setInspectorHook` (returns a disposer). Multiple\n\t * observers can attach simultaneously — all registered hooks fire for\n\t * every event.\n\t */\n\t_inspectorHooks: Set<NodeInspectorHook> | undefined;\n\n\t// --- Actions (built once in the constructor) ---\n\treadonly _actions: NodeActions;\n\n\tconstructor(deps: readonly Node[], fn: NodeFn | undefined, opts: NodeOptions<T>) {\n\t\t// Bind to config FIRST so meta nodes inherit it. Touching a hook\n\t\t// getter below freezes the config on first node creation.\n\t\tthis._config = opts.config ?? defaultConfig;\n\t\tvoid this._config.onMessage;\n\n\t\tthis._optsName = opts.name;\n\t\tthis._describeKind = opts.describeKind;\n\t\tthis._equals = (opts.equals ?? (Object.is as (a: T, b: T) => boolean)) as (\n\t\t\ta: T,\n\t\t\tb: T,\n\t\t) => boolean;\n\t\tthis._resubscribable = opts.resubscribable ?? false;\n\t\tthis._resetOnTeardown = opts.resetOnTeardown ?? false;\n\t\tthis._autoComplete = opts.completeWhenDepsComplete ?? true;\n\t\tthis._autoError = opts.errorWhenDepsError ?? true;\n\t\tthis._pausable = opts.pausable ?? true;\n\t\tthis._guard = opts.guard;\n\t\tthis._fn = fn;\n\n\t\t// `undefined` is the sentinel (\"no cached value\") so `initial: undefined`\n\t\t// is treated as absent. `null` is a valid DATA value and sets the cache.\n\t\tthis._cached = opts.initial !== undefined ? (opts.initial as T) : undefined;\n\t\t// State-with-initial starts \"settled\"; everything else starts \"sentinel\".\n\t\tthis._status =\n\t\t\tdeps.length === 0 && fn == null && this._cached !== undefined ? \"settled\" : \"sentinel\";\n\n\t\t// Versioning\n\t\t// Hash resolution: per-node `opts.versioningHash` wins; then the\n\t\t// bound config's `defaultHashFn`; then the vendored sync SHA-256.\n\t\t// Hot-path workloads that want a faster hash (xxHash, FNV-1a) can\n\t\t// set it once at app init via `configure(cfg => { cfg.defaultHashFn = ... })`.\n\t\tthis._hashFn = opts.versioningHash ?? this._config.defaultHashFn ?? defaultHash;\n\t\t// Versioning level resolution: per-node `opts.versioning` wins; if\n\t\t// absent, fall back to the bound config's `defaultVersioning`. `null`\n\t\t// stays unversioned. Explicit levels (0, 1, …) attach the versioning\n\t\t// info at construction so the next DATA emit advances it.\n\t\tconst versioningLevel: VersioningLevel | undefined =\n\t\t\topts.versioning ?? this._config.defaultVersioning;\n\t\tthis._versioningLevel = versioningLevel;\n\t\tthis._versioning =\n\t\t\tversioningLevel != null\n\t\t\t\t? createVersioning(versioningLevel, this._cached === undefined ? undefined : this._cached, {\n\t\t\t\t\t\tid: opts.versioningId,\n\t\t\t\t\t\thash: this._hashFn,\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\n\t\t// Per-dep records: one DepRecord per declared dep.\n\t\tthis._deps = deps.map(createDepRecord);\n\n\t\t// Meta companions (simple state children; inherit config + guard).\n\t\tconst meta: Record<string, Node> = {};\n\t\tfor (const [k, v] of Object.entries(opts.meta ?? {})) {\n\t\t\tconst metaOpts: NodeOptions<unknown> = {\n\t\t\t\tinitial: v,\n\t\t\t\tname: `${opts.name ?? \"node\"}:meta:${k}`,\n\t\t\t\tdescribeKind: \"state\",\n\t\t\t\tconfig: this._config,\n\t\t\t};\n\t\t\tif (opts.guard != null) metaOpts.guard = opts.guard;\n\t\t\tmeta[k] = new NodeImpl<unknown>([], undefined, metaOpts);\n\t\t}\n\t\tObject.freeze(meta);\n\t\tthis.meta = meta;\n\t\tthis._hasMeta = Object.keys(meta).length > 0;\n\n\t\t// Actions: built once, closure over `this`. Every call goes through\n\t\t// `_emit` which owns the full dispatch invariant — tier sort,\n\t\t// synthetic DIRTY prefix, equals substitution, and phase dispatch.\n\t\t// One call = one wave. Multiple calls produce multiple waves.\n\t\t// No accumulation, no user-facing bundle builder.\n\t\tconst self = this;\n\t\tthis._actions = {\n\t\t\temit(value: unknown): void {\n\t\t\t\tself._emit([[DATA, value] as Message]);\n\t\t\t},\n\t\t\tdown(messageOrMessages: Message | Messages): void {\n\t\t\t\tself._emit(normalizeMessages(messageOrMessages));\n\t\t\t},\n\t\t\tup(messageOrMessages: Message | Messages): void {\n\t\t\t\tself._emitUp(normalizeMessages(messageOrMessages));\n\t\t\t},\n\t\t};\n\n\t\t// Bind commonly detached protocol methods.\n\t\tthis.down = this.down.bind(this);\n\t\tthis.up = this.up.bind(this);\n\t}\n\n\t// --- Derived state ---\n\n\tprivate get _isTerminal(): boolean {\n\t\treturn this._status === \"completed\" || this._status === \"errored\";\n\t}\n\n\t// --- Public getters ---\n\n\tget name(): string | undefined {\n\t\treturn this._optsName;\n\t}\n\n\tget status(): NodeStatus {\n\t\treturn this._status;\n\t}\n\n\tget cache(): T | undefined | null {\n\t\treturn this._cached === undefined ? undefined : (this._cached as T);\n\t}\n\n\tget lastMutation(): Readonly<{ actor: Actor; timestamp_ns: number }> | undefined {\n\t\treturn this._lastMutation;\n\t}\n\n\tget v(): Readonly<NodeVersionInfo> | undefined {\n\t\treturn this._versioning;\n\t}\n\n\thasGuard(): boolean {\n\t\treturn this._guard != null;\n\t}\n\n\t/**\n\t * @internal Retroactively attach (or upgrade) versioning state on this\n\t * node. Intended for `Graph.setVersioning(level)` bulk application and\n\t * for rare cases where a specific node needs to be bumped to a higher\n\t * level (e.g., `v0 → v1`) after construction.\n\t *\n\t * **Safety:** the mutation is rejected mid-wave. Specifically,\n\t * throws if the node is currently executing its fn (`_isExecutingFn`).\n\t * Callers at quiescent points — before the first sink subscribes, or\n\t * after all sinks unsubscribe, or between external `down()` / `emit()`\n\t * invocations — are safe. The re-entrance window that motivated §10.6.4\n\t * removal was the \"transition `_versioning` from `undefined` to a fresh\n\t * object mid-`_updateState`\" case; that path is now guarded.\n\t *\n\t * **Monotonicity:** levels can only go up. Downgrade (e.g., `v1 → v0`)\n\t * is a no-op — once a node carries higher-level metadata, dropping it\n\t * mid-graph would tear the linked-history invariant for v1 and above.\n\t *\n\t * **Linked-history boundary (D1, 2026-04-13):** upgrading v0 → v1\n\t * produces a **fresh history root**. The new v1 state has `cid =\n\t * hash(currentCachedValue)` and `prev = null`, not a synthetic `prev`\n\t * anchored to any previous v0 value. The v0 monotonic `version` counter\n\t * is preserved across the upgrade, but the linked-cid chain (spec §7)\n\t * starts fresh at the upgrade point. Downstream audit tools that walk\n\t * `v.cid.prev` backwards through time will see a `null` boundary at\n\t * the upgrade — **this is intentional**: v0 had no cid to link to, and\n\t * fabricating one would lie about the hash. Callers that require an\n\t * unbroken cid chain from birth must attach versioning at construction\n\t * via `opts.versioning` or `config.defaultVersioning`, not retroactively.\n\t *\n\t * @param level - New minimum versioning level.\n\t * @param opts - Optional id / hash overrides; applied only if the\n\t * node currently has no versioning state.\n\t */\n\t_applyVersioning(level: VersioningLevel, opts?: { id?: string; hash?: HashFn }): void {\n\t\tif (this._isExecutingFn) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node \"${this.name}\": _applyVersioning cannot run mid-fn — ` +\n\t\t\t\t\t\"call it outside of `_execFn` (typically at graph setup time \" +\n\t\t\t\t\t\"before the first subscribe).\",\n\t\t\t);\n\t\t}\n\t\tconst currentLevel = this._versioningLevel;\n\t\tif (currentLevel != null && level <= currentLevel) {\n\t\t\t// Downgrade or no-op. Monotonic: higher levels only.\n\t\t\treturn;\n\t\t}\n\t\tconst hash = opts?.hash ?? this._hashFn;\n\t\tif (hash !== this._hashFn) this._hashFn = hash;\n\t\tconst initialValue = this._cached === undefined ? undefined : this._cached;\n\t\t// Preserve the existing id + version counter across upgrades so\n\t\t// downstream consumers watching `v.id` don't see an identity jump.\n\t\tconst current = this._versioning;\n\t\tconst preservedId = current?.id ?? opts?.id;\n\t\tconst preservedVersion = current?.version ?? 0;\n\t\tconst fresh = createVersioning(level, initialValue, {\n\t\t\tid: preservedId,\n\t\t\thash,\n\t\t});\n\t\tfresh.version = preservedVersion;\n\t\tthis._versioning = fresh;\n\t\tthis._versioningLevel = level;\n\t}\n\n\t/**\n\t * @internal Attach an inspector hook. Returns a disposer that removes\n\t * the hook. Used by `Graph.observe(path, { causal, derived })` to build\n\t * causal traces. Multiple hooks may be attached concurrently — all fire\n\t * for every event in registration order. Passing `undefined` is a no-op\n\t * and returns a no-op disposer.\n\t */\n\t_setInspectorHook(hook?: NodeInspectorHook): () => void {\n\t\tif (hook == null) return () => {};\n\t\tif (this._inspectorHooks == null) this._inspectorHooks = new Set();\n\t\tthis._inspectorHooks.add(hook);\n\t\treturn () => {\n\t\t\tthis._inspectorHooks?.delete(hook);\n\t\t\tif (this._inspectorHooks?.size === 0) this._inspectorHooks = undefined;\n\t\t};\n\t}\n\n\tallowsObserve(actor: Actor): boolean {\n\t\tif (this._guard == null) return true;\n\t\treturn this._guard(normalizeActor(actor), \"observe\");\n\t}\n\n\t// --- Guard helper ---\n\n\tprivate _checkGuard(options?: NodeTransportOptions): void {\n\t\tif (options?.internal || this._guard == null) return;\n\t\tconst actor = normalizeActor(options?.actor);\n\t\tconst action: GuardAction = options?.delivery === \"signal\" ? \"signal\" : \"write\";\n\t\tif (!this._guard(actor, action)) {\n\t\t\tthrow new GuardDenied({ actor, action, nodeName: this.name });\n\t\t}\n\t\tthis._lastMutation = { actor, timestamp_ns: wallClockNs() };\n\t}\n\n\t// --- Public transport ---\n\n\tdown(messageOrMessages: Message | Messages, options?: NodeTransportOptions): void {\n\t\tconst messages = normalizeMessages(messageOrMessages);\n\t\tif (messages.length === 0) return;\n\t\tthis._checkGuard(options);\n\t\tthis._emit(messages);\n\t}\n\n\temit(value: T | undefined | null, options?: NodeTransportOptions): void {\n\t\tthis._checkGuard(options);\n\t\tthis._emit([[DATA, value] as Message]);\n\t}\n\n\tup(messageOrMessages: Message | Messages, options?: NodeTransportOptions): void {\n\t\tif (this._deps.length === 0) return;\n\t\tconst messages = normalizeMessages(messageOrMessages);\n\t\tif (messages.length === 0) return;\n\t\tthis._checkGuard(options);\n\t\tconst forwardOpts: NodeTransportOptions = options ?? { internal: true };\n\t\t// Validate tier constraint before fanning out (B1.4 option a).\n\t\tthis._validateUpTiers(messages);\n\t\tfor (const d of this._deps) {\n\t\t\td.node.up?.(messages, forwardOpts);\n\t\t}\n\t}\n\n\t/**\n\t * @internal Internal up-path used by `actions.up(...)` from inside fn.\n\t * Same tier validation as public `up`, but bypasses the guard check\n\t * since the fn context is already inside an authorized operation.\n\t */\n\tprivate _emitUp(messages: Messages): void {\n\t\tif (this._deps.length === 0) return;\n\t\tif (messages.length === 0) return;\n\t\tthis._validateUpTiers(messages);\n\t\tfor (const d of this._deps) {\n\t\t\td.node.up?.(messages, { internal: true });\n\t\t}\n\t}\n\n\t/**\n\t * @internal Enforce spec §1.2 — up-direction messages are restricted to\n\t * tier 0–2 and tier 5 (START, DIRTY, INVALIDATE, PAUSE, RESUME,\n\t * TEARDOWN). Tier 3 (DATA/RESOLVED) and tier 4 (COMPLETE/ERROR) are\n\t * downstream-only. Emitting tier-3/4 via `up` would bypass equals\n\t * substitution and cache advance entirely and is a protocol bug.\n\t */\n\tprivate _validateUpTiers(messages: Messages): void {\n\t\tconst tierOf = this._config.tierOf;\n\t\tfor (const m of messages) {\n\t\t\tconst tier = tierOf(m[0]);\n\t\t\tif (tier === 3 || tier === 4) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Node \"${this.name}\": tier-${tier} messages cannot flow up — ` +\n\t\t\t\t\t\t\"DATA/RESOLVED/COMPLETE/ERROR are downstream-only. Use \" +\n\t\t\t\t\t\t\"`down(...)` for value delivery; `up(...)` is for control \" +\n\t\t\t\t\t\t\"signals (DIRTY, INVALIDATE, PAUSE, RESUME, TEARDOWN).\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tsubscribe(sink: NodeSink, actor?: Actor): () => void {\n\t\tif (actor != null && this._guard != null) {\n\t\t\tconst a = normalizeActor(actor);\n\t\t\tif (!this._guard(a, \"observe\")) {\n\t\t\t\tthrow new GuardDenied({ actor: a, action: \"observe\", nodeName: this.name });\n\t\t\t}\n\t\t}\n\n\t\t// Resubscribable terminal reset.\n\t\tconst wasTerminal = this._isTerminal;\n\t\tconst afterTerminalReset = wasTerminal && this._resubscribable;\n\t\tif (afterTerminalReset) {\n\t\t\tthis._cached = undefined;\n\t\t\tthis._status = \"sentinel\";\n\t\t\tthis._store = {};\n\t\t\tthis._hasCalledFnOnce = false;\n\t\t\tthis._waveHasNewData = false;\n\t\t\tthis._hasNewTerminal = false;\n\t\t\tthis._paused = false;\n\t\t\tthis._pendingWave = false;\n\t\t\tthis._pendingRerun = false;\n\t\t\tthis._isExecutingFn = false;\n\t\t\tthis._rerunDepth = 0;\n\t\t\tthis._dirtyDepCount = 0;\n\t\t\t// C0: clear pause state so a new subscriber after terminal-reset\n\t\t\t// starts from a clean pause lockset — otherwise a lockId from\n\t\t\t// the previous lifecycle would leave the node stuck paused and\n\t\t\t// swallow every emit.\n\t\t\tthis._pauseLocks = null;\n\t\t\tthis._pauseBuffer = null;\n\t\t\tfor (const d of this._deps) resetDepRecord(d);\n\t\t}\n\n\t\tthis._sinkCount += 1;\n\n\t\t// Subscribe ceremony via singleton.\n\t\t// Rollback on throw: undo the sinkCount bump — sink is not yet registered,\n\t\t// no _activate() has run, nothing else to clean up.\n\t\tlet subCleanup: (() => void) | undefined;\n\t\ttry {\n\t\t\tsubCleanup = this._config.onSubscribe(\n\t\t\t\tthis as unknown as NodeCtx,\n\t\t\t\tsink,\n\t\t\t\t{ sinkCount: this._sinkCount, afterTerminalReset },\n\t\t\t\tthis._actions,\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tthis._sinkCount -= 1;\n\t\t\tthrow err;\n\t\t}\n\n\t\t// Register sink AFTER START delivery (spec §2.2).\n\t\tif (this._sinks == null) {\n\t\t\tthis._sinks = sink;\n\t\t} else if (typeof this._sinks === \"function\") {\n\t\t\tthis._sinks = new Set<NodeSink>([this._sinks, sink]);\n\t\t} else {\n\t\t\tthis._sinks.add(sink);\n\t\t}\n\n\t\t// First-subscriber activation.\n\t\t// Rollback on throw: undo sink registration, sinkCount bump, and subCleanup.\n\t\t// _activate() rolls back its own partial dep subscriptions before re-throwing.\n\t\tconst isTerminalNow = this._isTerminal;\n\t\tif (this._sinkCount === 1 && !isTerminalNow) {\n\t\t\ttry {\n\t\t\t\tthis._activate();\n\t\t\t} catch (err) {\n\t\t\t\tthis._sinkCount -= 1;\n\t\t\t\tthis._removeSink(sink);\n\t\t\t\t// Restore status: onSubscribe emitted START which set _status to\n\t\t\t\t// \"pending\". With zero sinks the node is back to its pre-subscribe\n\t\t\t\t// state; reset so node.status reflects no active subscription.\n\t\t\t\tif (this._sinkCount === 0) this._status = \"sentinel\";\n\t\t\t\tif (typeof subCleanup === \"function\") {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsubCleanup();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* best-effort: subCleanup errors are secondary */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t}\n\n\t\t// Reflect \"activated but no value yet\" as pending.\n\t\tif (this._status === \"sentinel\" && this._cached === undefined) {\n\t\t\tthis._status = \"pending\";\n\t\t}\n\n\t\tlet removed = false;\n\t\treturn (): void => {\n\t\t\tif (removed) return;\n\t\t\tremoved = true;\n\t\t\tthis._sinkCount -= 1;\n\t\t\tthis._removeSink(sink);\n\t\t\tif (typeof subCleanup === \"function\") subCleanup();\n\t\t\tif (this._sinks == null) this._deactivate();\n\t\t};\n\t}\n\n\tprivate _removeSink(sink: NodeSink): void {\n\t\tif (this._sinks === sink) {\n\t\t\tthis._sinks = null;\n\t\t} else if (this._sinks != null && typeof this._sinks !== \"function\") {\n\t\t\tthis._sinks.delete(sink);\n\t\t\tif (this._sinks.size === 1) {\n\t\t\t\tconst [only] = this._sinks;\n\t\t\t\tthis._sinks = only;\n\t\t\t} else if (this._sinks.size === 0) {\n\t\t\t\tthis._sinks = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Lifecycle ---\n\n\t/**\n\t * @internal First-sink activation. For a producer (no deps + fn),\n\t * invokes fn once. For a compute node (has deps), subscribes to every\n\t * dep with the pre-set-dirty trick so the first-run gate waits for\n\t * every dep to settle at least once.\n\t */\n\t_activate(): void {\n\t\tif (this._deps.length === 0) {\n\t\t\tif (this._fn) this._execFn();\n\t\t\treturn;\n\t\t}\n\t\t// Pre-set every dep as sentinel BEFORE subscribing. If the first dep\n\t\t// delivers DATA synchronously during its subscribe callback, the\n\t\t// sentinel gate holds fn until all deps have contributed at least one\n\t\t// value. _dirtyDepCount starts at 0 — actual DIRTY messages from deps\n\t\t// drive it; pre-dirtying all deps would cause any dep that only delivers\n\t\t// [[START]] (no DATA) to appear permanently \"mid-wave\", which blocks\n\t\t// terminal propagation from other deps incorrectly.\n\t\tthis._dirtyDepCount = 0;\n\t\t// Capture the initial length BEFORE subscribing. `_addDep` can fire\n\t\t// synchronously during a dep's subscribe callback (e.g., via\n\t\t// `autoTrackNode` discovery in `_execFn`) and push new DepRecords.\n\t\t// Iterating `this._deps.length` live would mean this loop also\n\t\t// subscribes the new dep that `_addDep` already subscribed — a\n\t\t// double-subscribe bug. Snapshot the length instead; `_addDep`\n\t\t// owns the subscribe + counter bump for any dep it adds.\n\t\tconst initialLen = this._deps.length;\n\t\t// subscribedCount tracks how many deps were successfully subscribed.\n\t\t// On failure, only those deps need to be rolled back.\n\t\tlet subscribedCount = 0;\n\t\ttry {\n\t\t\tfor (let i = 0; i < initialLen; i++) {\n\t\t\t\tconst depIdx = i;\n\t\t\t\tconst dep = this._deps[i];\n\t\t\t\t// Pre-set to noopUnsub so the liveness check inside the callback\n\t\t\t\t// passes during synchronous push-on-subscribe (dep.unsub is non-null),\n\t\t\t\t// while still blocking stale drainPhase2 closures that fire after\n\t\t\t\t// _deactivate sets dep.unsub = null.\n\t\t\t\tdep.unsub = noopUnsub;\n\t\t\t\tdep.unsub = dep.node.subscribe((msgs) => {\n\t\t\t\t\t// Liveness check: dep.unsub === null means this subscription was\n\t\t\t\t\t// cancelled by _deactivate. Drop deliveries from stale drainPhase2\n\t\t\t\t\t// closures that outlived the subscription.\n\t\t\t\t\tif (dep.unsub === null) return;\n\t\t\t\t\t// Track whether any tier-3+ settlement-class message arrived\n\t\t\t\t\t// (DATA/RESOLVED at tier 3, COMPLETE/ERROR at tier 4). We only\n\t\t\t\t\t// fire `_maybeRunFnOnSettlement` once per sink call when at\n\t\t\t\t\t// least one settlement was processed — preserves the old\n\t\t\t\t\t// `_onDepMessage` semantic (which only triggered the check\n\t\t\t\t\t// after settlement-class messages) while letting multi-DATA\n\t\t\t\t\t// batches fire `fn` exactly once with the full wave (Bug 1\n\t\t\t\t\t// fix). Tier classification goes through the central\n\t\t\t\t\t// `config.tierOf` utility per spec §5.11 — never hardcode\n\t\t\t\t\t// message-type checks here.\n\t\t\t\t\tconst tierOf = this._config.tierOf;\n\t\t\t\t\tlet sawSettlement = false;\n\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\tif (tierOf(m[0]) >= 3) sawSettlement = true;\n\t\t\t\t\t\tthis._config.onMessage(\n\t\t\t\t\t\t\tthis as unknown as NodeCtx,\n\t\t\t\t\t\t\tm,\n\t\t\t\t\t\t\t{ direction: \"down-in\", depIndex: depIdx },\n\t\t\t\t\t\t\tthis._actions,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (sawSettlement) this._maybeRunFnOnSettlement();\n\t\t\t\t});\n\t\t\t\tsubscribedCount++;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\t// Dep at index `subscribedCount` failed — its dep.unsub is still noopUnsub.\n\t\t\t// Mark it null so the liveness check treats any queued closures as stale.\n\t\t\tthis._deps[subscribedCount].unsub = null;\n\t\t\t// Unsubscribe all deps that DID subscribe successfully (0..subscribedCount-1).\n\t\t\tfor (let j = 0; j < subscribedCount; j++) {\n\t\t\t\tconst d = this._deps[j];\n\t\t\t\tif (d.unsub != null) {\n\t\t\t\t\tconst u = d.unsub;\n\t\t\t\t\td.unsub = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tu();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* best-effort: dep unsub errors are secondary */\n\t\t\t\t\t}\n\t\t\t\t\tresetDepRecord(d);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._dirtyDepCount = 0;\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * @internal Append a dep post-construction. Used by `autoTrackNode`\n\t * (runtime dep discovery) and `Graph.connect()` (post-construction\n\t * wiring). Subscribes immediately — if DATA arrives synchronously\n\t * during subscribe and fn is currently executing, the re-run is\n\t * deferred via `_pendingRerun` flag (see `_execFn` guard).\n\t *\n\t * **Dedup:** idempotent on duplicate `depNode` — if `depNode` is\n\t * already in `_deps`, returns the existing index without mutating\n\t * state. Callers can safely invoke `_addDep` without their own\n\t * \"already added\" check. `autoTrackNode` still keeps a `depIndexMap`\n\t * as a fast-path lookup for known deps (returning cached `data[idx]`\n\t * without calling `_addDep` at all); this internal dedup is the\n\t * backstop for any caller that doesn't track its own dep set.\n\t *\n\t * @returns The index of the new dep in `_deps`, or the existing index\n\t * if the dep was already present.\n\t */\n\t_addDep(depNode: Node): number {\n\t\t// Dedup: idempotent on repeated adds of the same dep. Matches\n\t\t// reference equality — the DepRecord is keyed by `node` identity,\n\t\t// so a caller with a fresh `depNode` that observes as equal but\n\t\t// is a distinct object is treated as a new dep.\n\t\tfor (let i = 0; i < this._deps.length; i++) {\n\t\t\tif (this._deps[i].node === depNode) return i;\n\t\t}\n\t\tconst depIdx = this._deps.length;\n\t\tconst record = createDepRecord(depNode);\n\t\tthis._deps.push(record);\n\n\t\t// If the node is inactive (no subscribers yet), defer subscribe to\n\t\t// _activate(). Subscribing here would create a duplicate subscription\n\t\t// because _activate() unconditionally subscribes to all _deps entries.\n\t\t// _activate() resets _dirtyDepCount to 0 before subscribing, so pre-\n\t\t// dirtying is wasted work and causes counter underflow on the first DATA.\n\t\tif (this._sinks == null) return depIdx;\n\n\t\trecord.dirty = true;\n\t\t// New dep starts dirty — bump the A3 counter to match the pre-set flag.\n\t\t// Skipping the helper here because the record isn't in the array yet when\n\t\t// the helper would early-return on `dep.dirty === true`.\n\t\tthis._dirtyDepCount++;\n\t\t// Topology change → downstream sees a new wave. Skip when already\n\t\t// dirty (we're inside an in-flight wave and have already emitted).\n\t\t// `_depDirtied` can't do this for us because `record.dirty` was\n\t\t// pre-set above, which short-circuits its DIRTY-emit path.\n\t\tif (this._status !== \"dirty\") this._emit(DIRTY_ONLY_BATCH);\n\t\trecord.unsub = noopUnsub;\n\t\ttry {\n\t\t\trecord.unsub = depNode.subscribe((msgs) => {\n\t\t\t\tif (record.unsub === null) return;\n\t\t\t\t// Tier-3+ classification via central `tierOf` per spec §5.11\n\t\t\t\t// (Bug 1 fix — see _activate for details).\n\t\t\t\tconst tierOf = this._config.tierOf;\n\t\t\t\tlet sawSettlement = false;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (tierOf(m[0]) >= 3) sawSettlement = true;\n\t\t\t\t\tthis._config.onMessage(\n\t\t\t\t\t\tthis as unknown as NodeCtx,\n\t\t\t\t\t\tm,\n\t\t\t\t\t\t{ direction: \"down-in\", depIndex: depIdx },\n\t\t\t\t\t\tthis._actions,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (sawSettlement) this._maybeRunFnOnSettlement();\n\t\t\t});\n\t\t} catch (err) {\n\t\t\t// Rollback: remove the dep record we just pushed and undo the dirty\n\t\t\t// counter. record.unsub stays null (already cleared below) so any\n\t\t\t// drainPhase2 closures queued by subscribe before it threw are\n\t\t\t// treated as stale and dropped by the liveness check.\n\t\t\trecord.unsub = null;\n\t\t\tthis._deps.pop();\n\t\t\tthis._dirtyDepCount--;\n\t\t\t// Propagate: _execFn's catch block will emit ERROR, which settles\n\t\t\t// downstream nodes that received the DIRTY we emitted above.\n\t\t\tthrow err;\n\t\t}\n\t\treturn depIdx;\n\t}\n\n\t/**\n\t * @internal Unsubscribes from deps, fires fn cleanup (both shapes),\n\t * clears wave/store state, and (for compute nodes) drops `_cached` per\n\t * the ROM/RAM rule. Idempotent: second call is a no-op.\n\t *\n\t * @param skipStatusUpdate — When `true`, the caller takes responsibility\n\t * for setting `_status` after deactivation (e.g. TEARDOWN always sets\n\t * `\"sentinel\"` unconditionally). When `false` (default), deactivation\n\t * applies the ROM rule: compute nodes → `\"sentinel\"`, state nodes\n\t * preserve their current status.\n\t */\n\t_deactivate(skipStatusUpdate = false): void {\n\t\t// Fn cleanup — both () => void and { deactivation } forms fire here.\n\t\t// Note on cleanup-throw ERRORs: when deactivation runs as part of\n\t\t// last-sink-unsubscribe, `_sinks` is already `null` by the time this\n\t\t// method is reached, so any ERROR emitted here lands on\n\t\t// `_deliverToSinks`'s null-guard and is dropped by design. Cleanup\n\t\t// errors during teardown are best-effort — callers that need to\n\t\t// observe them should install a host-level error channel via\n\t\t// `configure()`.\n\t\tconst cleanup = this._cleanup;\n\t\tthis._cleanup = undefined;\n\t\tif (typeof cleanup === \"function\") {\n\t\t\ttry {\n\t\t\t\tcleanup();\n\t\t\t} catch (err) {\n\t\t\t\tthis._emit([[ERROR, this._wrapFnError(\"cleanup threw\", err)]]);\n\t\t\t}\n\t\t} else if (\n\t\t\tcleanup != null &&\n\t\t\ttypeof (cleanup as { deactivation?: unknown }).deactivation === \"function\"\n\t\t) {\n\t\t\ttry {\n\t\t\t\t(cleanup as { deactivation: () => void }).deactivation();\n\t\t\t} catch (err) {\n\t\t\t\tthis._emit([[ERROR, this._wrapFnError(\"cleanup.deactivation threw\", err)]]);\n\t\t\t}\n\t\t}\n\n\t\t// Disconnect from deps.\n\t\tfor (const d of this._deps) {\n\t\t\tif (d.unsub != null) {\n\t\t\t\tconst u = d.unsub;\n\t\t\t\td.unsub = null;\n\t\t\t\ttry {\n\t\t\t\t\tu();\n\t\t\t\t} catch {\n\t\t\t\t\t/* best-effort teardown of upstream subscription */\n\t\t\t\t}\n\t\t\t}\n\t\t\tresetDepRecord(d);\n\t\t}\n\n\t\t// Clear wave + store state.\n\t\tthis._waveHasNewData = false;\n\t\tthis._hasNewTerminal = false;\n\t\tthis._hasCalledFnOnce = false;\n\t\tthis._paused = false;\n\t\tthis._pendingWave = false;\n\t\tthis._pendingRerun = false;\n\t\tthis._rerunDepth = 0;\n\t\tthis._store = {};\n\t\t// A3 counter reset with DepRecord bulk-reset.\n\t\tthis._dirtyDepCount = 0;\n\t\t// C0 pause state: TEARDOWN is a hard reset. Buffered tier-3/4\n\t\t// messages from a paused `resumeAll` node are DISCARDED rather than\n\t\t// drained, matching \"teardown wipes in-flight state\" semantics.\n\t\t// Clearing both structures also prevents a memory leak on\n\t\t// non-resubscribable teardown, and guarantees a resubscribable\n\t\t// re-activation starts from `_paused === false` with no stale\n\t\t// lockset carried over from the previous lifecycle.\n\t\tthis._pauseLocks = null;\n\t\tthis._pauseBuffer = null;\n\n\t\t// ROM/RAM: compute nodes clear cache; pure state nodes preserve it.\n\t\tif (this._fn != null) {\n\t\t\tthis._cached = undefined;\n\t\t}\n\n\t\tif (!skipStatusUpdate) {\n\t\t\t// Compute nodes → \"sentinel\" (cache cleared, no value).\n\t\t\t// - Non-terminal: always reset.\n\t\t\t// - Terminal + resubscribable: reset (resubscribable means\n\t\t\t// \"can be re-activated after terminal\" — the terminal state\n\t\t\t// doesn't persist across subscription cycles).\n\t\t\t// - Terminal + non-resubscribable: preserve (stream is over).\n\t\t\t// State nodes preserve status (ROM rule, value is intrinsic).\n\t\t\tif (this._fn != null || this._deps.length > 0) {\n\t\t\t\tif (!this._isTerminal || this._resubscribable) {\n\t\t\t\t\tthis._status = \"sentinel\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Dep message dispatch (§3.5 singleton default) ---\n\n\t/**\n\t * @internal Default per-tier dispatch for incoming dep messages. Called\n\t * by `defaultOnMessage`. Updates the DepRecord, triggers wave\n\t * completion, and forwards passthrough traffic.\n\t */\n\t_onDepMessage(depIndex: number, msg: Message): void {\n\t\tconst dep = this._deps[depIndex];\n\t\tconst t = msg[0];\n\n\t\t// Fire inspector hooks before default dispatch. Common case: no hooks\n\t\t// (undefined slot). Multiple observers can attach simultaneously.\n\t\tif (this._inspectorHooks != null) {\n\t\t\tconst ev: NodeInspectorHookEvent = { kind: \"dep_message\", depIndex, message: msg };\n\t\t\tfor (const hook of this._inspectorHooks) hook(ev);\n\t\t}\n\n\t\t// Tier 0 (START) — informational, no state change.\n\t\tif (t === START) return;\n\n\t\t// Tier 1\n\t\tif (t === DIRTY) {\n\t\t\tthis._depDirtied(dep);\n\t\t\treturn;\n\t\t}\n\t\tif (t === INVALIDATE) {\n\t\t\tthis._depInvalidated(dep);\n\t\t\tthis._emit(INVALIDATE_ONLY_BATCH);\n\t\t\treturn;\n\t\t}\n\n\t\t// Tier 2 — PAUSE / RESUME flow downstream (spec §1.2). Lock bookkeeping\n\t\t// happens inside `_emit` so both `_onDepMessage` (PAUSE received from\n\t\t// a dep) and external `node.down([[PAUSE, lockId]])` (source\n\t\t// directly issuing PAUSE) hit the same path. Here we just forward —\n\t\t// `_emit` will consume the lock, update `_paused`, and broadcast.\n\t\tif (t === PAUSE || t === RESUME) {\n\t\t\tthis._emit([msg]);\n\t\t\treturn;\n\t\t}\n\n\t\t// Tier 5\n\t\tif (t === TEARDOWN) {\n\t\t\tthis._emit(TEARDOWN_ONLY_BATCH);\n\t\t\treturn;\n\t\t}\n\n\t\t// Tier 3 / 4 — centralized transitions keep the settlement counters\n\t\t// (`_dirtyDepCount`, `_sentinelDepCount`) in sync with the flags on\n\t\t// every DepRecord. A3 optimization: the two counters let\n\t\t// `_maybeRunFnOnSettlement` check wave completion in O(1) instead\n\t\t// of two `every(...)` scans.\n\t\tif (t === DATA) {\n\t\t\tthis._depSettledAsData(dep, msg[1]);\n\t\t} else if (t === RESOLVED) {\n\t\t\tthis._depSettledAsResolved(dep);\n\t\t} else if (t === COMPLETE) {\n\t\t\tthis._depSettledAsTerminal(dep, true);\n\t\t} else if (t === ERROR) {\n\t\t\tthis._depSettledAsTerminal(dep, msg[1]);\n\t\t} else {\n\t\t\t// Unknown type: forward as-is (spec §1.3.6 forward-compat).\n\t\t\tthis._emit([msg]);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this._fn) {\n\t\t\t// Passthrough: forward DATA/RESOLVED 1:1 through the unified\n\t\t\t// emit pipeline. `_emit` owns tier sort + synthetic DIRTY\n\t\t\t// prefix + equals substitution uniformly — no manual framing.\n\t\t\tif (t === DATA || t === RESOLVED) {\n\t\t\t\tthis._emit([msg]);\n\t\t\t}\n\t\t\tif (t === COMPLETE || t === ERROR) {\n\t\t\t\tthis._maybeAutoTerminalAfterWave();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// NOTE: `_maybeRunFnOnSettlement()` is intentionally NOT called here.\n\t\t// It is invoked once at the end of the dep-subscribe callback's\n\t\t// message-iteration loop so that a multi-message sink call —\n\t\t// e.g. `dep.down([[DATA, a], [DATA, b]])` — fires `fn` exactly\n\t\t// once with the full wave batch (`batchData = [[a, b]]`), per the\n\t\t// `_execFn` contract at line 1462: \"fn must see the full wave batch\".\n\t}\n\n\t// --- Centralized dep-state transitions (A3 settlement counters) ---\n\t//\n\t// Every mutation to `DepRecord.dirty` / `DepRecord.prevData` /\n\t// `DepRecord.terminal` must go through one of these helpers so the\n\t// `_dirtyDepCount` and `_sentinelDepCount` counters stay in sync with\n\t// the per-record flags. `_maybeRunFnOnSettlement` reads the counters\n\t// and never re-scans the `_deps` array.\n\n\t/**\n\t * Called when a dep transitions `dirty: false → true` (either from an\n\t * incoming DIRTY, or pre-set during `_activate` / `_addDep` /\n\t * `_depInvalidated`). No-op if the dep is already dirty. Fires the\n\t * downstream DIRTY emit if we're the first to dirty this wave.\n\t */\n\tprivate _depDirtied(dep: DepRecord): void {\n\t\tif (dep.dirty) return;\n\t\tdep.dirty = true;\n\t\tdep.involvedThisWave = true;\n\t\tthis._dirtyDepCount++;\n\t\t// First dep to dirty this wave → propagate DIRTY to our own sinks.\n\t\tif (this._status !== \"dirty\") {\n\t\t\tthis._emit(DIRTY_ONLY_BATCH);\n\t\t}\n\t}\n\n\t/**\n\t * Called when a dep delivers new DATA: clears dirty, stores the payload,\n\t * marks wave-has-data, and — if this is the dep's first DATA — clears\n\t * its sentinel slot so the first-run gate can open.\n\t */\n\tprivate _depSettledAsData(dep: DepRecord, value: unknown): void {\n\t\tif (dep.dirty) {\n\t\t\tdep.dirty = false;\n\t\t\tthis._dirtyDepCount--;\n\t\t}\n\t\tdep.involvedThisWave = true;\n\t\tdep.dataBatch.push(value);\n\t\tthis._waveHasNewData = true;\n\t}\n\n\t/**\n\t * Called when a dep emits RESOLVED (wave settled, value unchanged).\n\t * Clears dirty; does NOT touch `prevData` / `terminal` / sentinel\n\t * count — sentinel only exits on first DATA or terminal, not RESOLVED.\n\t */\n\tprivate _depSettledAsResolved(dep: DepRecord): void {\n\t\tif (dep.dirty) {\n\t\t\tdep.dirty = false;\n\t\t\tthis._dirtyDepCount--;\n\t\t}\n\t}\n\n\t/**\n\t * Called when a dep delivers COMPLETE (`terminal = true`) or ERROR\n\t * (`terminal = errorPayload`). Clears dirty, stores the terminal, and\n\t * — if the dep had never contributed a DATA yet — leaves sentinel\n\t * since the gate treats \"terminated without data\" as gate-open too.\n\t */\n\tprivate _depSettledAsTerminal(dep: DepRecord, terminal: unknown): void {\n\t\tif (dep.dirty) {\n\t\t\tdep.dirty = false;\n\t\t\tthis._dirtyDepCount--;\n\t\t}\n\t\tdep.terminal = terminal;\n\t\tdep.involvedThisWave = true;\n\t\tthis._hasNewTerminal = true;\n\t}\n\n\t/**\n\t * Called when a dep emits INVALIDATE: clears prevData, terminal, and\n\t * dataBatch. The dep is now back in the \"never delivered a real value\"\n\t * state — `prevData === undefined` so the sentinel check in fn will fire.\n\t */\n\tprivate _depInvalidated(dep: DepRecord): void {\n\t\tdep.prevData = undefined;\n\t\tdep.terminal = undefined;\n\t\tdep.dataBatch.length = 0;\n\t\tif (!dep.dirty) {\n\t\t\tdep.dirty = true;\n\t\t\tdep.involvedThisWave = true;\n\t\t\tthis._dirtyDepCount++;\n\t\t} else {\n\t\t\tdep.involvedThisWave = false; // cancel prior wave involvement\n\t\t}\n\t}\n\n\tprivate _maybeRunFnOnSettlement(): void {\n\t\tif (this._isTerminal && !this._resubscribable) return;\n\t\t// O(1) gate: `_dirtyDepCount === 0` means every dep has delivered its\n\t\t// settlement for this wave (DATA, RESOLVED, or terminal).\n\t\tif (this._dirtyDepCount > 0) return;\n\t\tif (this._paused) {\n\t\t\tthis._pendingWave = true;\n\t\t\treturn;\n\t\t}\n\t\t// Pre-fn skip: when no dep sent DATA this wave (all RESOLVED), skip\n\t\t// fn and emit RESOLVED directly. Transitive-skip optimization — leaf\n\t\t// fn is not re-run when a mid-chain node produces the same value.\n\t\tif (!this._waveHasNewData && !this._hasNewTerminal && this._hasCalledFnOnce) {\n\t\t\tthis._clearWaveFlags();\n\t\t\tthis._emit(RESOLVED_ONLY_BATCH);\n\t\t\tthis._maybeAutoTerminalAfterWave();\n\t\t\treturn;\n\t\t}\n\t\tif (this._fn) this._execFn();\n\t\tthis._maybeAutoTerminalAfterWave();\n\t}\n\n\tprivate _maybeAutoTerminalAfterWave(): void {\n\t\tif (this._deps.length === 0) return;\n\t\tif (this._isTerminal) return;\n\t\t// ERROR always propagates (unless rescue operator opts out via\n\t\t// errorWhenDepsError: false). Checked independently of _autoComplete\n\t\t// so operators with completeWhenDepsComplete: false still get\n\t\t// automatic error forwarding.\n\t\tconst erroredDep = this._deps.find((d) => d.terminal !== undefined && d.terminal !== true);\n\t\tif (erroredDep != null) {\n\t\t\tif (this._autoError) {\n\t\t\t\tthis._emit([[ERROR, erroredDep.terminal]]);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// COMPLETE only when autoComplete is true and ALL deps are terminal.\n\t\tif (this._autoComplete && this._deps.every((d) => d.terminal !== undefined)) {\n\t\t\tthis._emit(COMPLETE_ONLY_BATCH);\n\t\t}\n\t}\n\n\t// --- Fn execution ---\n\n\t/**\n\t * @internal Runs the node fn once. Default cleanup (function form) fires\n\t * before the new run; `{ deactivation }` cleanup survives.\n\t */\n\tprivate _execFn(): void {\n\t\tif (!this._fn) return;\n\t\tif (this._isTerminal && !this._resubscribable) return;\n\t\t// Re-entrance guard: if fn is currently executing (e.g. _addDep\n\t\t// triggered a synchronous DATA delivery → _maybeRunFnOnSettlement\n\t\t// → _execFn), defer the re-run until the current fn returns.\n\t\tif (this._isExecutingFn) {\n\t\t\tthis._pendingRerun = true;\n\t\t\treturn;\n\t\t}\n\n\t\t// Pre-run cleanup — only the function-form cleanup fires here.\n\t\tconst prevCleanup = this._cleanup;\n\t\tif (typeof prevCleanup === \"function\") {\n\t\t\tthis._cleanup = undefined;\n\t\t\ttry {\n\t\t\t\tprevCleanup();\n\t\t\t} catch (err) {\n\t\t\t\tthis._emit([[ERROR, this._wrapFnError(\"cleanup threw\", err)]]);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// { deactivation } cleanup is preserved across runs.\n\n\t\t// Snapshot dep state BEFORE clearing wave flags so the snapshot\n\t\t// reflects \"this wave\" rather than \"next wave\".\n\t\t// dataBatch is copied here because _clearWaveFlags truncates the live\n\t\t// array in-place (length = 0) — the fn must see the full wave batch.\n\t\tconst batchData: (readonly unknown[] | undefined)[] = this._deps.map((d) =>\n\t\t\t!d.involvedThisWave ? undefined : d.dataBatch.length > 0 ? [...d.dataBatch] : [],\n\t\t);\n\t\t// Snapshot prevData BEFORE committing this wave's values — fn sees the\n\t\t// stable values from the end of the previous wave, not the current wave.\n\t\t// undefined = \"never sent DATA\". null is a valid DATA value.\n\t\tconst prevData: unknown[] = this._deps.map((d) => d.prevData);\n\t\t// Commit: advance each dep's prevData to this wave's last DATA so the\n\t\t// NEXT wave's fn snapshot sees the current wave as \"previous\".\n\t\t// Use the already-copied batchData rather than dep.dataBatch to avoid\n\t\t// any ordering dependency with _clearWaveFlags.\n\t\tfor (let i = 0; i < this._deps.length; i++) {\n\t\t\tconst batch = batchData[i];\n\t\t\tif (batch != null && batch.length > 0) {\n\t\t\t\tthis._deps[i].prevData = batch[batch.length - 1] as unknown;\n\t\t\t}\n\t\t}\n\t\tconst terminalDeps = this._deps.map((d) => d.terminal);\n\t\tconst ctx: FnCtx = { prevData, terminalDeps, store: this._store };\n\n\t\tthis._hasCalledFnOnce = true;\n\t\tthis._clearWaveFlags();\n\n\t\t// Fire inspector hooks before fn runs — for Graph.observe causal traces.\n\t\tif (this._inspectorHooks != null) {\n\t\t\tconst ev: NodeInspectorHookEvent = { kind: \"run\", batchData, prevData };\n\t\t\tfor (const hook of this._inspectorHooks) hook(ev);\n\t\t}\n\n\t\tthis._isExecutingFn = true;\n\t\ttry {\n\t\t\tconst result = this._fn(batchData, this._actions, ctx);\n\t\t\tif (typeof result === \"function\") {\n\t\t\t\tthis._cleanup = result;\n\t\t\t} else if (\n\t\t\t\tresult != null &&\n\t\t\t\ttypeof result === \"object\" &&\n\t\t\t\ttypeof (result as { deactivation?: unknown }).deactivation === \"function\"\n\t\t\t) {\n\t\t\t\tthis._cleanup = result as { deactivation: () => void };\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis._emit([[ERROR, this._wrapFnError(\"fn threw\", err)]]);\n\t\t} finally {\n\t\t\tthis._isExecutingFn = false;\n\t\t\t// Run any pending rerun BEFORE clearing wave flags so the\n\t\t\t// rerun's settlement check sees \"this wave had new data\" and\n\t\t\t// skips the pre-fn-skip optimization. Without this ordering,\n\t\t\t// autoTrackNode discovery's second pass gets swallowed by\n\t\t\t// the pre-fn-skip path.\n\t\t\tif (this._pendingRerun) {\n\t\t\t\tthis._pendingRerun = false;\n\t\t\t\tthis._rerunDepth += 1;\n\t\t\t\tif (this._rerunDepth > MAX_RERUN_DEPTH) {\n\t\t\t\t\tthis._rerunDepth = 0;\n\t\t\t\t\tthis._emit([\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\tERROR,\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t`Node \"${this.name}\": _pendingRerun depth exceeded ${MAX_RERUN_DEPTH} — likely a reactive cycle`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t]);\n\t\t\t\t} else {\n\t\t\t\t\tthis._maybeRunFnOnSettlement();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Chain converged — reset the depth counter for the next wave.\n\t\t\t\tthis._rerunDepth = 0;\n\t\t\t}\n\t\t\t// Clear flags after rerun so any involvedThisWave/dataBatch set by\n\t\t\t// fn's _addDep subscribe handshakes doesn't leak into the next\n\t\t\t// wave's snapshot. The inner _execFn (if any) already did\n\t\t\t// its own pre-snapshot clear; this is for the case where\n\t\t\t// fn added deps but no rerun fired.\n\t\t\tthis._clearWaveFlags();\n\t\t}\n\t}\n\n\tprivate _clearWaveFlags(): void {\n\t\tthis._waveHasNewData = false;\n\t\tthis._hasNewTerminal = false;\n\t\tfor (const d of this._deps) {\n\t\t\td.involvedThisWave = false;\n\t\t\td.dataBatch.length = 0;\n\t\t}\n\t}\n\n\tprivate _wrapFnError(label: string, err: unknown): Error {\n\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\treturn new Error(`Node \"${this.name}\": ${label}: ${msg}`, { cause: err });\n\t}\n\n\t// --- Framing (tier sort + synthetic DIRTY prefix) ---\n\n\t/**\n\t * @internal Stable tier sort + synthetic DIRTY prefix for an outgoing\n\t * batch. Fast path: already-monotone single-tier batches (the common\n\t * case from interned singletons like `DIRTY_ONLY_BATCH`) return the\n\t * input unchanged. General path: decorate-sort-undecorate into a new\n\t * array, then prepend `[DIRTY]` after any tier-0 START entries when\n\t * a tier-3 payload is present and the node isn't already dirty.\n\t *\n\t * Single source of truth for the spec §1.3.1 framing invariant. Every\n\t * outgoing path hits `_frameBatch` exactly once via `_emit`.\n\t */\n\tprivate _frameBatch(messages: Messages): Messages {\n\t\tconst tierOf = this._config.tierOf;\n\t\t// Fast path: single message.\n\t\tif (messages.length === 1) {\n\t\t\tconst t = tierOf(messages[0][0]);\n\t\t\tif (t === 3 && this._status !== \"dirty\") {\n\t\t\t\treturn [DIRTY_MSG, messages[0]];\n\t\t\t}\n\t\t\treturn messages;\n\t\t}\n\t\t// Check monotonicity and tier-3 presence in a single pass.\n\t\tlet monotone = true;\n\t\tlet hasTier3 = false;\n\t\tlet hasDirty = false;\n\t\tlet prevTier = -1;\n\t\tfor (const m of messages) {\n\t\t\tconst tier = tierOf(m[0]);\n\t\t\tif (tier < prevTier) monotone = false;\n\t\t\tif (tier === 3) hasTier3 = true;\n\t\t\tif (m[0] === DIRTY) hasDirty = true;\n\t\t\tprevTier = tier;\n\t\t}\n\t\tlet sorted: Messages = messages;\n\t\tif (!monotone) {\n\t\t\t// Stable sort via index-keyed decoration.\n\t\t\tconst indexed = messages.map((m, i) => ({ m, i, tier: tierOf(m[0]) }));\n\t\t\tindexed.sort((a, b) => a.tier - b.tier || a.i - b.i);\n\t\t\tsorted = indexed.map((x) => x.m);\n\t\t}\n\t\tif (hasTier3 && !hasDirty && this._status !== \"dirty\") {\n\t\t\t// Insert DIRTY after any tier-0 START entries to preserve\n\t\t\t// monotonicity.\n\t\t\tlet insertAt = 0;\n\t\t\twhile (insertAt < sorted.length && tierOf(sorted[insertAt][0]) === 0) insertAt++;\n\t\t\tif (insertAt === 0) return [DIRTY_MSG, ...sorted];\n\t\t\treturn [...sorted.slice(0, insertAt), DIRTY_MSG, ...sorted.slice(insertAt)];\n\t\t}\n\t\treturn sorted;\n\t}\n\n\t// --- Emit pipeline ---\n\n\t/**\n\t * @internal The unified dispatch waist — one call = one wave.\n\t *\n\t * Pipeline stages, in order:\n\t *\n\t * 1. Early-return on empty batch.\n\t * 2. Terminal filter — post-COMPLETE/ERROR only TEARDOWN/INVALIDATE\n\t * still propagate so graph teardown and cache-clear still work.\n\t * 3. Tier sort (stable) — the batch can be in any order when it\n\t * arrives; the walker downstream (`downWithBatch`) assumes\n\t * ascending tier monotone, and so does `_updateState`'s tier-3\n\t * slice walk. This is the single source of truth for ordering.\n\t * 4. Synthetic DIRTY prefix — if a tier-3 payload is present, no\n\t * DIRTY is already in the batch, and the node isn't already in\n\t * `\"dirty\"` status, prepend `[DIRTY]` after any tier-0 START\n\t * entries. Guarantees spec §1.3.1 (DIRTY precedes DATA within\n\t * the same batch) uniformly across every entry point.\n\t * 5. PAUSE/RESUME lock bookkeeping (C0) — update `_pauseLocks`,\n\t * derive `_paused`, filter unknown-lockId RESUME, replay\n\t * bufferAll buffer on final lock release.\n\t * 6. Meta TEARDOWN fan-out — notify meta children before\n\t * `_updateState`'s TEARDOWN branch calls `_deactivate`. Hoisted\n\t * out of the walk to keep `_updateState` re-entrance-free.\n\t * 7. `_updateState` — walk the batch in tier order, advancing\n\t * `_cached` / `_status` / `_versioning` and running equals\n\t * substitution on tier-3 DATA (§3.5.1). Returns\n\t * `{finalMessages, equalsError?}`.\n\t * 8. `downWithBatch` dispatch (or bufferAll capture if paused with\n\t * `pausable: \"resumeAll\"`).\n\t * 9. Recursive ERROR emission if equals threw mid-walk.\n\t *\n\t * `node.down` / `node.emit` / `actions.down` / `actions.emit` all\n\t * converge here — the unified `_emit` waist (spec §1.3.1).\n\t */\n\t_emit(messages: Messages): void {\n\t\tif (messages.length === 0) return;\n\n\t\t// Terminal filter: after COMPLETE/ERROR (non-resubscribable), only\n\t\t// TEARDOWN / INVALIDATE still propagate so graph teardown and cache-\n\t\t// clear still work.\n\t\tlet deliverable = messages;\n\t\tconst terminal = this._isTerminal;\n\t\tif (terminal && !this._resubscribable) {\n\t\t\tconst pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);\n\t\t\tif (pass.length === 0) return;\n\t\t\tdeliverable = pass;\n\t\t}\n\n\t\t// Tier sort + synthetic DIRTY prefix (stages 3 + 4 of the emit\n\t\t// pipeline). `_frameBatch` is a no-op for pre-sorted single-msg\n\t\t// batches (the common case from the tuple-interning A2 call sites);\n\t\t// otherwise it produces a stable tier-sorted copy with `[DIRTY]`\n\t\t// auto-prepended after any tier-0 messages when a tier-3 payload\n\t\t// is present and the node isn't already dirty.\n\t\tdeliverable = this._frameBatch(deliverable);\n\n\t\t// C0 — PAUSE/RESUME lock tracking. Every tier-2 tuple MUST carry a\n\t\t// `lockId` payload. Each PAUSE / RESUME updates `_pauseLocks` and\n\t\t// derives `_paused` from set size — multi-pauser correctness\n\t\t// guarantees a node only resumes when every lock it holds is\n\t\t// released. All tier-2 messages are forwarded unconditionally so\n\t\t// downstream nodes on the propagation path keep their own lock\n\t\t// sets consistent (subscribers that joined the graph before any\n\t\t// PAUSE see the full lock history). `pausable: false` sources\n\t\t// forward PAUSE/RESUME but do not track locks — appropriate for\n\t\t// reactive timers that must keep ticking. Unknown-lockId RESUME\n\t\t// is swallowed to keep `dispose()` idempotent.\n\t\tlet filtered: Message[] | null = null;\n\t\tfor (let i = 0; i < deliverable.length; i++) {\n\t\t\tconst m = deliverable[i];\n\t\t\tconst t = m[0];\n\t\t\tif (t !== PAUSE && t !== RESUME) {\n\t\t\t\tif (filtered != null) filtered.push(m);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (m.length < 2) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Node \"${this.name}\": [[${t === PAUSE ? \"PAUSE\" : \"RESUME\"}]] must ` +\n\t\t\t\t\t\t\"carry a lockId payload — bare PAUSE/RESUME is a protocol \" +\n\t\t\t\t\t\t\"violation (C0 rule). Use `[[PAUSE, lockId]]` / \" +\n\t\t\t\t\t\t\"`[[RESUME, lockId]]`.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tlet forward = true;\n\t\t\tif (this._pausable !== false) {\n\t\t\t\tconst lockId = m[1];\n\t\t\t\tif (t === PAUSE) {\n\t\t\t\t\tif (this._pauseLocks == null) this._pauseLocks = new Set();\n\t\t\t\t\tthis._pauseLocks.add(lockId);\n\t\t\t\t\tthis._paused = true;\n\t\t\t\t\tif (this._pausable === \"resumeAll\" && this._pauseBuffer == null) {\n\t\t\t\t\t\tthis._pauseBuffer = [];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// RESUME\n\t\t\t\t\tif (this._pauseLocks == null || !this._pauseLocks.has(lockId)) {\n\t\t\t\t\t\t// Unknown lockId — swallow to keep dispose idempotent.\n\t\t\t\t\t\tforward = false;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis._pauseLocks.delete(lockId);\n\t\t\t\t\t\tif (this._pauseLocks.size === 0) {\n\t\t\t\t\t\t\tthis._paused = false;\n\t\t\t\t\t\t\t// Replay bufferAll buffer through the outgoing\n\t\t\t\t\t\t\t// pipeline BEFORE forwarding RESUME — subscribers\n\t\t\t\t\t\t\t// observe the deferred DATAs as part of the\n\t\t\t\t\t\t\t// pre-RESUME wake-up.\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// D2 (2026-04-13) semantic note: the recursive\n\t\t\t\t\t\t\t// `_emit(drain)` goes through the full pipeline\n\t\t\t\t\t\t\t// including `_updateState`'s equals substitution.\n\t\t\t\t\t\t\t// A buffered `[DATA, v]` whose value matches the\n\t\t\t\t\t\t\t// *pre-pause* cache will collapse to RESOLVED on\n\t\t\t\t\t\t\t// replay — producer \"pulses\" that write the same\n\t\t\t\t\t\t\t// value while paused are absorbed. This matches\n\t\t\t\t\t\t\t// diamond-safety intent: `.cache` stays coherent\n\t\t\t\t\t\t\t// with \"the last DATA actually delivered to\n\t\t\t\t\t\t\t// sinks\". Producers that need pulse semantics\n\t\t\t\t\t\t\t// (every write observable regardless of value)\n\t\t\t\t\t\t\t// should set `equals: () => false` on the node.\n\t\t\t\t\t\t\tif (this._pauseBuffer != null && this._pauseBuffer.length > 0) {\n\t\t\t\t\t\t\t\tconst drain = this._pauseBuffer;\n\t\t\t\t\t\t\t\tthis._pauseBuffer = [];\n\t\t\t\t\t\t\t\tthis._emit(drain);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Kick the held wave forward if one was pending.\n\t\t\t\t\t\t\tif (this._pendingWave) {\n\t\t\t\t\t\t\t\tthis._pendingWave = false;\n\t\t\t\t\t\t\t\tthis._maybeRunFnOnSettlement();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!forward) {\n\t\t\t\tif (filtered == null) filtered = deliverable.slice(0, i) as Message[];\n\t\t\t} else if (filtered != null) {\n\t\t\t\tfiltered.push(m);\n\t\t\t}\n\t\t}\n\t\tif (filtered != null) {\n\t\t\tif (filtered.length === 0) return;\n\t\t\tdeliverable = filtered;\n\t\t}\n\n\t\t// Meta TEARDOWN fan-out happens BEFORE `_updateState` so the walk\n\t\t// stays re-entrance-free: a meta node's own dispatch must not\n\t\t// re-enter the parent's outgoing pipeline while `this._cached` /\n\t\t// `this._status` are mid-commit. This preserves the spec ordering\n\t\t// \"meta propagates before deactivation\" — `_updateState`'s TEARDOWN\n\t\t// branch still runs `_deactivate` AFTER the meta children\n\t\t// have already been notified here.\n\t\tif (this._hasMeta && deliverable.some((m) => m[0] === TEARDOWN)) {\n\t\t\tfor (const k of Object.keys(this.meta)) {\n\t\t\t\ttry {\n\t\t\t\t\t(this.meta[k] as NodeImpl)._emit(TEARDOWN_ONLY_BATCH);\n\t\t\t\t} catch {\n\t\t\t\t\t/* best-effort */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// State update + equals substitution (§3.5.1 invariant). Returns the\n\t\t// possibly-rewritten batch and an optional equals-throw error. When\n\t\t// equals throws mid-walk we still deliver the successfully-walked\n\t\t// prefix to sinks before emitting ERROR, preserving cache/wire\n\t\t// coherence (user P2 option (i)).\n\t\tconst { finalMessages, equalsError } = this._updateState(deliverable);\n\n\t\t// Global inspector fan-out (Redux-DevTools-style tracer). Fires once\n\t\t// per outgoing batch, gated by `inspectorEnabled` so production paths\n\t\t// pay one boolean check. Hook errors are swallowed — instrumentation\n\t\t// must not break the data plane.\n\t\tif (finalMessages.length > 0 && this._config.inspectorEnabled) {\n\t\t\tconst inspector = this._config.globalInspector;\n\t\t\tif (inspector != null) {\n\t\t\t\ttry {\n\t\t\t\t\tinspector({ kind: \"emit\", node: this, messages: finalMessages });\n\t\t\t\t} catch {\n\t\t\t\t\t/* best-effort */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (finalMessages.length > 0) {\n\t\t\t// BufferAll: while paused with `pausable: \"resumeAll\"`, buffer\n\t\t\t// tier-3/4 payloads in order. Tier 0–2 and tier 5 continue to\n\t\t\t// dispatch synchronously — START/DIRTY/RESUME/PAUSE/TEARDOWN\n\t\t\t// must stay live so subscribers, downstream pausers, and graph\n\t\t\t// teardown all observe them. Cache/status advance has already\n\t\t\t// happened via `_updateState`, so the replay later just pushes\n\t\t\t// the deferred messages back through `downWithBatch`.\n\t\t\tif (this._paused && this._pausable === \"resumeAll\" && this._pauseBuffer != null) {\n\t\t\t\tconst tierOf = this._config.tierOf;\n\t\t\t\tconst immediate: Message[] = [];\n\t\t\t\tfor (const m of finalMessages) {\n\t\t\t\t\tconst tier = tierOf(m[0]);\n\t\t\t\t\tif (tier < 3 || tier === 5) {\n\t\t\t\t\t\timmediate.push(m);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis._pauseBuffer.push(m);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (immediate.length > 0) {\n\t\t\t\t\tthis._dispatchOrAccumulate(immediate);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis._dispatchOrAccumulate(finalMessages);\n\t\t\t}\n\t\t}\n\n\t\tif (equalsError != null) {\n\t\t\tthis._emit([[ERROR, equalsError]]);\n\t\t}\n\t}\n\n\t/**\n\t * @internal Walk an outgoing (already-framed) batch, updating own\n\t * cache / status / versioning and running equals substitution on\n\t * every tier-3 DATA (§3.5.1). Framing — tier sort and synthetic\n\t * DIRTY prefix — has already happened upstream in `_frameBatch`.\n\t * This walk trusts the input is in monotone tier order and that the\n\t * spec §1.3.1 DIRTY/RESOLVED precedence invariant is already\n\t * satisfied by the frame.\n\t *\n\t * Equals substitution: every DATA payload is compared against the\n\t * live `_cached`; when equal, the tuple is rewritten to `[RESOLVED]`\n\t * in a per-call copy and cache is not re-advanced. `.cache` remains\n\t * coherent with \"the last DATA payload this node actually sent\n\t * downstream\".\n\t *\n\t * Returns `{ finalMessages, equalsError? }`:\n\t * - `finalMessages` — the array to deliver to sinks (may be\n\t * `messages` unchanged, a rewritten copy with DATA→RESOLVED\n\t * substitutions, or a truncated prefix when equals throws mid-walk).\n\t * - `equalsError` — present only when the configured `equals` function\n\t * threw on some DATA message. `_emit` delivers the prefix first,\n\t * then emits a fresh ERROR batch via a recursive `_emit` call so\n\t * subscribers observe `[...walked_prefix, ERROR]` in order.\n\t */\n\tprivate _updateState(messages: Messages): {\n\t\tfinalMessages: Messages;\n\t\tequalsError?: Error;\n\t} {\n\t\tconst tierOf = this._config.tierOf;\n\t\tlet rewritten: Message[] | undefined;\n\t\tlet equalsError: Error | undefined;\n\t\tlet abortedAt = -1;\n\t\t// Count tier-3 messages (DATA + RESOLVED) in the batch. Equals\n\t\t// substitution is only worthwhile for a single tier-3 message —\n\t\t// an unchanged value can be rewritten to RESOLVED, enabling the\n\t\t// downstream pre-fn skip. With multiple tier-3 messages the\n\t\t// downstream fn must run regardless, so equals on each is wasted.\n\t\tlet dataCount = 0;\n\t\tfor (const m of messages) {\n\t\t\tif (tierOf(m[0]) === 3) dataCount++;\n\t\t}\n\t\tconst checkEquals = dataCount <= 1;\n\t\t// Version advances once per batch wave, not per DATA in the batch.\n\t\t// _cached only retains the last DATA value, so intermediate version\n\t\t// entries would reference values that can never be retrieved from cache.\n\t\t// Pre-scan for the last DATA index so we know when to fire advanceVersion.\n\t\tlet lastDataIdx = -1;\n\t\tif (this._versioning != null && dataCount > 1) {\n\t\t\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\t\t\tif (messages[i][0] === DATA) {\n\t\t\t\t\tlastDataIdx = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst m = messages[i];\n\t\t\tconst t = m[0];\n\t\t\tif (t === DATA) {\n\t\t\t\tif (m.length >= 2) {\n\t\t\t\t\tlet unchanged = false;\n\t\t\t\t\tif (checkEquals && this._cached !== undefined) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tunchanged = this._equals(this._cached as T, m[1] as T);\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t// Abort walk on equals throw: deliver successfully-walked\n\t\t\t\t\t\t\t// prefix, then caller emits ERROR. Excludes the throwing\n\t\t\t\t\t\t\t// message from the prefix.\n\t\t\t\t\t\t\tequalsError = this._wrapFnError(\"equals threw\", err);\n\t\t\t\t\t\t\tabortedAt = i;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (unchanged) {\n\t\t\t\t\t\tif (rewritten == null) rewritten = messages.slice(0, i) as Message[];\n\t\t\t\t\t\trewritten.push(RESOLVED_MSG);\n\t\t\t\t\t\tthis._status = \"resolved\";\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis._cached = m[1] as T;\n\t\t\t\t\tif (this._versioning != null) {\n\t\t\t\t\t\t// dataCount <= 1: lastDataIdx is -1; advance unconditionally\n\t\t\t\t\t\t// (single DATA, correct as before).\n\t\t\t\t\t\t// dataCount > 1: only advance on the last DATA in the batch.\n\t\t\t\t\t\tif (lastDataIdx < 0 || i === lastDataIdx) {\n\t\t\t\t\t\t\tadvanceVersion(this._versioning, m[1], this._hashFn);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis._status = \"settled\";\n\t\t\t\tif (rewritten != null) rewritten.push(m);\n\t\t\t} else {\n\t\t\t\tif (rewritten != null) rewritten.push(m);\n\t\t\t\tif (t === DIRTY) {\n\t\t\t\t\tthis._status = \"dirty\";\n\t\t\t\t} else if (t === RESOLVED) {\n\t\t\t\t\tthis._status = \"resolved\";\n\t\t\t\t} else if (t === COMPLETE) {\n\t\t\t\t\tthis._status = \"completed\";\n\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\tthis._status = \"errored\";\n\t\t\t\t} else if (t === INVALIDATE) {\n\t\t\t\t\tthis._cached = undefined;\n\t\t\t\t\tthis._status = \"dirty\";\n\t\t\t\t\t// Function-form cleanup fires on invalidate (treats as \"re-run\").\n\t\t\t\t\tconst c = this._cleanup;\n\t\t\t\t\tif (typeof c === \"function\") {\n\t\t\t\t\t\tthis._cleanup = undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tc();\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* best-effort */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (t === TEARDOWN) {\n\t\t\t\t\tif (this._resetOnTeardown) this._cached = undefined;\n\t\t\t\t\t// Meta TEARDOWN fan-out was already performed by `_emit`\n\t\t\t\t\t// before this walk. Deactivate now that meta children\n\t\t\t\t\t// have been notified.\n\t\t\t\t\tthis._deactivate(/* skipStatusUpdate */ true);\n\t\t\t\t\t// TEARDOWN is a hard reset — unconditionally \"sentinel\",\n\t\t\t\t\t// even if the node was previously completed/errored.\n\t\t\t\t\tthis._status = \"sentinel\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst base: Messages =\n\t\t\tabortedAt >= 0\n\t\t\t\t? ((rewritten ?? (messages.slice(0, abortedAt) as Messages)) as Messages)\n\t\t\t\t: (rewritten ?? messages);\n\t\treturn equalsError != null ? { finalMessages: base, equalsError } : { finalMessages: base };\n\t}\n\n\tprivate _deliverToSinks = (messages: Messages): void => {\n\t\tif (this._sinks == null) return;\n\t\tif (typeof this._sinks === \"function\") {\n\t\t\tthis._sinks(messages);\n\t\t\treturn;\n\t\t}\n\t\t// Snapshot: a sink callback may unsubscribe itself or others\n\t\t// mid-iteration. Iterating the live Set would skip not-yet-visited\n\t\t// sinks that were removed.\n\t\tconst snapshot = [...this._sinks];\n\t\tfor (const sink of snapshot) sink(messages);\n\t};\n\n\t/**\n\t * @internal Dispatch entry point that respects the per-batch emit\n\t * accumulator (Bug 2). Inside an explicit `batch()` scope, append to\n\t * `_batchPendingMessages` and register a flush hook on first append.\n\t * Outside batch — or during a drain (where `flushInProgress` is true\n\t * but `batchDepth` is 0) — dispatch synchronously through `downWithBatch`.\n\t *\n\t * Per-emit state updates (`_frameBatch`, `_updateState`) have already\n\t * happened by the time we reach here; only the **downstream delivery**\n\t * is coalesced. Cache, version, and status are visible mid-batch on\n\t * the emitting node itself.\n\t */\n\tprivate _dispatchOrAccumulate(messages: Messages): void {\n\t\tif (isExplicitlyBatching()) {\n\t\t\tif (this._batchPendingMessages === null) {\n\t\t\t\tthis._batchPendingMessages = [];\n\t\t\t\tregisterBatchFlushHook(() => this._flushBatchPending());\n\t\t\t}\n\t\t\tfor (const m of messages) this._batchPendingMessages.push(m);\n\t\t\treturn;\n\t\t}\n\t\tdownWithBatch(this._deliverToSinks, messages, this._config.tierOf);\n\t}\n\n\t/**\n\t * @internal Flushes the accumulated batch through `downWithBatch` and\n\t * clears the pending state. Idempotent — safe to call when pending is\n\t * already null or empty (e.g. on a `batch()` throw, where the hook\n\t * fires for cleanup but the drainPhase queues are wiped after).\n\t *\n\t * Critical: the accumulated batch is interleaved per-emit framings like\n\t * `[DIRTY, DATA(1), DIRTY, DATA(2)]` — non-monotone tier order. We must\n\t * re-frame to sort by tier before handing to `downWithBatch`, which\n\t * assumes pre-sorted input. `_frameBatch` also handles the synthetic\n\t * DIRTY prepend rule (no-op here — `hasDirty` is true since each\n\t * accumulated emit already carries its own DIRTY prefix).\n\t */\n\tprivate _flushBatchPending(): void {\n\t\tconst pending = this._batchPendingMessages;\n\t\tif (pending === null) return;\n\t\tthis._batchPendingMessages = null;\n\t\tif (pending.length === 0) return;\n\t\tconst framed = this._frameBatch(pending);\n\t\tdownWithBatch(this._deliverToSinks, framed, this._config.tierOf);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nconst isNodeArray = (value: unknown): value is readonly Node[] => Array.isArray(value);\nconst isNodeOptionsObject = (value: unknown): value is NodeOptions<unknown> =>\n\ttypeof value === \"object\" && value != null && !Array.isArray(value);\n\n/**\n * Creates a reactive {@link Node} — the single GraphReFly primitive (§2).\n *\n * Typical shapes:\n * - `node([])` / `node({ initial: v })` — a manual source (state node).\n * - `node(producerFn, opts)` — a producer that runs on first-subscribe.\n * - `node(deps, computeFn, opts)` — a derived / effect node.\n *\n * For value-returning computations, prefer the sugar factories in `sugar.ts`\n * (`state`, `derived`, `effect`, `producer`, `dynamicNode`), which wrap user\n * fns with `actions.emit(userFn(data))`. Calling `node()` directly gives you\n * the raw `NodeFn` contract: explicit emission via `actions`, cleanup return.\n */\nexport function node<T = unknown>(\n\tdepsOrFn?: readonly Node[] | NodeFn | NodeOptions<T>,\n\tfnOrOpts?: NodeFn | NodeOptions<T>,\n\toptsArg?: NodeOptions<T>,\n): Node<T> {\n\tconst deps: readonly Node[] = isNodeArray(depsOrFn) ? depsOrFn : [];\n\tconst fn: NodeFn | undefined =\n\t\ttypeof depsOrFn === \"function\"\n\t\t\t? depsOrFn\n\t\t\t: typeof fnOrOpts === \"function\"\n\t\t\t\t? fnOrOpts\n\t\t\t\t: undefined;\n\tlet opts: NodeOptions<T> = {};\n\tif (isNodeArray(depsOrFn)) {\n\t\topts = ((isNodeOptionsObject(fnOrOpts) ? fnOrOpts : optsArg) ?? {}) as NodeOptions<T>;\n\t} else if (isNodeOptionsObject(depsOrFn)) {\n\t\topts = depsOrFn as NodeOptions<T>;\n\t} else {\n\t\topts = ((isNodeOptionsObject(fnOrOpts) ? fnOrOpts : optsArg) ?? {}) as NodeOptions<T>;\n\t}\n\treturn new NodeImpl<T>(deps, fn, opts);\n}\n","import type { Actor } from \"./actor.js\";\nimport { accessHintForGuard } from \"./guard.js\";\nimport { type Node, type NodeDescribeKind, NodeImpl } from \"./node.js\";\n\n/**\n * JSON-shaped slice of a node for `Graph.describe()`\n * (GRAPHREFLY-SPEC §3.6, Appendix B).\n */\nexport type DescribeNodeOutput = {\n\ttype: NodeDescribeKind;\n\tstatus?: Node[\"status\"];\n\tdeps: string[];\n\tmeta?: Record<string, unknown>;\n\tname?: string;\n\tvalue?: unknown;\n\t/** True when the node is in `\"sentinel\"` state (no value ever). */\n\tsentinel?: boolean;\n\tv?: { id: string; version: number; cid?: string; prev?: string | null };\n\tguard?: string;\n\tlastMutation?: Readonly<{ actor: Actor; timestamp_ns: number }>;\n\t/**\n\t * Latest reason annotation attached via `graph.trace(path, reason)`,\n\t * when present. Populated by `Graph.describe` only — `describeNode`\n\t * has no graph context.\n\t */\n\treason?: string;\n};\n\n/** Detail level for progressive disclosure (Phase 3.3b). */\nexport type DescribeDetail = \"minimal\" | \"standard\" | \"full\";\n\n/** Valid field names for `describe({ fields: [...] })`. */\nexport type DescribeField =\n\t| \"type\"\n\t| \"status\"\n\t| \"value\"\n\t| \"deps\"\n\t| \"meta\"\n\t| \"v\"\n\t| \"guard\"\n\t| \"lastMutation\"\n\t| `meta.${string}`;\n\n/** Resolve which fields to include based on detail level or explicit field list. */\nexport function resolveDescribeFields(\n\tdetail?: DescribeDetail,\n\tfields?: readonly DescribeField[],\n): Set<string> | null {\n\tif (fields != null && fields.length > 0) return new Set(fields);\n\tswitch (detail) {\n\t\tcase \"standard\":\n\t\t\treturn new Set([\"type\", \"status\", \"value\", \"deps\", \"meta\", \"v\"]);\n\t\tcase \"full\":\n\t\t\treturn null;\n\t\tdefault:\n\t\t\treturn new Set([\"type\", \"deps\"]);\n\t}\n}\n\nfunction inferDescribeType(n: NodeImpl): NodeDescribeKind {\n\tif (n._describeKind != null) return n._describeKind;\n\tconst hasDeps = n._deps.length > 0;\n\tif (!hasDeps) return n._fn != null ? \"producer\" : \"state\";\n\t// With deps: derived (passthrough falls under derived, no fn → derived shape).\n\treturn \"derived\";\n}\n\n/**\n * Reads the current cached value of every companion meta field on a node,\n * suitable for merging into `describe()`-style JSON.\n *\n * Values come from {@link Node.cache}, which returns the last settled cache.\n * If a meta field is in `\"dirty\"` status (DIRTY received, DATA pending), the\n * snapshot contains the *previous* value — check `node.meta[key].status`\n * when freshness matters.\n */\nexport function metaSnapshot(node: Node): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(node.meta)) {\n\t\ttry {\n\t\t\tout[key] = child.cache;\n\t\t} catch {\n\t\t\t/* omit key — describe tooling still gets other fields */\n\t\t}\n\t}\n\treturn out;\n}\n\n/**\n * Builds a single-node slice of `Graph.describe()` JSON (structure + `meta`\n * snapshot). Parity with `graphrefly-py` `describe_node`.\n */\nexport function describeNode(node: Node, includeFields?: Set<string> | null): DescribeNodeOutput {\n\tconst all = includeFields == null;\n\tconst metaKeys: string[] | null =\n\t\t!all && includeFields != null\n\t\t\t? [...includeFields].filter((f) => f.startsWith(\"meta.\")).map((f) => f.slice(5))\n\t\t\t: null;\n\tconst wantsMeta = all || includeFields!.has(\"meta\") || (metaKeys != null && metaKeys.length > 0);\n\n\tlet type: NodeDescribeKind = \"state\";\n\tlet deps: string[] = [];\n\n\tif (node instanceof NodeImpl) {\n\t\ttype = inferDescribeType(node);\n\t\tdeps = node._deps.map((d) => d.node.name ?? \"\");\n\t}\n\n\tconst out: DescribeNodeOutput = { type, deps };\n\n\tif (all || includeFields!.has(\"status\")) {\n\t\tout.status = node.status;\n\t}\n\n\tconst guard = node instanceof NodeImpl ? node._guard : undefined;\n\n\tif (wantsMeta) {\n\t\tconst rawMeta: Record<string, unknown> = { ...metaSnapshot(node) };\n\t\tif (guard != null && rawMeta.access === undefined) {\n\t\t\trawMeta.access = accessHintForGuard(guard);\n\t\t}\n\t\tif (metaKeys != null && metaKeys.length > 0 && !includeFields!.has(\"meta\")) {\n\t\t\tconst filtered: Record<string, unknown> = {};\n\t\t\tfor (const k of metaKeys) {\n\t\t\t\tif (k in rawMeta) filtered[k] = rawMeta[k];\n\t\t\t}\n\t\t\tout.meta = filtered;\n\t\t} else {\n\t\t\tout.meta = rawMeta;\n\t\t}\n\t}\n\n\tif (node.name != null) {\n\t\tout.name = node.name;\n\t}\n\n\tif (all || includeFields!.has(\"value\")) {\n\t\tif (node.status === \"sentinel\") out.sentinel = true;\n\t\ttry {\n\t\t\tout.value = node.cache;\n\t\t} catch {\n\t\t\t/* omit value */\n\t\t}\n\t}\n\n\tif ((all || includeFields!.has(\"v\")) && node.v != null) {\n\t\tconst vInfo: NonNullable<DescribeNodeOutput[\"v\"]> = {\n\t\t\tid: node.v.id,\n\t\t\tversion: node.v.version,\n\t\t};\n\t\tif (\"cid\" in node.v) {\n\t\t\tvInfo.cid = (node.v as { cid: string }).cid;\n\t\t\tvInfo.prev = (node.v as { prev: string | null }).prev;\n\t\t}\n\t\tout.v = vInfo;\n\t}\n\n\tif (all || includeFields!.has(\"guard\")) {\n\t\tif (guard != null) out.guard = accessHintForGuard(guard);\n\t}\n\n\tif (all || includeFields!.has(\"lastMutation\")) {\n\t\tif (node.lastMutation != null) out.lastMutation = node.lastMutation;\n\t}\n\n\treturn out;\n}\n","/**\n * Sugar constructors over the raw `node()` primitive.\n *\n * Each factory wraps a user-friendly function into the canonical\n * `NodeFn = (data, actions, ctx) => cleanup | void` shape, then calls\n * `node(...)`. This is the only place `actions.emit(...)` is invoked\n * on behalf of the user — if you need finer control (multi-emission,\n * raw `actions.down` / `actions.up`, cleanup return), use the raw\n * `node()` factory from `./node.js` directly.\n *\n * See SESSION-foundation-redesign.md §8.5 + §10.6 for the rewrite.\n */\n\nimport type { NodeActions } from \"./config.js\";\nimport { RESOLVED } from \"./messages.js\";\nimport {\n\ttype FnCtx,\n\ttype Node,\n\ttype NodeFn,\n\ttype NodeFnCleanup,\n\tNodeImpl,\n\ttype NodeOptions,\n\tnode,\n} from \"./node.js\";\n\n// ---------------------------------------------------------------------------\n// Shared sentinel guard\n// ---------------------------------------------------------------------------\n\n/**\n * Returns `true` when fn should be suppressed (RESOLVED emitted instead).\n *\n * Fires when `allowPartial` is `false` and any dep has never delivered DATA:\n * `data[i]` absent this wave AND `ctx.prevData[i] === undefined`.\n *\n * `undefined` is the protocol-reserved \"never sent DATA\" sentinel. `null` is\n * a valid DATA value and will NOT trigger the guard.\n */\nfunction sentinelGuard(\n\tbatchData: readonly (readonly unknown[] | undefined)[],\n\tctx: FnCtx,\n\tallowPartial: boolean,\n): boolean {\n\tif (allowPartial) return false;\n\treturn batchData.some(\n\t\t(batch, i) => !(batch != null && batch.length > 0) && ctx.prevData[i] === undefined,\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// state — manual source with an optional initial value\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a manual source node. Drive it with `state.emit(v)` (framed,\n * diamond-safe) or `state.down([[DATA, v]])` (raw compat path).\n *\n * @param initial - Starting cached value. Pass `undefined` or `null`\n * explicitly to cache that value; omit to leave the node in `\"sentinel\"`.\n * @param opts - Optional {@link NodeOptions} (excluding `initial`).\n */\nexport function state<T>(initial: T, opts?: Omit<NodeOptions<T>, \"initial\">): Node<T> {\n\treturn node<T>([], { ...opts, initial });\n}\n\n// ---------------------------------------------------------------------------\n// producer — no-deps source with a compute body\n// ---------------------------------------------------------------------------\n\n/**\n * User-level producer compute: runs once on first-subscriber activation.\n * Receives `actions` for imperative emission and `ctx` for FnCtx (typically\n * only `store` is useful on a producer — no deps means `prevData` and\n * `terminalDeps` are empty).\n */\nexport type ProducerFn = (\n\tactions: NodeActions,\n\tctx: FnCtx,\n\t// biome-ignore lint/suspicious/noConfusingVoidType: matches NodeFn — see its JSDoc.\n) => NodeFnCleanup | void;\n\n/**\n * Creates a producer node with no deps; `fn` runs once when the first\n * subscriber connects. Return a cleanup function (`() => void`) or\n * `{ deactivation: () => void }` to register teardown.\n *\n * @example\n * ```ts\n * const ticker = producer((actions) => {\n * const id = setInterval(() => actions.emit(Date.now()), 1000);\n * return () => clearInterval(id);\n * });\n * ```\n */\nexport function producer<T = unknown>(fn: ProducerFn, opts?: NodeOptions<T>): Node<T> {\n\tconst wrapped: NodeFn = (_data, actions, ctx) => fn(actions, ctx) ?? undefined;\n\treturn node<T>(wrapped, { describeKind: \"producer\", ...opts });\n}\n\n// ---------------------------------------------------------------------------\n// derived — dep-driven pure compute\n// ---------------------------------------------------------------------------\n\n/**\n * User-level derived compute: receives the latest DATA from each dep and\n * returns the new value. The sugar wraps it with `actions.emit(fn(...))`\n * so the return value flows through the framed emit pipeline.\n *\n * For derived nodes that need to inspect `ctx.prevData` / `ctx.terminalDeps`\n * / `ctx.store`, accept the optional second parameter.\n */\nexport type DerivedFn<T> = (data: readonly unknown[], ctx: FnCtx) => T | undefined | null;\n\n/**\n * Creates a derived node that computes **one output per wave** from the latest\n * value of each dependency — **snapshot / combine semantics**.\n *\n * `fn` receives one scalar per dep (the last DATA value seen this wave, or the\n * prior-wave value as fallback). It is called once per settled wave and emits\n * a single value via `actions.emit`. The equals check then suppresses the\n * emission as `RESOLVED` if the output has not changed.\n *\n * **Not for streaming one-to-one transforms.** If each DATA value in a batch\n * must produce a corresponding output (e.g. transforming every item emitted by\n * `fromIter` individually), use {@link map} or raw `node()` with full batch\n * iteration instead. `derived` only sees the *last* value per dep when a batch\n * carries multiple DATAs.\n *\n * @example\n * ```ts\n * const a = state(1);\n * const b = derived([a], ([x]) => (x as number) * 2);\n * ```\n */\nexport function derived<T = unknown>(\n\tdeps: readonly Node[],\n\tfn: DerivedFn<T>,\n\topts?: NodeOptions<T> & { partial?: boolean },\n): Node<T> {\n\tconst allowPartial = opts?.partial ?? false;\n\tconst wrapped: NodeFn = (batchData, actions, ctx) => {\n\t\t// Sentinel guard: if any dep has never sent DATA, emit RESOLVED.\n\t\t// Uses ctx.prevData[i] === undefined (the \"never sent\" sentinel).\n\t\t// null is valid DATA and won't trigger this. Skipped when partial:true.\n\t\tif (sentinelGuard(batchData, ctx, allowPartial)) {\n\t\t\tactions.down([[RESOLVED]]);\n\t\t\treturn undefined;\n\t\t}\n\t\t// Unwrap batch-per-dep to single latest scalar per dep.\n\t\t// Batch non-null+non-empty → take last value from this wave;\n\t\t// otherwise fall back to ctx.prevData[i] (last value from prior wave).\n\t\t// undefined means \"never sent DATA\" — sentinelGuard already blocks this\n\t\t// fn when partial:false and any dep is unset, so partial:true callers\n\t\t// receive undefined for uninitiated deps (same as JS convention).\n\t\tconst data = batchData.map((batch, i) =>\n\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t);\n\t\tactions.emit(fn(data, ctx));\n\t\treturn undefined;\n\t};\n\treturn node<T>(deps, wrapped, { describeKind: \"derived\", ...opts });\n}\n\n// ---------------------------------------------------------------------------\n// effect — dep-driven side effect, no auto-emit\n// ---------------------------------------------------------------------------\n\n/**\n * User-level effect compute: fires when deps settle. Return value is NOT\n * auto-emitted — use `actions.emit(v)` / `actions.down(msgs)` explicitly if\n * the effect also wants to produce downstream messages. Return a cleanup\n * function or `{ deactivation }` to register teardown.\n */\nexport type EffectFn = (\n\tdata: readonly unknown[],\n\tactions: NodeActions,\n\tctx: FnCtx,\n\t// biome-ignore lint/suspicious/noConfusingVoidType: matches NodeFn — see its JSDoc.\n) => NodeFnCleanup | void;\n\n/**\n * Runs a side-effect when deps settle. Return value is not auto-emitted.\n *\n * @example\n * ```ts\n * effect([source], ([v]) => {\n * console.log(v);\n * });\n * ```\n */\nexport function effect(\n\tdeps: readonly Node[],\n\tfn: EffectFn,\n\topts?: NodeOptions<unknown> & { partial?: boolean },\n): Node<unknown> {\n\tconst allowPartial = opts?.partial ?? false;\n\tconst wrapped: NodeFn = (batchData, actions, ctx) => {\n\t\t// Sentinel guard: hold effect until all deps have initialised.\n\t\t// Matches pre-wave2 framework gate behaviour. Use partial:true to allow\n\t\t// the effect to fire before all deps have delivered their first value.\n\t\tif (sentinelGuard(batchData, ctx, allowPartial)) {\n\t\t\tactions.down([[RESOLVED]]);\n\t\t\treturn undefined;\n\t\t}\n\t\tconst data = batchData.map((batch, i) =>\n\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t);\n\t\treturn fn(data, actions, ctx) ?? undefined;\n\t};\n\treturn node(deps, wrapped, { describeKind: \"effect\", ...opts });\n}\n\n// ---------------------------------------------------------------------------\n// dynamicNode — track-proxy wrapper over `derived`\n// ---------------------------------------------------------------------------\n\n/**\n * Proxy handed to a {@link DynamicFn}. `track(dep)` returns the dep's\n * latest DATA payload, as delivered through the protocol. Reading from\n * `track` does NOT bypass the message protocol — it reads the internal\n * `DepRecord.prevData` (the stable end-of-previous-wave value) that\n * `_onDepMessage` already populated. If a dep has not yet sent DATA,\n * `track` returns `undefined`.\n */\nexport type TrackFn = (dep: Node) => unknown;\n\n/** User-level dynamicNode compute. */\nexport type DynamicFn<T> = (track: TrackFn, ctx: FnCtx) => T | undefined | null;\n\n/**\n * Sugar over `derived(...)` that exposes dep values via a `track(dep)`\n * proxy instead of positional `data[i]`. All declared `allDeps` participate\n * in wave tracking, so the first fn run waits for every dep to settle.\n * Unused deps that update just re-run fn; `equals` absorbs unchanged\n * outputs as RESOLVED.\n *\n * P3-compliant: `track(dep)` reads from the framework-managed\n * `DepRecord.prevData` populated by the protocol, never from\n * `dep.cache`.\n *\n * @example\n * ```ts\n * const a = state(1);\n * const b = state(10);\n * const sum = dynamicNode([a, b], (track) => (track(a) as number) + (track(b) as number));\n * ```\n */\nexport function dynamicNode<T = unknown>(\n\tallDeps: readonly Node[],\n\tfn: DynamicFn<T>,\n\topts?: NodeOptions<T> & { partial?: boolean },\n): Node<T> {\n\tconst depIndex = new Map<Node, number>();\n\tallDeps.forEach((d, i) => {\n\t\tdepIndex.set(d, i);\n\t});\n\treturn derived<T>(\n\t\tallDeps,\n\t\t// data[i] is already sugar-unwrapped to a scalar by derived()'s wrapper.\n\t\t(data, ctx) => {\n\t\t\tconst track: TrackFn = (dep) => {\n\t\t\t\tconst i = depIndex.get(dep);\n\t\t\t\tif (i == null) {\n\t\t\t\t\tthrow new Error(`dynamicNode: untracked dep \"${dep.name ?? \"<unnamed>\"}\"`);\n\t\t\t\t}\n\t\t\t\treturn data[i];\n\t\t\t};\n\t\t\treturn fn(track, ctx);\n\t\t},\n\t\topts,\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// pipe — left-to-right operator composition\n// ---------------------------------------------------------------------------\n// autoTrackNode — runtime dep discovery (Jotai/signals compat)\n// ---------------------------------------------------------------------------\n\n/**\n * Like {@link dynamicNode} but deps are discovered at runtime via `track()`\n * calls — no upfront `allDeps` array needed. Designed for pull-based compat\n * layers (Jotai atoms, TC39 Signals) where deps are unknown until fn runs.\n *\n * **Two-phase discovery:**\n * 1. fn runs. Each `track(dep)` for an unknown dep: subscribes immediately\n * via `_addDep`, returns `dep.cache` as a stub (P3 boundary exception).\n * Result is discarded (discovery run).\n * 2. New deps settle (DATA from subscribe handshake). Wave machinery\n * re-triggers fn. `track(dep)` now returns protocol-delivered `data[i]`.\n * If MORE unknown deps appear, repeat step 1.\n * 3. Converges when no new deps found → real run → `actions.emit(result)`.\n *\n * P3 violation is limited to discovery runs. Once all deps are known,\n * subsequent waves use protocol-delivered values exclusively.\n *\n * Re-entrance safety: `_addDep` subscribes immediately. If the dep delivers\n * DATA synchronously during fn execution, `_execFn`'s re-entrance guard\n * defers the re-run to after the current fn returns.\n *\n * @param opts - Optional {@link AutoTrackOptions}. Pass `{ partial: true }` to\n * allow fn to run before all known deps have delivered their first value\n * (useful for optional/secondary deps).\n *\n * @example\n * ```ts\n * const a = state(1), b = state(2);\n * const sum = autoTrackNode((track) => track(a) + track(b));\n * // deps [a, b] discovered automatically on first run\n * ```\n */\n/**\n * Options for {@link autoTrackNode}.\n */\nexport interface AutoTrackOptions<T> extends NodeOptions<T> {\n\t/**\n\t * When `true`, fn may run before all known deps have delivered their first\n\t * DATA. Unknown deps return `undefined` via `track()`, which the fn must\n\t * handle explicitly. Useful when some deps are \"nice-to-have\" — e.g. a\n\t * primary computation should continue while a secondary dep is still\n\t * initialising.\n\t *\n\t * When `false` (default), fn is held until every known dep has delivered at\n\t * least one DATA value — a RESOLVED is emitted for the wave instead.\n\t * This matches `derived()` semantics and is the correct default for\n\t * pull-based compat layers (Signals, Jotai) where all deps must be\n\t * initialised before the computation is meaningful.\n\t *\n\t * @default false\n\t */\n\tpartial?: boolean;\n}\n\nexport function autoTrackNode<T = unknown>(\n\tfn: (track: TrackFn, ctx: FnCtx) => T | undefined | null,\n\topts?: AutoTrackOptions<T>,\n): Node<T> {\n\tlet implRef: NodeImpl<T>;\n\tconst depIndexMap = new Map<Node, number>();\n\tconst allowPartial = opts?.partial ?? false;\n\n\tconst wrappedFn: NodeFn = (batchData, actions, ctx) => {\n\t\tlet foundNew = false;\n\t\tconst track: TrackFn = (dep) => {\n\t\t\tconst idx = depIndexMap.get(dep);\n\t\t\tif (idx !== undefined) {\n\t\t\t\t// Known dep — return latest protocol-delivered value.\n\t\t\t\t// batch non-null+non-empty → latest from this wave;\n\t\t\t\t// otherwise fall back to ctx.prevData (last known value).\n\t\t\t\tif (idx < batchData.length) {\n\t\t\t\t\tconst batch = batchData[idx];\n\t\t\t\t\tif (batch != null && batch.length > 0) return batch.at(-1);\n\t\t\t\t\treturn ctx.prevData[idx];\n\t\t\t\t}\n\t\t\t\treturn dep.cache;\n\t\t\t}\n\t\t\t// Unknown dep — discovery phase.\n\t\t\tfoundNew = true;\n\t\t\tconst newIdx = implRef._addDep(dep);\n\t\t\tdepIndexMap.set(dep, newIdx);\n\t\t\treturn dep.cache; // P3 boundary exception (discovery stub)\n\t\t};\n\n\t\t// Sentinel guard (skipped when partial:true): if any known dep has never\n\t\t// delivered DATA (no DATA this wave AND ctx.prevData[idx] === undefined), emit RESOLVED\n\t\t// and defer. Mirrors derived()'s guard for the same sequential-handshake\n\t\t// scenario: when a node re-activates outside a batch and dep A delivers\n\t\t// synchronously before dep B is even subscribed, this holds fn until all\n\t\t// known deps have initialised rather than running with B=undefined.\n\t\t// Uses ctx.prevData[idx] === undefined — the protocol sentinel for\n\t\t// \"dep never sent DATA\". null is valid DATA and won't trigger this.\n\t\t// Only active when depIndexMap is non-empty (initial discovery runs are\n\t\t// unaffected) and when partial:false (default).\n\t\tif (!allowPartial && depIndexMap.size > 0) {\n\t\t\tfor (const [, idx] of depIndexMap) {\n\t\t\t\tif (idx < batchData.length) {\n\t\t\t\t\tconst batch = batchData[idx];\n\t\t\t\t\tif (!(batch != null && batch.length > 0) && ctx.prevData[idx] === undefined) {\n\t\t\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = fn(track, ctx);\n\t\t\tif (!foundNew) {\n\t\t\t\t// Real run — all deps known, protocol-delivered values.\n\t\t\t\tactions.emit(result);\n\t\t\t\t// Clear any stale discovery error from a prior run.\n\t\t\t\tif (ctx.store.__autoTrackLastDiscoveryError != null) {\n\t\t\t\t\tdelete ctx.store.__autoTrackLastDiscoveryError;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Discovery run — result discarded. New deps are subscribed via\n\t\t\t// _addDep. Their DATA delivery triggers _maybeRunFnOnSettlement\n\t\t\t// via the _pendingRerun mechanism, which will re-call fn.\n\t\t} catch (err) {\n\t\t\tif (!foundNew) throw err;\n\t\t\t// Discovery run threw — most likely a stale `.cache` read (P3\n\t\t\t// boundary exception), which the protocol-delivered retry will\n\t\t\t// not hit. Preserve the error on `ctx.store` for inspection; if\n\t\t\t// the retry succeeds, the flag is cleared above. If fn has a\n\t\t\t// real bug unrelated to cache, the non-discovery retry will\n\t\t\t// re-throw it out of `_execFn`.\n\t\t\tctx.store.__autoTrackLastDiscoveryError = err;\n\t\t}\n\t\treturn undefined;\n\t};\n\n\timplRef = new NodeImpl<T>([], wrappedFn, {\n\t\tdescribeKind: \"derived\",\n\t\t...opts,\n\t});\n\treturn implRef;\n}\n\n// ---------------------------------------------------------------------------\n// pipe — left-to-right operator composition\n// ---------------------------------------------------------------------------\n\n/** Unary operator used by {@link pipe}. */\nexport type PipeOperator = (n: Node) => Node;\n\n/**\n * Composes unary operators left-to-right; returns the final node.\n *\n * @example\n * ```ts\n * const out = pipe(\n * source,\n * (n) => map(n, (x) => x + 1),\n * (n) => filter(n, (x) => x > 0),\n * );\n * ```\n */\nexport function pipe(source: Node, ...ops: PipeOperator[]): Node {\n\tlet current = source;\n\tfor (const op of ops) current = op(current);\n\treturn current;\n}\n"],"mappings":"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","names":["node","batch","batch"]}
|