Skip to content

EidosNodeStore (client-side node state)

Verified against the code · 421c63ba3fab5894cdb9e707eed8ef694881d60d · 2026-10-20

956 D1b-1 (2026-10-20): 1 finding(s) fixed — the EidosCore.nodeCache getter was removed in 5.2f (#906); only app.js's const alias remains. Re-verified against main 421c63ba.

Layer: frontend

EidosNodeStore (frontend/eidos-node-store.js) is the ONE canonical owner of client-side node state. Before trin 5.2b (#891) the module-level nodeCache map in app.js held the answer to "what do we know about node uid" alongside the _navGen nav-generation counter. That ownership moved into one module: the store replaces the nodeCache map and now owns _nodes (the backing map) plus _navGen. Spec SSOT: docs/specs/eidos-nodestore-tree.md §3, §4.1, §5.2b, §8.

What it owns

  • _nodes — uid -> JSON-LD node, the backing map.
  • _navGen — a nav-generation counter plugins use to drop fetches started before the user navigated (the former app.js _navGen lives here now).

Nothing else may hold a parallel map keyed by uid.

The live-alias contract

app.js holds const nodeCache = window.EidosNodeStore._nodes — the SAME object. This is by design: existing index call-sites inside app.js (nodeCache[uid]) stay unchanged. It is the only alias left: 5.2f (#906) removed the EidosCore.nodeCache getter (it is absent from the EidosCore literal and the export snapshot, and the B4 ratchet freezes any EidosCore.nodeCache reference at 0). The contract on the store is therefore:

  • invalidate('all') deletes every key IN PLACE — it never reassigns _nodes. A reassignment would break the alias and leave stale references in app.js.
  • upsert(uid, node) writes _nodes[uid] = node in place.
  • patch(uid, delta) mutates _nodes[uid] in place (no-op with a warning if uid is unknown — never auto-creates).

Reads (get, getBatch, snapshot) return the same live references the alias returns — callers who need a defensive copy make one.

The B3 ratchet

tests/test_frontend_node_tree_ratchet.py freezes the number of cross-file reads of window.EidosCore.nodeCache or core.nodeCache outside app.js and the stores. The baseline was 31 at 5.2b; 5.2c migrated every one to a call through EidosNodeStore.get/getBatch/snapshot, driving the counter to 0 (frozen at equality — not <=). A new plugin read raises the count and trips the ratchet.

B1 (WRITE-bypass — EidosCore.{nodeCache}[x] = y and .push(...) in frontend/** outside the stores) is also frozen at 0 since 5.2b.

EidosNodeStore.nav.generation() and .bump() replace the old app.js _navGen. The pattern: a long-running fetch captures const g = nav.generation() at start and bails on completion if nav.generation() !== g. bump() runs at every navigation. The seam lives in the store because the nav gate is a property of the nodes-we-care-about-right-now, not of app.js wiring.

Load order

<script src="frontend/eidos-action.js">
<script src="frontend/eidos-node-store.js">   <!-- defines window.EidosNodeStore -->
<script src="frontend/eidos-tree.js">         <!-- consumes it in _ingest -->
<script src="frontend/app.js">                <!-- const nodeCache = window.EidosNodeStore._nodes -->