@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
TypeScript
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 };