Import it from @grafloria/engine.
Holds the diagram to its one hard invariant, and buffers the ops that cannot be applied yet.
Drives the model directly. Its writes MUST NOT be captured as ops (they are derived, and broadcasting them would race with the very ops they are derived from) — the Replica runs every call into this class with capture suppressed.
tsclass ReferentialIntegrity
Methods
constructor( private readonly diagram: DiagramModel, private readonly lww: LwwRegistry )get quarantined(): string[]— Link ids currently held out of the document. Small; usually empty.isHeld(id: string): boolean— Is this link being held out of the document?held(id: string): LinkModel | undefined— The held instance, so a write can still reach a link that is out of the document.release(id: string): void— Force a held link back into the document, endpoints or no endpoints.
Only for undo: to UNDO the creation of a link that is currently quarantined, the link
has to be in the document for removeLink() to be a real mutation that capture can
mint an op from. Without this the undo silently does nothing, the link's presence
register still says "present", and resurrecting its node brings back a link the user
explicitly took back. The following reconcile() re-quarantines it if it is still
orphaned, so this is a keyhole, not a hole.
note(op: Op): void— Keep the port map and the order watermark current with the ops that change structure.divert(op: Op): boolean— Can this op be applied to the document right now?
Returns true when the op was DIVERTED — written into a link this class is holding out
of the document — in which case the caller must not also hand it to applyOp.
A held link still takes its property writes, through the SAME mutators a live one uses. An entity that took a different write path in quarantine would drift from a live one, and the drift would only surface on release, long after anything could point at the cause.
settle(op: Op): void— The INCREMENTAL invariant check: only what THIS op could possibly have broken.
A full sweep after every local edit is O(links), and a local edit stream is n ops long, so a bulk load — importing a document into a live session — was O(n²). Measured: 2,000 nodes and 2,000 links took 8.5 SECONDS through a Replica, against ~90ms without one. Nothing caught it, because no perf gate in this repo drives a Replica. That is the shape of defect this codebase keeps shipping, and I very nearly shipped another one.
So each op pays only for what it can actually affect:
add node → only a QUARANTINED link can become live. The quarantine is almost always empty, so this is free. (A live link cannot break when a node ARRIVES.) add link → that one link. O(1). remove link → drop it from the quarantine. O(1). remove node → live links attached to it can be orphaned. O(links) — but deleting a node is a human action, not a loop. set ports → same as removing a node: an endpoint may have vanished. set endpoints → that one link. O(1).
A bulk load is all adds, so it is linear again.
reconcile(): void— Re-evaluate the invariant over the WHOLE document.
Once per BATCH of remote ops, and once at the end of a replay — where the invariant is a function of the batch's final state, not of the path through it (a link orphaned mid-batch and re-parented by the end of it was never really orphaned).
forget(op: Op): void— Drop a link from quarantine for good — its presence register says it is gone.
Was this page helpful?