@fluxstack/live-client 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +27 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +27 -2
- package/dist/index.js.map +1 -1
- package/dist/live-client.browser.global.js +27 -3
- package/dist/live-client.browser.global.js.map +1 -1
- package/package.json +49 -49
- package/src/component.ts +22 -1
- package/src/rooms.ts +22 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/connection.ts","../src/component.ts","../src/rooms.ts","../src/upload.ts","../src/persistence.ts","../src/state-validator.ts"],"sourcesContent":["// @fluxstack/live-client - Framework-agnostic browser client\r\n//\r\n// This package provides the core WebSocket connection, room management,\r\n// file upload, state persistence, and validation utilities.\r\n// It has NO dependency on any UI framework (React, Vue, etc.).\r\n//\r\n// Quick start (browser IIFE):\r\n// const counter = FluxstackLive.useLive('Counter', { count: 0 })\r\n// counter.on(state => document.getElementById('count').textContent = state.count)\r\n// counter.call('increment')\r\n\r\n// Connection\r\nexport { LiveConnection } from './connection'\r\nexport type {\r\n LiveAuthOptions,\r\n LiveClientAuth,\r\n LiveConnectionOptions,\r\n LiveConnectionState,\r\n} from './connection'\r\n\r\n// Component Handle (vanilla JS equivalent of Live.use)\r\nexport { LiveComponentHandle } from './component'\r\nexport type { LiveComponentOptions } from './component'\r\n\r\n// Rooms\r\nexport { RoomManager } from './rooms'\r\nexport type {\r\n RoomClientMessage,\r\n RoomServerMessage,\r\n RoomHandle,\r\n RoomProxy,\r\n RoomManagerOptions,\r\n EventHandler,\r\n Unsubscribe,\r\n} from './rooms'\r\n\r\n// Upload\r\nexport {\r\n AdaptiveChunkSizer,\r\n ChunkedUploader,\r\n createBinaryChunkMessage,\r\n} from './upload'\r\nexport type {\r\n AdaptiveChunkConfig,\r\n ChunkMetrics,\r\n ChunkedUploadOptions,\r\n ChunkedUploadState,\r\n} from './upload'\r\n\r\n// Persistence\r\nexport {\r\n persistState,\r\n getPersistedState,\r\n clearPersistedState,\r\n} from './persistence'\r\nexport type { PersistedState } from './persistence'\r\n\r\n// State Validation\r\nexport { StateValidator } from './state-validator'\r\nexport type {\r\n StateValidation,\r\n StateConflict,\r\n HybridState,\r\n} from './state-validator'\r\n\r\n// ===== useLive — simplified API for vanilla JS =====\r\n\r\nimport { LiveConnection } from './connection'\r\nimport type { LiveConnectionOptions } from './connection'\r\nimport { LiveComponentHandle } from './component'\r\nimport type { LiveComponentOptions } from './component'\r\n\r\n/** Shared connection singleton — created once, reused by all useLive() calls */\r\nlet _sharedConnection: LiveConnection | null = null\r\nlet _sharedConnectionUrl: string | null = null\r\n\r\n/** Status listeners for the shared connection */\r\ntype ConnectionStatusCallback = (connected: boolean) => void\r\nconst _statusListeners = new Set<ConnectionStatusCallback>()\r\n\r\nfunction getOrCreateConnection(url?: string): LiveConnection {\r\n const resolvedUrl = url ?? `ws://${typeof location !== 'undefined' ? location.host : 'localhost:3000'}/api/live/ws`\r\n\r\n // Reuse existing connection if same URL\r\n if (_sharedConnection && _sharedConnectionUrl === resolvedUrl) {\r\n return _sharedConnection\r\n }\r\n\r\n // Destroy old connection if URL changed\r\n if (_sharedConnection) {\r\n _sharedConnection.destroy()\r\n }\r\n\r\n _sharedConnection = new LiveConnection({ url: resolvedUrl })\r\n _sharedConnectionUrl = resolvedUrl\r\n\r\n _sharedConnection.onStateChange((state) => {\r\n for (const cb of _statusListeners) {\r\n cb(state.connected)\r\n }\r\n })\r\n\r\n return _sharedConnection\r\n}\r\n\r\nexport interface UseLiveOptions {\r\n /** WebSocket URL. Auto-detected from window.location if omitted. */\r\n url?: string\r\n /** Room to join on mount */\r\n room?: string\r\n /** User ID for component isolation */\r\n userId?: string\r\n /** Auto-mount when connected. Default: true */\r\n autoMount?: boolean\r\n /** Enable debug logging. Default: false */\r\n debug?: boolean\r\n}\r\n\r\nexport interface UseLiveHandle<TState extends Record<string, any> = Record<string, any>> {\r\n /** Call a server action */\r\n call: <R = any>(action: string, payload?: Record<string, any>) => Promise<R>\r\n /** Subscribe to state changes. Returns unsubscribe function. */\r\n on: (callback: (state: TState, delta: Partial<TState> | null) => void) => () => void\r\n /** Subscribe to errors. Returns unsubscribe function. */\r\n onError: (callback: (error: string) => void) => () => void\r\n /** Current state (read-only snapshot) */\r\n readonly state: Readonly<TState>\r\n /** Whether the component is mounted on the server */\r\n readonly mounted: boolean\r\n /** Server-assigned component ID */\r\n readonly componentId: string | null\r\n /** Last error message */\r\n readonly error: string | null\r\n /** Destroy the component and clean up */\r\n destroy: () => void\r\n /** Access the underlying LiveComponentHandle */\r\n readonly handle: LiveComponentHandle<TState>\r\n}\r\n\r\n/**\r\n * Create a live component with minimal boilerplate.\r\n * Manages the WebSocket connection automatically (singleton).\r\n *\r\n * @example Browser IIFE\r\n * ```html\r\n * <script src=\"/live-client.js\"></script>\r\n * <script>\r\n * const counter = FluxstackLive.useLive('Counter', { count: 0 })\r\n * counter.on(state => {\r\n * document.getElementById('count').textContent = state.count\r\n * })\r\n * document.querySelector('.inc').onclick = () => counter.call('increment')\r\n * </script>\r\n * ```\r\n *\r\n * @example ES modules\r\n * ```ts\r\n * import { useLive } from '@fluxstack/live-client'\r\n * const counter = useLive('Counter', { count: 0 }, { url: 'ws://localhost:3000/api/live/ws' })\r\n * counter.on(state => console.log(state.count))\r\n * counter.call('increment')\r\n * ```\r\n */\r\nexport function useLive<TState extends Record<string, any> = Record<string, any>>(\r\n componentName: string,\r\n initialState: TState,\r\n options: UseLiveOptions = {},\r\n): UseLiveHandle<TState> {\r\n const { url, room, userId, autoMount = true, debug = false } = options\r\n\r\n const connection = getOrCreateConnection(url)\r\n const handle = new LiveComponentHandle<TState>(connection, componentName, {\r\n initialState,\r\n room,\r\n userId,\r\n autoMount,\r\n debug,\r\n })\r\n\r\n return {\r\n call: (action, payload) => handle.call(action, payload ?? {}),\r\n on: (callback) => handle.onStateChange(callback),\r\n onError: (callback) => handle.onError(callback),\r\n get state() { return handle.state },\r\n get mounted() { return handle.mounted },\r\n get componentId() { return handle.componentId },\r\n get error() { return handle.error },\r\n destroy: () => handle.destroy(),\r\n handle,\r\n }\r\n}\r\n\r\n/**\r\n * Subscribe to the shared connection status (connected/disconnected).\r\n * Useful for showing a global status indicator.\r\n *\r\n * @example\r\n * ```js\r\n * FluxstackLive.onConnectionChange(connected => {\r\n * statusEl.textContent = connected ? 'Connected' : 'Disconnected'\r\n * })\r\n * ```\r\n */\r\nexport function onConnectionChange(callback: ConnectionStatusCallback): () => void {\r\n _statusListeners.add(callback)\r\n // Immediately fire with current state if connection exists\r\n if (_sharedConnection) {\r\n callback(_sharedConnection.state.connected)\r\n }\r\n return () => { _statusListeners.delete(callback) }\r\n}\r\n\r\n/**\r\n * Get or create the shared connection instance.\r\n * Useful when you need direct access to the connection.\r\n */\r\nexport function getConnection(url?: string): LiveConnection {\r\n return getOrCreateConnection(url)\r\n}\r\n","// @fluxstack/live-client - WebSocket Connection Manager\r\n//\r\n// Framework-agnostic WebSocket connection with auto-reconnect, heartbeat,\r\n// request-response pattern, and component message routing.\r\n\r\nimport type { WebSocketMessage, WebSocketResponse } from '@fluxstack/live'\r\n\r\n/** Auth credentials to send during WebSocket connection */\r\nexport interface LiveAuthOptions {\r\n /** JWT or opaque token */\r\n token?: string\r\n /** Provider name (if multiple auth providers configured) */\r\n provider?: string\r\n /** Additional credentials (publicKey, signature, etc.) */\r\n [key: string]: unknown\r\n}\r\n\r\nexport interface LiveConnectionOptions {\r\n /** WebSocket URL. Auto-detected from window.location if omitted. */\r\n url?: string\r\n /** Auth credentials to send on connection */\r\n auth?: LiveAuthOptions\r\n /** Auto-connect on creation. Default: true */\r\n autoConnect?: boolean\r\n /** Reconnect interval in ms. Default: 1000 */\r\n reconnectInterval?: number\r\n /** Max reconnect attempts. Default: 5 */\r\n maxReconnectAttempts?: number\r\n /** Heartbeat interval in ms. Default: 30000 */\r\n heartbeatInterval?: number\r\n /** Enable debug logging. Default: false */\r\n debug?: boolean\r\n}\r\n\r\n/** Auth state exposed to the client */\r\nexport interface LiveClientAuth {\r\n authenticated: boolean\r\n /** Session data from the server. Shape defined by your LiveAuthProvider. */\r\n session: Record<string, unknown> | null\r\n}\r\n\r\nexport interface LiveConnectionState {\r\n connected: boolean\r\n connecting: boolean\r\n error: string | null\r\n connectionId: string | null\r\n authenticated: boolean\r\n /** Auth context with session data */\r\n auth: LiveClientAuth\r\n}\r\n\r\ntype StateChangeCallback = (state: LiveConnectionState) => void\r\ntype ComponentCallback = (message: WebSocketResponse) => void\r\n\r\n/**\r\n * Framework-agnostic WebSocket connection manager.\r\n * Handles reconnection, heartbeat, request-response pattern, and message routing.\r\n */\r\nexport class LiveConnection {\r\n private ws: WebSocket | null = null\r\n private options: Required<Omit<LiveConnectionOptions, 'url' | 'auth'>> & { url?: string; auth?: LiveAuthOptions }\r\n private reconnectAttempts = 0\r\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null\r\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null\r\n private componentCallbacks = new Map<string, ComponentCallback>()\r\n private binaryCallbacks = new Map<string, (payload: Uint8Array) => void>()\r\n private roomBinaryHandlers = new Set<(frame: Uint8Array) => void>()\r\n private pendingRequests = new Map<string, {\r\n resolve: (value: any) => void\r\n reject: (error: any) => void\r\n timeout: ReturnType<typeof setTimeout>\r\n }>()\r\n private stateListeners = new Set<StateChangeCallback>()\r\n private _state: LiveConnectionState = {\r\n connected: false,\r\n connecting: false,\r\n error: null,\r\n connectionId: null,\r\n authenticated: false,\r\n auth: { authenticated: false, session: null },\r\n }\r\n\r\n constructor(options: LiveConnectionOptions = {}) {\r\n this.options = {\r\n url: options.url,\r\n auth: options.auth,\r\n autoConnect: options.autoConnect ?? true,\r\n reconnectInterval: options.reconnectInterval ?? 1000,\r\n maxReconnectAttempts: options.maxReconnectAttempts ?? 5,\r\n heartbeatInterval: options.heartbeatInterval ?? 30000,\r\n debug: options.debug ?? false,\r\n }\r\n\r\n if (this.options.autoConnect) {\r\n this.connect()\r\n }\r\n }\r\n\r\n get state(): LiveConnectionState {\r\n return { ...this._state }\r\n }\r\n\r\n /** Subscribe to connection state changes */\r\n onStateChange(callback: StateChangeCallback): () => void {\r\n this.stateListeners.add(callback)\r\n return () => { this.stateListeners.delete(callback) }\r\n }\r\n\r\n private setState(patch: Partial<LiveConnectionState>) {\r\n this._state = { ...this._state, ...patch }\r\n for (const cb of this.stateListeners) {\r\n cb(this._state)\r\n }\r\n }\r\n\r\n private getWebSocketUrl(): string {\r\n if (this.options.url) {\r\n return this.options.url\r\n } else if (typeof window === 'undefined') {\r\n return 'ws://localhost:3000/api/live/ws'\r\n } else {\r\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\r\n return `${protocol}//${window.location.host}/api/live/ws`\r\n }\r\n }\r\n\r\n private log(message: string, data?: any) {\r\n if (this.options.debug) {\r\n console.log(`[LiveConnection] ${message}`, data || '')\r\n }\r\n }\r\n\r\n /** Generate unique request ID */\r\n generateRequestId(): string {\r\n return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\r\n }\r\n\r\n /** Connect to WebSocket server */\r\n connect(): void {\r\n if (this.ws?.readyState === WebSocket.CONNECTING) {\r\n this.log('Already connecting, skipping...')\r\n return\r\n }\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.log('Already connected, skipping...')\r\n return\r\n }\r\n\r\n this.setState({ connecting: true, error: null })\r\n const url = this.getWebSocketUrl()\r\n this.log('Connecting...', { url })\r\n\r\n try {\r\n const ws = new WebSocket(url)\r\n ws.binaryType = 'arraybuffer'\r\n this.ws = ws\r\n\r\n ws.onopen = () => {\r\n this.log('Connected')\r\n this.setState({ connected: true, connecting: false })\r\n this.reconnectAttempts = 0\r\n this.startHeartbeat()\r\n }\r\n\r\n ws.onmessage = (event) => {\r\n // Binary message path (BINARY_STATE_DELTA)\r\n if (event.data instanceof ArrayBuffer) {\r\n this.handleBinaryMessage(new Uint8Array(event.data))\r\n return\r\n }\r\n\r\n try {\r\n const parsed = JSON.parse(event.data)\r\n // Server may send batched messages as a JSON array\r\n if (Array.isArray(parsed)) {\r\n for (const msg of parsed) {\r\n this.log('Received', { type: msg.type, componentId: msg.componentId })\r\n this.handleMessage(msg)\r\n }\r\n } else {\r\n this.log('Received', { type: parsed.type, componentId: parsed.componentId })\r\n this.handleMessage(parsed)\r\n }\r\n } catch {\r\n this.log('Failed to parse message')\r\n this.setState({ error: 'Failed to parse message' })\r\n }\r\n }\r\n\r\n ws.onclose = (event) => {\r\n this.log('Disconnected', { code: event.code, reason: event.reason })\r\n this.setState({ connected: false, connecting: false, connectionId: null, authenticated: false, auth: { authenticated: false, session: null } })\r\n this.stopHeartbeat()\r\n\r\n // Server rejected connection due to CSRF origin validation — don't retry\r\n if (event.code === 4003) {\r\n this.setState({ error: 'Connection rejected: origin not allowed' })\r\n return\r\n }\r\n\r\n this.attemptReconnect()\r\n }\r\n\r\n ws.onerror = () => {\r\n this.log('WebSocket error')\r\n this.setState({ error: 'WebSocket connection error', connecting: false })\r\n }\r\n } catch (error) {\r\n this.setState({\r\n connecting: false,\r\n error: error instanceof Error ? error.message : 'Connection failed',\r\n })\r\n }\r\n }\r\n\r\n /** Disconnect from WebSocket server */\r\n disconnect(): void {\r\n if (this.reconnectTimeout) {\r\n clearTimeout(this.reconnectTimeout)\r\n this.reconnectTimeout = null\r\n }\r\n this.stopHeartbeat()\r\n if (this.ws) {\r\n this.ws.close()\r\n this.ws = null\r\n }\r\n this.reconnectAttempts = this.options.maxReconnectAttempts\r\n this.setState({ connected: false, connecting: false, connectionId: null })\r\n }\r\n\r\n /** Manual reconnect */\r\n reconnect(): void {\r\n this.disconnect()\r\n this.reconnectAttempts = 0\r\n setTimeout(() => this.connect(), 100)\r\n }\r\n\r\n private attemptReconnect(): void {\r\n if (this.reconnectAttempts < this.options.maxReconnectAttempts) {\r\n this.reconnectAttempts++\r\n this.log(`Reconnecting... (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})`)\r\n this.reconnectTimeout = setTimeout(() => this.connect(), this.options.reconnectInterval)\r\n } else {\r\n this.setState({ error: 'Max reconnection attempts reached' })\r\n }\r\n }\r\n\r\n private consecutiveHeartbeatFailures = 0\r\n private static readonly MAX_HEARTBEAT_FAILURES = 3\r\n\r\n private startHeartbeat(): void {\r\n this.stopHeartbeat()\r\n this.consecutiveHeartbeatFailures = 0\r\n this.heartbeatInterval = setInterval(() => {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n let failed = false\r\n for (const componentId of this.componentCallbacks.keys()) {\r\n this.sendMessage({\r\n type: 'COMPONENT_PING',\r\n componentId,\r\n timestamp: Date.now(),\r\n }).catch(() => { failed = true })\r\n }\r\n if (failed) {\r\n this.consecutiveHeartbeatFailures++\r\n this.log(`Heartbeat failed (${this.consecutiveHeartbeatFailures}/${LiveConnection.MAX_HEARTBEAT_FAILURES})`)\r\n if (this.consecutiveHeartbeatFailures >= LiveConnection.MAX_HEARTBEAT_FAILURES) {\r\n this.log('Too many heartbeat failures, reconnecting...')\r\n this.setState({ error: 'Heartbeat failed' })\r\n this.reconnect()\r\n }\r\n } else {\r\n this.consecutiveHeartbeatFailures = 0\r\n }\r\n }\r\n }, this.options.heartbeatInterval)\r\n }\r\n\r\n private stopHeartbeat(): void {\r\n if (this.heartbeatInterval) {\r\n clearInterval(this.heartbeatInterval)\r\n this.heartbeatInterval = null\r\n }\r\n }\r\n\r\n private handleMessage(response: WebSocketResponse): void {\r\n // Handle connection established\r\n if (response.type === 'CONNECTION_ESTABLISHED') {\r\n this.setState({\r\n connectionId: response.connectionId || null,\r\n authenticated: (response as any).authenticated || false,\r\n })\r\n\r\n // Send AUTH message if credentials provided (always via socket, never in URL)\r\n const auth = this.options.auth\r\n if (auth && Object.keys(auth).some(k => auth[k])) {\r\n this.sendMessageAndWait({ type: 'AUTH', payload: auth } as any)\r\n .then(authResp => {\r\n const payload = (authResp as any).payload\r\n if (payload?.authenticated) {\r\n this.setState({\r\n authenticated: true,\r\n auth: { authenticated: true, session: payload.session || null },\r\n })\r\n }\r\n })\r\n .catch(() => {})\r\n }\r\n }\r\n\r\n // Handle auth response\r\n if (response.type === 'AUTH_RESPONSE') {\r\n const payload = (response as any).payload\r\n const authenticated = payload?.authenticated || false\r\n this.setState({\r\n authenticated,\r\n auth: {\r\n authenticated,\r\n session: authenticated ? (payload?.session || null) : null,\r\n },\r\n })\r\n }\r\n\r\n // Handle pending requests (request-response pattern)\r\n if (response.requestId && this.pendingRequests.has(response.requestId)) {\r\n const request = this.pendingRequests.get(response.requestId)!\r\n clearTimeout(request.timeout)\r\n this.pendingRequests.delete(response.requestId)\r\n\r\n if (response.success !== false) {\r\n request.resolve(response)\r\n } else {\r\n if (response.error?.includes?.('COMPONENT_REHYDRATION_REQUIRED')) {\r\n request.resolve(response)\r\n } else {\r\n request.reject(new Error(response.error || 'Request failed'))\r\n }\r\n }\r\n return\r\n }\r\n\r\n // Broadcast messages go to ALL components (not just sender)\r\n if (response.type === 'BROADCAST') {\r\n this.componentCallbacks.forEach((callback, compId) => {\r\n if (compId !== response.componentId) {\r\n callback(response)\r\n }\r\n })\r\n return\r\n }\r\n\r\n // Route message to specific component\r\n if (response.componentId) {\r\n const callback = this.componentCallbacks.get(response.componentId)\r\n if (callback) {\r\n callback(response)\r\n } else {\r\n this.log('No callback registered for component:', response.componentId)\r\n }\r\n }\r\n }\r\n\r\n /** Send message without waiting for response */\r\n async sendMessage(message: WebSocketMessage): Promise<void> {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n throw new Error('WebSocket is not connected')\r\n }\r\n const messageWithTimestamp = { ...message, timestamp: Date.now() }\r\n this.ws.send(JSON.stringify(messageWithTimestamp))\r\n this.log('Sent', { type: message.type, componentId: message.componentId })\r\n }\r\n\r\n /** Send message and wait for response */\r\n async sendMessageAndWait(message: WebSocketMessage, timeout = 10000): Promise<WebSocketResponse> {\r\n return new Promise((resolve, reject) => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n reject(new Error('WebSocket is not connected'))\r\n return\r\n }\r\n\r\n const requestId = this.generateRequestId()\r\n\r\n const timeoutHandle = setTimeout(() => {\r\n this.pendingRequests.delete(requestId)\r\n reject(new Error(`Request timeout after ${timeout}ms`))\r\n }, timeout)\r\n\r\n this.pendingRequests.set(requestId, { resolve, reject, timeout: timeoutHandle })\r\n\r\n try {\r\n const messageWithRequestId = {\r\n ...message,\r\n requestId,\r\n expectResponse: true,\r\n timestamp: Date.now(),\r\n }\r\n this.ws.send(JSON.stringify(messageWithRequestId))\r\n this.log('Sent with requestId', { requestId, type: message.type })\r\n } catch (error) {\r\n clearTimeout(timeoutHandle)\r\n this.pendingRequests.delete(requestId)\r\n reject(error)\r\n }\r\n })\r\n }\r\n\r\n /** Send binary data and wait for response (for file uploads) */\r\n async sendBinaryAndWait(data: ArrayBuffer, requestId: string, timeout = 10000): Promise<WebSocketResponse> {\r\n return new Promise((resolve, reject) => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n reject(new Error('WebSocket is not connected'))\r\n return\r\n }\r\n\r\n const timeoutHandle = setTimeout(() => {\r\n this.pendingRequests.delete(requestId)\r\n reject(new Error(`Binary request timeout after ${timeout}ms`))\r\n }, timeout)\r\n\r\n this.pendingRequests.set(requestId, { resolve, reject, timeout: timeoutHandle })\r\n\r\n try {\r\n this.ws.send(data)\r\n this.log('Sent binary', { requestId, size: data.byteLength })\r\n } catch (error) {\r\n clearTimeout(timeoutHandle)\r\n this.pendingRequests.delete(requestId)\r\n reject(error)\r\n }\r\n })\r\n }\r\n\r\n /** Parse and route binary frames (state delta, room events, room state) */\r\n private handleBinaryMessage(buffer: Uint8Array): void {\r\n if (buffer.length < 3) return\r\n\r\n const frameType = buffer[0]\r\n\r\n if (frameType === 0x01) {\r\n // BINARY_STATE_DELTA: [0x01][idLen:u8][compId:utf8][payload]\r\n const idLen = buffer[1]\r\n if (buffer.length < 2 + idLen) return\r\n const componentId = new TextDecoder().decode(buffer.subarray(2, 2 + idLen))\r\n const payload = buffer.subarray(2 + idLen)\r\n\r\n const callback = this.binaryCallbacks.get(componentId)\r\n if (callback) callback(payload)\r\n } else if (frameType === 0x02 || frameType === 0x03) {\r\n // BINARY_ROOM_EVENT (0x02) or BINARY_ROOM_STATE (0x03)\r\n // Route to all registered room binary handlers (RoomManager instances)\r\n for (const callback of this.roomBinaryHandlers) {\r\n callback(buffer)\r\n }\r\n }\r\n }\r\n\r\n /** Register a handler for binary room frames (0x02 / 0x03). Returns unsubscribe. */\r\n registerRoomBinaryHandler(callback: (frame: Uint8Array) => void): () => void {\r\n this.roomBinaryHandlers.add(callback)\r\n return () => { this.roomBinaryHandlers.delete(callback) }\r\n }\r\n\r\n /** Register a binary message handler for a component */\r\n registerBinaryHandler(componentId: string, callback: (payload: Uint8Array) => void): () => void {\r\n this.binaryCallbacks.set(componentId, callback)\r\n return () => { this.binaryCallbacks.delete(componentId) }\r\n }\r\n\r\n /** Register a component message callback */\r\n registerComponent(componentId: string, callback: ComponentCallback): () => void {\r\n this.log('Registering component', componentId)\r\n this.componentCallbacks.set(componentId, callback)\r\n return () => {\r\n this.componentCallbacks.delete(componentId)\r\n this.log('Unregistered component', componentId)\r\n }\r\n }\r\n\r\n /** Unregister a component */\r\n unregisterComponent(componentId: string): void {\r\n this.componentCallbacks.delete(componentId)\r\n }\r\n\r\n /** Authenticate (or re-authenticate) the WebSocket connection */\r\n async authenticate(credentials: LiveAuthOptions): Promise<boolean> {\r\n try {\r\n const response = await this.sendMessageAndWait(\r\n { type: 'AUTH', payload: credentials } as any,\r\n 5000\r\n )\r\n const payload = (response as any).payload\r\n const success = payload?.authenticated || false\r\n this.setState({\r\n authenticated: success,\r\n auth: {\r\n authenticated: success,\r\n session: success ? (payload?.session || null) : null,\r\n },\r\n })\r\n return success\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n /** Get the raw WebSocket instance */\r\n getWebSocket(): WebSocket | null {\r\n return this.ws\r\n }\r\n\r\n /** Destroy the connection and clean up all resources */\r\n destroy(): void {\r\n this.disconnect()\r\n this.componentCallbacks.clear()\r\n this.binaryCallbacks.clear()\r\n this.roomBinaryHandlers.clear()\r\n for (const [, req] of this.pendingRequests) {\r\n clearTimeout(req.timeout)\r\n req.reject(new Error('Connection destroyed'))\r\n }\r\n this.pendingRequests.clear()\r\n this.stateListeners.clear()\r\n }\r\n}\r\n","// @fluxstack/live-client - LiveComponentHandle\r\n//\r\n// High-level vanilla JS wrapper for live components.\r\n// Equivalent to Live.use() in @fluxstack/live-react but without React.\r\n//\r\n// Usage:\r\n// const connection = new LiveConnection({ url: 'ws://...' })\r\n// const counter = new LiveComponentHandle(connection, 'Counter', { count: 0 })\r\n// counter.onStateChange((state) => updateUI(state))\r\n// await counter.mount()\r\n// await counter.call('increment')\r\n\r\nimport type { WebSocketResponse } from '@fluxstack/live'\r\nimport type { LiveConnection } from './connection'\r\n\r\n// ===== Deep Merge (always-on, retrocompatible) =====\r\n\r\nfunction isPlainObject(v: unknown): v is Record<string, any> {\r\n return v !== null && typeof v === 'object' && !Array.isArray(v)\r\n && Object.getPrototypeOf(v) === Object.prototype\r\n}\r\n\r\nfunction deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>, seen?: Set<object>): T {\r\n if (!seen) seen = new Set()\r\n if (seen.has(source as object)) return target\r\n seen.add(source as object)\r\n\r\n const result = { ...target }\r\n for (const key of Object.keys(source) as Array<keyof T>) {\r\n const newVal = source[key]\r\n const oldVal = result[key]\r\n if (isPlainObject(oldVal) && isPlainObject(newVal)) {\r\n result[key] = deepMerge(oldVal as any, newVal as any, seen)\r\n } else {\r\n result[key] = newVal as T[keyof T]\r\n }\r\n }\r\n return result\r\n}\r\n\r\nexport interface LiveComponentOptions<TState = Record<string, any>> {\r\n /** Initial state to merge with server defaults */\r\n initialState?: Partial<TState>\r\n /** Room to join on mount */\r\n room?: string\r\n /** User ID for component isolation */\r\n userId?: string\r\n /** Auto-mount when connection is ready. Default: true */\r\n autoMount?: boolean\r\n /** Enable debug logging. Default: false */\r\n debug?: boolean\r\n}\r\n\r\ntype StateChangeCallback<TState> = (state: TState, delta: Partial<TState> | null) => void\r\ntype ErrorCallback = (error: string) => void\r\n\r\n/**\r\n * High-level handle for a live component instance.\r\n * Manages mount lifecycle, state sync, and action calling.\r\n * Framework-agnostic — works with vanilla JS, Vue, Svelte, etc.\r\n */\r\nexport class LiveComponentHandle<TState extends Record<string, any> = Record<string, any>> {\r\n private connection: LiveConnection\r\n private componentName: string\r\n private options: Required<Omit<LiveComponentOptions<TState>, 'initialState' | 'room' | 'userId'>> & {\r\n initialState: Partial<TState>\r\n room?: string\r\n userId?: string\r\n }\r\n\r\n private _componentId: string | null = null\r\n private _state: TState\r\n private _mounted = false\r\n private _mounting = false\r\n private _error: string | null = null\r\n\r\n private stateListeners = new Set<StateChangeCallback<TState>>()\r\n private errorListeners = new Set<ErrorCallback>()\r\n private unregisterComponent: (() => void) | null = null\r\n private unsubConnection: (() => void) | null = null\r\n\r\n constructor(\r\n connection: LiveConnection,\r\n componentName: string,\r\n options: LiveComponentOptions<TState> = {},\r\n ) {\r\n this.connection = connection\r\n this.componentName = componentName\r\n this._state = (options.initialState ?? {}) as TState\r\n\r\n this.options = {\r\n initialState: options.initialState ?? {},\r\n room: options.room,\r\n userId: options.userId,\r\n autoMount: options.autoMount ?? true,\r\n debug: options.debug ?? false,\r\n }\r\n\r\n // Auto-mount when connection is ready\r\n if (this.options.autoMount) {\r\n if (this.connection.state.connected) {\r\n this.mount()\r\n }\r\n this.unsubConnection = this.connection.onStateChange((connState) => {\r\n if (connState.connected && !this._mounted && !this._mounting) {\r\n this.mount()\r\n }\r\n })\r\n }\r\n }\r\n\r\n // ── Getters ──\r\n\r\n /** Current component state */\r\n get state(): Readonly<TState> { return this._state }\r\n\r\n /** Server-assigned component ID (null before mount) */\r\n get componentId(): string | null { return this._componentId }\r\n\r\n /** Whether the component has been mounted */\r\n get mounted(): boolean { return this._mounted }\r\n\r\n /** Whether the component is currently mounting */\r\n get mounting(): boolean { return this._mounting }\r\n\r\n /** Last error message */\r\n get error(): string | null { return this._error }\r\n\r\n // ── Lifecycle ──\r\n\r\n /** Mount the component on the server */\r\n async mount(): Promise<void> {\r\n if (this._mounted || this._mounting) return\r\n if (!this.connection.state.connected) {\r\n throw new Error('Cannot mount: not connected')\r\n }\r\n\r\n this._mounting = true\r\n this._error = null\r\n this.log('Mounting...')\r\n\r\n try {\r\n const response = await this.connection.sendMessageAndWait({\r\n type: 'COMPONENT_MOUNT',\r\n componentId: `mount-${this.componentName}`,\r\n payload: {\r\n component: this.componentName,\r\n props: this.options.initialState,\r\n room: this.options.room,\r\n userId: this.options.userId,\r\n },\r\n })\r\n\r\n if (!response.success) {\r\n throw new Error(response.error || 'Mount failed')\r\n }\r\n\r\n const result = (response as any).result\r\n this._componentId = result.componentId\r\n this._mounted = true\r\n this._mounting = false\r\n\r\n // Merge initial state from server\r\n const serverState = result.initialState || {}\r\n this._state = { ...this._state, ...serverState }\r\n\r\n // Register for component messages (state updates, deltas, errors)\r\n this.unregisterComponent = this.connection.registerComponent(\r\n this._componentId!,\r\n (msg) => this.handleServerMessage(msg),\r\n )\r\n\r\n this.log('Mounted', { componentId: this._componentId })\r\n this.notifyStateChange(this._state, null)\r\n } catch (err) {\r\n this._mounting = false\r\n const errorMsg = err instanceof Error ? err.message : String(err)\r\n this._error = errorMsg\r\n this.notifyError(errorMsg)\r\n throw err\r\n }\r\n }\r\n\r\n /** Unmount the component from the server */\r\n async unmount(): Promise<void> {\r\n if (!this._mounted || !this._componentId) return\r\n\r\n this.log('Unmounting...')\r\n\r\n try {\r\n await this.connection.sendMessage({\r\n type: 'COMPONENT_UNMOUNT',\r\n componentId: this._componentId,\r\n })\r\n } catch {\r\n // Ignore unmount errors (connection may already be closed)\r\n }\r\n\r\n this.cleanup()\r\n }\r\n\r\n /** Destroy the handle and clean up all resources */\r\n destroy(): void {\r\n this.unmount().catch(() => {})\r\n if (this.unsubConnection) {\r\n this.unsubConnection()\r\n this.unsubConnection = null\r\n }\r\n this.stateListeners.clear()\r\n this.errorListeners.clear()\r\n }\r\n\r\n // ── Actions ──\r\n\r\n /**\r\n * Call an action on the server component.\r\n * Returns the action's return value.\r\n */\r\n async call<R = any>(action: string, payload: Record<string, any> = {}): Promise<R> {\r\n if (!this._mounted || !this._componentId) {\r\n throw new Error(`Cannot call '${action}': component not mounted`)\r\n }\r\n\r\n this.log(`Calling action: ${action}`, payload)\r\n\r\n const response = await this.connection.sendMessageAndWait({\r\n type: 'CALL_ACTION',\r\n componentId: this._componentId,\r\n action,\r\n payload,\r\n })\r\n\r\n if (!response.success) {\r\n const errorMsg = response.error || `Action '${action}' failed`\r\n this._error = errorMsg\r\n this.notifyError(errorMsg)\r\n throw new Error(errorMsg)\r\n }\r\n\r\n return (response as any).result\r\n }\r\n\r\n /**\r\n * Fire an action without waiting for a response (fire-and-forget).\r\n * Useful for high-frequency operations like game input where the\r\n * server doesn't need to send back a result.\r\n */\r\n fire(action: string, payload: Record<string, any> = {}): void {\r\n if (!this._mounted || !this._componentId) return\r\n\r\n this.connection.sendMessage({\r\n type: 'CALL_ACTION',\r\n componentId: this._componentId,\r\n action,\r\n payload,\r\n expectResponse: false,\r\n } as any)\r\n }\r\n\r\n // ── State ──\r\n\r\n /**\r\n * Subscribe to state changes.\r\n * Callback receives the full new state and the delta (or null for full updates).\r\n * Returns an unsubscribe function.\r\n */\r\n onStateChange(callback: StateChangeCallback<TState>): () => void {\r\n this.stateListeners.add(callback)\r\n return () => { this.stateListeners.delete(callback) }\r\n }\r\n\r\n /**\r\n * Register a binary decoder for this component.\r\n * When the server sends a BINARY_STATE_DELTA frame targeting this component,\r\n * the decoder converts the raw payload into a delta object which is merged into state.\r\n * Returns an unsubscribe function.\r\n */\r\n setBinaryDecoder(decoder: (buffer: Uint8Array) => Record<string, any>): () => void {\r\n if (!this._componentId) {\r\n throw new Error('Component must be mounted before setting binary decoder')\r\n }\r\n\r\n return this.connection.registerBinaryHandler(this._componentId, (payload: Uint8Array) => {\r\n try {\r\n const delta = decoder(payload) as Partial<TState>\r\n this._state = deepMerge(this._state, delta) as TState\r\n this.notifyStateChange(this._state, delta as Partial<TState>)\r\n } catch (e) {\r\n console.error('Binary decode error:', e)\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * Subscribe to errors.\r\n * Returns an unsubscribe function.\r\n */\r\n onError(callback: ErrorCallback): () => void {\r\n this.errorListeners.add(callback)\r\n return () => { this.errorListeners.delete(callback) }\r\n }\r\n\r\n // ── Internal ──\r\n\r\n private handleServerMessage(msg: WebSocketResponse): void {\r\n switch (msg.type) {\r\n case 'STATE_UPDATE': {\r\n const newState = (msg as any).payload?.state\r\n if (newState) {\r\n this._state = deepMerge(this._state, newState)\r\n this.notifyStateChange(this._state, null)\r\n }\r\n break\r\n }\r\n\r\n case 'STATE_DELTA': {\r\n const delta = (msg as any).payload?.delta\r\n if (delta) {\r\n this._state = deepMerge(this._state, delta)\r\n this.notifyStateChange(this._state, delta)\r\n }\r\n break\r\n }\r\n\r\n case 'ERROR': {\r\n const error = (msg as any).error || 'Unknown error'\r\n this._error = error\r\n this.notifyError(error)\r\n break\r\n }\r\n\r\n default:\r\n this.log('Unhandled message type:', msg.type)\r\n }\r\n }\r\n\r\n private notifyStateChange(state: TState, delta: Partial<TState> | null): void {\r\n for (const cb of this.stateListeners) {\r\n cb(state, delta)\r\n }\r\n }\r\n\r\n private notifyError(error: string): void {\r\n for (const cb of this.errorListeners) {\r\n cb(error)\r\n }\r\n }\r\n\r\n private cleanup(): void {\r\n if (this.unregisterComponent) {\r\n this.unregisterComponent()\r\n this.unregisterComponent = null\r\n }\r\n this._componentId = null\r\n this._mounted = false\r\n this._mounting = false\r\n }\r\n\r\n private log(message: string, data?: any): void {\r\n if (this.options.debug) {\r\n console.log(`[Live:${this.componentName}] ${message}`, data ?? '')\r\n }\r\n }\r\n}\r\n","// @fluxstack/live-client - Room Manager (Client-side)\r\n//\r\n// Framework-agnostic room system for managing multi-room WebSocket communication.\r\n// Used by framework-specific adapters (React, Vue, etc.).\r\n\r\n// ===== Deep Merge (always-on, retrocompatible) =====\r\n\r\nfunction isPlainObject(v: unknown): v is Record<string, any> {\r\n return v !== null && typeof v === 'object' && !Array.isArray(v)\r\n && Object.getPrototypeOf(v) === Object.prototype\r\n}\r\n\r\nfunction deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>, seen?: Set<object>): T {\r\n if (!seen) seen = new Set()\r\n if (seen.has(source as object)) return target\r\n seen.add(source as object)\r\n\r\n const result = { ...target }\r\n for (const key of Object.keys(source) as Array<keyof T>) {\r\n const newVal = source[key]\r\n const oldVal = result[key]\r\n if (isPlainObject(oldVal) && isPlainObject(newVal)) {\r\n result[key] = deepMerge(oldVal as any, newVal as any, seen)\r\n } else {\r\n result[key] = newVal as T[keyof T]\r\n }\r\n }\r\n return result\r\n}\r\n\r\ntype EventHandler<T = any> = (data: T) => void\r\ntype Unsubscribe = () => void\r\n\r\n// ===== Binary Room Frame Constants =====\r\n\r\nconst BINARY_ROOM_EVENT = 0x02\r\nconst BINARY_ROOM_STATE = 0x03\r\n\r\n// ===== Lightweight msgpack decoder (client-side, decode-only) =====\r\n\r\nconst _decoder = new TextDecoder()\r\n\r\nfunction msgpackDecode(buf: Uint8Array): unknown {\r\n return _decodeAt(buf, 0).value\r\n}\r\n\r\nfunction _decodeAt(buf: Uint8Array, offset: number): { value: unknown; offset: number } {\r\n if (offset >= buf.length) return { value: null, offset }\r\n const byte = buf[offset]\r\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)\r\n\r\n if (byte < 0x80) return { value: byte, offset: offset + 1 }\r\n if (byte >= 0x80 && byte <= 0x8f) return _decodeMap(buf, offset + 1, byte & 0x0f)\r\n if (byte >= 0x90 && byte <= 0x9f) return _decodeArr(buf, offset + 1, byte & 0x0f)\r\n if (byte >= 0xa0 && byte <= 0xbf) {\r\n const len = byte & 0x1f\r\n return { value: _decoder.decode(buf.subarray(offset + 1, offset + 1 + len)), offset: offset + 1 + len }\r\n }\r\n if (byte >= 0xe0) return { value: byte - 256, offset: offset + 1 }\r\n\r\n switch (byte) {\r\n case 0xc0: return { value: null, offset: offset + 1 }\r\n case 0xc2: return { value: false, offset: offset + 1 }\r\n case 0xc3: return { value: true, offset: offset + 1 }\r\n case 0xc4: { const l = buf[offset + 1]; return { value: buf.slice(offset + 2, offset + 2 + l), offset: offset + 2 + l } }\r\n case 0xc5: { const l = view.getUint16(offset + 1, false); return { value: buf.slice(offset + 3, offset + 3 + l), offset: offset + 3 + l } }\r\n case 0xc6: { const l = view.getUint32(offset + 1, false); return { value: buf.slice(offset + 5, offset + 5 + l), offset: offset + 5 + l } }\r\n case 0xcb: return { value: view.getFloat64(offset + 1, false), offset: offset + 9 }\r\n case 0xcc: return { value: buf[offset + 1], offset: offset + 2 }\r\n case 0xcd: return { value: view.getUint16(offset + 1, false), offset: offset + 3 }\r\n case 0xce: return { value: view.getUint32(offset + 1, false), offset: offset + 5 }\r\n case 0xd0: return { value: view.getInt8(offset + 1), offset: offset + 2 }\r\n case 0xd1: return { value: view.getInt16(offset + 1, false), offset: offset + 3 }\r\n case 0xd2: return { value: view.getInt32(offset + 1, false), offset: offset + 5 }\r\n case 0xd9: { const l = buf[offset + 1]; return { value: _decoder.decode(buf.subarray(offset + 2, offset + 2 + l)), offset: offset + 2 + l } }\r\n case 0xda: { const l = view.getUint16(offset + 1, false); return { value: _decoder.decode(buf.subarray(offset + 3, offset + 3 + l)), offset: offset + 3 + l } }\r\n case 0xdb: { const l = view.getUint32(offset + 1, false); return { value: _decoder.decode(buf.subarray(offset + 5, offset + 5 + l)), offset: offset + 5 + l } }\r\n case 0xdc: return _decodeArr(buf, offset + 3, view.getUint16(offset + 1, false))\r\n case 0xdd: return _decodeArr(buf, offset + 5, view.getUint32(offset + 1, false))\r\n case 0xde: return _decodeMap(buf, offset + 3, view.getUint16(offset + 1, false))\r\n case 0xdf: return _decodeMap(buf, offset + 5, view.getUint32(offset + 1, false))\r\n }\r\n return { value: null, offset: offset + 1 }\r\n}\r\n\r\nfunction _decodeArr(buf: Uint8Array, offset: number, count: number): { value: unknown[]; offset: number } {\r\n const arr: unknown[] = new Array(count)\r\n for (let i = 0; i < count; i++) { const r = _decodeAt(buf, offset); arr[i] = r.value; offset = r.offset }\r\n return { value: arr, offset }\r\n}\r\n\r\nfunction _decodeMap(buf: Uint8Array, offset: number, count: number): { value: Record<string, unknown>; offset: number } {\r\n const obj: Record<string, unknown> = {}\r\n for (let i = 0; i < count; i++) {\r\n const k = _decodeAt(buf, offset); offset = k.offset\r\n const v = _decodeAt(buf, offset); offset = v.offset\r\n obj[String(k.value)] = v.value\r\n }\r\n return { value: obj, offset }\r\n}\r\n\r\n/** Parse a binary room frame: [frameType][compIdLen][compId][roomIdLen][roomId][eventLen:u16][event][payload] */\r\nfunction parseRoomFrame(buf: Uint8Array): {\r\n frameType: number; componentId: string; roomId: string; event: string; payload: Uint8Array\r\n} | null {\r\n if (buf.length < 6) return null\r\n let offset = 0\r\n const frameType = buf[offset++]\r\n const compIdLen = buf[offset++]\r\n if (offset + compIdLen > buf.length) return null\r\n const componentId = _decoder.decode(buf.subarray(offset, offset + compIdLen)); offset += compIdLen\r\n const roomIdLen = buf[offset++]\r\n if (offset + roomIdLen > buf.length) return null\r\n const roomId = _decoder.decode(buf.subarray(offset, offset + roomIdLen)); offset += roomIdLen\r\n if (offset + 2 > buf.length) return null\r\n const eventLen = (buf[offset] << 8) | buf[offset + 1]; offset += 2\r\n if (offset + eventLen > buf.length) return null\r\n const event = _decoder.decode(buf.subarray(offset, offset + eventLen)); offset += eventLen\r\n return { frameType, componentId, roomId, event, payload: buf.subarray(offset) }\r\n}\r\n\r\n/** Reserved property names on RoomHandle/RoomProxy (never fall through to state) */\r\nconst ROOM_RESERVED_KEYS = new Set<string | symbol>([\r\n 'id', 'joined', 'state', 'join', 'leave', 'emit', 'on', 'onSystem', 'setState',\r\n 'call', 'apply', 'bind', 'prototype', 'length', 'name', 'arguments', 'caller',\r\n Symbol.toPrimitive, Symbol.toStringTag, Symbol.hasInstance,\r\n])\r\n\r\n/** Wrap a handle/proxy so unknown property access falls through to state */\r\nfunction wrapWithStateProxy<T extends object>(\r\n target: T,\r\n getState: () => any,\r\n setStateFn: (updates: any) => void,\r\n): T {\r\n return new Proxy(target, {\r\n get(obj, prop, receiver) {\r\n if (ROOM_RESERVED_KEYS.has(prop) || typeof prop === 'symbol') {\r\n return Reflect.get(obj, prop, receiver)\r\n }\r\n const desc = Object.getOwnPropertyDescriptor(obj, prop)\r\n if (desc) return Reflect.get(obj, prop, receiver)\r\n if (prop in obj) return Reflect.get(obj, prop, receiver)\r\n const st = getState()\r\n return st?.[prop]\r\n },\r\n set(_obj, prop, value) {\r\n if (typeof prop === 'symbol') return false\r\n setStateFn({ [prop]: value })\r\n return true\r\n },\r\n })\r\n}\r\n\r\n/** Reserved keys on RoomHandle/RoomProxy — cannot be state fields */\r\ntype RoomReservedKeys = 'id' | 'joined' | 'state' | 'join' | 'leave' | 'emit' | 'on' | 'onSystem' | 'setState'\r\n\r\n/** State fields accessible directly on handle/proxy (excludes reserved method names) */\r\ntype RoomStateFields<TState> = TState extends Record<string, any>\r\n ? { readonly [K in Exclude<keyof TState, RoomReservedKeys>]: TState[K] }\r\n : unknown\r\n\r\n/** Message from client to server */\r\nexport interface RoomClientMessage {\r\n type: 'ROOM_JOIN' | 'ROOM_LEAVE' | 'ROOM_EMIT' | 'ROOM_STATE_GET' | 'ROOM_STATE_SET'\r\n componentId: string\r\n roomId: string\r\n event?: string\r\n data?: any\r\n timestamp: number\r\n}\r\n\r\n/** Message from server to client */\r\nexport interface RoomServerMessage {\r\n type: 'ROOM_EVENT' | 'ROOM_STATE' | 'ROOM_SYSTEM' | 'ROOM_JOINED' | 'ROOM_LEFT'\r\n componentId: string\r\n roomId: string\r\n event: string\r\n data: any\r\n timestamp: number\r\n}\r\n\r\n/** Interface of an individual room handle */\r\nexport type RoomHandle<TState = any, TEvents extends Record<string, any> = Record<string, any>> = {\r\n readonly id: string\r\n readonly joined: boolean\r\n readonly state: TState\r\n join: (initialState?: TState) => Promise<void>\r\n leave: () => Promise<void>\r\n emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void\r\n on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe\r\n onSystem: (event: string, handler: EventHandler) => Unsubscribe\r\n setState: (updates: Partial<TState>) => void\r\n} & RoomStateFields<TState>\r\n\r\n/** Infer TEvents from a LiveRoom class (via $events brand) or use T directly as events map */\r\nexport type InferRoomEvents<T> =\r\n T extends { $events: infer E extends Record<string, any> } ? E :\r\n T extends Record<string, any> ? T :\r\n Record<string, any>\r\n\r\n/** Proxy interface for $room - callable as function or object */\r\nexport type RoomProxy<TState = any, TEvents extends Record<string, any> = Record<string, any>> = {\r\n /** Get a typed room handle. Pass the Room class or events interface as generic:\r\n * `$room<CounterRoom>('counter:global').on('counter:updated', data => ...)` */\r\n <T = TEvents>(roomId: string): RoomHandle<any, InferRoomEvents<T>>\r\n readonly id: string | null\r\n readonly joined: boolean\r\n readonly state: TState\r\n join: (initialState?: TState) => Promise<void>\r\n leave: () => Promise<void>\r\n emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void\r\n on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe\r\n onSystem: (event: string, handler: EventHandler) => Unsubscribe\r\n setState: (updates: Partial<TState>) => void\r\n} & RoomStateFields<TState>\r\n\r\nexport interface RoomManagerOptions {\r\n componentId: string | null\r\n defaultRoom?: string\r\n sendMessage: (msg: any) => void\r\n sendMessageAndWait: (msg: any, timeout?: number) => Promise<any>\r\n onMessage: (handler: (msg: RoomServerMessage) => void) => Unsubscribe\r\n /** Optional: register for binary room frames (0x02 ROOM_EVENT, 0x03 ROOM_STATE) */\r\n onBinaryMessage?: (handler: (frame: Uint8Array) => void) => Unsubscribe\r\n}\r\n\r\n/** Client-side room manager. Framework-agnostic. */\r\nexport class RoomManager<TState = any, TEvents extends Record<string, any> = Record<string, any>> {\r\n private componentId: string | null\r\n private defaultRoom: string | null\r\n private rooms = new Map<string, {\r\n joined: boolean\r\n state: TState\r\n handlers: Map<string, Set<EventHandler>>\r\n }>()\r\n private handles = new Map<string, RoomHandle<TState, TEvents>>()\r\n private sendMessage: (msg: any) => void\r\n private sendMessageAndWait: (msg: any, timeout?: number) => Promise<any>\r\n private globalUnsubscribe: Unsubscribe | null = null\r\n private binaryUnsubscribe: Unsubscribe | null = null\r\n private onBinaryMessage: ((handler: (frame: Uint8Array) => void) => Unsubscribe) | null = null\r\n private onMessageFactory: ((handler: (msg: RoomServerMessage) => void) => Unsubscribe) | null = null\r\n\r\n constructor(options: RoomManagerOptions) {\r\n this.componentId = options.componentId\r\n this.defaultRoom = options.defaultRoom || null\r\n this.sendMessage = options.sendMessage\r\n this.sendMessageAndWait = options.sendMessageAndWait\r\n this.onBinaryMessage = options.onBinaryMessage ?? null\r\n this.onMessageFactory = options.onMessage\r\n this.globalUnsubscribe = options.onMessage((msg) => this.handleServerMessage(msg))\r\n if (options.onBinaryMessage) {\r\n this.binaryUnsubscribe = options.onBinaryMessage((frame) => this.handleBinaryFrame(frame))\r\n }\r\n }\r\n\r\n /** Re-subscribe message and binary handlers (needed after destroy/remount in React Strict Mode) */\r\n resubscribe(): void {\r\n if (!this.globalUnsubscribe && this.onMessageFactory) {\r\n this.globalUnsubscribe = this.onMessageFactory((msg) => this.handleServerMessage(msg))\r\n }\r\n if (!this.binaryUnsubscribe && this.onBinaryMessage) {\r\n this.binaryUnsubscribe = this.onBinaryMessage((frame) => this.handleBinaryFrame(frame))\r\n }\r\n }\r\n\r\n private handleServerMessage(msg: RoomServerMessage): void {\r\n if (msg.componentId !== this.componentId) return\r\n\r\n const room = this.rooms.get(msg.roomId)\r\n if (!room) return\r\n\r\n switch (msg.type) {\r\n case 'ROOM_EVENT':\r\n case 'ROOM_SYSTEM': {\r\n const handlers = room.handlers.get(msg.event)\r\n if (handlers) {\r\n for (const handler of handlers) {\r\n try { handler(msg.data) } catch (error) {\r\n console.error(`[Room:${msg.roomId}] Handler error for '${msg.event}':`, error)\r\n }\r\n }\r\n }\r\n break\r\n }\r\n\r\n case 'ROOM_STATE': {\r\n // Server sends data: { state: actualChanges } — extract the actual changes\r\n const stateChanges = msg.data?.state ?? msg.data\r\n room.state = deepMerge(room.state as Record<string, any>, stateChanges) as TState\r\n const stateHandlers = room.handlers.get('$state:change')\r\n if (stateHandlers) {\r\n for (const handler of stateHandlers) handler(stateChanges)\r\n }\r\n break\r\n }\r\n\r\n case 'ROOM_JOINED':\r\n room.joined = true\r\n if (msg.data?.state) room.state = msg.data.state\r\n break\r\n\r\n case 'ROOM_LEFT':\r\n room.joined = false\r\n break\r\n }\r\n }\r\n\r\n /** Handle binary room frames (0x02 ROOM_EVENT, 0x03 ROOM_STATE) */\r\n private handleBinaryFrame(frame: Uint8Array): void {\r\n const parsed = parseRoomFrame(frame)\r\n if (!parsed) return\r\n if (parsed.componentId !== this.componentId) return\r\n\r\n const room = this.rooms.get(parsed.roomId)\r\n if (!room) return\r\n\r\n const data = msgpackDecode(parsed.payload)\r\n\r\n if (parsed.frameType === BINARY_ROOM_EVENT) {\r\n // Dispatch to event handlers\r\n const handlers = room.handlers.get(parsed.event)\r\n if (handlers) {\r\n for (const handler of handlers) {\r\n try { handler(data) } catch (error) {\r\n console.error(`[Room:${parsed.roomId}] Handler error for '${parsed.event}':`, error)\r\n }\r\n }\r\n }\r\n } else if (parsed.frameType === BINARY_ROOM_STATE) {\r\n // State update: data is { state: changes } or just changes\r\n const stateChanges = (data as any)?.state ?? data\r\n room.state = deepMerge(room.state as Record<string, any>, stateChanges as Record<string, any>) as TState\r\n const stateHandlers = room.handlers.get('$state:change')\r\n if (stateHandlers) {\r\n for (const handler of stateHandlers) handler(stateChanges)\r\n }\r\n }\r\n }\r\n\r\n private getOrCreateRoom(roomId: string) {\r\n if (!this.rooms.has(roomId)) {\r\n this.rooms.set(roomId, {\r\n joined: false,\r\n state: {} as TState,\r\n handlers: new Map(),\r\n })\r\n }\r\n return this.rooms.get(roomId)!\r\n }\r\n\r\n /** Create handle for a specific room (cached) */\r\n createHandle(roomId: string): RoomHandle<TState, TEvents> {\r\n if (this.handles.has(roomId)) return this.handles.get(roomId)!\r\n\r\n const room = this.getOrCreateRoom(roomId)\r\n\r\n // RoomStateFields are fulfilled at runtime by the Proxy wrapper\r\n const handle = {\r\n get id() { return roomId },\r\n get joined() { return room.joined },\r\n get state() { return room.state },\r\n\r\n join: async (initialState?: TState) => {\r\n if (!this.componentId) throw new Error('Component not mounted')\r\n if (room.joined) return\r\n\r\n if (initialState) room.state = initialState\r\n\r\n const response = await this.sendMessageAndWait({\r\n type: 'ROOM_JOIN',\r\n componentId: this.componentId,\r\n roomId,\r\n data: { initialState: room.state },\r\n timestamp: Date.now(),\r\n }, 5000)\r\n\r\n if (response?.success) {\r\n room.joined = true\r\n if (response.state) room.state = response.state\r\n }\r\n },\r\n\r\n leave: async () => {\r\n if (!this.componentId || !room.joined) return\r\n\r\n await this.sendMessageAndWait({\r\n type: 'ROOM_LEAVE',\r\n componentId: this.componentId,\r\n roomId,\r\n timestamp: Date.now(),\r\n }, 5000)\r\n\r\n room.joined = false\r\n room.handlers.clear()\r\n },\r\n\r\n emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => {\r\n if (!this.componentId) return\r\n this.sendMessage({\r\n type: 'ROOM_EMIT',\r\n componentId: this.componentId,\r\n roomId,\r\n event: event as string,\r\n data,\r\n timestamp: Date.now(),\r\n })\r\n },\r\n\r\n on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): Unsubscribe => {\r\n const eventKey = event as string\r\n if (!room.handlers.has(eventKey)) room.handlers.set(eventKey, new Set())\r\n room.handlers.get(eventKey)!.add(handler)\r\n return () => { room.handlers.get(eventKey)?.delete(handler) }\r\n },\r\n\r\n onSystem: (event: string, handler: EventHandler): Unsubscribe => {\r\n const eventKey = `$${event}`\r\n if (!room.handlers.has(eventKey)) room.handlers.set(eventKey, new Set())\r\n room.handlers.get(eventKey)!.add(handler)\r\n return () => { room.handlers.get(eventKey)?.delete(handler) }\r\n },\r\n\r\n setState: (updates: Partial<TState>) => {\r\n if (!this.componentId) return\r\n room.state = deepMerge(room.state as Record<string, any>, updates as Record<string, any>) as TState\r\n this.sendMessage({\r\n type: 'ROOM_STATE_SET',\r\n componentId: this.componentId,\r\n roomId,\r\n data: updates,\r\n timestamp: Date.now(),\r\n })\r\n },\r\n }\r\n\r\n const proxied = wrapWithStateProxy(\r\n handle,\r\n () => room.state,\r\n (updates: Partial<TState>) => handle.setState(updates),\r\n )\r\n this.handles.set(roomId, proxied as RoomHandle<TState, TEvents>)\r\n return proxied as RoomHandle<TState, TEvents>\r\n }\r\n\r\n /** Create the $room proxy */\r\n createProxy(): RoomProxy<TState, TEvents> {\r\n const self = this\r\n\r\n const proxyFn = function(roomId: string): RoomHandle<TState, TEvents> {\r\n return self.createHandle(roomId)\r\n } as RoomProxy<TState, TEvents>\r\n\r\n const defaultHandle = this.defaultRoom ? this.createHandle(this.defaultRoom) : null\r\n\r\n Object.defineProperties(proxyFn, {\r\n id: { get: () => this.defaultRoom },\r\n joined: { get: () => defaultHandle?.joined ?? false },\r\n state: { get: () => defaultHandle?.state ?? ({} as TState) },\r\n join: {\r\n value: async (initialState?: TState) => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.join(initialState)\r\n },\r\n },\r\n leave: {\r\n value: async () => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.leave()\r\n },\r\n },\r\n emit: {\r\n value: <K extends keyof TEvents>(event: K, data: TEvents[K]) => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.emit(event, data)\r\n },\r\n },\r\n on: {\r\n value: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): Unsubscribe => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.on(event, handler)\r\n },\r\n },\r\n onSystem: {\r\n value: (event: string, handler: EventHandler): Unsubscribe => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.onSystem(event, handler)\r\n },\r\n },\r\n setState: {\r\n value: (updates: Partial<TState>) => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.setState(updates)\r\n },\r\n },\r\n })\r\n\r\n // Wrap top-level proxy so $room.players reads from default room state\r\n if (this.defaultRoom && defaultHandle) {\r\n const room = this.getOrCreateRoom(this.defaultRoom)\r\n return wrapWithStateProxy(\r\n proxyFn,\r\n () => room.state,\r\n (updates: Partial<TState>) => defaultHandle.setState(updates),\r\n ) as RoomProxy<TState, TEvents>\r\n }\r\n\r\n return proxyFn\r\n }\r\n\r\n /** List of rooms currently joined */\r\n getJoinedRooms(): string[] {\r\n const joined: string[] = []\r\n for (const [id, room] of this.rooms) {\r\n if (room.joined) joined.push(id)\r\n }\r\n return joined\r\n }\r\n\r\n /** Update componentId (when component mounts) */\r\n setComponentId(id: string | null): void {\r\n this.componentId = id\r\n }\r\n\r\n /** Cleanup — unsubscribes handlers but keeps factory refs for resubscribe() */\r\n destroy(): void {\r\n this.globalUnsubscribe?.()\r\n this.globalUnsubscribe = null\r\n this.binaryUnsubscribe?.()\r\n this.binaryUnsubscribe = null\r\n for (const [, room] of this.rooms) {\r\n room.handlers.clear()\r\n }\r\n this.rooms.clear()\r\n this.handles.clear()\r\n }\r\n}\r\n\r\nexport type { EventHandler, Unsubscribe }\r\n","// @fluxstack/live-client - Chunked Upload Manager\r\n//\r\n// Framework-agnostic chunked file upload with adaptive sizing and binary protocol.\r\n\r\nimport type {\r\n FileUploadStartMessage,\r\n FileUploadChunkMessage,\r\n FileUploadCompleteMessage,\r\n FileUploadProgressResponse,\r\n FileUploadCompleteResponse,\r\n BinaryChunkHeader,\r\n} from '@fluxstack/live'\r\n\r\n// ===== Adaptive Chunk Sizer =====\r\n\r\nexport interface AdaptiveChunkConfig {\r\n minChunkSize: number\r\n maxChunkSize: number\r\n initialChunkSize: number\r\n targetLatency: number\r\n adjustmentFactor: number\r\n measurementWindow: number\r\n}\r\n\r\nexport interface ChunkMetrics {\r\n chunkIndex: number\r\n chunkSize: number\r\n startTime: number\r\n endTime: number\r\n latency: number\r\n throughput: number\r\n success: boolean\r\n}\r\n\r\nexport class AdaptiveChunkSizer {\r\n private config: Required<AdaptiveChunkConfig>\r\n private currentChunkSize: number\r\n private metrics: ChunkMetrics[] = []\r\n private consecutiveErrors = 0\r\n private consecutiveSuccesses = 0\r\n\r\n constructor(config: Partial<AdaptiveChunkConfig> = {}) {\r\n this.config = {\r\n minChunkSize: config.minChunkSize ?? 16 * 1024,\r\n maxChunkSize: config.maxChunkSize ?? 1024 * 1024,\r\n initialChunkSize: config.initialChunkSize ?? 64 * 1024,\r\n targetLatency: config.targetLatency ?? 200,\r\n adjustmentFactor: config.adjustmentFactor ?? 1.5,\r\n measurementWindow: config.measurementWindow ?? 3,\r\n }\r\n this.currentChunkSize = this.config.initialChunkSize\r\n }\r\n\r\n getChunkSize(): number {\r\n return this.currentChunkSize\r\n }\r\n\r\n recordChunkStart(_chunkIndex: number): number {\r\n return Date.now()\r\n }\r\n\r\n recordChunkComplete(chunkIndex: number, chunkSize: number, startTime: number, success: boolean): void {\r\n const endTime = Date.now()\r\n const latency = endTime - startTime\r\n const throughput = success ? (chunkSize / latency) * 1000 : 0\r\n\r\n this.metrics.push({ chunkIndex, chunkSize, startTime, endTime, latency, throughput, success })\r\n\r\n if (this.metrics.length > this.config.measurementWindow * 2) {\r\n this.metrics = this.metrics.slice(-this.config.measurementWindow * 2)\r\n }\r\n\r\n if (success) {\r\n this.consecutiveSuccesses++\r\n this.consecutiveErrors = 0\r\n this.adjustUp(latency)\r\n } else {\r\n this.consecutiveErrors++\r\n this.consecutiveSuccesses = 0\r\n this.adjustDown()\r\n }\r\n }\r\n\r\n private adjustUp(latency: number): void {\r\n if (this.consecutiveSuccesses < 2) return\r\n if (latency > this.config.targetLatency) return\r\n\r\n const latencyRatio = this.config.targetLatency / latency\r\n let newSize = Math.floor(this.currentChunkSize * Math.min(latencyRatio, this.config.adjustmentFactor))\r\n newSize = Math.min(newSize, this.config.maxChunkSize)\r\n if (newSize > this.currentChunkSize) this.currentChunkSize = newSize\r\n }\r\n\r\n private adjustDown(): void {\r\n const decreaseFactor = this.consecutiveErrors > 1 ? 2 : this.config.adjustmentFactor\r\n let newSize = Math.floor(this.currentChunkSize / decreaseFactor)\r\n newSize = Math.max(newSize, this.config.minChunkSize)\r\n if (newSize < this.currentChunkSize) this.currentChunkSize = newSize\r\n }\r\n\r\n getAverageThroughput(): number {\r\n const recent = this.metrics.slice(-this.config.measurementWindow).filter(m => m.success)\r\n if (recent.length === 0) return 0\r\n return recent.reduce((sum, m) => sum + m.throughput, 0) / recent.length\r\n }\r\n\r\n getStats() {\r\n return {\r\n currentChunkSize: this.currentChunkSize,\r\n averageThroughput: this.getAverageThroughput(),\r\n consecutiveSuccesses: this.consecutiveSuccesses,\r\n consecutiveErrors: this.consecutiveErrors,\r\n totalMeasurements: this.metrics.length,\r\n }\r\n }\r\n\r\n reset(): void {\r\n this.currentChunkSize = this.config.initialChunkSize\r\n this.metrics = []\r\n this.consecutiveErrors = 0\r\n this.consecutiveSuccesses = 0\r\n }\r\n}\r\n\r\n// ===== Binary Protocol =====\r\n\r\n/**\r\n * Creates a binary message with header + data\r\n * Format: [4 bytes header length LE][JSON header][binary data]\r\n */\r\nexport function createBinaryChunkMessage(header: BinaryChunkHeader, chunkData: Uint8Array): ArrayBuffer {\r\n const headerJson = JSON.stringify(header)\r\n const headerBytes = new TextEncoder().encode(headerJson)\r\n\r\n const totalSize = 4 + headerBytes.length + chunkData.length\r\n const buffer = new ArrayBuffer(totalSize)\r\n const view = new DataView(buffer)\r\n const uint8View = new Uint8Array(buffer)\r\n\r\n view.setUint32(0, headerBytes.length, true)\r\n uint8View.set(headerBytes, 4)\r\n uint8View.set(chunkData, 4 + headerBytes.length)\r\n\r\n return buffer\r\n}\r\n\r\n// ===== Chunked Uploader =====\r\n\r\nexport interface ChunkedUploadOptions {\r\n chunkSize?: number\r\n maxFileSize?: number\r\n allowedTypes?: string[]\r\n sendMessageAndWait: (message: any, timeout?: number) => Promise<any>\r\n sendBinaryAndWait?: (data: ArrayBuffer, requestId: string, timeout?: number) => Promise<any>\r\n onProgress?: (progress: number, bytesUploaded: number, totalBytes: number) => void\r\n onComplete?: (response: FileUploadCompleteResponse) => void\r\n onError?: (error: string) => void\r\n adaptiveChunking?: boolean\r\n adaptiveConfig?: Partial<AdaptiveChunkConfig>\r\n useBinaryProtocol?: boolean\r\n}\r\n\r\nexport interface ChunkedUploadState {\r\n uploading: boolean\r\n progress: number\r\n error: string | null\r\n uploadId: string | null\r\n bytesUploaded: number\r\n totalBytes: number\r\n}\r\n\r\n/**\r\n * Framework-agnostic chunked file uploader.\r\n * Manages the upload lifecycle without any UI framework dependency.\r\n */\r\nexport class ChunkedUploader {\r\n private options: Required<Pick<ChunkedUploadOptions, 'chunkSize' | 'maxFileSize' | 'allowedTypes' | 'useBinaryProtocol' | 'adaptiveChunking'>> & ChunkedUploadOptions\r\n private abortController: AbortController | null = null\r\n private adaptiveSizer: AdaptiveChunkSizer | null = null\r\n private _state: ChunkedUploadState = {\r\n uploading: false,\r\n progress: 0,\r\n error: null,\r\n uploadId: null,\r\n bytesUploaded: 0,\r\n totalBytes: 0,\r\n }\r\n private stateListeners = new Set<(state: ChunkedUploadState) => void>()\r\n\r\n constructor(\r\n private componentId: string,\r\n options: ChunkedUploadOptions,\r\n ) {\r\n this.options = {\r\n chunkSize: options.chunkSize ?? 64 * 1024,\r\n maxFileSize: options.maxFileSize ?? 50 * 1024 * 1024,\r\n allowedTypes: options.allowedTypes ?? [],\r\n useBinaryProtocol: options.useBinaryProtocol ?? true,\r\n adaptiveChunking: options.adaptiveChunking ?? false,\r\n ...options,\r\n }\r\n\r\n if (this.options.adaptiveChunking) {\r\n this.adaptiveSizer = new AdaptiveChunkSizer({\r\n initialChunkSize: this.options.chunkSize,\r\n minChunkSize: this.options.chunkSize,\r\n maxChunkSize: 1024 * 1024,\r\n ...options.adaptiveConfig,\r\n })\r\n }\r\n }\r\n\r\n get state(): ChunkedUploadState {\r\n return { ...this._state }\r\n }\r\n\r\n onStateChange(callback: (state: ChunkedUploadState) => void): () => void {\r\n this.stateListeners.add(callback)\r\n return () => { this.stateListeners.delete(callback) }\r\n }\r\n\r\n private setState(patch: Partial<ChunkedUploadState>) {\r\n this._state = { ...this._state, ...patch }\r\n for (const cb of this.stateListeners) cb(this._state)\r\n }\r\n\r\n async uploadFile(file: File): Promise<void> {\r\n const { allowedTypes, maxFileSize, chunkSize, sendMessageAndWait, sendBinaryAndWait, useBinaryProtocol } = this.options\r\n const canUseBinary = useBinaryProtocol && sendBinaryAndWait\r\n\r\n // Validate\r\n if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {\r\n const error = `Invalid file type: ${file.type}. Allowed: ${allowedTypes.join(', ')}`\r\n this.setState({ error })\r\n this.options.onError?.(error)\r\n return\r\n }\r\n\r\n if (file.size > maxFileSize) {\r\n const error = `File too large: ${file.size} bytes. Max: ${maxFileSize} bytes`\r\n this.setState({ error })\r\n this.options.onError?.(error)\r\n return\r\n }\r\n\r\n try {\r\n const uploadId = `upload-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`\r\n this.abortController = new AbortController()\r\n this.adaptiveSizer?.reset()\r\n\r\n this.setState({ uploading: true, progress: 0, error: null, uploadId, bytesUploaded: 0, totalBytes: file.size })\r\n\r\n const initialChunkSize = this.adaptiveSizer?.getChunkSize() ?? chunkSize\r\n\r\n // Start upload\r\n const startMessage: FileUploadStartMessage = {\r\n type: 'FILE_UPLOAD_START',\r\n componentId: this.componentId,\r\n uploadId,\r\n filename: file.name,\r\n fileType: file.type,\r\n fileSize: file.size,\r\n chunkSize,\r\n requestId: `start-${uploadId}`,\r\n }\r\n\r\n const startResponse = await sendMessageAndWait(startMessage, 10000)\r\n if (!startResponse?.success) throw new Error(startResponse?.error || 'Failed to start upload')\r\n\r\n let offset = 0\r\n let chunkIndex = 0\r\n const estimatedTotalChunks = Math.ceil(file.size / initialChunkSize)\r\n\r\n while (offset < file.size) {\r\n if (this.abortController?.signal.aborted) throw new Error('Upload cancelled')\r\n\r\n const currentChunkSize = this.adaptiveSizer?.getChunkSize() ?? chunkSize\r\n const chunkEnd = Math.min(offset + currentChunkSize, file.size)\r\n const sliceBuffer = await file.slice(offset, chunkEnd).arrayBuffer()\r\n const chunkBytes = new Uint8Array(sliceBuffer)\r\n const chunkStartTime = this.adaptiveSizer?.recordChunkStart(chunkIndex) ?? 0\r\n const requestId = `chunk-${uploadId}-${chunkIndex}`\r\n\r\n try {\r\n let progressResponse: FileUploadProgressResponse | undefined\r\n\r\n if (canUseBinary) {\r\n const header: BinaryChunkHeader = {\r\n type: 'FILE_UPLOAD_CHUNK',\r\n componentId: this.componentId,\r\n uploadId,\r\n chunkIndex,\r\n totalChunks: estimatedTotalChunks,\r\n requestId,\r\n }\r\n const binaryMessage = createBinaryChunkMessage(header, chunkBytes)\r\n progressResponse = await sendBinaryAndWait!(binaryMessage, requestId, 10000) as FileUploadProgressResponse\r\n } else {\r\n let binary = ''\r\n for (let j = 0; j < chunkBytes.length; j++) binary += String.fromCharCode(chunkBytes[j])\r\n\r\n const chunkMessage: FileUploadChunkMessage = {\r\n type: 'FILE_UPLOAD_CHUNK',\r\n componentId: this.componentId,\r\n uploadId,\r\n chunkIndex,\r\n totalChunks: estimatedTotalChunks,\r\n data: btoa(binary),\r\n requestId,\r\n }\r\n progressResponse = await sendMessageAndWait!(chunkMessage, 10000) as FileUploadProgressResponse\r\n }\r\n\r\n if (progressResponse) {\r\n this.setState({ progress: progressResponse.progress, bytesUploaded: progressResponse.bytesUploaded })\r\n this.options.onProgress?.(progressResponse.progress, progressResponse.bytesUploaded, file.size)\r\n }\r\n\r\n this.adaptiveSizer?.recordChunkComplete(chunkIndex, chunkBytes.length, chunkStartTime, true)\r\n } catch (error) {\r\n this.adaptiveSizer?.recordChunkComplete(chunkIndex, chunkBytes.length, chunkStartTime, false)\r\n throw error\r\n }\r\n\r\n offset += chunkBytes.length\r\n chunkIndex++\r\n\r\n if (!this.options.adaptiveChunking) {\r\n await new Promise(resolve => setTimeout(resolve, 10))\r\n }\r\n }\r\n\r\n // Complete\r\n const completeMessage: FileUploadCompleteMessage = {\r\n type: 'FILE_UPLOAD_COMPLETE',\r\n componentId: this.componentId,\r\n uploadId,\r\n requestId: `complete-${uploadId}`,\r\n }\r\n\r\n const completeResponse = await sendMessageAndWait(completeMessage, 10000) as FileUploadCompleteResponse\r\n\r\n if (completeResponse?.success) {\r\n this.setState({ uploading: false, progress: 100, bytesUploaded: file.size })\r\n this.options.onComplete?.(completeResponse)\r\n } else {\r\n throw new Error(completeResponse?.error || 'Upload completion failed')\r\n }\r\n } catch (error: any) {\r\n this.setState({ uploading: false, error: error.message })\r\n this.options.onError?.(error.message)\r\n }\r\n }\r\n\r\n cancelUpload(): void {\r\n if (this.abortController) {\r\n this.abortController.abort()\r\n this.setState({ uploading: false, error: 'Upload cancelled' })\r\n }\r\n }\r\n\r\n reset(): void {\r\n this._state = { uploading: false, progress: 0, error: null, uploadId: null, bytesUploaded: 0, totalBytes: 0 }\r\n for (const cb of this.stateListeners) cb(this._state)\r\n }\r\n}\r\n","// @fluxstack/live-client - State Persistence\r\n//\r\n// Utilities for persisting and recovering component state via localStorage.\r\n\r\nconst STORAGE_KEY_PREFIX = 'fluxstack_component_'\r\nconst STATE_MAX_AGE = 24 * 60 * 60 * 1000 // 24 hours\r\n\r\nexport interface PersistedState {\r\n componentName: string\r\n signedState: any\r\n room?: string\r\n userId?: string\r\n lastUpdate: number\r\n}\r\n\r\nexport function persistState(\r\n enabled: boolean,\r\n name: string,\r\n signedState: any,\r\n room?: string,\r\n userId?: string,\r\n): void {\r\n if (!enabled) return\r\n try {\r\n localStorage.setItem(`${STORAGE_KEY_PREFIX}${name}`, JSON.stringify({\r\n componentName: name, signedState, room, userId, lastUpdate: Date.now(),\r\n }))\r\n } catch (e) {\r\n if (typeof console !== 'undefined') {\r\n console.warn(`[fluxstack] Failed to persist state for '${name}':`, e instanceof Error ? e.message : e)\r\n }\r\n }\r\n}\r\n\r\nexport function getPersistedState(enabled: boolean, name: string): PersistedState | null {\r\n if (!enabled) return null\r\n try {\r\n const stored = localStorage.getItem(`${STORAGE_KEY_PREFIX}${name}`)\r\n if (!stored) return null\r\n const state: PersistedState = JSON.parse(stored)\r\n if (Date.now() - state.lastUpdate > STATE_MAX_AGE) {\r\n localStorage.removeItem(`${STORAGE_KEY_PREFIX}${name}`)\r\n return null\r\n }\r\n return state\r\n } catch { return null }\r\n}\r\n\r\nexport function clearPersistedState(enabled: boolean, name: string): void {\r\n if (!enabled) return\r\n try { localStorage.removeItem(`${STORAGE_KEY_PREFIX}${name}`) } catch {}\r\n}\r\n","// @fluxstack/live-client - State Validation Utilities\r\n\r\nexport interface StateValidation {\r\n checksum: string\r\n version: number\r\n timestamp: number\r\n source: 'client' | 'server' | 'mount'\r\n}\r\n\r\nexport interface StateConflict {\r\n property: string\r\n clientValue: any\r\n serverValue: any\r\n timestamp: number\r\n resolved: boolean\r\n}\r\n\r\nexport interface HybridState<T> {\r\n data: T\r\n validation: StateValidation\r\n status: 'synced' | 'pending' | 'conflict'\r\n}\r\n\r\nexport class StateValidator {\r\n static generateChecksum(state: any): string {\r\n const json = JSON.stringify(state, Object.keys(state).sort())\r\n let hash = 0\r\n for (let i = 0; i < json.length; i++) {\r\n const char = json.charCodeAt(i)\r\n hash = ((hash << 5) - hash) + char\r\n hash = hash & hash\r\n }\r\n return Math.abs(hash).toString(16)\r\n }\r\n\r\n static createValidation(\r\n state: any,\r\n source: 'client' | 'server' | 'mount' = 'client',\r\n ): StateValidation {\r\n return {\r\n checksum: this.generateChecksum(state),\r\n version: Date.now(),\r\n timestamp: Date.now(),\r\n source,\r\n }\r\n }\r\n\r\n static detectConflicts<T>(\r\n clientState: T,\r\n serverState: T,\r\n excludeFields: string[] = ['lastUpdated', 'version'],\r\n ): StateConflict[] {\r\n const conflicts: StateConflict[] = []\r\n const clientKeys = Object.keys(clientState as any)\r\n const serverKeys = Object.keys(serverState as any)\r\n const allKeys = Array.from(new Set([...clientKeys, ...serverKeys]))\r\n\r\n for (const key of allKeys) {\r\n if (excludeFields.includes(key)) continue\r\n const clientValue = (clientState as any)?.[key]\r\n const serverValue = (serverState as any)?.[key]\r\n if (JSON.stringify(clientValue) !== JSON.stringify(serverValue)) {\r\n conflicts.push({\r\n property: key,\r\n clientValue,\r\n serverValue,\r\n timestamp: Date.now(),\r\n resolved: false,\r\n })\r\n }\r\n }\r\n\r\n return conflicts\r\n }\r\n\r\n static mergeStates<T>(\r\n clientState: T,\r\n serverState: T,\r\n conflicts: StateConflict[],\r\n strategy: 'client' | 'server' | 'smart' = 'smart',\r\n ): T {\r\n const merged = { ...clientState }\r\n\r\n for (const conflict of conflicts) {\r\n switch (strategy) {\r\n case 'client':\r\n break\r\n case 'server':\r\n (merged as any)[conflict.property] = conflict.serverValue\r\n break\r\n case 'smart':\r\n if (conflict.property === 'lastUpdated') {\r\n (merged as any)[conflict.property] = conflict.serverValue\r\n } else if (typeof conflict.serverValue === 'number' && typeof conflict.clientValue === 'number') {\r\n (merged as any)[conflict.property] = Math.max(conflict.serverValue, conflict.clientValue)\r\n } else {\r\n (merged as any)[conflict.property] = conflict.serverValue\r\n }\r\n break\r\n }\r\n }\r\n\r\n return merged\r\n }\r\n\r\n static validateState<T>(hybridState: HybridState<T>): boolean {\r\n const currentChecksum = this.generateChecksum(hybridState.data)\r\n return currentChecksum === hybridState.validation.checksum\r\n }\r\n\r\n static updateValidation<T>(\r\n hybridState: HybridState<T>,\r\n source: 'client' | 'server' | 'mount' = 'client',\r\n ): HybridState<T> {\r\n return {\r\n ...hybridState,\r\n validation: this.createValidation(hybridState.data, source),\r\n status: 'synced',\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["isPlainObject","deepMerge"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/connection.ts","../src/component.ts","../src/rooms.ts","../src/upload.ts","../src/persistence.ts","../src/state-validator.ts"],"sourcesContent":["// @fluxstack/live-client - Framework-agnostic browser client\r\n//\r\n// This package provides the core WebSocket connection, room management,\r\n// file upload, state persistence, and validation utilities.\r\n// It has NO dependency on any UI framework (React, Vue, etc.).\r\n//\r\n// Quick start (browser IIFE):\r\n// const counter = FluxstackLive.useLive('Counter', { count: 0 })\r\n// counter.on(state => document.getElementById('count').textContent = state.count)\r\n// counter.call('increment')\r\n\r\n// Connection\r\nexport { LiveConnection } from './connection'\r\nexport type {\r\n LiveAuthOptions,\r\n LiveClientAuth,\r\n LiveConnectionOptions,\r\n LiveConnectionState,\r\n} from './connection'\r\n\r\n// Component Handle (vanilla JS equivalent of Live.use)\r\nexport { LiveComponentHandle } from './component'\r\nexport type { LiveComponentOptions } from './component'\r\n\r\n// Rooms\r\nexport { RoomManager } from './rooms'\r\nexport type {\r\n RoomClientMessage,\r\n RoomServerMessage,\r\n RoomHandle,\r\n RoomProxy,\r\n RoomManagerOptions,\r\n EventHandler,\r\n Unsubscribe,\r\n} from './rooms'\r\n\r\n// Upload\r\nexport {\r\n AdaptiveChunkSizer,\r\n ChunkedUploader,\r\n createBinaryChunkMessage,\r\n} from './upload'\r\nexport type {\r\n AdaptiveChunkConfig,\r\n ChunkMetrics,\r\n ChunkedUploadOptions,\r\n ChunkedUploadState,\r\n} from './upload'\r\n\r\n// Persistence\r\nexport {\r\n persistState,\r\n getPersistedState,\r\n clearPersistedState,\r\n} from './persistence'\r\nexport type { PersistedState } from './persistence'\r\n\r\n// State Validation\r\nexport { StateValidator } from './state-validator'\r\nexport type {\r\n StateValidation,\r\n StateConflict,\r\n HybridState,\r\n} from './state-validator'\r\n\r\n// ===== useLive — simplified API for vanilla JS =====\r\n\r\nimport { LiveConnection } from './connection'\r\nimport type { LiveConnectionOptions } from './connection'\r\nimport { LiveComponentHandle } from './component'\r\nimport type { LiveComponentOptions } from './component'\r\n\r\n/** Shared connection singleton — created once, reused by all useLive() calls */\r\nlet _sharedConnection: LiveConnection | null = null\r\nlet _sharedConnectionUrl: string | null = null\r\n\r\n/** Status listeners for the shared connection */\r\ntype ConnectionStatusCallback = (connected: boolean) => void\r\nconst _statusListeners = new Set<ConnectionStatusCallback>()\r\n\r\nfunction getOrCreateConnection(url?: string): LiveConnection {\r\n const resolvedUrl = url ?? `ws://${typeof location !== 'undefined' ? location.host : 'localhost:3000'}/api/live/ws`\r\n\r\n // Reuse existing connection if same URL\r\n if (_sharedConnection && _sharedConnectionUrl === resolvedUrl) {\r\n return _sharedConnection\r\n }\r\n\r\n // Destroy old connection if URL changed\r\n if (_sharedConnection) {\r\n _sharedConnection.destroy()\r\n }\r\n\r\n _sharedConnection = new LiveConnection({ url: resolvedUrl })\r\n _sharedConnectionUrl = resolvedUrl\r\n\r\n _sharedConnection.onStateChange((state) => {\r\n for (const cb of _statusListeners) {\r\n cb(state.connected)\r\n }\r\n })\r\n\r\n return _sharedConnection\r\n}\r\n\r\nexport interface UseLiveOptions {\r\n /** WebSocket URL. Auto-detected from window.location if omitted. */\r\n url?: string\r\n /** Room to join on mount */\r\n room?: string\r\n /** User ID for component isolation */\r\n userId?: string\r\n /** Auto-mount when connected. Default: true */\r\n autoMount?: boolean\r\n /** Enable debug logging. Default: false */\r\n debug?: boolean\r\n}\r\n\r\nexport interface UseLiveHandle<TState extends Record<string, any> = Record<string, any>> {\r\n /** Call a server action */\r\n call: <R = any>(action: string, payload?: Record<string, any>) => Promise<R>\r\n /** Subscribe to state changes. Returns unsubscribe function. */\r\n on: (callback: (state: TState, delta: Partial<TState> | null) => void) => () => void\r\n /** Subscribe to errors. Returns unsubscribe function. */\r\n onError: (callback: (error: string) => void) => () => void\r\n /** Current state (read-only snapshot) */\r\n readonly state: Readonly<TState>\r\n /** Whether the component is mounted on the server */\r\n readonly mounted: boolean\r\n /** Server-assigned component ID */\r\n readonly componentId: string | null\r\n /** Last error message */\r\n readonly error: string | null\r\n /** Destroy the component and clean up */\r\n destroy: () => void\r\n /** Access the underlying LiveComponentHandle */\r\n readonly handle: LiveComponentHandle<TState>\r\n}\r\n\r\n/**\r\n * Create a live component with minimal boilerplate.\r\n * Manages the WebSocket connection automatically (singleton).\r\n *\r\n * @example Browser IIFE\r\n * ```html\r\n * <script src=\"/live-client.js\"></script>\r\n * <script>\r\n * const counter = FluxstackLive.useLive('Counter', { count: 0 })\r\n * counter.on(state => {\r\n * document.getElementById('count').textContent = state.count\r\n * })\r\n * document.querySelector('.inc').onclick = () => counter.call('increment')\r\n * </script>\r\n * ```\r\n *\r\n * @example ES modules\r\n * ```ts\r\n * import { useLive } from '@fluxstack/live-client'\r\n * const counter = useLive('Counter', { count: 0 }, { url: 'ws://localhost:3000/api/live/ws' })\r\n * counter.on(state => console.log(state.count))\r\n * counter.call('increment')\r\n * ```\r\n */\r\nexport function useLive<TState extends Record<string, any> = Record<string, any>>(\r\n componentName: string,\r\n initialState: TState,\r\n options: UseLiveOptions = {},\r\n): UseLiveHandle<TState> {\r\n const { url, room, userId, autoMount = true, debug = false } = options\r\n\r\n const connection = getOrCreateConnection(url)\r\n const handle = new LiveComponentHandle<TState>(connection, componentName, {\r\n initialState,\r\n room,\r\n userId,\r\n autoMount,\r\n debug,\r\n })\r\n\r\n return {\r\n call: (action, payload) => handle.call(action, payload ?? {}),\r\n on: (callback) => handle.onStateChange(callback),\r\n onError: (callback) => handle.onError(callback),\r\n get state() { return handle.state },\r\n get mounted() { return handle.mounted },\r\n get componentId() { return handle.componentId },\r\n get error() { return handle.error },\r\n destroy: () => handle.destroy(),\r\n handle,\r\n }\r\n}\r\n\r\n/**\r\n * Subscribe to the shared connection status (connected/disconnected).\r\n * Useful for showing a global status indicator.\r\n *\r\n * @example\r\n * ```js\r\n * FluxstackLive.onConnectionChange(connected => {\r\n * statusEl.textContent = connected ? 'Connected' : 'Disconnected'\r\n * })\r\n * ```\r\n */\r\nexport function onConnectionChange(callback: ConnectionStatusCallback): () => void {\r\n _statusListeners.add(callback)\r\n // Immediately fire with current state if connection exists\r\n if (_sharedConnection) {\r\n callback(_sharedConnection.state.connected)\r\n }\r\n return () => { _statusListeners.delete(callback) }\r\n}\r\n\r\n/**\r\n * Get or create the shared connection instance.\r\n * Useful when you need direct access to the connection.\r\n */\r\nexport function getConnection(url?: string): LiveConnection {\r\n return getOrCreateConnection(url)\r\n}\r\n","// @fluxstack/live-client - WebSocket Connection Manager\r\n//\r\n// Framework-agnostic WebSocket connection with auto-reconnect, heartbeat,\r\n// request-response pattern, and component message routing.\r\n\r\nimport type { WebSocketMessage, WebSocketResponse } from '@fluxstack/live'\r\n\r\n/** Auth credentials to send during WebSocket connection */\r\nexport interface LiveAuthOptions {\r\n /** JWT or opaque token */\r\n token?: string\r\n /** Provider name (if multiple auth providers configured) */\r\n provider?: string\r\n /** Additional credentials (publicKey, signature, etc.) */\r\n [key: string]: unknown\r\n}\r\n\r\nexport interface LiveConnectionOptions {\r\n /** WebSocket URL. Auto-detected from window.location if omitted. */\r\n url?: string\r\n /** Auth credentials to send on connection */\r\n auth?: LiveAuthOptions\r\n /** Auto-connect on creation. Default: true */\r\n autoConnect?: boolean\r\n /** Reconnect interval in ms. Default: 1000 */\r\n reconnectInterval?: number\r\n /** Max reconnect attempts. Default: 5 */\r\n maxReconnectAttempts?: number\r\n /** Heartbeat interval in ms. Default: 30000 */\r\n heartbeatInterval?: number\r\n /** Enable debug logging. Default: false */\r\n debug?: boolean\r\n}\r\n\r\n/** Auth state exposed to the client */\r\nexport interface LiveClientAuth {\r\n authenticated: boolean\r\n /** Session data from the server. Shape defined by your LiveAuthProvider. */\r\n session: Record<string, unknown> | null\r\n}\r\n\r\nexport interface LiveConnectionState {\r\n connected: boolean\r\n connecting: boolean\r\n error: string | null\r\n connectionId: string | null\r\n authenticated: boolean\r\n /** Auth context with session data */\r\n auth: LiveClientAuth\r\n}\r\n\r\ntype StateChangeCallback = (state: LiveConnectionState) => void\r\ntype ComponentCallback = (message: WebSocketResponse) => void\r\n\r\n/**\r\n * Framework-agnostic WebSocket connection manager.\r\n * Handles reconnection, heartbeat, request-response pattern, and message routing.\r\n */\r\nexport class LiveConnection {\r\n private ws: WebSocket | null = null\r\n private options: Required<Omit<LiveConnectionOptions, 'url' | 'auth'>> & { url?: string; auth?: LiveAuthOptions }\r\n private reconnectAttempts = 0\r\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null\r\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null\r\n private componentCallbacks = new Map<string, ComponentCallback>()\r\n private binaryCallbacks = new Map<string, (payload: Uint8Array) => void>()\r\n private roomBinaryHandlers = new Set<(frame: Uint8Array) => void>()\r\n private pendingRequests = new Map<string, {\r\n resolve: (value: any) => void\r\n reject: (error: any) => void\r\n timeout: ReturnType<typeof setTimeout>\r\n }>()\r\n private stateListeners = new Set<StateChangeCallback>()\r\n private _state: LiveConnectionState = {\r\n connected: false,\r\n connecting: false,\r\n error: null,\r\n connectionId: null,\r\n authenticated: false,\r\n auth: { authenticated: false, session: null },\r\n }\r\n\r\n constructor(options: LiveConnectionOptions = {}) {\r\n this.options = {\r\n url: options.url,\r\n auth: options.auth,\r\n autoConnect: options.autoConnect ?? true,\r\n reconnectInterval: options.reconnectInterval ?? 1000,\r\n maxReconnectAttempts: options.maxReconnectAttempts ?? 5,\r\n heartbeatInterval: options.heartbeatInterval ?? 30000,\r\n debug: options.debug ?? false,\r\n }\r\n\r\n if (this.options.autoConnect) {\r\n this.connect()\r\n }\r\n }\r\n\r\n get state(): LiveConnectionState {\r\n return { ...this._state }\r\n }\r\n\r\n /** Subscribe to connection state changes */\r\n onStateChange(callback: StateChangeCallback): () => void {\r\n this.stateListeners.add(callback)\r\n return () => { this.stateListeners.delete(callback) }\r\n }\r\n\r\n private setState(patch: Partial<LiveConnectionState>) {\r\n this._state = { ...this._state, ...patch }\r\n for (const cb of this.stateListeners) {\r\n cb(this._state)\r\n }\r\n }\r\n\r\n private getWebSocketUrl(): string {\r\n if (this.options.url) {\r\n return this.options.url\r\n } else if (typeof window === 'undefined') {\r\n return 'ws://localhost:3000/api/live/ws'\r\n } else {\r\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\r\n return `${protocol}//${window.location.host}/api/live/ws`\r\n }\r\n }\r\n\r\n private log(message: string, data?: any) {\r\n if (this.options.debug) {\r\n console.log(`[LiveConnection] ${message}`, data || '')\r\n }\r\n }\r\n\r\n /** Generate unique request ID */\r\n generateRequestId(): string {\r\n return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\r\n }\r\n\r\n /** Connect to WebSocket server */\r\n connect(): void {\r\n if (this.ws?.readyState === WebSocket.CONNECTING) {\r\n this.log('Already connecting, skipping...')\r\n return\r\n }\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.log('Already connected, skipping...')\r\n return\r\n }\r\n\r\n this.setState({ connecting: true, error: null })\r\n const url = this.getWebSocketUrl()\r\n this.log('Connecting...', { url })\r\n\r\n try {\r\n const ws = new WebSocket(url)\r\n ws.binaryType = 'arraybuffer'\r\n this.ws = ws\r\n\r\n ws.onopen = () => {\r\n this.log('Connected')\r\n this.setState({ connected: true, connecting: false })\r\n this.reconnectAttempts = 0\r\n this.startHeartbeat()\r\n }\r\n\r\n ws.onmessage = (event) => {\r\n // Binary message path (BINARY_STATE_DELTA)\r\n if (event.data instanceof ArrayBuffer) {\r\n this.handleBinaryMessage(new Uint8Array(event.data))\r\n return\r\n }\r\n\r\n try {\r\n const parsed = JSON.parse(event.data)\r\n // Server may send batched messages as a JSON array\r\n if (Array.isArray(parsed)) {\r\n for (const msg of parsed) {\r\n this.log('Received', { type: msg.type, componentId: msg.componentId })\r\n this.handleMessage(msg)\r\n }\r\n } else {\r\n this.log('Received', { type: parsed.type, componentId: parsed.componentId })\r\n this.handleMessage(parsed)\r\n }\r\n } catch {\r\n this.log('Failed to parse message')\r\n this.setState({ error: 'Failed to parse message' })\r\n }\r\n }\r\n\r\n ws.onclose = (event) => {\r\n this.log('Disconnected', { code: event.code, reason: event.reason })\r\n this.setState({ connected: false, connecting: false, connectionId: null, authenticated: false, auth: { authenticated: false, session: null } })\r\n this.stopHeartbeat()\r\n\r\n // Server rejected connection due to CSRF origin validation — don't retry\r\n if (event.code === 4003) {\r\n this.setState({ error: 'Connection rejected: origin not allowed' })\r\n return\r\n }\r\n\r\n this.attemptReconnect()\r\n }\r\n\r\n ws.onerror = () => {\r\n this.log('WebSocket error')\r\n this.setState({ error: 'WebSocket connection error', connecting: false })\r\n }\r\n } catch (error) {\r\n this.setState({\r\n connecting: false,\r\n error: error instanceof Error ? error.message : 'Connection failed',\r\n })\r\n }\r\n }\r\n\r\n /** Disconnect from WebSocket server */\r\n disconnect(): void {\r\n if (this.reconnectTimeout) {\r\n clearTimeout(this.reconnectTimeout)\r\n this.reconnectTimeout = null\r\n }\r\n this.stopHeartbeat()\r\n if (this.ws) {\r\n this.ws.close()\r\n this.ws = null\r\n }\r\n this.reconnectAttempts = this.options.maxReconnectAttempts\r\n this.setState({ connected: false, connecting: false, connectionId: null })\r\n }\r\n\r\n /** Manual reconnect */\r\n reconnect(): void {\r\n this.disconnect()\r\n this.reconnectAttempts = 0\r\n setTimeout(() => this.connect(), 100)\r\n }\r\n\r\n private attemptReconnect(): void {\r\n if (this.reconnectAttempts < this.options.maxReconnectAttempts) {\r\n this.reconnectAttempts++\r\n this.log(`Reconnecting... (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})`)\r\n this.reconnectTimeout = setTimeout(() => this.connect(), this.options.reconnectInterval)\r\n } else {\r\n this.setState({ error: 'Max reconnection attempts reached' })\r\n }\r\n }\r\n\r\n private consecutiveHeartbeatFailures = 0\r\n private static readonly MAX_HEARTBEAT_FAILURES = 3\r\n\r\n private startHeartbeat(): void {\r\n this.stopHeartbeat()\r\n this.consecutiveHeartbeatFailures = 0\r\n this.heartbeatInterval = setInterval(() => {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n let failed = false\r\n for (const componentId of this.componentCallbacks.keys()) {\r\n this.sendMessage({\r\n type: 'COMPONENT_PING',\r\n componentId,\r\n timestamp: Date.now(),\r\n }).catch(() => { failed = true })\r\n }\r\n if (failed) {\r\n this.consecutiveHeartbeatFailures++\r\n this.log(`Heartbeat failed (${this.consecutiveHeartbeatFailures}/${LiveConnection.MAX_HEARTBEAT_FAILURES})`)\r\n if (this.consecutiveHeartbeatFailures >= LiveConnection.MAX_HEARTBEAT_FAILURES) {\r\n this.log('Too many heartbeat failures, reconnecting...')\r\n this.setState({ error: 'Heartbeat failed' })\r\n this.reconnect()\r\n }\r\n } else {\r\n this.consecutiveHeartbeatFailures = 0\r\n }\r\n }\r\n }, this.options.heartbeatInterval)\r\n }\r\n\r\n private stopHeartbeat(): void {\r\n if (this.heartbeatInterval) {\r\n clearInterval(this.heartbeatInterval)\r\n this.heartbeatInterval = null\r\n }\r\n }\r\n\r\n private handleMessage(response: WebSocketResponse): void {\r\n // Handle connection established\r\n if (response.type === 'CONNECTION_ESTABLISHED') {\r\n this.setState({\r\n connectionId: response.connectionId || null,\r\n authenticated: (response as any).authenticated || false,\r\n })\r\n\r\n // Send AUTH message if credentials provided (always via socket, never in URL)\r\n const auth = this.options.auth\r\n if (auth && Object.keys(auth).some(k => auth[k])) {\r\n this.sendMessageAndWait({ type: 'AUTH', payload: auth } as any)\r\n .then(authResp => {\r\n const payload = (authResp as any).payload\r\n if (payload?.authenticated) {\r\n this.setState({\r\n authenticated: true,\r\n auth: { authenticated: true, session: payload.session || null },\r\n })\r\n }\r\n })\r\n .catch(() => {})\r\n }\r\n }\r\n\r\n // Handle auth response\r\n if (response.type === 'AUTH_RESPONSE') {\r\n const payload = (response as any).payload\r\n const authenticated = payload?.authenticated || false\r\n this.setState({\r\n authenticated,\r\n auth: {\r\n authenticated,\r\n session: authenticated ? (payload?.session || null) : null,\r\n },\r\n })\r\n }\r\n\r\n // Handle pending requests (request-response pattern)\r\n if (response.requestId && this.pendingRequests.has(response.requestId)) {\r\n const request = this.pendingRequests.get(response.requestId)!\r\n clearTimeout(request.timeout)\r\n this.pendingRequests.delete(response.requestId)\r\n\r\n if (response.success !== false) {\r\n request.resolve(response)\r\n } else {\r\n if (response.error?.includes?.('COMPONENT_REHYDRATION_REQUIRED')) {\r\n request.resolve(response)\r\n } else {\r\n request.reject(new Error(response.error || 'Request failed'))\r\n }\r\n }\r\n return\r\n }\r\n\r\n // Broadcast messages go to ALL components (not just sender)\r\n if (response.type === 'BROADCAST') {\r\n this.componentCallbacks.forEach((callback, compId) => {\r\n if (compId !== response.componentId) {\r\n callback(response)\r\n }\r\n })\r\n return\r\n }\r\n\r\n // Route message to specific component\r\n if (response.componentId) {\r\n const callback = this.componentCallbacks.get(response.componentId)\r\n if (callback) {\r\n callback(response)\r\n } else {\r\n this.log('No callback registered for component:', response.componentId)\r\n }\r\n }\r\n }\r\n\r\n /** Send message without waiting for response */\r\n async sendMessage(message: WebSocketMessage): Promise<void> {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n throw new Error('WebSocket is not connected')\r\n }\r\n const messageWithTimestamp = { ...message, timestamp: Date.now() }\r\n this.ws.send(JSON.stringify(messageWithTimestamp))\r\n this.log('Sent', { type: message.type, componentId: message.componentId })\r\n }\r\n\r\n /** Send message and wait for response */\r\n async sendMessageAndWait(message: WebSocketMessage, timeout = 10000): Promise<WebSocketResponse> {\r\n return new Promise((resolve, reject) => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n reject(new Error('WebSocket is not connected'))\r\n return\r\n }\r\n\r\n const requestId = this.generateRequestId()\r\n\r\n const timeoutHandle = setTimeout(() => {\r\n this.pendingRequests.delete(requestId)\r\n reject(new Error(`Request timeout after ${timeout}ms`))\r\n }, timeout)\r\n\r\n this.pendingRequests.set(requestId, { resolve, reject, timeout: timeoutHandle })\r\n\r\n try {\r\n const messageWithRequestId = {\r\n ...message,\r\n requestId,\r\n expectResponse: true,\r\n timestamp: Date.now(),\r\n }\r\n this.ws.send(JSON.stringify(messageWithRequestId))\r\n this.log('Sent with requestId', { requestId, type: message.type })\r\n } catch (error) {\r\n clearTimeout(timeoutHandle)\r\n this.pendingRequests.delete(requestId)\r\n reject(error)\r\n }\r\n })\r\n }\r\n\r\n /** Send binary data and wait for response (for file uploads) */\r\n async sendBinaryAndWait(data: ArrayBuffer, requestId: string, timeout = 10000): Promise<WebSocketResponse> {\r\n return new Promise((resolve, reject) => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n reject(new Error('WebSocket is not connected'))\r\n return\r\n }\r\n\r\n const timeoutHandle = setTimeout(() => {\r\n this.pendingRequests.delete(requestId)\r\n reject(new Error(`Binary request timeout after ${timeout}ms`))\r\n }, timeout)\r\n\r\n this.pendingRequests.set(requestId, { resolve, reject, timeout: timeoutHandle })\r\n\r\n try {\r\n this.ws.send(data)\r\n this.log('Sent binary', { requestId, size: data.byteLength })\r\n } catch (error) {\r\n clearTimeout(timeoutHandle)\r\n this.pendingRequests.delete(requestId)\r\n reject(error)\r\n }\r\n })\r\n }\r\n\r\n /** Parse and route binary frames (state delta, room events, room state) */\r\n private handleBinaryMessage(buffer: Uint8Array): void {\r\n if (buffer.length < 3) return\r\n\r\n const frameType = buffer[0]\r\n\r\n if (frameType === 0x01) {\r\n // BINARY_STATE_DELTA: [0x01][idLen:u8][compId:utf8][payload]\r\n const idLen = buffer[1]\r\n if (buffer.length < 2 + idLen) return\r\n const componentId = new TextDecoder().decode(buffer.subarray(2, 2 + idLen))\r\n const payload = buffer.subarray(2 + idLen)\r\n\r\n const callback = this.binaryCallbacks.get(componentId)\r\n if (callback) callback(payload)\r\n } else if (frameType === 0x02 || frameType === 0x03) {\r\n // BINARY_ROOM_EVENT (0x02) or BINARY_ROOM_STATE (0x03)\r\n // Route to all registered room binary handlers (RoomManager instances)\r\n for (const callback of this.roomBinaryHandlers) {\r\n callback(buffer)\r\n }\r\n }\r\n }\r\n\r\n /** Register a handler for binary room frames (0x02 / 0x03). Returns unsubscribe. */\r\n registerRoomBinaryHandler(callback: (frame: Uint8Array) => void): () => void {\r\n this.roomBinaryHandlers.add(callback)\r\n return () => { this.roomBinaryHandlers.delete(callback) }\r\n }\r\n\r\n /** Register a binary message handler for a component */\r\n registerBinaryHandler(componentId: string, callback: (payload: Uint8Array) => void): () => void {\r\n this.binaryCallbacks.set(componentId, callback)\r\n return () => { this.binaryCallbacks.delete(componentId) }\r\n }\r\n\r\n /** Register a component message callback */\r\n registerComponent(componentId: string, callback: ComponentCallback): () => void {\r\n this.log('Registering component', componentId)\r\n this.componentCallbacks.set(componentId, callback)\r\n return () => {\r\n this.componentCallbacks.delete(componentId)\r\n this.log('Unregistered component', componentId)\r\n }\r\n }\r\n\r\n /** Unregister a component */\r\n unregisterComponent(componentId: string): void {\r\n this.componentCallbacks.delete(componentId)\r\n }\r\n\r\n /** Authenticate (or re-authenticate) the WebSocket connection */\r\n async authenticate(credentials: LiveAuthOptions): Promise<boolean> {\r\n try {\r\n const response = await this.sendMessageAndWait(\r\n { type: 'AUTH', payload: credentials } as any,\r\n 5000\r\n )\r\n const payload = (response as any).payload\r\n const success = payload?.authenticated || false\r\n this.setState({\r\n authenticated: success,\r\n auth: {\r\n authenticated: success,\r\n session: success ? (payload?.session || null) : null,\r\n },\r\n })\r\n return success\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n /** Get the raw WebSocket instance */\r\n getWebSocket(): WebSocket | null {\r\n return this.ws\r\n }\r\n\r\n /** Destroy the connection and clean up all resources */\r\n destroy(): void {\r\n this.disconnect()\r\n this.componentCallbacks.clear()\r\n this.binaryCallbacks.clear()\r\n this.roomBinaryHandlers.clear()\r\n for (const [, req] of this.pendingRequests) {\r\n clearTimeout(req.timeout)\r\n req.reject(new Error('Connection destroyed'))\r\n }\r\n this.pendingRequests.clear()\r\n this.stateListeners.clear()\r\n }\r\n}\r\n","// @fluxstack/live-client - LiveComponentHandle\r\n//\r\n// High-level vanilla JS wrapper for live components.\r\n// Equivalent to Live.use() in @fluxstack/live-react but without React.\r\n//\r\n// Usage:\r\n// const connection = new LiveConnection({ url: 'ws://...' })\r\n// const counter = new LiveComponentHandle(connection, 'Counter', { count: 0 })\r\n// counter.onStateChange((state) => updateUI(state))\r\n// await counter.mount()\r\n// await counter.call('increment')\r\n\r\nimport type { WebSocketResponse } from '@fluxstack/live'\r\nimport type { LiveConnection } from './connection'\r\n\r\n// ===== Deep Merge (always-on, retrocompatible) =====\r\n\r\nfunction isPlainObject(v: unknown): v is Record<string, any> {\r\n return v !== null && typeof v === 'object' && !Array.isArray(v)\r\n && Object.getPrototypeOf(v) === Object.prototype\r\n}\r\n\r\n/**\r\n * Apply a STATE_DELTA coming from the server (component state).\r\n *\r\n * Semantics (matches core's `deepAssign`, fixes #6):\r\n * - Top-level `null` is a real value (set to null).\r\n * - Nested `null` is the deletion sentinel from `computeDeepDiff`.\r\n * - `undefined` is skipped — it never crosses the wire.\r\n */\r\nfunction deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>, seen?: Set<object>): T {\r\n return deepMergeImpl(target, source, 0, seen)\r\n}\r\n\r\nfunction deepMergeImpl<T extends Record<string, any>>(target: T, source: Partial<T>, depth: number, seen?: Set<object>): T {\r\n if (!seen) seen = new Set()\r\n if (seen.has(source as object)) return target\r\n seen.add(source as object)\r\n\r\n const result = { ...target }\r\n for (const key of Object.keys(source) as Array<keyof T>) {\r\n const newVal = source[key]\r\n if (newVal === undefined) continue\r\n if (newVal === null) {\r\n if (depth === 0) {\r\n result[key] = null as T[keyof T]\r\n } else {\r\n delete result[key]\r\n }\r\n continue\r\n }\r\n const oldVal = result[key]\r\n if (isPlainObject(oldVal) && isPlainObject(newVal)) {\r\n result[key] = deepMergeImpl(oldVal as any, newVal as any, depth + 1, seen) as T[keyof T]\r\n } else {\r\n result[key] = newVal as T[keyof T]\r\n }\r\n }\r\n return result\r\n}\r\n\r\nexport interface LiveComponentOptions<TState = Record<string, any>> {\r\n /** Initial state to merge with server defaults */\r\n initialState?: Partial<TState>\r\n /** Room to join on mount */\r\n room?: string\r\n /** User ID for component isolation */\r\n userId?: string\r\n /** Auto-mount when connection is ready. Default: true */\r\n autoMount?: boolean\r\n /** Enable debug logging. Default: false */\r\n debug?: boolean\r\n}\r\n\r\ntype StateChangeCallback<TState> = (state: TState, delta: Partial<TState> | null) => void\r\ntype ErrorCallback = (error: string) => void\r\n\r\n/**\r\n * High-level handle for a live component instance.\r\n * Manages mount lifecycle, state sync, and action calling.\r\n * Framework-agnostic — works with vanilla JS, Vue, Svelte, etc.\r\n */\r\nexport class LiveComponentHandle<TState extends Record<string, any> = Record<string, any>> {\r\n private connection: LiveConnection\r\n private componentName: string\r\n private options: Required<Omit<LiveComponentOptions<TState>, 'initialState' | 'room' | 'userId'>> & {\r\n initialState: Partial<TState>\r\n room?: string\r\n userId?: string\r\n }\r\n\r\n private _componentId: string | null = null\r\n private _state: TState\r\n private _mounted = false\r\n private _mounting = false\r\n private _error: string | null = null\r\n\r\n private stateListeners = new Set<StateChangeCallback<TState>>()\r\n private errorListeners = new Set<ErrorCallback>()\r\n private unregisterComponent: (() => void) | null = null\r\n private unsubConnection: (() => void) | null = null\r\n\r\n constructor(\r\n connection: LiveConnection,\r\n componentName: string,\r\n options: LiveComponentOptions<TState> = {},\r\n ) {\r\n this.connection = connection\r\n this.componentName = componentName\r\n this._state = (options.initialState ?? {}) as TState\r\n\r\n this.options = {\r\n initialState: options.initialState ?? {},\r\n room: options.room,\r\n userId: options.userId,\r\n autoMount: options.autoMount ?? true,\r\n debug: options.debug ?? false,\r\n }\r\n\r\n // Auto-mount when connection is ready\r\n if (this.options.autoMount) {\r\n if (this.connection.state.connected) {\r\n this.mount()\r\n }\r\n this.unsubConnection = this.connection.onStateChange((connState) => {\r\n if (connState.connected && !this._mounted && !this._mounting) {\r\n this.mount()\r\n }\r\n })\r\n }\r\n }\r\n\r\n // ── Getters ──\r\n\r\n /** Current component state */\r\n get state(): Readonly<TState> { return this._state }\r\n\r\n /** Server-assigned component ID (null before mount) */\r\n get componentId(): string | null { return this._componentId }\r\n\r\n /** Whether the component has been mounted */\r\n get mounted(): boolean { return this._mounted }\r\n\r\n /** Whether the component is currently mounting */\r\n get mounting(): boolean { return this._mounting }\r\n\r\n /** Last error message */\r\n get error(): string | null { return this._error }\r\n\r\n // ── Lifecycle ──\r\n\r\n /** Mount the component on the server */\r\n async mount(): Promise<void> {\r\n if (this._mounted || this._mounting) return\r\n if (!this.connection.state.connected) {\r\n throw new Error('Cannot mount: not connected')\r\n }\r\n\r\n this._mounting = true\r\n this._error = null\r\n this.log('Mounting...')\r\n\r\n try {\r\n const response = await this.connection.sendMessageAndWait({\r\n type: 'COMPONENT_MOUNT',\r\n componentId: `mount-${this.componentName}`,\r\n payload: {\r\n component: this.componentName,\r\n props: this.options.initialState,\r\n room: this.options.room,\r\n userId: this.options.userId,\r\n },\r\n })\r\n\r\n if (!response.success) {\r\n throw new Error(response.error || 'Mount failed')\r\n }\r\n\r\n const result = (response as any).result\r\n this._componentId = result.componentId\r\n this._mounted = true\r\n this._mounting = false\r\n\r\n // Merge initial state from server\r\n const serverState = result.initialState || {}\r\n this._state = { ...this._state, ...serverState }\r\n\r\n // Register for component messages (state updates, deltas, errors)\r\n this.unregisterComponent = this.connection.registerComponent(\r\n this._componentId!,\r\n (msg) => this.handleServerMessage(msg),\r\n )\r\n\r\n this.log('Mounted', { componentId: this._componentId })\r\n this.notifyStateChange(this._state, null)\r\n } catch (err) {\r\n this._mounting = false\r\n const errorMsg = err instanceof Error ? err.message : String(err)\r\n this._error = errorMsg\r\n this.notifyError(errorMsg)\r\n throw err\r\n }\r\n }\r\n\r\n /** Unmount the component from the server */\r\n async unmount(): Promise<void> {\r\n if (!this._mounted || !this._componentId) return\r\n\r\n this.log('Unmounting...')\r\n\r\n try {\r\n await this.connection.sendMessage({\r\n type: 'COMPONENT_UNMOUNT',\r\n componentId: this._componentId,\r\n })\r\n } catch {\r\n // Ignore unmount errors (connection may already be closed)\r\n }\r\n\r\n this.cleanup()\r\n }\r\n\r\n /** Destroy the handle and clean up all resources */\r\n destroy(): void {\r\n this.unmount().catch(() => {})\r\n if (this.unsubConnection) {\r\n this.unsubConnection()\r\n this.unsubConnection = null\r\n }\r\n this.stateListeners.clear()\r\n this.errorListeners.clear()\r\n }\r\n\r\n // ── Actions ──\r\n\r\n /**\r\n * Call an action on the server component.\r\n * Returns the action's return value.\r\n */\r\n async call<R = any>(action: string, payload: Record<string, any> = {}): Promise<R> {\r\n if (!this._mounted || !this._componentId) {\r\n throw new Error(`Cannot call '${action}': component not mounted`)\r\n }\r\n\r\n this.log(`Calling action: ${action}`, payload)\r\n\r\n const response = await this.connection.sendMessageAndWait({\r\n type: 'CALL_ACTION',\r\n componentId: this._componentId,\r\n action,\r\n payload,\r\n })\r\n\r\n if (!response.success) {\r\n const errorMsg = response.error || `Action '${action}' failed`\r\n this._error = errorMsg\r\n this.notifyError(errorMsg)\r\n throw new Error(errorMsg)\r\n }\r\n\r\n return (response as any).result\r\n }\r\n\r\n /**\r\n * Fire an action without waiting for a response (fire-and-forget).\r\n * Useful for high-frequency operations like game input where the\r\n * server doesn't need to send back a result.\r\n */\r\n fire(action: string, payload: Record<string, any> = {}): void {\r\n if (!this._mounted || !this._componentId) return\r\n\r\n this.connection.sendMessage({\r\n type: 'CALL_ACTION',\r\n componentId: this._componentId,\r\n action,\r\n payload,\r\n expectResponse: false,\r\n } as any)\r\n }\r\n\r\n // ── State ──\r\n\r\n /**\r\n * Subscribe to state changes.\r\n * Callback receives the full new state and the delta (or null for full updates).\r\n * Returns an unsubscribe function.\r\n */\r\n onStateChange(callback: StateChangeCallback<TState>): () => void {\r\n this.stateListeners.add(callback)\r\n return () => { this.stateListeners.delete(callback) }\r\n }\r\n\r\n /**\r\n * Register a binary decoder for this component.\r\n * When the server sends a BINARY_STATE_DELTA frame targeting this component,\r\n * the decoder converts the raw payload into a delta object which is merged into state.\r\n * Returns an unsubscribe function.\r\n */\r\n setBinaryDecoder(decoder: (buffer: Uint8Array) => Record<string, any>): () => void {\r\n if (!this._componentId) {\r\n throw new Error('Component must be mounted before setting binary decoder')\r\n }\r\n\r\n return this.connection.registerBinaryHandler(this._componentId, (payload: Uint8Array) => {\r\n try {\r\n const delta = decoder(payload) as Partial<TState>\r\n this._state = deepMerge(this._state, delta) as TState\r\n this.notifyStateChange(this._state, delta as Partial<TState>)\r\n } catch (e) {\r\n console.error('Binary decode error:', e)\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * Subscribe to errors.\r\n * Returns an unsubscribe function.\r\n */\r\n onError(callback: ErrorCallback): () => void {\r\n this.errorListeners.add(callback)\r\n return () => { this.errorListeners.delete(callback) }\r\n }\r\n\r\n // ── Internal ──\r\n\r\n private handleServerMessage(msg: WebSocketResponse): void {\r\n switch (msg.type) {\r\n case 'STATE_UPDATE': {\r\n const newState = (msg as any).payload?.state\r\n if (newState) {\r\n this._state = deepMerge(this._state, newState)\r\n this.notifyStateChange(this._state, null)\r\n }\r\n break\r\n }\r\n\r\n case 'STATE_DELTA': {\r\n const delta = (msg as any).payload?.delta\r\n if (delta) {\r\n this._state = deepMerge(this._state, delta)\r\n this.notifyStateChange(this._state, delta)\r\n }\r\n break\r\n }\r\n\r\n case 'ERROR': {\r\n const error = (msg as any).error || 'Unknown error'\r\n this._error = error\r\n this.notifyError(error)\r\n break\r\n }\r\n\r\n default:\r\n this.log('Unhandled message type:', msg.type)\r\n }\r\n }\r\n\r\n private notifyStateChange(state: TState, delta: Partial<TState> | null): void {\r\n for (const cb of this.stateListeners) {\r\n cb(state, delta)\r\n }\r\n }\r\n\r\n private notifyError(error: string): void {\r\n for (const cb of this.errorListeners) {\r\n cb(error)\r\n }\r\n }\r\n\r\n private cleanup(): void {\r\n if (this.unregisterComponent) {\r\n this.unregisterComponent()\r\n this.unregisterComponent = null\r\n }\r\n this._componentId = null\r\n this._mounted = false\r\n this._mounting = false\r\n }\r\n\r\n private log(message: string, data?: any): void {\r\n if (this.options.debug) {\r\n console.log(`[Live:${this.componentName}] ${message}`, data ?? '')\r\n }\r\n }\r\n}\r\n","// @fluxstack/live-client - Room Manager (Client-side)\r\n//\r\n// Framework-agnostic room system for managing multi-room WebSocket communication.\r\n// Used by framework-specific adapters (React, Vue, etc.).\r\n\r\n// ===== Deep Merge (always-on, retrocompatible) =====\r\n\r\nfunction isPlainObject(v: unknown): v is Record<string, any> {\r\n return v !== null && typeof v === 'object' && !Array.isArray(v)\r\n && Object.getPrototypeOf(v) === Object.prototype\r\n}\r\n\r\n/**\r\n * Apply a room STATE_DELTA coming from the server.\r\n *\r\n * Semantics (matches core's `deepAssign`, fixes #6):\r\n * - Top-level `null` is a real value (set to null).\r\n * - Nested `null` is the deletion sentinel from `computeDeepDiff`.\r\n * - `undefined` is skipped — it never crosses the wire.\r\n */\r\nfunction deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>, seen?: Set<object>): T {\r\n return deepMergeImpl(target, source, 0, seen)\r\n}\r\n\r\nfunction deepMergeImpl<T extends Record<string, any>>(target: T, source: Partial<T>, depth: number, seen?: Set<object>): T {\r\n if (!seen) seen = new Set()\r\n if (seen.has(source as object)) return target\r\n seen.add(source as object)\r\n\r\n const result = { ...target }\r\n for (const key of Object.keys(source) as Array<keyof T>) {\r\n const newVal = source[key]\r\n if (newVal === undefined) continue\r\n if (newVal === null) {\r\n if (depth === 0) {\r\n result[key] = null as T[keyof T]\r\n } else {\r\n delete result[key]\r\n }\r\n continue\r\n }\r\n const oldVal = result[key]\r\n if (isPlainObject(oldVal) && isPlainObject(newVal)) {\r\n result[key] = deepMergeImpl(oldVal as any, newVal as any, depth + 1, seen) as T[keyof T]\r\n } else {\r\n result[key] = newVal as T[keyof T]\r\n }\r\n }\r\n return result\r\n}\r\n\r\ntype EventHandler<T = any> = (data: T) => void\r\ntype Unsubscribe = () => void\r\n\r\n// ===== Binary Room Frame Constants =====\r\n\r\nconst BINARY_ROOM_EVENT = 0x02\r\nconst BINARY_ROOM_STATE = 0x03\r\n\r\n// ===== Lightweight msgpack decoder (client-side, decode-only) =====\r\n\r\nconst _decoder = new TextDecoder()\r\n\r\nfunction msgpackDecode(buf: Uint8Array): unknown {\r\n return _decodeAt(buf, 0).value\r\n}\r\n\r\nfunction _decodeAt(buf: Uint8Array, offset: number): { value: unknown; offset: number } {\r\n if (offset >= buf.length) return { value: null, offset }\r\n const byte = buf[offset]\r\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)\r\n\r\n if (byte < 0x80) return { value: byte, offset: offset + 1 }\r\n if (byte >= 0x80 && byte <= 0x8f) return _decodeMap(buf, offset + 1, byte & 0x0f)\r\n if (byte >= 0x90 && byte <= 0x9f) return _decodeArr(buf, offset + 1, byte & 0x0f)\r\n if (byte >= 0xa0 && byte <= 0xbf) {\r\n const len = byte & 0x1f\r\n return { value: _decoder.decode(buf.subarray(offset + 1, offset + 1 + len)), offset: offset + 1 + len }\r\n }\r\n if (byte >= 0xe0) return { value: byte - 256, offset: offset + 1 }\r\n\r\n switch (byte) {\r\n case 0xc0: return { value: null, offset: offset + 1 }\r\n case 0xc2: return { value: false, offset: offset + 1 }\r\n case 0xc3: return { value: true, offset: offset + 1 }\r\n case 0xc4: { const l = buf[offset + 1]; return { value: buf.slice(offset + 2, offset + 2 + l), offset: offset + 2 + l } }\r\n case 0xc5: { const l = view.getUint16(offset + 1, false); return { value: buf.slice(offset + 3, offset + 3 + l), offset: offset + 3 + l } }\r\n case 0xc6: { const l = view.getUint32(offset + 1, false); return { value: buf.slice(offset + 5, offset + 5 + l), offset: offset + 5 + l } }\r\n case 0xcb: return { value: view.getFloat64(offset + 1, false), offset: offset + 9 }\r\n case 0xcc: return { value: buf[offset + 1], offset: offset + 2 }\r\n case 0xcd: return { value: view.getUint16(offset + 1, false), offset: offset + 3 }\r\n case 0xce: return { value: view.getUint32(offset + 1, false), offset: offset + 5 }\r\n case 0xd0: return { value: view.getInt8(offset + 1), offset: offset + 2 }\r\n case 0xd1: return { value: view.getInt16(offset + 1, false), offset: offset + 3 }\r\n case 0xd2: return { value: view.getInt32(offset + 1, false), offset: offset + 5 }\r\n case 0xd9: { const l = buf[offset + 1]; return { value: _decoder.decode(buf.subarray(offset + 2, offset + 2 + l)), offset: offset + 2 + l } }\r\n case 0xda: { const l = view.getUint16(offset + 1, false); return { value: _decoder.decode(buf.subarray(offset + 3, offset + 3 + l)), offset: offset + 3 + l } }\r\n case 0xdb: { const l = view.getUint32(offset + 1, false); return { value: _decoder.decode(buf.subarray(offset + 5, offset + 5 + l)), offset: offset + 5 + l } }\r\n case 0xdc: return _decodeArr(buf, offset + 3, view.getUint16(offset + 1, false))\r\n case 0xdd: return _decodeArr(buf, offset + 5, view.getUint32(offset + 1, false))\r\n case 0xde: return _decodeMap(buf, offset + 3, view.getUint16(offset + 1, false))\r\n case 0xdf: return _decodeMap(buf, offset + 5, view.getUint32(offset + 1, false))\r\n }\r\n return { value: null, offset: offset + 1 }\r\n}\r\n\r\nfunction _decodeArr(buf: Uint8Array, offset: number, count: number): { value: unknown[]; offset: number } {\r\n const arr: unknown[] = new Array(count)\r\n for (let i = 0; i < count; i++) { const r = _decodeAt(buf, offset); arr[i] = r.value; offset = r.offset }\r\n return { value: arr, offset }\r\n}\r\n\r\nfunction _decodeMap(buf: Uint8Array, offset: number, count: number): { value: Record<string, unknown>; offset: number } {\r\n const obj: Record<string, unknown> = {}\r\n for (let i = 0; i < count; i++) {\r\n const k = _decodeAt(buf, offset); offset = k.offset\r\n const v = _decodeAt(buf, offset); offset = v.offset\r\n obj[String(k.value)] = v.value\r\n }\r\n return { value: obj, offset }\r\n}\r\n\r\n/** Parse a binary room frame: [frameType][compIdLen][compId][roomIdLen][roomId][eventLen:u16][event][payload] */\r\nfunction parseRoomFrame(buf: Uint8Array): {\r\n frameType: number; componentId: string; roomId: string; event: string; payload: Uint8Array\r\n} | null {\r\n if (buf.length < 6) return null\r\n let offset = 0\r\n const frameType = buf[offset++]\r\n const compIdLen = buf[offset++]\r\n if (offset + compIdLen > buf.length) return null\r\n const componentId = _decoder.decode(buf.subarray(offset, offset + compIdLen)); offset += compIdLen\r\n const roomIdLen = buf[offset++]\r\n if (offset + roomIdLen > buf.length) return null\r\n const roomId = _decoder.decode(buf.subarray(offset, offset + roomIdLen)); offset += roomIdLen\r\n if (offset + 2 > buf.length) return null\r\n const eventLen = (buf[offset] << 8) | buf[offset + 1]; offset += 2\r\n if (offset + eventLen > buf.length) return null\r\n const event = _decoder.decode(buf.subarray(offset, offset + eventLen)); offset += eventLen\r\n return { frameType, componentId, roomId, event, payload: buf.subarray(offset) }\r\n}\r\n\r\n/** Reserved property names on RoomHandle/RoomProxy (never fall through to state) */\r\nconst ROOM_RESERVED_KEYS = new Set<string | symbol>([\r\n 'id', 'joined', 'state', 'join', 'leave', 'emit', 'on', 'onSystem', 'setState',\r\n 'call', 'apply', 'bind', 'prototype', 'length', 'name', 'arguments', 'caller',\r\n Symbol.toPrimitive, Symbol.toStringTag, Symbol.hasInstance,\r\n])\r\n\r\n/** Wrap a handle/proxy so unknown property access falls through to state */\r\nfunction wrapWithStateProxy<T extends object>(\r\n target: T,\r\n getState: () => any,\r\n setStateFn: (updates: any) => void,\r\n): T {\r\n return new Proxy(target, {\r\n get(obj, prop, receiver) {\r\n if (ROOM_RESERVED_KEYS.has(prop) || typeof prop === 'symbol') {\r\n return Reflect.get(obj, prop, receiver)\r\n }\r\n const desc = Object.getOwnPropertyDescriptor(obj, prop)\r\n if (desc) return Reflect.get(obj, prop, receiver)\r\n if (prop in obj) return Reflect.get(obj, prop, receiver)\r\n const st = getState()\r\n return st?.[prop]\r\n },\r\n set(_obj, prop, value) {\r\n if (typeof prop === 'symbol') return false\r\n setStateFn({ [prop]: value })\r\n return true\r\n },\r\n })\r\n}\r\n\r\n/** Reserved keys on RoomHandle/RoomProxy — cannot be state fields */\r\ntype RoomReservedKeys = 'id' | 'joined' | 'state' | 'join' | 'leave' | 'emit' | 'on' | 'onSystem' | 'setState'\r\n\r\n/** State fields accessible directly on handle/proxy (excludes reserved method names) */\r\ntype RoomStateFields<TState> = TState extends Record<string, any>\r\n ? { readonly [K in Exclude<keyof TState, RoomReservedKeys>]: TState[K] }\r\n : unknown\r\n\r\n/** Message from client to server */\r\nexport interface RoomClientMessage {\r\n type: 'ROOM_JOIN' | 'ROOM_LEAVE' | 'ROOM_EMIT' | 'ROOM_STATE_GET' | 'ROOM_STATE_SET'\r\n componentId: string\r\n roomId: string\r\n event?: string\r\n data?: any\r\n timestamp: number\r\n}\r\n\r\n/** Message from server to client */\r\nexport interface RoomServerMessage {\r\n type: 'ROOM_EVENT' | 'ROOM_STATE' | 'ROOM_SYSTEM' | 'ROOM_JOINED' | 'ROOM_LEFT'\r\n componentId: string\r\n roomId: string\r\n event: string\r\n data: any\r\n timestamp: number\r\n}\r\n\r\n/** Interface of an individual room handle */\r\nexport type RoomHandle<TState = any, TEvents extends Record<string, any> = Record<string, any>> = {\r\n readonly id: string\r\n readonly joined: boolean\r\n readonly state: TState\r\n join: (initialState?: TState) => Promise<void>\r\n leave: () => Promise<void>\r\n emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void\r\n on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe\r\n onSystem: (event: string, handler: EventHandler) => Unsubscribe\r\n setState: (updates: Partial<TState>) => void\r\n} & RoomStateFields<TState>\r\n\r\n/** Infer TEvents from a LiveRoom class (via $events brand) or use T directly as events map */\r\nexport type InferRoomEvents<T> =\r\n T extends { $events: infer E extends Record<string, any> } ? E :\r\n T extends Record<string, any> ? T :\r\n Record<string, any>\r\n\r\n/** Proxy interface for $room - callable as function or object */\r\nexport type RoomProxy<TState = any, TEvents extends Record<string, any> = Record<string, any>> = {\r\n /** Get a typed room handle. Pass the Room class or events interface as generic:\r\n * `$room<CounterRoom>('counter:global').on('counter:updated', data => ...)` */\r\n <T = TEvents>(roomId: string): RoomHandle<any, InferRoomEvents<T>>\r\n readonly id: string | null\r\n readonly joined: boolean\r\n readonly state: TState\r\n join: (initialState?: TState) => Promise<void>\r\n leave: () => Promise<void>\r\n emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void\r\n on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe\r\n onSystem: (event: string, handler: EventHandler) => Unsubscribe\r\n setState: (updates: Partial<TState>) => void\r\n} & RoomStateFields<TState>\r\n\r\nexport interface RoomManagerOptions {\r\n componentId: string | null\r\n defaultRoom?: string\r\n sendMessage: (msg: any) => void\r\n sendMessageAndWait: (msg: any, timeout?: number) => Promise<any>\r\n onMessage: (handler: (msg: RoomServerMessage) => void) => Unsubscribe\r\n /** Optional: register for binary room frames (0x02 ROOM_EVENT, 0x03 ROOM_STATE) */\r\n onBinaryMessage?: (handler: (frame: Uint8Array) => void) => Unsubscribe\r\n}\r\n\r\n/** Client-side room manager. Framework-agnostic. */\r\nexport class RoomManager<TState = any, TEvents extends Record<string, any> = Record<string, any>> {\r\n private componentId: string | null\r\n private defaultRoom: string | null\r\n private rooms = new Map<string, {\r\n joined: boolean\r\n state: TState\r\n handlers: Map<string, Set<EventHandler>>\r\n }>()\r\n private handles = new Map<string, RoomHandle<TState, TEvents>>()\r\n private sendMessage: (msg: any) => void\r\n private sendMessageAndWait: (msg: any, timeout?: number) => Promise<any>\r\n private globalUnsubscribe: Unsubscribe | null = null\r\n private binaryUnsubscribe: Unsubscribe | null = null\r\n private onBinaryMessage: ((handler: (frame: Uint8Array) => void) => Unsubscribe) | null = null\r\n private onMessageFactory: ((handler: (msg: RoomServerMessage) => void) => Unsubscribe) | null = null\r\n\r\n constructor(options: RoomManagerOptions) {\r\n this.componentId = options.componentId\r\n this.defaultRoom = options.defaultRoom || null\r\n this.sendMessage = options.sendMessage\r\n this.sendMessageAndWait = options.sendMessageAndWait\r\n this.onBinaryMessage = options.onBinaryMessage ?? null\r\n this.onMessageFactory = options.onMessage\r\n this.globalUnsubscribe = options.onMessage((msg) => this.handleServerMessage(msg))\r\n if (options.onBinaryMessage) {\r\n this.binaryUnsubscribe = options.onBinaryMessage((frame) => this.handleBinaryFrame(frame))\r\n }\r\n }\r\n\r\n /** Re-subscribe message and binary handlers (needed after destroy/remount in React Strict Mode) */\r\n resubscribe(): void {\r\n if (!this.globalUnsubscribe && this.onMessageFactory) {\r\n this.globalUnsubscribe = this.onMessageFactory((msg) => this.handleServerMessage(msg))\r\n }\r\n if (!this.binaryUnsubscribe && this.onBinaryMessage) {\r\n this.binaryUnsubscribe = this.onBinaryMessage((frame) => this.handleBinaryFrame(frame))\r\n }\r\n }\r\n\r\n private handleServerMessage(msg: RoomServerMessage): void {\r\n if (msg.componentId !== this.componentId) return\r\n\r\n const room = this.rooms.get(msg.roomId)\r\n if (!room) return\r\n\r\n switch (msg.type) {\r\n case 'ROOM_EVENT':\r\n case 'ROOM_SYSTEM': {\r\n const handlers = room.handlers.get(msg.event)\r\n if (handlers) {\r\n for (const handler of handlers) {\r\n try { handler(msg.data) } catch (error) {\r\n console.error(`[Room:${msg.roomId}] Handler error for '${msg.event}':`, error)\r\n }\r\n }\r\n }\r\n break\r\n }\r\n\r\n case 'ROOM_STATE': {\r\n // Server sends data: { state: actualChanges } — extract the actual changes\r\n const stateChanges = msg.data?.state ?? msg.data\r\n room.state = deepMerge(room.state as Record<string, any>, stateChanges) as TState\r\n const stateHandlers = room.handlers.get('$state:change')\r\n if (stateHandlers) {\r\n for (const handler of stateHandlers) handler(stateChanges)\r\n }\r\n break\r\n }\r\n\r\n case 'ROOM_JOINED':\r\n room.joined = true\r\n if (msg.data?.state) room.state = msg.data.state\r\n break\r\n\r\n case 'ROOM_LEFT':\r\n room.joined = false\r\n break\r\n }\r\n }\r\n\r\n /** Handle binary room frames (0x02 ROOM_EVENT, 0x03 ROOM_STATE) */\r\n private handleBinaryFrame(frame: Uint8Array): void {\r\n const parsed = parseRoomFrame(frame)\r\n if (!parsed) return\r\n if (parsed.componentId !== this.componentId) return\r\n\r\n const room = this.rooms.get(parsed.roomId)\r\n if (!room) return\r\n\r\n const data = msgpackDecode(parsed.payload)\r\n\r\n if (parsed.frameType === BINARY_ROOM_EVENT) {\r\n // Dispatch to event handlers\r\n const handlers = room.handlers.get(parsed.event)\r\n if (handlers) {\r\n for (const handler of handlers) {\r\n try { handler(data) } catch (error) {\r\n console.error(`[Room:${parsed.roomId}] Handler error for '${parsed.event}':`, error)\r\n }\r\n }\r\n }\r\n } else if (parsed.frameType === BINARY_ROOM_STATE) {\r\n // State update: data is { state: changes } or just changes\r\n const stateChanges = (data as any)?.state ?? data\r\n room.state = deepMerge(room.state as Record<string, any>, stateChanges as Record<string, any>) as TState\r\n const stateHandlers = room.handlers.get('$state:change')\r\n if (stateHandlers) {\r\n for (const handler of stateHandlers) handler(stateChanges)\r\n }\r\n }\r\n }\r\n\r\n private getOrCreateRoom(roomId: string) {\r\n if (!this.rooms.has(roomId)) {\r\n this.rooms.set(roomId, {\r\n joined: false,\r\n state: {} as TState,\r\n handlers: new Map(),\r\n })\r\n }\r\n return this.rooms.get(roomId)!\r\n }\r\n\r\n /** Create handle for a specific room (cached) */\r\n createHandle(roomId: string): RoomHandle<TState, TEvents> {\r\n if (this.handles.has(roomId)) return this.handles.get(roomId)!\r\n\r\n const room = this.getOrCreateRoom(roomId)\r\n\r\n // RoomStateFields are fulfilled at runtime by the Proxy wrapper\r\n const handle = {\r\n get id() { return roomId },\r\n get joined() { return room.joined },\r\n get state() { return room.state },\r\n\r\n join: async (initialState?: TState) => {\r\n if (!this.componentId) throw new Error('Component not mounted')\r\n if (room.joined) return\r\n\r\n if (initialState) room.state = initialState\r\n\r\n const response = await this.sendMessageAndWait({\r\n type: 'ROOM_JOIN',\r\n componentId: this.componentId,\r\n roomId,\r\n data: { initialState: room.state },\r\n timestamp: Date.now(),\r\n }, 5000)\r\n\r\n if (response?.success) {\r\n room.joined = true\r\n if (response.state) room.state = response.state\r\n }\r\n },\r\n\r\n leave: async () => {\r\n if (!this.componentId || !room.joined) return\r\n\r\n await this.sendMessageAndWait({\r\n type: 'ROOM_LEAVE',\r\n componentId: this.componentId,\r\n roomId,\r\n timestamp: Date.now(),\r\n }, 5000)\r\n\r\n room.joined = false\r\n room.handlers.clear()\r\n },\r\n\r\n emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => {\r\n if (!this.componentId) return\r\n this.sendMessage({\r\n type: 'ROOM_EMIT',\r\n componentId: this.componentId,\r\n roomId,\r\n event: event as string,\r\n data,\r\n timestamp: Date.now(),\r\n })\r\n },\r\n\r\n on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): Unsubscribe => {\r\n const eventKey = event as string\r\n if (!room.handlers.has(eventKey)) room.handlers.set(eventKey, new Set())\r\n room.handlers.get(eventKey)!.add(handler)\r\n return () => { room.handlers.get(eventKey)?.delete(handler) }\r\n },\r\n\r\n onSystem: (event: string, handler: EventHandler): Unsubscribe => {\r\n const eventKey = `$${event}`\r\n if (!room.handlers.has(eventKey)) room.handlers.set(eventKey, new Set())\r\n room.handlers.get(eventKey)!.add(handler)\r\n return () => { room.handlers.get(eventKey)?.delete(handler) }\r\n },\r\n\r\n setState: (updates: Partial<TState>) => {\r\n if (!this.componentId) return\r\n room.state = deepMerge(room.state as Record<string, any>, updates as Record<string, any>) as TState\r\n this.sendMessage({\r\n type: 'ROOM_STATE_SET',\r\n componentId: this.componentId,\r\n roomId,\r\n data: updates,\r\n timestamp: Date.now(),\r\n })\r\n },\r\n }\r\n\r\n const proxied = wrapWithStateProxy(\r\n handle,\r\n () => room.state,\r\n (updates: Partial<TState>) => handle.setState(updates),\r\n )\r\n this.handles.set(roomId, proxied as RoomHandle<TState, TEvents>)\r\n return proxied as RoomHandle<TState, TEvents>\r\n }\r\n\r\n /** Create the $room proxy */\r\n createProxy(): RoomProxy<TState, TEvents> {\r\n const self = this\r\n\r\n const proxyFn = function(roomId: string): RoomHandle<TState, TEvents> {\r\n return self.createHandle(roomId)\r\n } as RoomProxy<TState, TEvents>\r\n\r\n const defaultHandle = this.defaultRoom ? this.createHandle(this.defaultRoom) : null\r\n\r\n Object.defineProperties(proxyFn, {\r\n id: { get: () => this.defaultRoom },\r\n joined: { get: () => defaultHandle?.joined ?? false },\r\n state: { get: () => defaultHandle?.state ?? ({} as TState) },\r\n join: {\r\n value: async (initialState?: TState) => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.join(initialState)\r\n },\r\n },\r\n leave: {\r\n value: async () => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.leave()\r\n },\r\n },\r\n emit: {\r\n value: <K extends keyof TEvents>(event: K, data: TEvents[K]) => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.emit(event, data)\r\n },\r\n },\r\n on: {\r\n value: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): Unsubscribe => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.on(event, handler)\r\n },\r\n },\r\n onSystem: {\r\n value: (event: string, handler: EventHandler): Unsubscribe => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.onSystem(event, handler)\r\n },\r\n },\r\n setState: {\r\n value: (updates: Partial<TState>) => {\r\n if (!defaultHandle) throw new Error('No default room set')\r\n return defaultHandle.setState(updates)\r\n },\r\n },\r\n })\r\n\r\n // Wrap top-level proxy so $room.players reads from default room state\r\n if (this.defaultRoom && defaultHandle) {\r\n const room = this.getOrCreateRoom(this.defaultRoom)\r\n return wrapWithStateProxy(\r\n proxyFn,\r\n () => room.state,\r\n (updates: Partial<TState>) => defaultHandle.setState(updates),\r\n ) as RoomProxy<TState, TEvents>\r\n }\r\n\r\n return proxyFn\r\n }\r\n\r\n /** List of rooms currently joined */\r\n getJoinedRooms(): string[] {\r\n const joined: string[] = []\r\n for (const [id, room] of this.rooms) {\r\n if (room.joined) joined.push(id)\r\n }\r\n return joined\r\n }\r\n\r\n /** Update componentId (when component mounts) */\r\n setComponentId(id: string | null): void {\r\n this.componentId = id\r\n }\r\n\r\n /** Cleanup — unsubscribes handlers but keeps factory refs for resubscribe() */\r\n destroy(): void {\r\n this.globalUnsubscribe?.()\r\n this.globalUnsubscribe = null\r\n this.binaryUnsubscribe?.()\r\n this.binaryUnsubscribe = null\r\n for (const [, room] of this.rooms) {\r\n room.handlers.clear()\r\n }\r\n this.rooms.clear()\r\n this.handles.clear()\r\n }\r\n}\r\n\r\nexport type { EventHandler, Unsubscribe }\r\n","// @fluxstack/live-client - Chunked Upload Manager\r\n//\r\n// Framework-agnostic chunked file upload with adaptive sizing and binary protocol.\r\n\r\nimport type {\r\n FileUploadStartMessage,\r\n FileUploadChunkMessage,\r\n FileUploadCompleteMessage,\r\n FileUploadProgressResponse,\r\n FileUploadCompleteResponse,\r\n BinaryChunkHeader,\r\n} from '@fluxstack/live'\r\n\r\n// ===== Adaptive Chunk Sizer =====\r\n\r\nexport interface AdaptiveChunkConfig {\r\n minChunkSize: number\r\n maxChunkSize: number\r\n initialChunkSize: number\r\n targetLatency: number\r\n adjustmentFactor: number\r\n measurementWindow: number\r\n}\r\n\r\nexport interface ChunkMetrics {\r\n chunkIndex: number\r\n chunkSize: number\r\n startTime: number\r\n endTime: number\r\n latency: number\r\n throughput: number\r\n success: boolean\r\n}\r\n\r\nexport class AdaptiveChunkSizer {\r\n private config: Required<AdaptiveChunkConfig>\r\n private currentChunkSize: number\r\n private metrics: ChunkMetrics[] = []\r\n private consecutiveErrors = 0\r\n private consecutiveSuccesses = 0\r\n\r\n constructor(config: Partial<AdaptiveChunkConfig> = {}) {\r\n this.config = {\r\n minChunkSize: config.minChunkSize ?? 16 * 1024,\r\n maxChunkSize: config.maxChunkSize ?? 1024 * 1024,\r\n initialChunkSize: config.initialChunkSize ?? 64 * 1024,\r\n targetLatency: config.targetLatency ?? 200,\r\n adjustmentFactor: config.adjustmentFactor ?? 1.5,\r\n measurementWindow: config.measurementWindow ?? 3,\r\n }\r\n this.currentChunkSize = this.config.initialChunkSize\r\n }\r\n\r\n getChunkSize(): number {\r\n return this.currentChunkSize\r\n }\r\n\r\n recordChunkStart(_chunkIndex: number): number {\r\n return Date.now()\r\n }\r\n\r\n recordChunkComplete(chunkIndex: number, chunkSize: number, startTime: number, success: boolean): void {\r\n const endTime = Date.now()\r\n const latency = endTime - startTime\r\n const throughput = success ? (chunkSize / latency) * 1000 : 0\r\n\r\n this.metrics.push({ chunkIndex, chunkSize, startTime, endTime, latency, throughput, success })\r\n\r\n if (this.metrics.length > this.config.measurementWindow * 2) {\r\n this.metrics = this.metrics.slice(-this.config.measurementWindow * 2)\r\n }\r\n\r\n if (success) {\r\n this.consecutiveSuccesses++\r\n this.consecutiveErrors = 0\r\n this.adjustUp(latency)\r\n } else {\r\n this.consecutiveErrors++\r\n this.consecutiveSuccesses = 0\r\n this.adjustDown()\r\n }\r\n }\r\n\r\n private adjustUp(latency: number): void {\r\n if (this.consecutiveSuccesses < 2) return\r\n if (latency > this.config.targetLatency) return\r\n\r\n const latencyRatio = this.config.targetLatency / latency\r\n let newSize = Math.floor(this.currentChunkSize * Math.min(latencyRatio, this.config.adjustmentFactor))\r\n newSize = Math.min(newSize, this.config.maxChunkSize)\r\n if (newSize > this.currentChunkSize) this.currentChunkSize = newSize\r\n }\r\n\r\n private adjustDown(): void {\r\n const decreaseFactor = this.consecutiveErrors > 1 ? 2 : this.config.adjustmentFactor\r\n let newSize = Math.floor(this.currentChunkSize / decreaseFactor)\r\n newSize = Math.max(newSize, this.config.minChunkSize)\r\n if (newSize < this.currentChunkSize) this.currentChunkSize = newSize\r\n }\r\n\r\n getAverageThroughput(): number {\r\n const recent = this.metrics.slice(-this.config.measurementWindow).filter(m => m.success)\r\n if (recent.length === 0) return 0\r\n return recent.reduce((sum, m) => sum + m.throughput, 0) / recent.length\r\n }\r\n\r\n getStats() {\r\n return {\r\n currentChunkSize: this.currentChunkSize,\r\n averageThroughput: this.getAverageThroughput(),\r\n consecutiveSuccesses: this.consecutiveSuccesses,\r\n consecutiveErrors: this.consecutiveErrors,\r\n totalMeasurements: this.metrics.length,\r\n }\r\n }\r\n\r\n reset(): void {\r\n this.currentChunkSize = this.config.initialChunkSize\r\n this.metrics = []\r\n this.consecutiveErrors = 0\r\n this.consecutiveSuccesses = 0\r\n }\r\n}\r\n\r\n// ===== Binary Protocol =====\r\n\r\n/**\r\n * Creates a binary message with header + data\r\n * Format: [4 bytes header length LE][JSON header][binary data]\r\n */\r\nexport function createBinaryChunkMessage(header: BinaryChunkHeader, chunkData: Uint8Array): ArrayBuffer {\r\n const headerJson = JSON.stringify(header)\r\n const headerBytes = new TextEncoder().encode(headerJson)\r\n\r\n const totalSize = 4 + headerBytes.length + chunkData.length\r\n const buffer = new ArrayBuffer(totalSize)\r\n const view = new DataView(buffer)\r\n const uint8View = new Uint8Array(buffer)\r\n\r\n view.setUint32(0, headerBytes.length, true)\r\n uint8View.set(headerBytes, 4)\r\n uint8View.set(chunkData, 4 + headerBytes.length)\r\n\r\n return buffer\r\n}\r\n\r\n// ===== Chunked Uploader =====\r\n\r\nexport interface ChunkedUploadOptions {\r\n chunkSize?: number\r\n maxFileSize?: number\r\n allowedTypes?: string[]\r\n sendMessageAndWait: (message: any, timeout?: number) => Promise<any>\r\n sendBinaryAndWait?: (data: ArrayBuffer, requestId: string, timeout?: number) => Promise<any>\r\n onProgress?: (progress: number, bytesUploaded: number, totalBytes: number) => void\r\n onComplete?: (response: FileUploadCompleteResponse) => void\r\n onError?: (error: string) => void\r\n adaptiveChunking?: boolean\r\n adaptiveConfig?: Partial<AdaptiveChunkConfig>\r\n useBinaryProtocol?: boolean\r\n}\r\n\r\nexport interface ChunkedUploadState {\r\n uploading: boolean\r\n progress: number\r\n error: string | null\r\n uploadId: string | null\r\n bytesUploaded: number\r\n totalBytes: number\r\n}\r\n\r\n/**\r\n * Framework-agnostic chunked file uploader.\r\n * Manages the upload lifecycle without any UI framework dependency.\r\n */\r\nexport class ChunkedUploader {\r\n private options: Required<Pick<ChunkedUploadOptions, 'chunkSize' | 'maxFileSize' | 'allowedTypes' | 'useBinaryProtocol' | 'adaptiveChunking'>> & ChunkedUploadOptions\r\n private abortController: AbortController | null = null\r\n private adaptiveSizer: AdaptiveChunkSizer | null = null\r\n private _state: ChunkedUploadState = {\r\n uploading: false,\r\n progress: 0,\r\n error: null,\r\n uploadId: null,\r\n bytesUploaded: 0,\r\n totalBytes: 0,\r\n }\r\n private stateListeners = new Set<(state: ChunkedUploadState) => void>()\r\n\r\n constructor(\r\n private componentId: string,\r\n options: ChunkedUploadOptions,\r\n ) {\r\n this.options = {\r\n chunkSize: options.chunkSize ?? 64 * 1024,\r\n maxFileSize: options.maxFileSize ?? 50 * 1024 * 1024,\r\n allowedTypes: options.allowedTypes ?? [],\r\n useBinaryProtocol: options.useBinaryProtocol ?? true,\r\n adaptiveChunking: options.adaptiveChunking ?? false,\r\n ...options,\r\n }\r\n\r\n if (this.options.adaptiveChunking) {\r\n this.adaptiveSizer = new AdaptiveChunkSizer({\r\n initialChunkSize: this.options.chunkSize,\r\n minChunkSize: this.options.chunkSize,\r\n maxChunkSize: 1024 * 1024,\r\n ...options.adaptiveConfig,\r\n })\r\n }\r\n }\r\n\r\n get state(): ChunkedUploadState {\r\n return { ...this._state }\r\n }\r\n\r\n onStateChange(callback: (state: ChunkedUploadState) => void): () => void {\r\n this.stateListeners.add(callback)\r\n return () => { this.stateListeners.delete(callback) }\r\n }\r\n\r\n private setState(patch: Partial<ChunkedUploadState>) {\r\n this._state = { ...this._state, ...patch }\r\n for (const cb of this.stateListeners) cb(this._state)\r\n }\r\n\r\n async uploadFile(file: File): Promise<void> {\r\n const { allowedTypes, maxFileSize, chunkSize, sendMessageAndWait, sendBinaryAndWait, useBinaryProtocol } = this.options\r\n const canUseBinary = useBinaryProtocol && sendBinaryAndWait\r\n\r\n // Validate\r\n if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {\r\n const error = `Invalid file type: ${file.type}. Allowed: ${allowedTypes.join(', ')}`\r\n this.setState({ error })\r\n this.options.onError?.(error)\r\n return\r\n }\r\n\r\n if (file.size > maxFileSize) {\r\n const error = `File too large: ${file.size} bytes. Max: ${maxFileSize} bytes`\r\n this.setState({ error })\r\n this.options.onError?.(error)\r\n return\r\n }\r\n\r\n try {\r\n const uploadId = `upload-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`\r\n this.abortController = new AbortController()\r\n this.adaptiveSizer?.reset()\r\n\r\n this.setState({ uploading: true, progress: 0, error: null, uploadId, bytesUploaded: 0, totalBytes: file.size })\r\n\r\n const initialChunkSize = this.adaptiveSizer?.getChunkSize() ?? chunkSize\r\n\r\n // Start upload\r\n const startMessage: FileUploadStartMessage = {\r\n type: 'FILE_UPLOAD_START',\r\n componentId: this.componentId,\r\n uploadId,\r\n filename: file.name,\r\n fileType: file.type,\r\n fileSize: file.size,\r\n chunkSize,\r\n requestId: `start-${uploadId}`,\r\n }\r\n\r\n const startResponse = await sendMessageAndWait(startMessage, 10000)\r\n if (!startResponse?.success) throw new Error(startResponse?.error || 'Failed to start upload')\r\n\r\n let offset = 0\r\n let chunkIndex = 0\r\n const estimatedTotalChunks = Math.ceil(file.size / initialChunkSize)\r\n\r\n while (offset < file.size) {\r\n if (this.abortController?.signal.aborted) throw new Error('Upload cancelled')\r\n\r\n const currentChunkSize = this.adaptiveSizer?.getChunkSize() ?? chunkSize\r\n const chunkEnd = Math.min(offset + currentChunkSize, file.size)\r\n const sliceBuffer = await file.slice(offset, chunkEnd).arrayBuffer()\r\n const chunkBytes = new Uint8Array(sliceBuffer)\r\n const chunkStartTime = this.adaptiveSizer?.recordChunkStart(chunkIndex) ?? 0\r\n const requestId = `chunk-${uploadId}-${chunkIndex}`\r\n\r\n try {\r\n let progressResponse: FileUploadProgressResponse | undefined\r\n\r\n if (canUseBinary) {\r\n const header: BinaryChunkHeader = {\r\n type: 'FILE_UPLOAD_CHUNK',\r\n componentId: this.componentId,\r\n uploadId,\r\n chunkIndex,\r\n totalChunks: estimatedTotalChunks,\r\n requestId,\r\n }\r\n const binaryMessage = createBinaryChunkMessage(header, chunkBytes)\r\n progressResponse = await sendBinaryAndWait!(binaryMessage, requestId, 10000) as FileUploadProgressResponse\r\n } else {\r\n let binary = ''\r\n for (let j = 0; j < chunkBytes.length; j++) binary += String.fromCharCode(chunkBytes[j])\r\n\r\n const chunkMessage: FileUploadChunkMessage = {\r\n type: 'FILE_UPLOAD_CHUNK',\r\n componentId: this.componentId,\r\n uploadId,\r\n chunkIndex,\r\n totalChunks: estimatedTotalChunks,\r\n data: btoa(binary),\r\n requestId,\r\n }\r\n progressResponse = await sendMessageAndWait!(chunkMessage, 10000) as FileUploadProgressResponse\r\n }\r\n\r\n if (progressResponse) {\r\n this.setState({ progress: progressResponse.progress, bytesUploaded: progressResponse.bytesUploaded })\r\n this.options.onProgress?.(progressResponse.progress, progressResponse.bytesUploaded, file.size)\r\n }\r\n\r\n this.adaptiveSizer?.recordChunkComplete(chunkIndex, chunkBytes.length, chunkStartTime, true)\r\n } catch (error) {\r\n this.adaptiveSizer?.recordChunkComplete(chunkIndex, chunkBytes.length, chunkStartTime, false)\r\n throw error\r\n }\r\n\r\n offset += chunkBytes.length\r\n chunkIndex++\r\n\r\n if (!this.options.adaptiveChunking) {\r\n await new Promise(resolve => setTimeout(resolve, 10))\r\n }\r\n }\r\n\r\n // Complete\r\n const completeMessage: FileUploadCompleteMessage = {\r\n type: 'FILE_UPLOAD_COMPLETE',\r\n componentId: this.componentId,\r\n uploadId,\r\n requestId: `complete-${uploadId}`,\r\n }\r\n\r\n const completeResponse = await sendMessageAndWait(completeMessage, 10000) as FileUploadCompleteResponse\r\n\r\n if (completeResponse?.success) {\r\n this.setState({ uploading: false, progress: 100, bytesUploaded: file.size })\r\n this.options.onComplete?.(completeResponse)\r\n } else {\r\n throw new Error(completeResponse?.error || 'Upload completion failed')\r\n }\r\n } catch (error: any) {\r\n this.setState({ uploading: false, error: error.message })\r\n this.options.onError?.(error.message)\r\n }\r\n }\r\n\r\n cancelUpload(): void {\r\n if (this.abortController) {\r\n this.abortController.abort()\r\n this.setState({ uploading: false, error: 'Upload cancelled' })\r\n }\r\n }\r\n\r\n reset(): void {\r\n this._state = { uploading: false, progress: 0, error: null, uploadId: null, bytesUploaded: 0, totalBytes: 0 }\r\n for (const cb of this.stateListeners) cb(this._state)\r\n }\r\n}\r\n","// @fluxstack/live-client - State Persistence\r\n//\r\n// Utilities for persisting and recovering component state via localStorage.\r\n\r\nconst STORAGE_KEY_PREFIX = 'fluxstack_component_'\r\nconst STATE_MAX_AGE = 24 * 60 * 60 * 1000 // 24 hours\r\n\r\nexport interface PersistedState {\r\n componentName: string\r\n signedState: any\r\n room?: string\r\n userId?: string\r\n lastUpdate: number\r\n}\r\n\r\nexport function persistState(\r\n enabled: boolean,\r\n name: string,\r\n signedState: any,\r\n room?: string,\r\n userId?: string,\r\n): void {\r\n if (!enabled) return\r\n try {\r\n localStorage.setItem(`${STORAGE_KEY_PREFIX}${name}`, JSON.stringify({\r\n componentName: name, signedState, room, userId, lastUpdate: Date.now(),\r\n }))\r\n } catch (e) {\r\n if (typeof console !== 'undefined') {\r\n console.warn(`[fluxstack] Failed to persist state for '${name}':`, e instanceof Error ? e.message : e)\r\n }\r\n }\r\n}\r\n\r\nexport function getPersistedState(enabled: boolean, name: string): PersistedState | null {\r\n if (!enabled) return null\r\n try {\r\n const stored = localStorage.getItem(`${STORAGE_KEY_PREFIX}${name}`)\r\n if (!stored) return null\r\n const state: PersistedState = JSON.parse(stored)\r\n if (Date.now() - state.lastUpdate > STATE_MAX_AGE) {\r\n localStorage.removeItem(`${STORAGE_KEY_PREFIX}${name}`)\r\n return null\r\n }\r\n return state\r\n } catch { return null }\r\n}\r\n\r\nexport function clearPersistedState(enabled: boolean, name: string): void {\r\n if (!enabled) return\r\n try { localStorage.removeItem(`${STORAGE_KEY_PREFIX}${name}`) } catch {}\r\n}\r\n","// @fluxstack/live-client - State Validation Utilities\r\n\r\nexport interface StateValidation {\r\n checksum: string\r\n version: number\r\n timestamp: number\r\n source: 'client' | 'server' | 'mount'\r\n}\r\n\r\nexport interface StateConflict {\r\n property: string\r\n clientValue: any\r\n serverValue: any\r\n timestamp: number\r\n resolved: boolean\r\n}\r\n\r\nexport interface HybridState<T> {\r\n data: T\r\n validation: StateValidation\r\n status: 'synced' | 'pending' | 'conflict'\r\n}\r\n\r\nexport class StateValidator {\r\n static generateChecksum(state: any): string {\r\n const json = JSON.stringify(state, Object.keys(state).sort())\r\n let hash = 0\r\n for (let i = 0; i < json.length; i++) {\r\n const char = json.charCodeAt(i)\r\n hash = ((hash << 5) - hash) + char\r\n hash = hash & hash\r\n }\r\n return Math.abs(hash).toString(16)\r\n }\r\n\r\n static createValidation(\r\n state: any,\r\n source: 'client' | 'server' | 'mount' = 'client',\r\n ): StateValidation {\r\n return {\r\n checksum: this.generateChecksum(state),\r\n version: Date.now(),\r\n timestamp: Date.now(),\r\n source,\r\n }\r\n }\r\n\r\n static detectConflicts<T>(\r\n clientState: T,\r\n serverState: T,\r\n excludeFields: string[] = ['lastUpdated', 'version'],\r\n ): StateConflict[] {\r\n const conflicts: StateConflict[] = []\r\n const clientKeys = Object.keys(clientState as any)\r\n const serverKeys = Object.keys(serverState as any)\r\n const allKeys = Array.from(new Set([...clientKeys, ...serverKeys]))\r\n\r\n for (const key of allKeys) {\r\n if (excludeFields.includes(key)) continue\r\n const clientValue = (clientState as any)?.[key]\r\n const serverValue = (serverState as any)?.[key]\r\n if (JSON.stringify(clientValue) !== JSON.stringify(serverValue)) {\r\n conflicts.push({\r\n property: key,\r\n clientValue,\r\n serverValue,\r\n timestamp: Date.now(),\r\n resolved: false,\r\n })\r\n }\r\n }\r\n\r\n return conflicts\r\n }\r\n\r\n static mergeStates<T>(\r\n clientState: T,\r\n serverState: T,\r\n conflicts: StateConflict[],\r\n strategy: 'client' | 'server' | 'smart' = 'smart',\r\n ): T {\r\n const merged = { ...clientState }\r\n\r\n for (const conflict of conflicts) {\r\n switch (strategy) {\r\n case 'client':\r\n break\r\n case 'server':\r\n (merged as any)[conflict.property] = conflict.serverValue\r\n break\r\n case 'smart':\r\n if (conflict.property === 'lastUpdated') {\r\n (merged as any)[conflict.property] = conflict.serverValue\r\n } else if (typeof conflict.serverValue === 'number' && typeof conflict.clientValue === 'number') {\r\n (merged as any)[conflict.property] = Math.max(conflict.serverValue, conflict.clientValue)\r\n } else {\r\n (merged as any)[conflict.property] = conflict.serverValue\r\n }\r\n break\r\n }\r\n }\r\n\r\n return merged\r\n }\r\n\r\n static validateState<T>(hybridState: HybridState<T>): boolean {\r\n const currentChecksum = this.generateChecksum(hybridState.data)\r\n return currentChecksum === hybridState.validation.checksum\r\n }\r\n\r\n static updateValidation<T>(\r\n hybridState: HybridState<T>,\r\n source: 'client' | 'server' | 'mount' = 'client',\r\n ): HybridState<T> {\r\n return {\r\n ...hybridState,\r\n validation: this.createValidation(hybridState.data, source),\r\n status: 'synced',\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["isPlainObject","deepMerge","deepMergeImpl"]}
|