Reference

API reference

The essentials of every public class in @afterrealism/dendri-client and @afterrealism/dendri-y. Full option types are in Configuration; TypeScript declarations ship with the packages.

Package entry points

Import pathExports
@afterrealism/dendri-clientDendri, MsgPackDendri, Room, createDendriStore, DendriServerAPI, errors, enums, types.
…/storeStore-only bundle: createDendriStore and store types.
…/msgpackMsgPack wire serializer.
…/reactuseDendriStore hook (peer dep: react ≥ 18).
…/vueuseDendriStore composable (peer dep: vue ≥ 3.3).
…/sveltetoSvelteStore (no dependency).
@afterrealism/dendri-yDendriYjsProvider, DendriRoomLike.

class Dendri

A peer: one identity plus a signaling connection. Extends a typed event emitter.

new Dendri(options: DendriOptions)
new Dendri(id: string, options?: DendriOptions)

Properties

PropertyTypeDescription
idstring | nullThe peer's brokering ID (assigned by the server unless you passed one).
openbooleantrue once connected to the signaling server.
connectionStateConnectionStateCurrent state machine value (see lifecycle).
disconnectedbooleanWhether the signaling connection is currently down.
destroyedbooleanWhether destroy() has been called.

Methods

MethodReturnsDescription
connect(peerId, options?)DataConnectionOpen a data connection to a remote peer. Options: label, metadata, serialization, reliable.
call(peerId, stream, options?)MediaConnectionStart an audio/video call with a local MediaStream.
connectHybrid(peerId, options?)HybridConnectionData connection with automatic WebRTC ⇄ relay fallback and upgrade retries.
joinRoom(name) / leaveRoom(name)voidLow-level room membership (the Room class manages this for you).
getConnection(peerId, connectionId)DataConnection | MediaConnection | nullLook up an existing connection.
disconnect()voidClose the signaling connection but keep existing P2P connections alive.
reconnect()voidRe-establish signaling with the previous ID.
destroy()voidClose everything: all connections plus signaling. Terminal.
diagnostics()objectSnapshot of connection internals for debugging.

Events

EventPayloadFired when
open(id: string)Connected to the signaling server.
connection(conn: DataConnection)A remote peer opened a data connection to you.
call(call: MediaConnection)A remote peer is calling you.
closeThe peer was destroyed.
disconnected(currentId: string)Signaling connection dropped (auto-reconnect kicks in).
reconnectedSignaling successfully re-established; room state is re-sent.
error(err: DendriError)Fatal or connection-level errors.
roomPeers(room: string, peers: string[])The server sent the current peer list for a room.
presenceUpdate(peerId, room, data)A peer updated its presence.
connectionStateChanged(state, previous)The connection state machine transitioned.

MsgPackDendri is a drop-in subclass that serializes data connections with MsgPack instead of JSON/BinaryPack — smaller payloads for object-heavy traffic.

DataConnection & MediaConnection

MemberOnDescription
send(data)DataSend any serializable payload (binary supported; large messages are chunked).
answer(stream?)MediaAccept an incoming call, optionally attaching your own stream.
close()BothTear down the connection.
openBothtrue once usable.
peerBothThe remote peer's ID.
metadata / labelDataValues set by the initiator at connect() time.
localStream / remoteStreamMediaThe attached media streams.
on("open" | "data" | "close" | "error")DataLifecycle + payload events.
on("stream" | "close" | "error")Mediastream delivers the remote MediaStream.

class Room

Star-topology room lifecycle: host claiming, peer discovery, reconnection, and deterministic host migration.

import { Dendri, Room } from "@afterrealism/dendri-client";

const room = new Room("room-id", { migrationTimeout: 10_000 });
room.join(Dendri, { url: "wss://signal.example.com" });

Members

