Sync
Collaboration needs a way to move bytes between peers. @oh-just-another/network provides that: small, interchangeable transports behind one Transport interface, which the collaboration layer plugs into. The CRDT logic doesn't care how messages travel โ only that they arrive.
The transport interfaceโ
Every transport implements the same Transport contract, so you pick the one that fits your topology and the rest of the stack is unchanged.
BroadcastChannelTransportโ syncs across tabs/windows of the same browser. Zero infrastructure; ideal for local multi-window demos.WebSocketTransportโ syncs across machines through a relay server. Auto-reconnects with exponential backoff; exposes aWebSocketStatus(status/onStatusChange) to surface in the UI, andWebSocketTransportOptionsfor reconnect delays or a customWebSocketimplementation.
import { BroadcastChannelTransport, WebSocketTransport } from "@oh-just-another/network";
// Same-browser, across tabs:
const local = new BroadcastChannelTransport("room-foo");
// Cross-machine, through a relay:
const remote = new WebSocketTransport("wss://example.com/room-foo");
const offStatus = remote.onStatusChange((status) => console.log(status));
Wiring a transport to collaborationโ
The transport is handed to @oh-just-another/collab's TransportProvider, which multiplexes the Yjs document, awareness, and sync-requests over the single channel (tagged with a one-byte discriminator):
import * as Y from "yjs";
import { BroadcastChannelTransport } from "@oh-just-another/network";
import { TransportProvider, CollabAwareness } from "@oh-just-another/collab";
const doc = new Y.Doc();
const awareness = new CollabAwareness(doc);
const transport = new BroadcastChannelTransport("room-foo");
const provider = new TransportProvider({ doc, transport, awareness: awareness.awareness });
// ...later
provider.destroy();
transport.close();
Only document content replicates: elements, links, layers, annotations and document-level viewport settings (grid, snap). Camera state โ pan, zoom, rotation โ is per-user and never travels between peers.
On join, the provider requests the peers' current state; when a transport reports a reconnect (onStatusChange โ "open" after a drop), it re-requests state, offers its own, and re-announces local presence โ frames lost on the dying socket never leave the docs diverged.
See Collaboration for the full editor binding.
Encryption over any transportโ
Because transports are interchangeable, end-to-end encryption is a wrapper rather than a special case. EncryptedTransport (from @oh-just-another/collab) wraps any Transport so payloads are AES-GCM encrypted client-side โ a blind relay forwards only ciphertext and never sees scene data.
Bring your ownโ
The Transport interface is the extension point: implement it over WebRTC, a message queue, or any channel you control, and the collaboration layer works unchanged.