@durable-streams/y-durable-streams
v0.2.1
Published
Yjs provider for Durable Streams - sync Yjs documents over append-only streams
Downloads
268
Maintainers
Readme
@durable-streams/y-durable-streams
Yjs provider for Durable Streams - sync Yjs documents over append-only streams with optional awareness (presence) support.
Overview
This package provides a Yjs provider that syncs documents using the Durable Streams protocol. Unlike WebSocket-based providers, it uses HTTP long-polling over append-only streams, making it simpler to deploy and scale.
Key benefits:
- No WebSocket infrastructure - Works with standard HTTP load balancers and CDNs
- Built-in persistence - Document history is stored in the stream itself
- Scalable - Clients connect directly to streams, no central sync server needed
- Presence support - Optional awareness stream for cursors, selections, and user status
Installation
npm install @durable-streams/y-durable-streams yjs y-protocols lib0Quick Start
import { DurableStreamsProvider } from "@durable-streams/y-durable-streams"
import * as Y from "yjs"
import { Awareness } from "y-protocols/awareness"
const doc = new Y.Doc()
const awareness = new Awareness(doc)
const provider = new DurableStreamsProvider({
doc,
documentStream: {
url: "https://your-server.com/v1/stream/rooms/my-room",
transport: "sse",
},
awarenessStream: {
url: "https://your-server.com/v1/stream/presence/my-room",
protocol: awareness,
transport: "sse",
},
})
provider.on("synced", (synced) => {
console.log("Synced:", synced)
})Usage
Document Only (No Presence)
const provider = new DurableStreamsProvider({
doc,
documentStream: {
url: "https://your-server.com/v1/stream/rooms/my-room",
transport: "sse",
},
})With Authentication
const provider = new DurableStreamsProvider({
doc,
documentStream: {
url: "https://your-server.com/v1/stream/rooms/my-room",
transport: "sse",
headers: {
Authorization: "Bearer your-token",
},
},
awarenessStream: {
url: "https://your-server.com/v1/stream/presence/my-room",
protocol: awareness,
transport: "sse",
headers: {
Authorization: "Bearer your-token",
},
},
})Transport Mode
By default, the provider uses Server-Sent Events (SSE) for real-time updates. You can switch to long-polling if needed:
const provider = new DurableStreamsProvider({
doc,
documentStream: {
url: "https://your-server.com/v1/stream/rooms/my-room",
transport: "long-poll", // Use long-poll instead of SSE
},
awarenessStream: {
url: "https://your-server.com/v1/stream/presence/my-room",
protocol: awareness,
transport: "long-poll", // Can configure each stream independently
},
})Note: When using SSE with binary streams, base64 encoding is automatically applied.
Manual Connection
const provider = new DurableStreamsProvider({
doc,
documentStream: { url, transport: "sse" },
connect: false, // Don't connect automatically
})
// Set up listeners first
provider.on("synced", handleSync)
provider.on("error", handleError)
// Then connect
provider.connect()Event Handling
// Sync state changes
provider.on("synced", (synced: boolean) => {
if (synced) {
console.log("Document is synced with server")
}
})
// Connection status changes
provider.on("status", (status: ProviderStatus) => {
console.log("Status:", status) // "disconnected" | "connecting" | "connected"
})
// Error handling
provider.on("error", (error: Error) => {
console.error("Provider error:", error)
})Cleanup
// Disconnect temporarily
provider.disconnect()
// Reconnect
provider.connect()
// Destroy permanently
provider.destroy()API
DurableStreamsProvider
class DurableStreamsProvider {
constructor(options: DurableStreamsProviderOptions)
// Properties
readonly doc: Y.Doc
readonly synced: boolean
readonly connected: boolean
readonly connecting: boolean
readonly status: ProviderStatus
// Methods
connect(): void
disconnect(): void
destroy(): void
// Events
on(event: "synced", handler: (synced: boolean) => void): void
on(event: "status", handler: (status: ProviderStatus) => void): void
on(event: "error", handler: (error: Error) => void): void
}Options
interface DurableStreamsProviderOptions {
doc: Y.Doc
documentStream: StreamConfig
awarenessStream?: AwarenessConfig
connect?: boolean // default: true
debug?: boolean // default: false
}
type TransportMode = "long-poll" | "sse"
interface StreamConfig {
url: string | URL
headers?: Record<string, string | (() => string)>
transport: TransportMode // required
}
interface AwarenessConfig extends StreamConfig {
protocol: Awareness
}How It Works
The provider uses two separate durable streams:
Document Stream - Stores Yjs document updates. Reads from the beginning to replay full document history on connect.
Awareness Stream - Syncs presence data (cursors, selections, user info). Reads from current position since historical presence is not needed.
Both streams support two transport modes for real-time updates:
- SSE (default) - Lower latency, uses Server-Sent Events with automatic base64 encoding for binary data
- Long-polling - Compatible with all HTTP infrastructure, useful as fallback
Updates are encoded using lib0's VarUint8Array framing.
License
Apache-2.0
