import { type RoomSynchronizationState, type MediaRequestState, type MediaRequestEvent } from './Lifecycle.js'; import type { MediaSessionConfig, MediaSessionProvider } from './MediaSession.js'; import { VoiceClient, type VoiceOptions } from './VoiceClient.js'; import { VideoClient, type VideoOptions } from './VideoClient.js'; import type { RuntimeTransport, RoomPlayer } from './types.js'; import { PlayerDirectory } from './PlayerDirectory.js'; import { ChatClient } from './ChatClient.js'; export interface RoomClientOptions { /** Retry the socket after an unexpected close. Defaults to true. */ autoReconnect?: boolean; /** Base delay for reconnection backoff in milliseconds. Defaults to 500. */ reconnectBaseDelayMs?: number; /** Upper bound for reconnection backoff in milliseconds. Defaults to 8000. */ reconnectMaxDelayMs?: number; /** Per-action acknowledgement timeout in milliseconds. Defaults to 10000. */ actionTimeoutMs?: number; /** Maximum time for each connection/reconnection to synchronize. Defaults to 10000. */ synchronizationTimeoutMs?: number; } export interface RoomSnapshot { version: number; state: unknown; players: RoomPlayer[]; } export interface RoomEnterOptions { timeoutMs?: number; signal?: AbortSignal; } export interface RealtimeFrame { tick: number; createdAt: string | null; payload: unknown; } export declare const MAX_REALTIME_INPUT_BYTES: number; export declare class RealtimeInputTooLargeError extends Error { readonly bytes: number; readonly limit: number; readonly name = "RealtimeInputTooLargeError"; readonly code = "REALTIME_INPUT_TOO_LARGE"; constructor(bytes: number, limit?: number); } export declare function realtimePayloadBytes(payload: unknown): number; export declare class RoomClient { readonly roomId: string; private readonly transport; readonly userId: string | null; readonly getIceServers?: ((signal: AbortSignal) => Promise) | undefined; readonly getMediaSession?: MediaSessionProvider | undefined; readonly players: PlayerDirectory; private versionChecksSupported; private versionMonitor; private delta; private deltaRecovering; private lifecycle; private get state(); private offMessage?; private offState?; private lastServerSeq; private roomVersion; private valueListeners; private realtimeListeners; private errorListeners; private latestState; private dispatchLifecycle; private inbound; private hasState; private inputSequence; private lastRealtimeTick; private stateListeners; private versionListeners; private connectionListeners; private snapshotListeners; private streamListeners; private connecting?; private disconnecting?; private generation; private cancellationEpoch; private cancelConnect?; private synchronization?; private synchronizationLifecycle; private get synchronized(); private actionBarrierRequired; private synchronizationTimer?; private syncListeners; private membershipVersion; private authenticatedUserId; private pending; private latestMediaRequestIdValue; private mediaRequestHistories; private completedActions; private actionRetryTimer?; private voiceClient?; private videoClient?; private chats; private closedByCaller; private reconnectAttempts; private reconnectTimer?; private entryDelays; private lifecycleOff?; private readonly options; constructor(roomId: string, transport: RuntimeTransport, options?: RoomClientOptions, userId?: string | null, getIceServers?: ((signal: AbortSignal) => Promise) | undefined, getMediaSession?: MediaSessionProvider | undefined); get connectionState(): string; /** Latest authoritative state version observed from the server. */ get version(): number; /** Last `server_seq` used as the reconnect cursor. */ get serverSequence(): number; get latestMediaRequestId(): string | null; mediaRequestHistory(id?: string): { from: MediaRequestState; event: MediaRequestEvent; to: MediaRequestState; }[]; /** Compatibility API: transport readiness only. Games should use enter(). */ connect(): Promise; /** Connect and wait for this connection's full, authenticated resume snapshot. */ enter({ timeoutMs, signal }?: RoomEnterOptions): Promise; get synchronizationState(): RoomSynchronizationState; get synchronizationHistory(): { from: RoomSynchronizationState; event: import("./Lifecycle.js").RoomSynchronizationEvent; to: RoomSynchronizationState; }[]; get isSynchronized(): boolean; onSyncChange(listener: (ready: boolean) => void): () => void; private clearSynchronizationTimer; private setSynchronized; private requestSynchronization; private handleTransportState; private resynchronize; private observeLifecycle; private scheduleReconnect; disconnect(): Promise; /** One voice session per room. Configure on first access, then reuse it. */ voice(options?: VoiceOptions): VoiceClient; /** Camera and microphone session. Use instead of voice() for video calls. */ video(options?: VideoOptions): VideoClient; getPlayers(): RoomPlayer[]; chat(channel?: string): ChatClient; onPlayersChange(listener: (players: RoomPlayer[]) => void): () => void; /** Unified durable state: initial snapshot, recovery, and subsequent actions. */ onState(listener: (state: unknown) => void): () => void; onRealtimeSnapshot(listener: (frame: RealtimeFrame) => void): () => void; onError(listener: (error: Error) => void): () => void; private publishState; onStateChange(listener: (state: unknown) => void): () => void; onVersionChange(listener: (version: number) => void): () => void; onConnectionChange(listener: (state: string) => void): () => void; /** * Fires whenever the server sends a full room view (resume snapshot or member * presence). This is the recovery path that carries authoritative state for * late joiners and reconnects. */ onRoomSnapshot(listener: (room: { version: number; state: unknown; players: unknown; }) => void): () => void; /** * Subscribes to lossy `stream` messages from other peers. Stream is never * authoritative: use it for cursors, presence hints and host snapshots. */ onStream(listener: (message: { channel: string; payload: unknown; playerId?: string | null; }) => void): () => void; private notify; private transition; /** * Resolves with the authoritative `ActionResult` (request_id/version/state/ * events). The action is version-safe by default: it sends the latest version * observed from the server, so a caller does not have to track versions to * avoid optimistic-lock conflicts. */ action(type: string, payload: unknown, options?: { requestId?: string; expectedVersion?: number; }): Promise; private sendAction; private retryActions; private rememberMediaRequest; /** @internal Credentials use the authenticated room socket, including long-lived sessions. */ requestMediaSession(request: { channel: string; media: 'audio' | 'video'; }, signal: AbortSignal): Promise; ready(): Promise; leaveRoom(): Promise; /** Sends a low-latency, lossy stream message. Stream is never authoritative. */ stream(channel: string, payload?: unknown): Promise; /** Auto-sequenced realtime input. Attach onRealtimeSnapshot before connecting. */ sendInput(payload?: Record): Promise; /** Sends the latest realtime input for this player; the server ticks snapshots. */ input(sequence: number, payload?: unknown): Promise; private validateRealtimeInput; private failPending; private applyVersion; private receive; private processReceived; }