MemberDescription
isHost / hostId / peerIdTopology info for this peer.
peers / peerCountKnown remote peers (excluding self).
broadcast(data, { topic? })Send to all peers; falls back to the server relay when P2P isn't available.
broadcastBinary(bytes, { topic })Broadcast a Uint8Array; receivers get bytes back intact on every path.
broadcastWithAck(data, timeoutMs?)Resolves when every connected peer has ACKed (at-least-once delivery).
subscribe(topic, handler)Receive messages on a topic. Returns an unsubscribe function.
onData(handler)Receive all data regardless of topic.
registerRpcMethod(name, handler)Expose a method other peers can call. Returns an unregister function.
performRpc(method, payload, { timeout?, peerId? })Call a remote method; targets one peer or broadcasts to all handlers.
setPresence(data) / getPresence(peerId) / getOthers()Publish and read presence state.
leave()Leave the room and clean up all connections and managers.

Events

EventPayloadFired when
joined(peerId, isHost)This peer joined the room.
peerJoined / peerLeft(peerId)A remote peer joined or left.
data(peerId, data)Data received from any peer.
hostChanged(newHostId)Host migration completed.
error(err: Error)Room-level failures.

createDendriStore()

A framework-agnostic reactive wrapper around a Room. See Framework guides for usage; the surface:

interface DendriStore {
	// Reactive state (also available as an immutable snapshot)
	readonly connectionState: ConnectionState;
	readonly isHost: boolean;
	readonly myPeerId: string | null;
	readonly hostId: string | null;
	readonly peers: readonly string[];
	readonly peerCount: number;
	readonly presences: ReadonlyMap<string, unknown>;

	// Actions
	join(roomId: string): void;
	leave(): void;
	broadcast(data: unknown, options?: { topic?: string }): void;
	broadcastBinary(bytes: Uint8Array, options: { topic: string }): void;
	broadcastWithAck(data: unknown, timeout?: number): Promise<void>;
	setPresence(data: Record<string, unknown>): void;

	// Event registration — each returns an unsubscribe function
	onPeerJoin(handler: (peerId: string) => void): () => void;
	onPeerLeave(handler: (peerId: string) => void): () => void;
	onData(handler: (data: unknown, peerId: string) => void): () => void;
	onPresenceUpdate(handler: (peerId: string, data: unknown) => void): () => void;
	onHostChanged(handler: (hostId: string) => void): () => void;

	// Framework integration
	subscribe(listener: () => void): () => void;              // state changes
	subscribe(topic: string, handler: (data, peerId) => void): () => void; // topic messages
	getSnapshot(): DendriStoreSnapshot;

	destroy(): void;
}

Framework adapters

ImportSignatureNotes
…/reactuseDendriStore(store): DendriStoreSnapshotBuilt on useSyncExternalStore; concurrent-safe.
…/vueuseDendriStore(store): Readonly<ShallowRef<DendriStoreSnapshot>>Auto-unsubscribes on unmount.
…/sveltetoSvelteStore(store): SvelteReadable<DendriStoreSnapshot>Standard store contract; works with $ syntax in Svelte 4 and 5.

class DendriServerAPI

Typed client for the server's REST endpoints:

MethodReturns
getHealth()Liveness payload from /health.
listPeers(room?)Peer IDs (server must run with --allow_discovery).
generatePeerId()A fresh server-generated peer ID.
getTurnCredentials()Ephemeral { iceServers } (server must run with --turn_secret).
getAnalytics()Analytics dataset descriptor.

class DendriYjsProvider

new DendriYjsProvider(options: {
	room: DendriRoomLike;   // a DendriStore satisfies this
	doc: Y.Doc;
	awareness?: Awareness;
})

provider.destroy(): void; // detach listeners, clean up awareness state

DendriRoomLike is the minimal surface the provider needs (myPeerId, broadcastBinary, subscribe, onPeerJoin, onPeerLeave) — any object implementing it works, which is how the provider stays framework- and transport-agnostic. Full guide: Collaborative editing.

Errors

Connection-level failures surface as DendriError, which carries a stable type (e.g. ID_TAKEN, NETWORK, SERVER_ERROR), a retryable flag, and optional details. Always attach an error listener to peers and rooms — unhandled peer errors are fatal to the peer.