# Classes

Import these from `@grafloria/engine`.

## Classes

### `BroadcastChannelTransport`

```ts
class BroadcastChannelTransport implements SyncTransport
```

**Methods**

- `constructor(private readonly options: BroadcastChannelTransportOptions)`
- `get status(): TransportStatus`
- `static isSupported(): boolean` (static) — True when this environment can actually do cross-tab sync.
- `connect(): void` — Open (or re-open) the channel. Idempotent.
- `send(message: SyncMessage): void` — Broadcast to the other peers. A no-op — NOT an error — while disconnected.
- `onMessage(handler: (m: SyncMessage) => void): Unsubscribe` — Inbound messages from other peers. Never our own.
- `onStatus(handler: (s: TransportStatus) => void): Unsubscribe` — Connection transitions. THE hook the whole reconnect story hangs on: the adapter
subscribes here and fires an anti-entropy round the moment it hears 'connected'
again. A transport that never reports its status can never be caught up.
- `disconnect(): void` — Simulates (and, on `pagehide`, effects) a drop. Re-`connect()` to come back.
- `close(): void` — Tear down for good.

### `MemoryHub`

A shared bus. One hub = one document = one "room".

```ts
class MemoryHub
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `traffic` | `Array<{ from: ActorId; message: SyncMessage }>` | `[]` | Every message that crossed the bus — the wire tap the tests assert against. |

**Methods**

- `connect(actor: ActorId): MemoryTransport`
- `deliver(from: MemoryTransport, message: SyncMessage): void` — Deliver to everyone EXCEPT the sender. A peer never hears its own echo.
- `detach(port: MemoryTransport): void`
- `get peerCount(): number`

### `MemoryTransport`

```ts
class MemoryTransport implements SyncTransport
```

**Methods**

- `constructor( private readonly hub: MemoryHub, readonly actor: ActorId )`
- `get status(): TransportStatus`
- `send(message: SyncMessage): void` — Broadcast to the other peers. A no-op — NOT an error — while disconnected.
- `accept(message: SyncMessage): void` — Inbound. Ignored while disconnected — a dropped peer hears nothing, by definition.
- `onMessage(handler: Handler): Unsubscribe` — Inbound messages from other peers. Never our own.
- `onStatus(handler: (s: TransportStatus) => void): Unsubscribe` — Connection transitions. THE hook the whole reconnect story hangs on: the adapter
subscribes here and fires an anti-entropy round the moment it hears 'connected'
again. A transport that never reports its status can never be caught up.
- `connect(): void` — Open (or re-open) the channel. Idempotent.
- `disconnect(): void` — Close the channel but stay re-openable — this is what a "drop" is.
- `close(): void` — Tear down for good.

### `OpBatcher`

Accumulates local ops and flushes them as one coalesced, correctly-ordered batch.

```ts
class OpBatcher
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `queued` |  | `0` | Ops that went in. |
| `sent` |  | `0` | Ops that went out. `queued - sent` is what coalescing saved. |
| `batches` |  | `0` | Flushes performed. |

**Methods**

- `constructor(private readonly options: OpBatcherOptions)`
- `get size(): number`
- `push(op: Op): void`
- `flush(): void` — Send whatever is queued, right now. Idempotent, and a no-op when empty.
- `discard(): void` — Drop the queue WITHOUT sending.

Used on disconnect, and it is safe for exactly one reason: the ops are already in the
local log, so the peer will get them from the next anti-entropy round. If they were
not, this would be a silent data-loss hatch.
- `dispose(): void`

### `VersionVector`

A peer's exact position in the shared history, per actor.

Fed ONLY with ops the log genuinely accepted (`Replica.receive()` returns exactly
those). Feed it a duplicate and `count` over-counts, which fakes a hole and triggers a
pointless repair — wasteful, not wrong, but the discipline is worth keeping.

```ts
class VersionVector
```

**Methods**

- `observe(op: Op): void` — Record ONE op we now hold. Must be genuinely new — see the class doc.
- `observeAll(ops: Iterable<Op>): void`
- `frontier(actor: ActorId): ActorFrontier`
- `get actorCount(): number`
- `toJSON(): VersionVectorJSON`
- `static fromOps(ops: Iterable<Op>): VersionVector` (static) — Rebuild from a log — used on resume-from-disk, and by the tests as an oracle.

### `WebSocketTransport`

```ts
class WebSocketTransport implements SyncTransport
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `attempts` |  | `0` | Reconnect attempts made. Asserted on by the backoff test. |

**Methods**

- `constructor(private readonly options: WebSocketTransportOptions)`
- `get status(): TransportStatus`
- `connect(): void` — Open (or re-open) the channel. Idempotent.
- `send(message: SyncMessage): void` — Broadcast to the other peers. A no-op — NOT an error — while disconnected.
- `onMessage(handler: (m: SyncMessage) => void): Unsubscribe` — Inbound messages from other peers. Never our own.
- `onStatus(handler: (s: TransportStatus) => void): Unsubscribe` — Connection transitions. THE hook the whole reconnect story hangs on: the adapter
subscribes here and fires an anti-entropy round the moment it hears 'connected'
again. A transport that never reports its status can never be caught up.
- `disconnect(): void` — Close the channel but stay re-openable — this is what a "drop" is.
- `close(): void` — Tear down for good.
