Interfaces
Import these from @grafloria/engine.
Interfaces
ActorFrontier
What one peer holds from one actor. Three numbers; see the header for why not one.
tsinterface ActorFrontier
Properties
| Name | Type | Default | Description |
|---|---|---|---|
max | number | Highest clock seen from this actor. | |
count | number | How many of this actor's ops we hold. A hole below max shows up here. | |
hash | number | Order-independent fold of the clocks held. Catches a hole count cannot see. |
AwarenessChange
tsinterface AwarenessChange
Properties
| Name | Type | Default | Description |
|---|---|---|---|
added | ActorId[] | ||
updated | ActorId[] | ||
removed | ActorId[] |
AwarenessMessage
Ephemeral presence. NEVER reaches the op log. See the header.
tsinterface AwarenessMessage
Properties
| Name | Type | Default | Description |
|---|---|---|---|
t | 'awareness' | ||
from | ActorId | ||
state | AwarenessState | null | null ⇒ this peer is gone (an explicit, immediate tombstone; the TTL is the backup). | |
seq | number | Per-peer sequence. Awareness is last-write-wins PER PEER, and a reordering transport will hand you an old cursor after a new one — without this, the cursor jumps backwards. It is not a Lamport clock: peers never compare each other's awareness, only their own successive samples. |
AwarenessOptions
tsinterface AwarenessOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
actor | ActorId | ||
timeoutMs? | number | Drop a peer we have not heard from in this long. | |
now? | () => number | Wall clock — injectable so the expiry tests are not a race against real time. |
AwarenessState
Anything a peer publishes about ITSELF that is not a document edit.
tsinterface AwarenessState
Properties
| Name | Type | Default | Description |
|---|---|---|---|
name? | string | Human name for the badge. | |
color? | string | CSS colour. Derived deterministically from the actor id when absent. | |
cursor? | { x: number; y: number } | null | Pointer position in WORLD coordinates — never screen: peers have different cameras. | |
selection? | string[] | Entity ids this peer has selected. |
BroadcastChannelLike
The slice of the BroadcastChannel API we use — so a test can hand us a fake.
tsinterface BroadcastChannelLike
Properties
| Name | Type | Default | Description |
|---|---|---|---|
onmessage | ((event: { data: unknown }) => void) | null |
Members
postMessage(data: unknown): voidclose(): void
BroadcastChannelTransportOptions
tsinterface BroadcastChannelTransportOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
name | string | The room. Every peer on the same name shares a document. Namespace it by doc id. | |
actor | ActorId | ||
create? | (name: string) => BroadcastChannelLike | Injectable factory — the default reaches for the global. |
ByeMessage
"I am leaving." Best-effort — the TTL is what actually guarantees cleanup.
tsinterface ByeMessage
Properties
| Name | Type | Default | Description |
|---|---|---|---|
t | 'bye' | ||
from | ActorId |
CausalBufferOptions
tsinterface CausalBufferOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
maxPending? | number | Hard cap on held ops. A malicious or badly broken peer must not be able to make us buffer without limit; past the cap we release the oldest held op anyway (it will apply as a no-op and be logged, which is exactly what would have happened before this file existed — degraded, but bounded, and counted so it is visible). |
CausalSplit
Where an op sits: released to the replica, or waiting on a dependency.
tsinterface CausalSplit
Properties
| Name | Type | Default | Description |
|---|---|---|---|
ready | Op[] | Ready NOW, in total order. Hand straight to Replica.receive(). | |
held | number | Newly held this round (diagnostics). |
HelloMessage
"I have joined." Carries the sender's frontier so an existing peer can push the newcomer the history it lacks WITHOUT the newcomer having to ask a second time.
tsinterface HelloMessage
Properties
| Name | Type | Default | Description |
|---|---|---|---|
t | 'hello' | ||
from | ActorId | ||
vv | VersionVectorJSON | ||
awareness? | AwarenessState | The sender's presence, so peers see the badge on the join frame, not 5s later. |
OpBatcherOptions
tsinterface OpBatcherOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
onFlush | (kept: Op[], dropped: Op[]) => void | Where a flushed batch goes. Never called with an empty kept. | |
intervalMs? | number | How long to accumulate, in ms. 16 ≈ one frame: a drag's pointermoves collapse into one message per frame, which is the point. | |
maxBatch? | number | Flush immediately once the queue reaches this many ops, regardless of the timer. Backpressure: a scripted bulk import can emit ten thousand ops inside one tick, and an unbounded queue would hand the transport a single ten-megabyte frame. | |
setTimer? | (cb: () => void, ms: number) => unknown | Injectable timer, so tests are deterministic and Node/browser both work. | |
clearTimer? | (handle: unknown) => void |
OpsMessage
A batch of document ops. The only message that reaches the op log.
tsinterface OpsMessage
Properties
| Name | Type | Default | Description |
|---|---|---|---|
t | 'ops' | ||
from | ActorId | ||
ops | Op[] |
PeerPresence
Another peer, as far as we know.
tsinterface PeerPresence
Properties
| Name | Type | Default | Description |
|---|---|---|---|
actor | ActorId | ||
state | AwarenessState | ||
lastSeen | number | Wall-clock ms of the last message from them. NOT a Lamport clock — see below. | |
seq | number | Their sequence number for state. Guards against a reordered cursor sample. |
SyncAdapterOptions
tsinterface SyncAdapterOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
batch? | Omit<OpBatcherOptions, 'onFlush'> | false | Batching. false sends every op the instant it happens — legal, and a bad idea. | |
relay? | boolean | Forward ops that were NEW to us on to our other peers. | |
syncIntervalMs? | number | Periodic anti-entropy, ms. 0 disables it (the tests drive sync() by hand). | |
heartbeatMs? | number | Re-publish our awareness this often so peers do not time us out while we sit still. | |
awarenessThrottleMs? | number | Minimum ms between awareness sends. 60Hz in, ~20Hz out. | |
awarenessTimeoutMs? | number | Drop a peer's presence after this long without a word. | |
setTimer? | (cb: () => void, ms: number) => unknown | Injectables, so every timing test is deterministic instead of a race. | |
clearTimer? | (handle: unknown) => void | ||
setInterval? | (cb: () => void, ms: number) => unknown | ||
clearInterval? | (handle: unknown) => void | ||
now? | () => number |
SyncSessionOptions
Also has every member of SyncAdapterOptions, listed on its own entry.
tsinterface SyncSessionOptions extends SyncAdapterOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
actor | ActorId | ||
startClock? | number | Resume the Lamport clock from a persisted tail. |
SyncStats
Counters. Every one of them is something a test asserts on, not decoration.
tsinterface SyncStats
Properties
| Name | Type | Default | Description |
|---|---|---|---|
messagesSent | number | Messages handed to the transport. | |
messagesReceived | number | Messages taken from the transport. | |
opsSent | number | Ops we put on the wire (post-coalescing). | |
opsReceived | number | Ops that arrived. | |
opsDuplicate | number | …of which the log had already seen: duplicate delivery, absorbed. | |
opsHeld | number | Ops currently HELD by the causal buffer, waiting for an add. | |
syncsRequested | number | Anti-entropy rounds we asked for. | |
repairs | number | Times a peer's frontier turned out to have a HOLE and we resent an actor's history. | |
opsCoalesced | number | Ops the batcher swallowed because a later write superseded them. | |
reconnects | number | Reconnects observed. |
SyncTransport
A pipe that moves SyncMessages between peers.
The delivery model is BROADCAST-TO-OTHERS: send() delivers to every other peer on
the channel and never echoes back to the sender. (A star topology through a server, a
BroadcastChannel between tabs, and a full WebRTC mesh all present this way; a
point-to-point transport presents itself as a channel with one other peer.)
tsinterface SyncTransport
Properties
| Name | Type | Default | Description |
|---|---|---|---|
status | TransportStatus |
Members
send(message: SyncMessage): void— Broadcast to the other peers. A no-op — NOT an error — while disconnected.onMessage(handler: (message: SyncMessage) => void): Unsubscribe— Inbound messages from other peers. Never our own.onStatus(handler: (status: 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.
UnreliableOptions
tsinterface UnreliableOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
seed? | number | ||
dropRate? | number | P(message is simply never delivered). | |
duplicateRate? | number | P(message is delivered more than once). | |
delayRate? | number | P(message is held in flight instead of delivered now) — this is what reorders. | |
maxDuplicates? | number | Max extra copies when duplicating. |
WebSocketLike
The standard WebSocket surface — satisfied by the browser global AND by ws.
tsinterface WebSocketLike
Properties
| Name | Type | Default | Description |
|---|---|---|---|
readyState | number | ||
onopen | ((event?: unknown) => void) | null | ||
onmessage | ((event: { data: unknown }) => void) | null | ||
onclose | ((event?: unknown) => void) | null | ||
onerror | ((event?: unknown) => void) | null |
Members
send(data: string): voidclose(code?: number, reason?: string): void
WebSocketTransportOptions
tsinterface WebSocketTransportOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
url | string | ||
socketFactory? | (url: string) => WebSocketLike | The constructor. Defaults to the global. ws satisfies this in Node. | |
reconnect? | boolean | Reconnect backoff. The first retry waits reconnectBaseMs, then doubles to a cap. | |
reconnectBaseMs? | number | ||
reconnectMaxMs? | number | ||
setTimer? | (cb: () => void, ms: number) => unknown | Injectable, so the backoff test does not take 30 seconds of wall clock. | |
clearTimer? | (h: unknown) => void |
Was this page helpful?