/**
 * Browser-side Yjs collaboration provider for the TipTap editor.
 *
 * Wraps `y-websocket`'s `WebsocketProvider` with sensible defaults and a
 * narrow public API. The wire protocol is byte-compatible with the
 * cms-core realtime gateway shipped in PR #106, so the standard
 * `WebsocketProvider` connects without any custom client code.
 *
 * Auth flows through the browser's cookie jar — `cms-admin` already uses
 * cookie-based sessions, and the WebSocket upgrade request carries those
 * cookies automatically against same-origin gateways. For cross-origin
 * deployments, attach a query-string token via the `params` option.
 */
import { Awareness } from 'y-protocols/awareness';
import { WebsocketProvider } from 'y-websocket';
import { IndexeddbPersistence } from 'y-indexeddb';
import * as Y from 'yjs';
/** Connection status reported via `onStatus` and surfaced in the editor UI. */
export type CollaborationStatus = 'connecting' | 'connected' | 'disconnected';
/**
 * Offline-draft persistence status. Slice 6 layers a local IndexedDB
 * mirror on top of the y-websocket transport so edits made while
 * disconnected aren't lost. The lifecycle is:
 *
 * - `unsupported` — `indexedDB` is unavailable (Safari private mode,
 *   SSR, jsdom-without-fake-indexeddb).
 * - `pending` — provider is being constructed; no local state yet.
 * - `loading` — IndexedDB is replaying the last saved snapshot into
 *   the Y.Doc.
 * - `ready` — the local store is initialised and every change is
 *   persisted locally before the WebSocket pushes it upstream.
 * - `error` — IndexedDB threw; the editor still works in-memory but
 *   refresh will lose pending edits.
 */
export type OfflinePersistenceStatus = 'unsupported' | 'pending' | 'loading' | 'ready' | 'error';
/** Per-user presence info that becomes the local awareness state. */
export interface CollaborationUser {
    /** Display name (used for cursor labels and the presence chip strip). */
    name: string;
    /** CSS color value for the user's cursor / chip background. */
    color: string;
    /** Optional avatar URL (32-square recommended). Rendered if present. */
    avatarUrl?: string;
    /** Stable user id from the cms-core session (used as a dedup key). */
    userId?: string;
}
export interface CollaborationOptions {
    /** Logical document id (becomes the y-websocket "room name"). */
    documentId: string;
    /**
     * Base URL of the realtime gateway, e.g. `wss://your.app/api/cms/realtime/sync`.
     * The provider appends `/${documentId}` automatically (y-websocket convention).
     * Use `ws://` for local dev.
     */
    url: string;
    /** Presence info for the local user. */
    user: CollaborationUser;
    /**
     * Extra query-string parameters (e.g. an auth token for cross-origin
     * deployments). Forwarded to `WebsocketProvider`'s `params` option.
     */
    params?: Record<string, string>;
    /** Called on every connection-state transition. */
    onStatus?: (status: CollaborationStatus) => void;
    /** Called on connection-level errors (e.g. 4401 auth rejection). */
    onError?: (err: Error) => void;
    /**
     * If `false`, the helper does not connect on construction. Useful for
     * tests and for editors that want a `Y.Doc` without network traffic.
     * Defaults to `true`.
     */
    connect?: boolean;
    /**
     * Offline-draft persistence. When `true` (default), the provider
     * mounts a `y-indexeddb` mirror so edits made while the WebSocket is
     * disconnected survive a refresh. Pass `false` to skip the mirror
     * (e.g. on shared kiosk machines where you don't want drafts in
     * IndexedDB).
     */
    offline?: boolean;
    /** Called on every offline-persistence status transition. */
    onOfflineStatus?: (status: OfflinePersistenceStatus) => void;
}
export interface CollaborationContext {
    /** Shared Y.Doc — bind this to the TipTap `Collaboration` extension. */
    ydoc: Y.Doc;
    /** The underlying y-websocket provider. */
    provider: WebsocketProvider;
    /** Awareness instance — bind to `CollaborationCursor` and the presence strip. */
    awareness: Awareness;
    /**
     * Local IndexedDB mirror, when `offline` is enabled and the runtime
     * supports it. `null` when persistence is disabled or unsupported
     * (e.g. SSR / Safari private browsing).
     */
    offlinePersistence: IndexeddbPersistence | null;
    /** Latest offline-persistence status. Mirrors `onOfflineStatus`. */
    offlineStatus: OfflinePersistenceStatus;
    /** Stop the provider, evict awareness, and destroy the Y.Doc. */
    destroy: () => void;
}
/** Optional dependency-injection seam used by tests to avoid real WebSockets. */
export interface CollaborationDeps {
    WebsocketProviderCtor?: typeof WebsocketProvider;
    /**
     * Override the IndexedDB persistence constructor (test seam). The
     * default uses `y-indexeddb` which requires a browser-like
     * `indexedDB` global; tests inject a no-op stub.
     */
    IndexeddbPersistenceCtor?: typeof IndexeddbPersistence;
    /**
     * Override the IndexedDB feature check. Defaults to inspecting
     * `globalThis.indexedDB`. Tests inject `() => true` to exercise the
     * persistence path under happy-dom.
     */
    isIndexedDbSupported?: () => boolean;
}
/**
 * Open a collaboration session for `documentId`. Must be called in a browser
 * context (jsdom is fine for tests when a fake provider is injected).
 *
 * The returned `ydoc` is the document the editor binds to via TipTap's
 * `Collaboration` extension; `awareness` carries cursor + presence state.
 * Always call `destroy()` on unmount to evict the local awareness state and
 * close the socket.
 */
export declare function createCollaborationProvider(opts: CollaborationOptions, deps?: CollaborationDeps): CollaborationContext;
/**
 * Reduce an Awareness map into a deduplicated array of `CollaborationUser`s.
 * Used by `PresenceChips` and any other UI that visualises co-editors.
 */
export interface PresenceSnapshot {
    /** Local user's client id (so UIs can omit themselves from the strip). */
    selfClientId: number;
    /** Connected users, deduplicated by `userId` when present, otherwise by clientId. */
    users: Array<{
        clientId: number;
        user: CollaborationUser;
    }>;
}
export declare function summarisePresence(awareness: Awareness): PresenceSnapshot;
//# sourceMappingURL=collaboration-provider.d.ts.map