/** * Phase 3.2: useRealtime Hook * * React hook for real-time collaborative editing with Puck. * Manages WebSocket connection and real-time document synchronization. */ import type { PuckData, ActorPresence, ActorState, ThreadEvent, PublishResult } from '@pantheon-systems/css-client'; /** * Parameters for the useRealtime hook. */ export interface UseRealtimeParams { /** WebSocket base URL (ws:// or wss://) */ baseUrl: string; /** API key for authentication */ apiKey?: string; /** Site ID */ siteId: string; /** Branch ID */ branchId: string; /** Document path (null means no document loaded yet) */ documentPath: string | null; /** Actor ID (user or agent ID) */ actorId: string; /** Actor type */ actorType: 'user' | 'agent'; /** * Session ID for agent authorization. * Obtained from startEdit() response. * When provided, enables server-side enforcement of Agent Politeness Protocol. */ sessionId?: string; /** * Initial data to seed the Y.Doc with before connecting. * When provided, the Y.Doc will be populated with this data before * the WebSocket connects, allowing the server to send only a delta * instead of the full document state. This eliminates the redundant * full-doc re-render on page load. * * Tracked via ref (not in effect deps) to avoid triggering reconnection * when data changes during editing. */ initialData?: PuckData | null; /** Whether real-time is enabled */ enabled?: boolean; /** Callback when remote changes arrive */ onRemoteUpdate?: (data: PuckData) => void; /** * Callback when presence update is received from server via WebSocket. * Called with the full list of actors in the document. */ onPresenceUpdate?: (actors: ActorPresence[]) => void; /** * Callback when another actor updates their focus regions. * Called with the actor ID and their new focus regions. */ onFocusRegionBroadcast?: (actorId: string, focusRegions: string[]) => void; /** * Optional token refresher for WebSocket reconnection. * Called when the WebSocket connection closes unexpectedly. * Should return a fresh token or null if the session cannot be refreshed. */ tokenRefresher?: () => Promise; /** * Callback when the server sends close code 4001 (server-initiated reload). * Called after the local Y.Doc is cleared, before automatic reconnection. */ onServerReload?: () => void; /** * Callback when the server closes with code 4002 (client lineage diverged). * Reconnection is halted; caller should reseed from REST and reconnect. */ onBaselineReset?: () => void; /** Called when a comment or thread on this document changes, from any editor. */ onThreadEvent?: (event: ThreadEvent) => void; /** * External reset key: incrementing this tears down the current client and * builds a fresh one with a new Y.Doc. Owned by the caller so that the REST * refetch can complete and update `initialData` BEFORE the new client seeds * its Y.Doc — preventing the new client from reseeding stale in-memory data. */ resetKey?: number; } /** * Return value from the useRealtime hook. */ export interface UseRealtimeReturn { /** Whether currently connected to the real-time session */ connected: boolean; /** Apply a local change (will be synced to other clients) */ applyLocalChange: (data: PuckData, puckActions?: { type: string; [key: string]: unknown; }[]) => void; /** Get the current snapshot from the Yjs document. Returns null if not connected. */ getSnapshot: () => PuckData | null; /** Any error that occurred */ error: Error | null; /** * Send focus regions update to the server. * @param regions - JSON paths the actor is focused on * @returns true if message was sent, false if not connected */ sendFocusRegions: (regions: string[]) => boolean; /** * Send a presence heartbeat to keep the connection alive. * @param state - Optional state update (active, idle, editing) */ sendHeartbeat: (state?: ActorState) => void; /** * Whether presence updates are available via WebSocket. * Returns true when connected and able to send/receive presence messages. */ presenceViaWebSocket: boolean; /** * The document path that the realtime connection is currently bound to. * Captured at the moment of connection establishment. Returns null when * disconnected or during connection transitions. Used by callers to verify * that data being sent matches the active connection's document. */ connectedDocumentPath: string | null; /** * Wait for the server to acknowledge that all preceding WebSocket messages * have been processed. Used before publish to ensure the DO has the latest edits. * @returns Promise that resolves when the server confirms delivery * @throws Error if not connected or if timeout expires */ waitForDelivery: () => Promise; /** * Request the server to publish the current document via WebSocket. * The server handles flush + publish internally, eliminating * client-side orchestration and race conditions. * @returns Promise that resolves with the publish result * @throws Error if not connected or if timeout expires */ requestPublish: () => Promise; } /** * Hook for real-time collaborative editing with Puck. * * @example * ```tsx * function Editor() { * const realtime = useRealtime({ * baseUrl: 'wss://api.example.com', * siteId: 'site-123', * branchId: 'branch-456', * documentPath: 'pages/home', * actorId: 'user-789', * actorType: 'user', * enabled: true, * onRemoteUpdate: (data) => setPuckData(data), * }); * * return ( * { * setPuckData(data); * realtime.applyLocalChange(data); * }} * /> * ); * } * ``` */ export declare function useRealtime(params: UseRealtimeParams): UseRealtimeReturn;