UNPKG

@durable-streams/y-durable-streams

Version:

Yjs provider for Durable Streams - sync Yjs documents over append-only streams

163 lines (161 loc) 4.35 kB
import * as Y from "yjs"; import * as awarenessProtocol from "y-protocols/awareness"; import { ObservableV2 } from "lib0/observable"; import { HeadersRecord } from "@durable-streams/client"; //#region src/yjs-provider.d.ts /** * Connection status of the provider. */ /** * Connection status of the provider. */ type YjsProviderStatus = `disconnected` | `connecting` | `connected`; /** * Options for creating a YjsProvider. */ interface YjsProviderOptions { /** * The Yjs document to synchronize. */ doc: Y.Doc; /** * Base URL of the Yjs server. * E.g., "http://localhost:4438/v1/yjs/my-service" */ baseUrl: string; /** * Document path (can include forward slashes). * E.g., "my-doc" or "project/chapter-1" */ docId: string; /** * Optional Awareness instance for presence support. */ awareness?: awarenessProtocol.Awareness; /** * Optional HTTP headers for requests. */ headers?: HeadersRecord; /** * Live mode for streaming updates. * @default "sse" */ liveMode?: `sse` | `long-poll`; /** * Whether to automatically connect on construction. * @default true */ connect?: boolean; } /** * Events emitted by the YjsProvider. */ interface YjsProviderEvents { synced: (synced: boolean) => void; status: (status: YjsProviderStatus) => void; error: (error: Error) => void; } /** * Interval for awareness heartbeats (15 seconds). */ declare const AWARENESS_HEARTBEAT_INTERVAL = 15e3; /** * YjsProvider for the Yjs Durable Streams Protocol. */ declare class YjsProvider extends ObservableV2<YjsProviderEvents> { readonly doc: Y.Doc; readonly awareness?: awarenessProtocol.Awareness; private readonly baseUrl; private readonly docId; private readonly headers; private readonly liveMode; private _state; private _connectionId; private _ctx; private _synced; private updatesStreamGeneration; private updatesSubscription; private sendingAwareness; private pendingAwareness; private awarenessHeartbeat; constructor(options: YjsProviderOptions); get synced(): boolean; private set synced(value); /** True when connected to the server */ get connected(): boolean; /** True when connection is in progress */ get connecting(): boolean; /** * Transition to a new connection state. * Returns false if the transition is invalid (logs a warning). */ private transition; /** * Create a new connection context with a unique ID. */ private createConnectionContext; /** * Check if a connection context is stale (disconnected or replaced). * Use this after every await to detect race conditions. */ private isStale; connect(): Promise<void>; disconnect(): Promise<void>; destroy(): void; /** * Flush any pending updates to the server. * * @internal This method is primarily for testing to ensure all batched * updates have been sent before making assertions. In production, updates * are sent automatically via the IdempotentProducer's batching/linger mechanism. */ flush(): Promise<void>; /** * Get the document URL. */ private docUrl; /** * Get the awareness URL for a named stream. */ private awarenessUrl; /** * Create the document on the server via PUT. * Idempotent: succeeds if document already exists with matching config. */ private ensureDocument; /** * Discover the current snapshot state via ?offset=snapshot. * Handles 307 redirect to determine starting offset. */ private discoverSnapshot; /** * Load a snapshot from the server. */ private loadSnapshot; private createUpdatesProducer; private closeUpdatesProducer; private startUpdatesStream; private runUpdatesStream; /** * Frame data with lib0 length-prefix encoding for transport. */ private static frameUpdate; /** * Apply lib0-framed updates from the server. */ private applyUpdates; /** * Apply lib0-framed awareness updates from the server. */ private applyAwarenessUpdates; private handleDocumentUpdate; private startAwareness; private handleAwarenessUpdate; private broadcastAwareness; private broadcastAwarenessRemoval; private sendAwareness; private subscribeAwareness; private isNotFoundError; private isAuthError; } //#endregion export { AWARENESS_HEARTBEAT_INTERVAL, YjsProvider, YjsProviderEvents, YjsProviderOptions, YjsProviderStatus };