# Replica

Import it from `@grafloria/engine`.

One peer.

Local edits to `diagram` are captured, appended to `log`, and handed to `onLocalOp`. Remote ops arrive at `receive()`, are de-duplicated, applied, and NOT echoed back.

```ts
class Replica
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `log` |  |  |  |

**Methods**

- `constructor( readonly diagram: DiagramModel, private readonly options: ReplicaOptions )`
- `get actor(): ActorId`
- `get clock(): number` — The clock to resume from, and the watermark a peer asks us to catch up past.
- `get quarantinedLinks(): string[]` — Links held out of the document because an endpoint node is missing. See integrity.ts.
- `get canUndo(): boolean`
- `get canRedo(): boolean`
- `receive(ops: readonly Op[]): Op[]` — Take ops from a peer.

Returns the ops that were actually NEW to us — which is what you forward to other
peers in a mesh, and which is exactly nothing when the same batch arrives twice.
- `undo(): Op[]` — UNDO MY LAST EDIT — not the last edit.

The per-actor property is structural: capture only ever sees LOCAL mutations (remote
ops are applied with capture suppressed), so nothing another peer did can be on this
stack to begin with. See undo.ts for what happens when my edit has already been
superseded by someone else's — the short version is that it does nothing, on purpose.
- `redo(): Op[]`
- `transact<T>(fn: () => T): T` — Group everything `fn` does into ONE undo step.

Deleting a node with three links is four ops and, without this, four presses of Ctrl-Z. Grouping is a LOCAL concern — it changes what one keypress takes back, never what goes
on the wire — so two peers may group differently and still converge.
- `history(): readonly Op[]` — Everything we know, in total order — the catch-up payload for a joining peer.
- `adopt(ops: readonly Op[]): void` — Adopt a persisted op-log tail whose EFFECTS ARE ALREADY IN THE MODEL.

This is `receive()`'s quiet twin, and the difference is the whole point. `receive()` is
for ops the model has not seen: it applies them. `adopt()` is for reopening a saved
document, where the snapshot ALREADY CONTAINS everything the ops did — so it seeds the
log (so a duplicate re-delivery is recognised), the LWW stamps (so a straggling older
write is still refused after a reload) and the repair index, and applies NOTHING.

Re-applying them would be harmless for content — the reducer's redundant-write guard
would drop every one — but it would bump every version counter and fire a full repaint
for a document that has not changed. Worse, it would make opening a file look like an
edit to anyone watching the model's change events.
- `dispose(): void`
