@smoregg/sdk 2.1.0 → 2.2.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/cjs/controller.cjs +69 -0
- package/dist/cjs/controller.cjs.map +1 -1
- package/dist/cjs/events.cjs +8 -2
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/screen.cjs +61 -0
- package/dist/cjs/screen.cjs.map +1 -1
- package/dist/cjs/testing.cjs +57 -0
- package/dist/cjs/testing.cjs.map +1 -1
- package/dist/cjs/transport/protocol.cjs.map +1 -1
- package/dist/cjs/types.cjs.map +1 -1
- package/dist/esm/controller.js +69 -0
- package/dist/esm/controller.js.map +1 -1
- package/dist/esm/events.js +8 -2
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/screen.js +61 -0
- package/dist/esm/screen.js.map +1 -1
- package/dist/esm/testing.js +57 -0
- package/dist/esm/testing.js.map +1 -1
- package/dist/esm/transport/protocol.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/types/controller.d.ts.map +1 -1
- package/dist/types/events.d.ts +4 -0
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/screen.d.ts.map +1 -1
- package/dist/types/testing.d.ts +2 -0
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/transport/protocol.d.ts +1 -0
- package/dist/types/transport/protocol.d.ts.map +1 -1
- package/dist/types/types.d.ts +41 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +138 -2
- package/dist/umd/smore-sdk.umd.js.map +1 -1
- package/dist/umd/smore-sdk.umd.min.js +1 -1
- package/dist/umd/smore-sdk.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/cjs/screen.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen.cjs","sources":["../../src/screen.ts"],"sourcesContent":["/**\n * createScreen - Factory function for Screen instances (Host/TV side)\n *\n * Synchronous factory with event emitter pattern. Returns a Screen instance\n * immediately. Use `.ready` to await full initialization, and `.on()` /\n * lifecycle methods to register handlers (can be called before ready).\n *\n * @example\n * ```ts\n * interface MyEvents {\n * tap: { x: number; y: number };\n * 'phase-update': { phase: 'lobby' | 'playing' | 'results' };\n * }\n *\n * const screen = createScreen<MyEvents>({ debug: true });\n *\n * screen.on('tap', (playerIndex, data) => {\n * console.log(`Player ${playerIndex} tapped at`, data.x, data.y);\n * });\n *\n * screen.onAllReady(() => startGame());\n * screen.onControllerJoin((playerIndex, info) => addPlayer(playerIndex, info));\n *\n * await screen.ready;\n * screen.broadcast('phase-update', { phase: 'playing' });\n * ```\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n Screen,\n ScreenConfig,\n ScreenEventHandler,\n ControllerInfo,\n PlayerIndex,\n RoomCode,\n GameResults,\n SmoreError,\n CharacterAppearance,\n} from './types';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport { isBridgeMessage, validateInitPayload, PROTOCOL_VERSION, type BridgeInitMessage, type BridgeUpdateMessage } from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName, SCREEN_LIFECYCLE_EVENTS } from './events';\nimport { DebugLogger } from './logger';\nimport { mapPlayerDTO, validatePayloadSize } from './shared';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\nfunction validatePlayerIndex(playerIndex: PlayerIndex, controllers: readonly ControllerInfo[]): void {\n if (typeof playerIndex !== 'number' || !Number.isInteger(playerIndex)) {\n throw new SmoreSDKError('INVALID_PLAYER', 'Player index must be an integer');\n }\n if (!controllers.some(c => c.playerIndex === playerIndex)) {\n throw new SmoreSDKError(\n 'INVALID_PLAYER',\n `No controller found with player index ${playerIndex}`,\n { details: { playerIndex } }\n );\n }\n}\n\n// =============================================================================\n// SCREEN IMPLEMENTATION\n// =============================================================================\n\nclass ScreenImpl<TEvents extends EventMap> implements Screen<TEvents> {\n private transport: Transport | null = null;\n private config: ScreenConfig;\n private logger: DebugLogger;\n\n private _controllers: ControllerInfo[] = [];\n private _roomCode: RoomCode = '';\n private _isReady = false;\n private _isDestroyed = false;\n private _initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n private eventHandlers = new Map<string, Set<ScreenEventHandler<unknown>>>();\n private registeredTransportHandlers: Array<{ event: string; handler: TransportEventHandler }> = [];\n private boundMessageHandler: ((e: MessageEvent) => void) | null = null;\n // Maps user-facing handler -> transport wrappedHandler for proper cleanup in on()/off()\n private handlerToTransport = new Map<Function, { event: string; transportHandler: TransportEventHandler }>();\n\n // Pending handlers registered via on() before transport is ready\n private _pendingHandlers: Array<{ event: string; handler: ScreenEventHandler<unknown> }> = [];\n\n // Unified lifecycle listener map (supports both onXxx() and on('$xxx') patterns)\n private _lifecycleListeners = new Map<string, Set<Function>>();\n\n // Outbound message buffer (messages sent before ready)\n private _outboundBuffer: Array<{ method: string; args: unknown[] }> = [];\n\n // Whether all-ready has fired\n private _allReadyFired = false;\n\n // Self-connection awareness\n private _isConnected = false;\n\n // Protocol versioning\n private _protocolVersion: number = PROTOCOL_VERSION;\n\n // Ready promise\n private _readyResolve!: () => void;\n private _readyReject!: (err: Error) => void;\n readonly ready: Promise<void>;\n\n constructor(config: ScreenConfig = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreScreen]');\n\n // Create ready promise\n this.ready = new Promise<void>((resolve, reject) => {\n this._readyResolve = resolve;\n this._readyReject = reject;\n });\n\n // Start initialization immediately\n this.startInitialization();\n }\n\n // ---------------------------------------------------------------------------\n // Initialization (called in constructor)\n // ---------------------------------------------------------------------------\n\n private startInitialization(): void {\n this.logger.lifecycle('Initializing screen...');\n\n const parentOrigin = this.config.parentOrigin ?? '*';\n const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;\n\n this._initTimeoutId = setTimeout(() => {\n this.cleanup();\n const error = new SmoreSDKError(\n 'TIMEOUT',\n `Screen initialization timed out after ${timeout}ms. ` +\n `Make sure the parent frame sends _bridge:init. ` +\n `Check that the iframe has correct sandbox attributes (allow-scripts required) and same-origin/cross-origin settings. ` +\n `Create a new Screen instance to retry (this instance has been cleaned up).`,\n { details: { timeout } }\n );\n this.handleError(error);\n this._readyReject(error);\n }, timeout);\n\n this.boundMessageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n\n const msg = e.data;\n if (!isBridgeMessage(msg)) return;\n\n if (msg.type === '_bridge:init') {\n clearTimeout(this._initTimeoutId!);\n const initPayload = (msg as BridgeInitMessage).payload;\n\n // MIN-A1-1: Runtime validation of _bridge:init payload structure\n try {\n validateInitPayload(initPayload);\n } catch (err) {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Invalid _bridge:init payload: ${err instanceof Error ? err.message : String(err)}`,\n { details: { payload: initPayload } }\n );\n this.logger.warn('_bridge:init validation failed', error);\n this.handleError(error);\n this._readyReject(error);\n return;\n }\n\n const initData = initPayload;\n\n if (initData.side !== 'host') {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Received init for wrong side: ${initData.side}. Expected \"host\".`,\n { details: { side: initData.side } }\n );\n this.handleError(error);\n this._readyReject(error);\n return;\n }\n\n // Initialize transport\n this.transport = this.config.transport ?? new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n\n // Protocol version negotiation\n const serverProtocolVersion = initData.protocolVersion;\n if (serverProtocolVersion !== undefined) {\n this._protocolVersion = serverProtocolVersion;\n if (serverProtocolVersion !== PROTOCOL_VERSION) {\n this.logger.warn(\n `Protocol version mismatch: SDK v${PROTOCOL_VERSION}, server v${serverProtocolVersion}. ` +\n `Some features may not work correctly.`\n );\n }\n }\n\n this._controllers = this.mapControllersFromInit(initData.players);\n\n // MIN-14: Warn if initialized with zero controllers\n if (this._controllers.length === 0) {\n this.logger.warn('Screen initialized with zero controllers');\n }\n\n this.setupEventHandlers();\n\n // Register all pending user event handlers\n for (const { event, handler } of this._pendingHandlers) {\n this.setupUserEventHandler(event, handler);\n }\n this._pendingHandlers = [];\n\n this._isConnected = true;\n this._isReady = true;\n\n // Flush buffered outbound messages\n for (const buffered of this._outboundBuffer) {\n try {\n switch (buffered.method) {\n case 'broadcast':\n this.broadcast(buffered.args[0] as any, buffered.args[1] as any);\n break;\n case 'sendToController':\n this.sendToController(buffered.args[0] as any, buffered.args[1] as any, buffered.args[2] as any);\n break;\n }\n } catch (err) {\n this.handleError(err instanceof SmoreSDKError ? err : new SmoreSDKError('UNKNOWN', 'Failed to flush buffered message'));\n }\n }\n this._outboundBuffer = [];\n\n this.logger.lifecycle('Screen ready', {\n roomCode: this._roomCode,\n controllers: this._controllers.length,\n });\n\n // Auto-signal ready unless explicitly disabled via config\n if (this.config.autoReady !== false) {\n this.logger.lifecycle('Auto-signaling ready (autoReady enabled)');\n this.signalReady();\n }\n\n this._readyResolve();\n } else if (msg.type === '_bridge:update') {\n if (!this._isReady) {\n this.logger.debug('Ignoring _bridge:update before init completes');\n return;\n }\n const updateData = (msg as BridgeUpdateMessage).payload;\n\n if (updateData.players && Array.isArray(updateData.players)) {\n const oldControllers = this._controllers;\n const newControllers = this.mapControllersFromInit(updateData.players);\n this._controllers = newControllers;\n\n // NOTE: _bridge:update is only delivered during initialization or when GameOverlay\n // re-renders (rare). Mid-game player updates are handled by transport events\n // (smore:player-joined, smore:player-left, etc.) which bypass _bridge:update entirely.\n // The join/leave detection below is a defensive fallback, not the primary update path.\n\n // Detect joins\n for (const nc of newControllers) {\n if (!oldControllers.some(oc => oc.playerIndex === nc.playerIndex)) {\n this.logger.lifecycle('Controller joined (via update)', { playerIndex: nc.playerIndex });\n this._emitLifecycle('$controller-join', nc.playerIndex, nc);\n }\n }\n\n // Detect leaves\n for (const oc of oldControllers) {\n if (!newControllers.some(nc => nc.playerIndex === oc.playerIndex)) {\n this.logger.lifecycle('Controller left (via update)', { playerIndex: oc.playerIndex });\n this._emitLifecycle('$controller-leave', oc.playerIndex);\n }\n }\n }\n this.logger.lifecycle('Room updated', {\n controllers: this._controllers.length,\n });\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n\n // Signal ready to parent\n window.parent.postMessage({ type: '_bridge:ready', protocolVersion: PROTOCOL_VERSION }, parentOrigin);\n this.logger.lifecycle('Sent _bridge:ready to parent');\n }\n\n private mapControllersFromInit(players: unknown[]): ControllerInfo[] {\n return (players as Record<string, unknown>[]).map((p, index) => mapPlayerDTO(p, index));\n }\n\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave/reconnect\n // These smore:* events are forwarded by IframeGameBridge's GAME_FACING_EVENTS allowlist.\n // Each handler updates _controllers to stay consistent and prevent duplicate\n // callbacks if _bridge:update also fires with the same data.\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_JOINED, (data: unknown) => {\n const payload = data as { player?: Record<string, unknown> };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const controllerInfo = mapPlayerDTO(playerData, playerData.playerIndex as number);\n if (this._controllers.some(c => c.playerIndex === controllerInfo.playerIndex)) return;\n this._controllers = [...this._controllers, controllerInfo];\n this.logger.lifecycle('Controller joined', { playerIndex: controllerInfo.playerIndex });\n this._emitLifecycle('$controller-join', controllerInfo.playerIndex, controllerInfo);\n }\n });\n\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_LEFT, (data: unknown) => {\n const payload = data as { player?: { playerIndex?: number }; playerIndex?: number };\n const playerIndex = payload?.player?.playerIndex ?? payload?.playerIndex;\n if (typeof playerIndex === 'number') {\n if (!this._controllers.some(c => c.playerIndex === playerIndex)) return;\n this._controllers = this._controllers.filter(c => c.playerIndex !== playerIndex);\n this.logger.lifecycle('Controller left', { playerIndex });\n this._emitLifecycle('$controller-leave', playerIndex);\n }\n });\n\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_DISCONNECTED, (data: unknown) => {\n const payload = data as { player?: { playerIndex?: number }; playerIndex?: number };\n const playerIndex = payload?.player?.playerIndex ?? payload?.playerIndex;\n if (typeof playerIndex === 'number') {\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n this.logger.lifecycle('Controller disconnected', { playerIndex });\n this._emitLifecycle('$controller-disconnect', playerIndex);\n }\n });\n\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_RECONNECTED, (data: unknown) => {\n const payload = data as { player?: Record<string, unknown> };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const controllerInfo = mapPlayerDTO(playerData, playerData.playerIndex as number);\n this._controllers = this._controllers.map(c =>\n c.playerIndex === controllerInfo.playerIndex ? controllerInfo : c\n );\n this.logger.lifecycle('Controller reconnected', { playerIndex: controllerInfo.playerIndex });\n this._emitLifecycle('$controller-reconnect', controllerInfo.playerIndex, controllerInfo);\n }\n });\n\n // Character updated: update appearance in _controllers\n // Server emits { player: player.toDTO(), room: room.toDTO() }\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_CHARACTER_UPDATED, (data: unknown) => {\n const payload = data as { player?: { playerIndex?: number; character?: Record<string, unknown> | null; name?: string; nickname?: string } };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const pi = playerData.playerIndex;\n const appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === pi ? { ...c, appearance } : c\n );\n this.logger.lifecycle('Player character updated', { playerIndex: pi });\n this._emitLifecycle('$character-updated', pi, appearance ?? null);\n }\n });\n\n // Rate limited: route through error handling\n this.registerTransportHandler(SMORE_EVENTS.RATE_LIMITED, (data: unknown) => {\n const payload = data as { event?: string };\n const eventName = payload?.event ?? 'unknown';\n this.handleError(\n new SmoreSDKError('RATE_LIMITED', `Server rate-limited event: ${eventName}`, {\n details: { event: eventName },\n })\n );\n });\n\n // All ready: all participants have signaled ready\n this.registerTransportHandler(SMORE_EVENTS.ALL_READY, () => {\n this.logger.lifecycle('All participants ready');\n this._allReadyFired = true;\n this._emitLifecycle('$all-ready');\n });\n\n // Self connection status\n this.registerTransportHandler(SMORE_EVENTS.SELF_DISCONNECTED, () => {\n this._isConnected = false;\n this.logger.lifecycle('Connection lost');\n this._emitLifecycle('$connection-change', false);\n });\n\n this.registerTransportHandler(SMORE_EVENTS.SELF_RECONNECTED, () => {\n this._isConnected = true;\n this.logger.lifecycle('Connection restored');\n this._emitLifecycle('$connection-change', true);\n });\n }\n\n /**\n * Sets up a user event handler with playerIndex extraction.\n *\n * Events received from controllers are dropped if they lack a playerIndex field.\n * This is a security measure to prevent controller impersonation - the relay server\n * automatically attaches playerIndex based on the sender's authenticated session,\n * ensuring controllers cannot forge events as other players.\n *\n * Note: `playerIndex` is a reserved field name in event payloads.\n * It is automatically extracted by the SDK and passed as the first argument\n * to Screen event handlers. Game developers must NOT use `playerIndex` as\n * a custom data field name -- it will be stripped from the data object.\n */\n private setupUserEventHandler(event: string, handler: ScreenEventHandler<unknown>): void {\n const wrappedHandler = (data: unknown) => {\n this.logger.receive(event, data);\n const payload = data as { playerIndex?: number };\n // `playerIndex` is a reserved field injected by the relay server.\n // It is extracted here and passed as the first argument to the handler.\n const { playerIndex, ...rest } = payload as { playerIndex?: number; [key: string]: unknown };\n if (typeof playerIndex === 'number') {\n try {\n handler(playerIndex, rest);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event, playerIndex },\n })\n );\n }\n } else {\n // Drop events without playerIndex to prevent impersonation.\n // The relay server attaches playerIndex automatically based on sender's session.\n this.logger.debug(`Dropping event \"${event}\" without playerIndex`, data);\n }\n };\n\n this.registerTransportHandler(event, wrappedHandler);\n\n // Also store in eventHandlers for on/off management\n let handlers = this.eventHandlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this.eventHandlers.set(event, handlers);\n }\n handlers.add(handler);\n\n // Map user handler to transport handler for cleanup\n this.handlerToTransport.set(handler as Function, { event, transportHandler: wrappedHandler });\n }\n\n private registerTransportHandler(event: string, handler: TransportEventHandler): void {\n if (!this.transport) return;\n this.transport.on(event, handler);\n this.registeredTransportHandlers.push({ event, handler });\n }\n\n // ---------------------------------------------------------------------------\n // Properties (readonly)\n // ---------------------------------------------------------------------------\n\n /**\n * Returns a new shallow copy of the controllers array on every access.\n * Cache the result if accessing repeatedly in the same frame/tick.\n */\n get controllers(): readonly ControllerInfo[] {\n return [...this._controllers];\n }\n\n get roomCode(): RoomCode {\n return this._roomCode;\n }\n\n get isReady(): boolean {\n return this._isReady;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n get protocolVersion(): number {\n return this._protocolVersion;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle Listener Helpers\n // ---------------------------------------------------------------------------\n\n private _addLifecycleListener(event: string, listener: Function): () => void {\n let set = this._lifecycleListeners.get(event);\n if (!set) {\n set = new Set();\n this._lifecycleListeners.set(event, set);\n }\n set.add(listener);\n return () => {\n set!.delete(listener);\n if (set!.size === 0) this._lifecycleListeners.delete(event);\n };\n }\n\n private _emitLifecycle(event: string, ...args: unknown[]): void {\n this._lifecycleListeners.get(event)?.forEach(cb => {\n try {\n (cb as Function)(...args);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in lifecycle handler for \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\n }\n });\n }\n\n private _hasLifecycleListeners(event: string): boolean {\n const set = this._lifecycleListeners.get(event);\n return set !== undefined && set.size > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle Methods\n // ---------------------------------------------------------------------------\n\n onAllReady(callback: () => void): () => void {\n if (this._allReadyFired) {\n callback();\n }\n return this._addLifecycleListener('$all-ready', callback);\n }\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-join', callback);\n }\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-leave', callback);\n }\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-disconnect', callback);\n }\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-reconnect', callback);\n }\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n return this._addLifecycleListener('$character-updated', callback);\n }\n\n onError(callback: (error: SmoreError) => void): () => void {\n return this._addLifecycleListener('$error', callback);\n }\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n return this._addLifecycleListener('$connection-change', callback);\n }\n\n // ---------------------------------------------------------------------------\n // Communication Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Send type-safe events to all controllers.\n *\n * Uses EventMap generic for compile-time type checking of event names and data payloads.\n *\n * @note Data should be an object. Primitive values will be wrapped as `{ data: value }` by the relay server.\n * @note Maximum payload size is 64KB. Data exceeding this limit will be silently dropped by the server.\n * @note Fire-and-forget sends (no callback) will silently fail if rate-limited.\n * Use the onError callback or smore:rate-limited event to detect rate limiting.\n *\n * Warning: Avoid sending primitive values directly (string, number, boolean).\n * Wrap in an object: broadcast('event', { value: 42 }) instead of broadcast('event', 42)\n */\n broadcast<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call broadcast() after destroy()');\n }\n if (!this._isReady || !this.transport) {\n this._outboundBuffer.push({ method: 'broadcast', args: [event, data] });\n this.logger.debug(`Buffered broadcast \"${event}\" (screen not ready yet)`);\n return;\n }\n validateEventName(event);\n validatePayloadSize(data);\n this.logger.send(event, data);\n this.transport!.emit(event, data);\n }\n\n /**\n * Send an event to a specific controller.\n *\n * **Reserved field:** `targetPlayerIndex` is automatically merged into the data payload\n * to route the event to the specified controller. Game developers should avoid using\n * `targetPlayerIndex` as a custom data field name to prevent conflicts.\n *\n * @note Data should be an object. Primitive values will be wrapped as `{ data: value }` by the relay server.\n * @note Maximum payload size is 64KB. Data exceeding this limit will be silently dropped by the server.\n *\n * @param playerIndex - Target controller's player index\n * @param event - Event name\n * @param data - Event data payload\n */\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>\n ): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call sendToController() after destroy()');\n }\n if (!this._isReady || !this.transport) {\n this._outboundBuffer.push({ method: 'sendToController', args: [playerIndex, event, data] });\n this.logger.debug(`Buffered sendToController \"${event}\" -> Player ${playerIndex} (screen not ready yet)`);\n return;\n }\n validateEventName(event);\n validatePlayerIndex(playerIndex, this._controllers);\n validatePayloadSize(data);\n\n // MIN-A2-2: Warn if targetPlayerIndex already exists in data (reserved field)\n if (data && typeof data === 'object' && 'targetPlayerIndex' in data) {\n this.logger.warn(\n `Event \"${event}\" data contains reserved field \"targetPlayerIndex\" which will be overwritten for routing.`\n );\n }\n\n this.logger.send(`${event} -> Player ${playerIndex}`, data);\n this.transport!.emit(event, {\n targetPlayerIndex: playerIndex,\n ...(data && typeof data === 'object' ? data : { data }),\n });\n }\n\n // ---------------------------------------------------------------------------\n // Game Lifecycle\n // ---------------------------------------------------------------------------\n\n gameOver(results?: GameResults): void {\n this.ensureReady('gameOver');\n this.logger.lifecycle('Game over', results);\n this.transport!.emit(SMORE_EVENTS.GAME_OVER, { results });\n }\n\n signalReady(): void {\n this.ensureReady('signalReady');\n this.logger.lifecycle('Signaling ready');\n this.transport!.emit(SMORE_EVENTS.GAME_READY, {});\n }\n\n // ---------------------------------------------------------------------------\n // Event Subscription\n // ---------------------------------------------------------------------------\n\n /**\n * Register an event handler for messages from controllers.\n *\n * Can be called before the Screen is ready. Handlers registered before ready\n * are queued and activated when the transport becomes available.\n */\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\n // Route lifecycle events ($-prefixed)\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = SCREEN_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\". Valid lifecycle events: ${Array.from(validEvents).join(', ')}`);\n }\n // Special handling for $all-ready: fire immediately if already happened\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n }\n return this._addLifecycleListener(event as string, handler as Function);\n }\n\n validateEventName(event);\n\n let handlers = this.eventHandlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this.eventHandlers.set(event, handlers);\n }\n handlers.add(handler as ScreenEventHandler<unknown>);\n\n // If transport ready, register immediately\n if (this.transport) {\n const wrappedHandler = (data: unknown) => {\n this.logger.receive(event, data);\n const payload = data as { playerIndex?: number };\n const { playerIndex, ...rest } = payload as { playerIndex?: number; [key: string]: unknown };\n if (typeof playerIndex === 'number') {\n try {\n handler(playerIndex, rest as EventData<TEvents, K>);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n } else {\n this.logger.debug(`Dropping event \"${event}\" without playerIndex`, data);\n }\n };\n this.registerTransportHandler(event, wrappedHandler);\n this.handlerToTransport.set(handler as Function, { event, transportHandler: wrappedHandler });\n } else {\n // Store for later registration when transport becomes available\n this._pendingHandlers.push({ event: event as string, handler: handler as ScreenEventHandler<unknown> });\n }\n\n // Return unsubscribe function\n return () => {\n handlers?.delete(handler as ScreenEventHandler<unknown>);\n if (handlers?.size === 0) {\n this.eventHandlers.delete(event);\n }\n\n // Remove from pending if not yet registered\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n\n // Remove from transport if registered\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== entry.transportHandler\n );\n this.handlerToTransport.delete(handler as Function);\n }\n };\n }\n\n /**\n * Register an event handler that will be called only once.\n *\n * The handler is automatically removed after the first invocation.\n *\n * **Important:** The wrapped handler cannot be removed via `off(event, originalHandler)`.\n * Use the returned unsubscribe function instead.\n *\n * @param event - Event name to listen for\n * @param handler - Handler function to call once\n * @returns Unsubscribe function to remove the handler before it fires\n */\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = SCREEN_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\"`);\n }\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n return () => {};\n }\n const wrapper = (...args: unknown[]) => {\n unsub();\n (handler as Function)(...args);\n };\n const unsub = this._addLifecycleListener(event as string, wrapper);\n return unsub;\n }\n\n const wrappedHandler: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n unsubscribe();\n handler(playerIndex, data);\n };\n const unsubscribe = this.on(event, wrappedHandler);\n return unsubscribe;\n }\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>\n ): void {\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n if (!handler) {\n this._lifecycleListeners.delete(event as string);\n } else {\n this._lifecycleListeners.get(event as string)?.delete(handler as Function);\n }\n return;\n }\n\n if (!handler) {\n // Remove all handlers for this event\n this.eventHandlers.delete(event);\n this.transport?.off(event);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(h => h.event !== event);\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) this.handlerToTransport.delete(key);\n }\n // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n const handlers = this.eventHandlers.get(event);\n handlers?.delete(handler as ScreenEventHandler<unknown>);\n if (handlers?.size === 0) {\n this.eventHandlers.delete(event);\n }\n // Remove specific transport handler\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== entry.transportHandler\n );\n this.handlerToTransport.delete(handler as Function);\n }\n // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n }\n }\n\n removeAllListeners(event?: string): void {\n if (event) {\n // Remove all handlers for specific event\n this.eventHandlers.delete(event);\n this.transport?.off(event);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(h => h.event !== event);\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) this.handlerToTransport.delete(key);\n }\n this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n // Remove all user event handlers\n for (const evt of [...this.eventHandlers.keys()]) {\n this.removeAllListeners(evt);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Utilities\n // ---------------------------------------------------------------------------\n\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return this._controllers.find((c) => c.playerIndex === playerIndex);\n }\n\n getControllerCount(): number {\n return this._controllers.filter((c) => c.connected).length;\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n destroy(): void {\n if (this._isDestroyed) return;\n\n this.logger.lifecycle('Destroying screen...');\n this._isDestroyed = true;\n this._isReady = false;\n\n this.cleanup();\n this.logger.lifecycle('Screen destroyed');\n }\n\n private cleanup(): void {\n // Clear init timeout if still pending\n if (this._initTimeoutId) {\n clearTimeout(this._initTimeoutId);\n this._initTimeoutId = null;\n }\n\n // Remove all registered transport handlers\n for (const { event, handler } of this.registeredTransportHandlers) {\n this.transport?.off(event, handler);\n }\n this.registeredTransportHandlers = [];\n\n // Clear event handlers\n this.eventHandlers.clear();\n this.handlerToTransport.clear();\n this._pendingHandlers = [];\n\n // Clear lifecycle callbacks\n this._lifecycleListeners.clear();\n this._isConnected = false;\n this._outboundBuffer = [];\n\n // Destroy transport\n if (this.transport instanceof PostMessageTransport) {\n this.transport.destroy();\n }\n this.transport = null;\n\n // Remove message listener\n if (this.boundMessageHandler) {\n window.removeEventListener('message', this.boundMessageHandler);\n this.boundMessageHandler = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Error Handling\n // ---------------------------------------------------------------------------\n\n private handleError(error: SmoreSDKError): void {\n // Always log at warn level so errors are never completely silent\n this.logger.warn(`Error in handler: ${error.message}`);\n const smoreError = error.toSmoreError();\n if (this._hasLifecycleListeners('$error')) {\n this._emitLifecycle('$error', smoreError);\n } else {\n this.logger.error(error.message, error.details);\n }\n }\n\n private ensureReady(method: string): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError(\n 'DESTROYED',\n `Cannot call ${method}() after destroy()`,\n { details: { method } }\n );\n }\n if (!this._isReady || !this.transport) {\n throw new SmoreSDKError(\n 'NOT_READY',\n `Cannot call ${method}() before screen is ready. Use await screen.ready.`,\n { details: { method } }\n );\n }\n }\n}\n\n// =============================================================================\n// FACTORY FUNCTION\n// =============================================================================\n\n/**\n * Create a Screen instance for the host/TV side of your game.\n *\n * Returns a Screen instance synchronously. The screen begins listening\n * for the bridge init message immediately. Register event handlers and\n * lifecycle callbacks on the instance, then await `.ready` if needed.\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Screen configuration (debug, parentOrigin, timeout)\n * @returns Screen instance\n *\n * @example\n * ```ts\n * const screen = createScreen<MyEvents>({ debug: true });\n *\n * screen.on('tap', (playerIndex, data) => {\n * screen.broadcast('round-result', { winner: playerIndex });\n * });\n *\n * screen.onAllReady(() => startGame());\n * screen.onControllerJoin((playerIndex, info) => addPlayer(playerIndex, info));\n *\n * await screen.ready;\n * ```\n */\nexport function createScreen<TEvents extends EventMap = EventMap>(\n config?: ScreenConfig\n): Screen<TEvents> {\n return new ScreenImpl<TEvents>(config);\n}\n"],"names":["SmoreSDKError","PROTOCOL_VERSION","DebugLogger","isBridgeMessage","validateInitPayload","PostMessageTransport","mapPlayerDTO","SMORE_EVENTS","validateEventName","validatePayloadSize","SCREEN_LIFECYCLE_EVENTS"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"screen.cjs","sources":["../../src/screen.ts"],"sourcesContent":["/**\n * createScreen - Factory function for Screen instances (Host/TV side)\n *\n * Synchronous factory with event emitter pattern. Returns a Screen instance\n * immediately. Use `.ready` to await full initialization, and `.on()` /\n * lifecycle methods to register handlers (can be called before ready).\n *\n * @example\n * ```ts\n * interface MyEvents {\n * tap: { x: number; y: number };\n * 'phase-update': { phase: 'lobby' | 'playing' | 'results' };\n * }\n *\n * const screen = createScreen<MyEvents>({ debug: true });\n *\n * screen.on('tap', (playerIndex, data) => {\n * console.log(`Player ${playerIndex} tapped at`, data.x, data.y);\n * });\n *\n * screen.onAllReady(() => startGame());\n * screen.onControllerJoin((playerIndex, info) => addPlayer(playerIndex, info));\n *\n * await screen.ready;\n * screen.broadcast('phase-update', { phase: 'playing' });\n * ```\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n Screen,\n ScreenConfig,\n ScreenEventHandler,\n ControllerInfo,\n PlayerIndex,\n RoomCode,\n GameResults,\n SmoreError,\n CharacterAppearance,\n} from './types';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport { isBridgeMessage, validateInitPayload, PROTOCOL_VERSION, type BridgeInitMessage, type BridgeUpdateMessage } from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName, SCREEN_LIFECYCLE_EVENTS } from './events';\nimport { DebugLogger } from './logger';\nimport { mapPlayerDTO, validatePayloadSize } from './shared';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\nfunction validatePlayerIndex(playerIndex: PlayerIndex, controllers: readonly ControllerInfo[]): void {\n if (typeof playerIndex !== 'number' || !Number.isInteger(playerIndex)) {\n throw new SmoreSDKError('INVALID_PLAYER', 'Player index must be an integer');\n }\n if (!controllers.some(c => c.playerIndex === playerIndex)) {\n throw new SmoreSDKError(\n 'INVALID_PLAYER',\n `No controller found with player index ${playerIndex}`,\n { details: { playerIndex } }\n );\n }\n}\n\n// =============================================================================\n// SCREEN IMPLEMENTATION\n// =============================================================================\n\nclass ScreenImpl<TEvents extends EventMap> implements Screen<TEvents> {\n private transport: Transport | null = null;\n private config: ScreenConfig;\n private logger: DebugLogger;\n\n private _controllers: ControllerInfo[] = [];\n private _roomCode: RoomCode = '';\n private _isReady = false;\n private _isDestroyed = false;\n private _initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n private eventHandlers = new Map<string, Set<ScreenEventHandler<unknown>>>();\n private registeredTransportHandlers: Array<{ event: string; handler: TransportEventHandler }> = [];\n private boundMessageHandler: ((e: MessageEvent) => void) | null = null;\n // Maps user-facing handler -> transport wrappedHandler for proper cleanup in on()/off()\n private handlerToTransport = new Map<Function, { event: string; transportHandler: TransportEventHandler }>();\n\n // Pending handlers registered via on() before transport is ready\n private _pendingHandlers: Array<{ event: string; handler: ScreenEventHandler<unknown> }> = [];\n\n // Unified lifecycle listener map (supports both onXxx() and on('$xxx') patterns)\n private _lifecycleListeners = new Map<string, Set<Function>>();\n\n // Outbound message buffer (messages sent before ready)\n private _outboundBuffer: Array<{ method: string; args: unknown[] }> = [];\n\n // Whether all-ready has fired\n private _allReadyFired = false;\n\n // Self-connection awareness\n private _isConnected = false;\n\n // Custom state management\n private _customStates = new Map<number, Record<string, any>>();\n private _stateChangeListeners = new Set<(playerIndex: number, state: Record<string, any>) => void>();\n\n // Protocol versioning\n private _protocolVersion: number = PROTOCOL_VERSION;\n\n // Ready promise\n private _readyResolve!: () => void;\n private _readyReject!: (err: Error) => void;\n readonly ready: Promise<void>;\n\n constructor(config: ScreenConfig = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreScreen]');\n\n // Create ready promise\n this.ready = new Promise<void>((resolve, reject) => {\n this._readyResolve = resolve;\n this._readyReject = reject;\n });\n\n // Start initialization immediately\n this.startInitialization();\n }\n\n // ---------------------------------------------------------------------------\n // Initialization (called in constructor)\n // ---------------------------------------------------------------------------\n\n private startInitialization(): void {\n this.logger.lifecycle('Initializing screen...');\n\n const parentOrigin = this.config.parentOrigin ?? '*';\n const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;\n\n this._initTimeoutId = setTimeout(() => {\n this.cleanup();\n const error = new SmoreSDKError(\n 'TIMEOUT',\n `Screen initialization timed out after ${timeout}ms. ` +\n `Make sure the parent frame sends _bridge:init. ` +\n `Check that the iframe has correct sandbox attributes (allow-scripts required) and same-origin/cross-origin settings. ` +\n `Create a new Screen instance to retry (this instance has been cleaned up).`,\n { details: { timeout } }\n );\n this.handleError(error);\n this._readyReject(error);\n }, timeout);\n\n this.boundMessageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n\n const msg = e.data;\n if (!isBridgeMessage(msg)) return;\n\n if (msg.type === '_bridge:init') {\n clearTimeout(this._initTimeoutId!);\n const initPayload = (msg as BridgeInitMessage).payload;\n\n // MIN-A1-1: Runtime validation of _bridge:init payload structure\n try {\n validateInitPayload(initPayload);\n } catch (err) {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Invalid _bridge:init payload: ${err instanceof Error ? err.message : String(err)}`,\n { details: { payload: initPayload } }\n );\n this.logger.warn('_bridge:init validation failed', error);\n this.handleError(error);\n this._readyReject(error);\n return;\n }\n\n const initData = initPayload;\n\n if (initData.side !== 'host') {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Received init for wrong side: ${initData.side}. Expected \"host\".`,\n { details: { side: initData.side } }\n );\n this.handleError(error);\n this._readyReject(error);\n return;\n }\n\n // Initialize transport\n this.transport = this.config.transport ?? new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n\n // Protocol version negotiation\n const serverProtocolVersion = initData.protocolVersion;\n if (serverProtocolVersion !== undefined) {\n this._protocolVersion = serverProtocolVersion;\n if (serverProtocolVersion !== PROTOCOL_VERSION) {\n this.logger.warn(\n `Protocol version mismatch: SDK v${PROTOCOL_VERSION}, server v${serverProtocolVersion}. ` +\n `Some features may not work correctly.`\n );\n }\n }\n\n this._controllers = this.mapControllersFromInit(initData.players);\n\n // MIN-14: Warn if initialized with zero controllers\n if (this._controllers.length === 0) {\n this.logger.warn('Screen initialized with zero controllers');\n }\n\n this.setupEventHandlers();\n\n // Register all pending user event handlers\n for (const { event, handler } of this._pendingHandlers) {\n this.setupUserEventHandler(event, handler);\n }\n this._pendingHandlers = [];\n\n this._isConnected = true;\n this._isReady = true;\n\n // Flush buffered outbound messages\n for (const buffered of this._outboundBuffer) {\n try {\n switch (buffered.method) {\n case 'broadcast':\n this.broadcast(buffered.args[0] as any, buffered.args[1] as any);\n break;\n case 'sendToController':\n this.sendToController(buffered.args[0] as any, buffered.args[1] as any, buffered.args[2] as any);\n break;\n }\n } catch (err) {\n this.handleError(err instanceof SmoreSDKError ? err : new SmoreSDKError('UNKNOWN', 'Failed to flush buffered message'));\n }\n }\n this._outboundBuffer = [];\n\n this.logger.lifecycle('Screen ready', {\n roomCode: this._roomCode,\n controllers: this._controllers.length,\n });\n\n // Auto-signal ready unless explicitly disabled via config\n if (this.config.autoReady !== false) {\n this.logger.lifecycle('Auto-signaling ready (autoReady enabled)');\n this.signalReady();\n }\n\n this._readyResolve();\n } else if (msg.type === '_bridge:update') {\n if (!this._isReady) {\n this.logger.debug('Ignoring _bridge:update before init completes');\n return;\n }\n const updateData = (msg as BridgeUpdateMessage).payload;\n\n if (updateData.players && Array.isArray(updateData.players)) {\n const oldControllers = this._controllers;\n const newControllers = this.mapControllersFromInit(updateData.players);\n this._controllers = newControllers;\n\n // NOTE: _bridge:update is only delivered during initialization or when GameOverlay\n // re-renders (rare). Mid-game player updates are handled by transport events\n // (smore:player-joined, smore:player-left, etc.) which bypass _bridge:update entirely.\n // The join/leave detection below is a defensive fallback, not the primary update path.\n\n // Detect joins\n for (const nc of newControllers) {\n if (!oldControllers.some(oc => oc.playerIndex === nc.playerIndex)) {\n this.logger.lifecycle('Controller joined (via update)', { playerIndex: nc.playerIndex });\n this._emitLifecycle('$controller-join', nc.playerIndex, nc);\n }\n }\n\n // Detect leaves\n for (const oc of oldControllers) {\n if (!newControllers.some(nc => nc.playerIndex === oc.playerIndex)) {\n this.logger.lifecycle('Controller left (via update)', { playerIndex: oc.playerIndex });\n this._emitLifecycle('$controller-leave', oc.playerIndex);\n }\n }\n }\n this.logger.lifecycle('Room updated', {\n controllers: this._controllers.length,\n });\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n\n // Signal ready to parent\n window.parent.postMessage({ type: '_bridge:ready', protocolVersion: PROTOCOL_VERSION }, parentOrigin);\n this.logger.lifecycle('Sent _bridge:ready to parent');\n }\n\n private mapControllersFromInit(players: unknown[]): ControllerInfo[] {\n return (players as Record<string, unknown>[]).map((p, index) => mapPlayerDTO(p, index));\n }\n\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave/reconnect\n // These smore:* events are forwarded by IframeGameBridge's GAME_FACING_EVENTS allowlist.\n // Each handler updates _controllers to stay consistent and prevent duplicate\n // callbacks if _bridge:update also fires with the same data.\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_JOINED, (data: unknown) => {\n const payload = data as { player?: Record<string, unknown> };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const controllerInfo = mapPlayerDTO(playerData, playerData.playerIndex as number);\n if (this._controllers.some(c => c.playerIndex === controllerInfo.playerIndex)) return;\n this._controllers = [...this._controllers, controllerInfo];\n this.logger.lifecycle('Controller joined', { playerIndex: controllerInfo.playerIndex });\n this._emitLifecycle('$controller-join', controllerInfo.playerIndex, controllerInfo);\n }\n });\n\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_LEFT, (data: unknown) => {\n const payload = data as { player?: { playerIndex?: number }; playerIndex?: number };\n const playerIndex = payload?.player?.playerIndex ?? payload?.playerIndex;\n if (typeof playerIndex === 'number') {\n if (!this._controllers.some(c => c.playerIndex === playerIndex)) return;\n this._controllers = this._controllers.filter(c => c.playerIndex !== playerIndex);\n this.logger.lifecycle('Controller left', { playerIndex });\n this._emitLifecycle('$controller-leave', playerIndex);\n }\n });\n\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_DISCONNECTED, (data: unknown) => {\n const payload = data as { player?: { playerIndex?: number }; playerIndex?: number };\n const playerIndex = payload?.player?.playerIndex ?? payload?.playerIndex;\n if (typeof playerIndex === 'number') {\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n this.logger.lifecycle('Controller disconnected', { playerIndex });\n this._emitLifecycle('$controller-disconnect', playerIndex);\n }\n });\n\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_RECONNECTED, (data: unknown) => {\n const payload = data as { player?: Record<string, unknown> };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const controllerInfo = mapPlayerDTO(playerData, playerData.playerIndex as number);\n this._controllers = this._controllers.map(c =>\n c.playerIndex === controllerInfo.playerIndex ? controllerInfo : c\n );\n this.logger.lifecycle('Controller reconnected', { playerIndex: controllerInfo.playerIndex });\n this._emitLifecycle('$controller-reconnect', controllerInfo.playerIndex, controllerInfo);\n }\n });\n\n // Character updated: update appearance in _controllers\n // Server emits { player: player.toDTO(), room: room.toDTO() }\n this.registerTransportHandler(SMORE_EVENTS.PLAYER_CHARACTER_UPDATED, (data: unknown) => {\n const payload = data as { player?: { playerIndex?: number; character?: Record<string, unknown> | null; name?: string; nickname?: string } };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const pi = playerData.playerIndex;\n const appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === pi ? { ...c, appearance } : c\n );\n this.logger.lifecycle('Player character updated', { playerIndex: pi });\n this._emitLifecycle('$character-updated', pi, appearance ?? null);\n }\n });\n\n // Rate limited: route through error handling\n this.registerTransportHandler(SMORE_EVENTS.RATE_LIMITED, (data: unknown) => {\n const payload = data as { event?: string };\n const eventName = payload?.event ?? 'unknown';\n this.handleError(\n new SmoreSDKError('RATE_LIMITED', `Server rate-limited event: ${eventName}`, {\n details: { event: eventName },\n })\n );\n });\n\n // All ready: all participants have signaled ready\n this.registerTransportHandler(SMORE_EVENTS.ALL_READY, () => {\n this.logger.lifecycle('All participants ready');\n this._allReadyFired = true;\n this._emitLifecycle('$all-ready');\n });\n\n // Self connection status\n this.registerTransportHandler(SMORE_EVENTS.SELF_DISCONNECTED, () => {\n this._isConnected = false;\n this.logger.lifecycle('Connection lost');\n this._emitLifecycle('$connection-change', false);\n });\n\n this.registerTransportHandler(SMORE_EVENTS.SELF_RECONNECTED, () => {\n this._isConnected = true;\n this.logger.lifecycle('Connection restored');\n this._emitLifecycle('$connection-change', true);\n });\n\n // Custom state changed: a controller's custom state was updated\n this.registerTransportHandler(SMORE_EVENTS.STATE_CHANGED, (raw: unknown) => {\n const data = raw as { playerIndex?: number; state?: Record<string, any> };\n if (typeof data?.playerIndex === 'number' && data.state) {\n this._customStates.set(data.playerIndex, data.state);\n this._stateChangeListeners.forEach(cb => {\n try {\n cb(data.playerIndex!, data.state!);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', 'Error in custom state change listener', {\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n });\n }\n });\n\n // All custom states: bulk state update\n this.registerTransportHandler(SMORE_EVENTS.STATE_ALL, (raw: unknown) => {\n const data = raw as { states?: Record<number, Record<string, any>> };\n if (data?.states) {\n for (const [key, value] of Object.entries(data.states)) {\n const pi = Number(key);\n this._customStates.set(pi, value);\n this._stateChangeListeners.forEach(cb => {\n try {\n cb(pi, value);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', 'Error in custom state change listener', {\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n });\n }\n }\n });\n }\n\n /**\n * Sets up a user event handler with playerIndex extraction.\n *\n * Events received from controllers are dropped if they lack a playerIndex field.\n * This is a security measure to prevent controller impersonation - the relay server\n * automatically attaches playerIndex based on the sender's authenticated session,\n * ensuring controllers cannot forge events as other players.\n *\n * Note: `playerIndex` is a reserved field name in event payloads.\n * It is automatically extracted by the SDK and passed as the first argument\n * to Screen event handlers. Game developers must NOT use `playerIndex` as\n * a custom data field name -- it will be stripped from the data object.\n */\n private setupUserEventHandler(event: string, handler: ScreenEventHandler<unknown>): void {\n const wrappedHandler = (data: unknown) => {\n this.logger.receive(event, data);\n const payload = data as { playerIndex?: number };\n // `playerIndex` is a reserved field injected by the relay server.\n // It is extracted here and passed as the first argument to the handler.\n const { playerIndex, ...rest } = payload as { playerIndex?: number; [key: string]: unknown };\n if (typeof playerIndex === 'number') {\n try {\n handler(playerIndex, rest);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event, playerIndex },\n })\n );\n }\n } else {\n // Drop events without playerIndex to prevent impersonation.\n // The relay server attaches playerIndex automatically based on sender's session.\n this.logger.debug(`Dropping event \"${event}\" without playerIndex`, data);\n }\n };\n\n this.registerTransportHandler(event, wrappedHandler);\n\n // Also store in eventHandlers for on/off management\n let handlers = this.eventHandlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this.eventHandlers.set(event, handlers);\n }\n handlers.add(handler);\n\n // Map user handler to transport handler for cleanup\n this.handlerToTransport.set(handler as Function, { event, transportHandler: wrappedHandler });\n }\n\n private registerTransportHandler(event: string, handler: TransportEventHandler): void {\n if (!this.transport) return;\n this.transport.on(event, handler);\n this.registeredTransportHandlers.push({ event, handler });\n }\n\n // ---------------------------------------------------------------------------\n // Properties (readonly)\n // ---------------------------------------------------------------------------\n\n /**\n * Returns a new shallow copy of the controllers array on every access.\n * Cache the result if accessing repeatedly in the same frame/tick.\n */\n get controllers(): readonly ControllerInfo[] {\n return [...this._controllers];\n }\n\n get roomCode(): RoomCode {\n return this._roomCode;\n }\n\n get isReady(): boolean {\n return this._isReady;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n get protocolVersion(): number {\n return this._protocolVersion;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle Listener Helpers\n // ---------------------------------------------------------------------------\n\n private _addLifecycleListener(event: string, listener: Function): () => void {\n let set = this._lifecycleListeners.get(event);\n if (!set) {\n set = new Set();\n this._lifecycleListeners.set(event, set);\n }\n set.add(listener);\n return () => {\n set!.delete(listener);\n if (set!.size === 0) this._lifecycleListeners.delete(event);\n };\n }\n\n private _emitLifecycle(event: string, ...args: unknown[]): void {\n this._lifecycleListeners.get(event)?.forEach(cb => {\n try {\n (cb as Function)(...args);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in lifecycle handler for \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\n }\n });\n }\n\n private _hasLifecycleListeners(event: string): boolean {\n const set = this._lifecycleListeners.get(event);\n return set !== undefined && set.size > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle Methods\n // ---------------------------------------------------------------------------\n\n onAllReady(callback: () => void): () => void {\n if (this._allReadyFired) {\n callback();\n }\n return this._addLifecycleListener('$all-ready', callback);\n }\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-join', callback);\n }\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-leave', callback);\n }\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-disconnect', callback);\n }\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-reconnect', callback);\n }\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n return this._addLifecycleListener('$character-updated', callback);\n }\n\n onError(callback: (error: SmoreError) => void): () => void {\n return this._addLifecycleListener('$error', callback);\n }\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n return this._addLifecycleListener('$connection-change', callback);\n }\n\n // ---------------------------------------------------------------------------\n // Custom State Methods\n // ---------------------------------------------------------------------------\n\n getControllerState(playerIndex: number): Record<string, any> | undefined {\n return this._customStates.get(playerIndex);\n }\n\n getAllControllerStates(): Record<number, Record<string, any>> {\n const result: Record<number, Record<string, any>> = {};\n for (const [key, value] of this._customStates) {\n result[key] = value;\n }\n return result;\n }\n\n onCustomStateChange(listener: (playerIndex: number, state: Record<string, any>) => void): () => void {\n this._stateChangeListeners.add(listener);\n return () => {\n this._stateChangeListeners.delete(listener);\n };\n }\n\n // ---------------------------------------------------------------------------\n // Communication Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Send type-safe events to all controllers.\n *\n * Uses EventMap generic for compile-time type checking of event names and data payloads.\n *\n * @note Data should be an object. Primitive values will be wrapped as `{ data: value }` by the relay server.\n * @note Maximum payload size is 64KB. Data exceeding this limit will be silently dropped by the server.\n * @note Fire-and-forget sends (no callback) will silently fail if rate-limited.\n * Use the onError callback or smore:rate-limited event to detect rate limiting.\n *\n * Warning: Avoid sending primitive values directly (string, number, boolean).\n * Wrap in an object: broadcast('event', { value: 42 }) instead of broadcast('event', 42)\n */\n broadcast<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call broadcast() after destroy()');\n }\n if (!this._isReady || !this.transport) {\n this._outboundBuffer.push({ method: 'broadcast', args: [event, data] });\n this.logger.debug(`Buffered broadcast \"${event}\" (screen not ready yet)`);\n return;\n }\n validateEventName(event);\n validatePayloadSize(data);\n this.logger.send(event, data);\n this.transport!.emit(event, data);\n }\n\n /**\n * Send an event to a specific controller.\n *\n * **Reserved field:** `targetPlayerIndex` is automatically merged into the data payload\n * to route the event to the specified controller. Game developers should avoid using\n * `targetPlayerIndex` as a custom data field name to prevent conflicts.\n *\n * @note Data should be an object. Primitive values will be wrapped as `{ data: value }` by the relay server.\n * @note Maximum payload size is 64KB. Data exceeding this limit will be silently dropped by the server.\n *\n * @param playerIndex - Target controller's player index\n * @param event - Event name\n * @param data - Event data payload\n */\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>\n ): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call sendToController() after destroy()');\n }\n if (!this._isReady || !this.transport) {\n this._outboundBuffer.push({ method: 'sendToController', args: [playerIndex, event, data] });\n this.logger.debug(`Buffered sendToController \"${event}\" -> Player ${playerIndex} (screen not ready yet)`);\n return;\n }\n validateEventName(event);\n validatePlayerIndex(playerIndex, this._controllers);\n validatePayloadSize(data);\n\n // MIN-A2-2: Warn if targetPlayerIndex already exists in data (reserved field)\n if (data && typeof data === 'object' && 'targetPlayerIndex' in data) {\n this.logger.warn(\n `Event \"${event}\" data contains reserved field \"targetPlayerIndex\" which will be overwritten for routing.`\n );\n }\n\n this.logger.send(`${event} -> Player ${playerIndex}`, data);\n this.transport!.emit(event, {\n targetPlayerIndex: playerIndex,\n ...(data && typeof data === 'object' ? data : { data }),\n });\n }\n\n // ---------------------------------------------------------------------------\n // Game Lifecycle\n // ---------------------------------------------------------------------------\n\n gameOver(results?: GameResults): void {\n this.ensureReady('gameOver');\n this.logger.lifecycle('Game over', results);\n this.transport!.emit(SMORE_EVENTS.GAME_OVER, { results });\n }\n\n signalReady(): void {\n this.ensureReady('signalReady');\n this.logger.lifecycle('Signaling ready');\n this.transport!.emit(SMORE_EVENTS.GAME_READY, {});\n }\n\n // ---------------------------------------------------------------------------\n // Event Subscription\n // ---------------------------------------------------------------------------\n\n /**\n * Register an event handler for messages from controllers.\n *\n * Can be called before the Screen is ready. Handlers registered before ready\n * are queued and activated when the transport becomes available.\n */\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\n // Route lifecycle events ($-prefixed)\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = SCREEN_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\". Valid lifecycle events: ${Array.from(validEvents).join(', ')}`);\n }\n // Special handling for $all-ready: fire immediately if already happened\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n }\n return this._addLifecycleListener(event as string, handler as Function);\n }\n\n validateEventName(event);\n\n let handlers = this.eventHandlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this.eventHandlers.set(event, handlers);\n }\n handlers.add(handler as ScreenEventHandler<unknown>);\n\n // If transport ready, register immediately\n if (this.transport) {\n const wrappedHandler = (data: unknown) => {\n this.logger.receive(event, data);\n const payload = data as { playerIndex?: number };\n const { playerIndex, ...rest } = payload as { playerIndex?: number; [key: string]: unknown };\n if (typeof playerIndex === 'number') {\n try {\n handler(playerIndex, rest as EventData<TEvents, K>);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n } else {\n this.logger.debug(`Dropping event \"${event}\" without playerIndex`, data);\n }\n };\n this.registerTransportHandler(event, wrappedHandler);\n this.handlerToTransport.set(handler as Function, { event, transportHandler: wrappedHandler });\n } else {\n // Store for later registration when transport becomes available\n this._pendingHandlers.push({ event: event as string, handler: handler as ScreenEventHandler<unknown> });\n }\n\n // Return unsubscribe function\n return () => {\n handlers?.delete(handler as ScreenEventHandler<unknown>);\n if (handlers?.size === 0) {\n this.eventHandlers.delete(event);\n }\n\n // Remove from pending if not yet registered\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n\n // Remove from transport if registered\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== entry.transportHandler\n );\n this.handlerToTransport.delete(handler as Function);\n }\n };\n }\n\n /**\n * Register an event handler that will be called only once.\n *\n * The handler is automatically removed after the first invocation.\n *\n * **Important:** The wrapped handler cannot be removed via `off(event, originalHandler)`.\n * Use the returned unsubscribe function instead.\n *\n * @param event - Event name to listen for\n * @param handler - Handler function to call once\n * @returns Unsubscribe function to remove the handler before it fires\n */\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = SCREEN_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\"`);\n }\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n return () => {};\n }\n const wrapper = (...args: unknown[]) => {\n unsub();\n (handler as Function)(...args);\n };\n const unsub = this._addLifecycleListener(event as string, wrapper);\n return unsub;\n }\n\n const wrappedHandler: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n unsubscribe();\n handler(playerIndex, data);\n };\n const unsubscribe = this.on(event, wrappedHandler);\n return unsubscribe;\n }\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>\n ): void {\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n if (!handler) {\n this._lifecycleListeners.delete(event as string);\n } else {\n this._lifecycleListeners.get(event as string)?.delete(handler as Function);\n }\n return;\n }\n\n if (!handler) {\n // Remove all handlers for this event\n this.eventHandlers.delete(event);\n this.transport?.off(event);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(h => h.event !== event);\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) this.handlerToTransport.delete(key);\n }\n // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n const handlers = this.eventHandlers.get(event);\n handlers?.delete(handler as ScreenEventHandler<unknown>);\n if (handlers?.size === 0) {\n this.eventHandlers.delete(event);\n }\n // Remove specific transport handler\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== entry.transportHandler\n );\n this.handlerToTransport.delete(handler as Function);\n }\n // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n }\n }\n\n removeAllListeners(event?: string): void {\n if (event) {\n // Remove all handlers for specific event\n this.eventHandlers.delete(event);\n this.transport?.off(event);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(h => h.event !== event);\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) this.handlerToTransport.delete(key);\n }\n this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n // Remove all user event handlers\n for (const evt of [...this.eventHandlers.keys()]) {\n this.removeAllListeners(evt);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Utilities\n // ---------------------------------------------------------------------------\n\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return this._controllers.find((c) => c.playerIndex === playerIndex);\n }\n\n getControllerCount(): number {\n return this._controllers.filter((c) => c.connected).length;\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n destroy(): void {\n if (this._isDestroyed) return;\n\n this.logger.lifecycle('Destroying screen...');\n this._isDestroyed = true;\n this._isReady = false;\n\n this.cleanup();\n this.logger.lifecycle('Screen destroyed');\n }\n\n private cleanup(): void {\n // Clear init timeout if still pending\n if (this._initTimeoutId) {\n clearTimeout(this._initTimeoutId);\n this._initTimeoutId = null;\n }\n\n // Remove all registered transport handlers\n for (const { event, handler } of this.registeredTransportHandlers) {\n this.transport?.off(event, handler);\n }\n this.registeredTransportHandlers = [];\n\n // Clear event handlers\n this.eventHandlers.clear();\n this.handlerToTransport.clear();\n this._pendingHandlers = [];\n\n // Clear lifecycle callbacks\n this._lifecycleListeners.clear();\n this._customStates.clear();\n this._stateChangeListeners.clear();\n this._isConnected = false;\n this._outboundBuffer = [];\n\n // Destroy transport\n if (this.transport instanceof PostMessageTransport) {\n this.transport.destroy();\n }\n this.transport = null;\n\n // Remove message listener\n if (this.boundMessageHandler) {\n window.removeEventListener('message', this.boundMessageHandler);\n this.boundMessageHandler = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Error Handling\n // ---------------------------------------------------------------------------\n\n private handleError(error: SmoreSDKError): void {\n // Always log at warn level so errors are never completely silent\n this.logger.warn(`Error in handler: ${error.message}`);\n const smoreError = error.toSmoreError();\n if (this._hasLifecycleListeners('$error')) {\n this._emitLifecycle('$error', smoreError);\n } else {\n this.logger.error(error.message, error.details);\n }\n }\n\n private ensureReady(method: string): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError(\n 'DESTROYED',\n `Cannot call ${method}() after destroy()`,\n { details: { method } }\n );\n }\n if (!this._isReady || !this.transport) {\n throw new SmoreSDKError(\n 'NOT_READY',\n `Cannot call ${method}() before screen is ready. Use await screen.ready.`,\n { details: { method } }\n );\n }\n }\n}\n\n// =============================================================================\n// FACTORY FUNCTION\n// =============================================================================\n\n/**\n * Create a Screen instance for the host/TV side of your game.\n *\n * Returns a Screen instance synchronously. The screen begins listening\n * for the bridge init message immediately. Register event handlers and\n * lifecycle callbacks on the instance, then await `.ready` if needed.\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Screen configuration (debug, parentOrigin, timeout)\n * @returns Screen instance\n *\n * @example\n * ```ts\n * const screen = createScreen<MyEvents>({ debug: true });\n *\n * screen.on('tap', (playerIndex, data) => {\n * screen.broadcast('round-result', { winner: playerIndex });\n * });\n *\n * screen.onAllReady(() => startGame());\n * screen.onControllerJoin((playerIndex, info) => addPlayer(playerIndex, info));\n *\n * await screen.ready;\n * ```\n */\nexport function createScreen<TEvents extends EventMap = EventMap>(\n config?: ScreenConfig\n): Screen<TEvents> {\n return new ScreenImpl<TEvents>(config);\n}\n"],"names":["SmoreSDKError","PROTOCOL_VERSION","DebugLogger","isBridgeMessage","validateInitPayload","PostMessageTransport","mapPlayerDTO","SMORE_EVENTS","validateEventName","validatePayloadSize","SCREEN_LIFECYCLE_EVENTS"],"mappings":"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"}
|
package/dist/cjs/testing.cjs
CHANGED
|
@@ -27,6 +27,8 @@ function createMockScreen(options = {}) {
|
|
|
27
27
|
const _readyPromise = new Promise((resolve) => {
|
|
28
28
|
_readyResolve = resolve;
|
|
29
29
|
});
|
|
30
|
+
const _customStates = /* @__PURE__ */ new Map();
|
|
31
|
+
const _stateChangeListeners = /* @__PURE__ */ new Set();
|
|
30
32
|
const broadcasts = [];
|
|
31
33
|
const sends = [];
|
|
32
34
|
const screen = {
|
|
@@ -128,6 +130,29 @@ function createMockScreen(options = {}) {
|
|
|
128
130
|
}
|
|
129
131
|
sends.push({ playerIndex, event, data });
|
|
130
132
|
},
|
|
133
|
+
// === Custom State Methods ===
|
|
134
|
+
getControllerState(playerIndex) {
|
|
135
|
+
return _customStates.get(playerIndex);
|
|
136
|
+
},
|
|
137
|
+
getAllControllerStates() {
|
|
138
|
+
const result = {};
|
|
139
|
+
_customStates.forEach((state, playerIndex) => {
|
|
140
|
+
result[playerIndex] = state;
|
|
141
|
+
});
|
|
142
|
+
return result;
|
|
143
|
+
},
|
|
144
|
+
onCustomStateChange(listener) {
|
|
145
|
+
_stateChangeListeners.add(listener);
|
|
146
|
+
return () => {
|
|
147
|
+
_stateChangeListeners.delete(listener);
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
simulateStateChange(playerIndex, state) {
|
|
151
|
+
const existing = _customStates.get(playerIndex) ?? {};
|
|
152
|
+
const next = { ...existing, ...state };
|
|
153
|
+
_customStates.set(playerIndex, next);
|
|
154
|
+
_stateChangeListeners.forEach((cb) => cb(playerIndex, next));
|
|
155
|
+
},
|
|
131
156
|
// === Game Lifecycle ===
|
|
132
157
|
gameOver(results) {
|
|
133
158
|
if (_isDestroyed) {
|
|
@@ -165,6 +190,8 @@ function createMockScreen(options = {}) {
|
|
|
165
190
|
return screen.onAllReady(handler);
|
|
166
191
|
case "$error":
|
|
167
192
|
return screen.onError(handler);
|
|
193
|
+
case "$connection-change":
|
|
194
|
+
return screen.onConnectionChange(handler);
|
|
168
195
|
}
|
|
169
196
|
}
|
|
170
197
|
events.validateEventName(event);
|
|
@@ -330,6 +357,8 @@ function createMockController(options = {}) {
|
|
|
330
357
|
const _readyPromise = new Promise((resolve) => {
|
|
331
358
|
_readyResolve = resolve;
|
|
332
359
|
});
|
|
360
|
+
const _customStates = /* @__PURE__ */ new Map();
|
|
361
|
+
const _stateChangeListeners = /* @__PURE__ */ new Set();
|
|
333
362
|
const sentEvents = [];
|
|
334
363
|
const controller = {
|
|
335
364
|
// === Properties ===
|
|
@@ -421,6 +450,24 @@ function createMockController(options = {}) {
|
|
|
421
450
|
getController(playerIndex) {
|
|
422
451
|
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
423
452
|
},
|
|
453
|
+
get me() {
|
|
454
|
+
return _controllers.find((c) => c.playerIndex === myPlayerIndex);
|
|
455
|
+
},
|
|
456
|
+
setState(state) {
|
|
457
|
+
const existing = _customStates.get(myPlayerIndex) ?? {};
|
|
458
|
+
const next = { ...existing, ...state };
|
|
459
|
+
_customStates.set(myPlayerIndex, next);
|
|
460
|
+
_stateChangeListeners.forEach((cb) => cb(myPlayerIndex, next));
|
|
461
|
+
},
|
|
462
|
+
getMyState() {
|
|
463
|
+
return _customStates.get(myPlayerIndex);
|
|
464
|
+
},
|
|
465
|
+
onCustomStateChange(listener) {
|
|
466
|
+
_stateChangeListeners.add(listener);
|
|
467
|
+
return () => {
|
|
468
|
+
_stateChangeListeners.delete(listener);
|
|
469
|
+
};
|
|
470
|
+
},
|
|
424
471
|
// === Communication Methods ===
|
|
425
472
|
send(event, data) {
|
|
426
473
|
if (_isDestroyed) {
|
|
@@ -458,6 +505,10 @@ function createMockController(options = {}) {
|
|
|
458
505
|
return controller.onError(handler);
|
|
459
506
|
case "$game-over":
|
|
460
507
|
return controller.onGameOver(handler);
|
|
508
|
+
case "$state-recovery":
|
|
509
|
+
return controller.onCustomStateChange(handler);
|
|
510
|
+
case "$connection-change":
|
|
511
|
+
return controller.onConnectionChange(handler);
|
|
461
512
|
}
|
|
462
513
|
}
|
|
463
514
|
events.validateEventName(event);
|
|
@@ -579,6 +630,12 @@ function createMockController(options = {}) {
|
|
|
579
630
|
simulateConnectionChange(connected) {
|
|
580
631
|
_isConnected = connected;
|
|
581
632
|
_onConnectionChangeCallbacks.forEach((cb) => cb(connected));
|
|
633
|
+
},
|
|
634
|
+
simulateStateChange(playerIndex, state) {
|
|
635
|
+
const existing = _customStates.get(playerIndex) ?? {};
|
|
636
|
+
const next = { ...existing, ...state };
|
|
637
|
+
_customStates.set(playerIndex, next);
|
|
638
|
+
_stateChangeListeners.forEach((cb) => cb(playerIndex, next));
|
|
582
639
|
}
|
|
583
640
|
};
|
|
584
641
|
if (autoReady) {
|