@smoregg/sdk 1.1.0 → 1.3.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/config.cjs +13 -0
- package/dist/cjs/config.cjs.map +1 -0
- package/dist/cjs/controller.cjs +4 -3
- package/dist/cjs/controller.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/screen.cjs +4 -3
- package/dist/cjs/screen.cjs.map +1 -1
- package/dist/cjs/testing.cjs +10 -22
- package/dist/cjs/testing.cjs.map +1 -1
- package/dist/esm/config.js +10 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/controller.js +4 -3
- package/dist/esm/controller.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/screen.js +4 -3
- package/dist/esm/screen.js.map +1 -1
- package/dist/esm/testing.js +10 -22
- package/dist/esm/testing.js.map +1 -1
- package/dist/types/config.d.ts +35 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/types.d.ts +24 -101
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +25 -28
- 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/SmoreHost.cjs +0 -306
- package/dist/cjs/SmoreHost.cjs.map +0 -1
- package/dist/cjs/SmorePlayer.cjs +0 -229
- package/dist/cjs/SmorePlayer.cjs.map +0 -1
- package/dist/cjs/components/DirectionPad.cjs +0 -68
- package/dist/cjs/components/DirectionPad.cjs.map +0 -1
- package/dist/cjs/components/DirectionPad.module.css.cjs +0 -12
- package/dist/cjs/components/DirectionPad.module.css.cjs.map +0 -1
- package/dist/cjs/components/HoldButton.cjs +0 -57
- package/dist/cjs/components/HoldButton.cjs.map +0 -1
- package/dist/cjs/components/HoldButton.module.css.cjs +0 -12
- package/dist/cjs/components/HoldButton.module.css.cjs.map +0 -1
- package/dist/cjs/components/IframeGameBridge.cjs +0 -115
- package/dist/cjs/components/IframeGameBridge.cjs.map +0 -1
- package/dist/cjs/components/SwipeArea.cjs +0 -58
- package/dist/cjs/components/SwipeArea.cjs.map +0 -1
- package/dist/cjs/components/SwipeArea.module.css.cjs +0 -12
- package/dist/cjs/components/SwipeArea.module.css.cjs.map +0 -1
- package/dist/cjs/components/TapButton.cjs +0 -58
- package/dist/cjs/components/TapButton.cjs.map +0 -1
- package/dist/cjs/components/TapButton.module.css.cjs +0 -12
- package/dist/cjs/components/TapButton.module.css.cjs.map +0 -1
- package/dist/cjs/context/RoomProvider.cjs +0 -118
- package/dist/cjs/context/RoomProvider.cjs.map +0 -1
- package/dist/cjs/hooks/useExternalGames.cjs +0 -49
- package/dist/cjs/hooks/useExternalGames.cjs.map +0 -1
- package/dist/cjs/hooks/useGameHost.cjs +0 -206
- package/dist/cjs/hooks/useGameHost.cjs.map +0 -1
- package/dist/cjs/hooks/useGamePlayer.cjs +0 -134
- package/dist/cjs/hooks/useGamePlayer.cjs.map +0 -1
- package/dist/cjs/iframe/index.cjs +0 -260
- package/dist/cjs/iframe/index.cjs.map +0 -1
- package/dist/cjs/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.cjs +0 -33
- package/dist/cjs/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.cjs.map +0 -1
- package/dist/cjs/server/index.cjs +0 -45
- package/dist/cjs/server/index.cjs.map +0 -1
- package/dist/cjs/transport/DirectTransport.cjs +0 -23
- package/dist/cjs/transport/DirectTransport.cjs.map +0 -1
- package/dist/cjs/utils/connectionMonitor.cjs +0 -77
- package/dist/cjs/utils/connectionMonitor.cjs.map +0 -1
- package/dist/cjs/utils/preloadAssets.cjs +0 -66
- package/dist/cjs/utils/preloadAssets.cjs.map +0 -1
- package/dist/cjs/utils/serverTime.cjs +0 -43
- package/dist/cjs/utils/serverTime.cjs.map +0 -1
- package/dist/esm/SmoreHost.js +0 -304
- package/dist/esm/SmoreHost.js.map +0 -1
- package/dist/esm/SmorePlayer.js +0 -227
- package/dist/esm/SmorePlayer.js.map +0 -1
- package/dist/esm/components/DirectionPad.js +0 -66
- package/dist/esm/components/DirectionPad.js.map +0 -1
- package/dist/esm/components/DirectionPad.module.css.js +0 -8
- package/dist/esm/components/DirectionPad.module.css.js.map +0 -1
- package/dist/esm/components/HoldButton.js +0 -55
- package/dist/esm/components/HoldButton.js.map +0 -1
- package/dist/esm/components/HoldButton.module.css.js +0 -8
- package/dist/esm/components/HoldButton.module.css.js.map +0 -1
- package/dist/esm/components/IframeGameBridge.js +0 -113
- package/dist/esm/components/IframeGameBridge.js.map +0 -1
- package/dist/esm/components/SwipeArea.js +0 -56
- package/dist/esm/components/SwipeArea.js.map +0 -1
- package/dist/esm/components/SwipeArea.module.css.js +0 -8
- package/dist/esm/components/SwipeArea.module.css.js.map +0 -1
- package/dist/esm/components/TapButton.js +0 -56
- package/dist/esm/components/TapButton.js.map +0 -1
- package/dist/esm/components/TapButton.module.css.js +0 -8
- package/dist/esm/components/TapButton.module.css.js.map +0 -1
- package/dist/esm/context/RoomProvider.js +0 -109
- package/dist/esm/context/RoomProvider.js.map +0 -1
- package/dist/esm/hooks/useExternalGames.js +0 -47
- package/dist/esm/hooks/useExternalGames.js.map +0 -1
- package/dist/esm/hooks/useGameHost.js +0 -204
- package/dist/esm/hooks/useGameHost.js.map +0 -1
- package/dist/esm/hooks/useGamePlayer.js +0 -132
- package/dist/esm/hooks/useGamePlayer.js.map +0 -1
- package/dist/esm/iframe/index.js +0 -257
- package/dist/esm/iframe/index.js.map +0 -1
- package/dist/esm/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.js +0 -29
- package/dist/esm/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.js.map +0 -1
- package/dist/esm/server/index.js +0 -43
- package/dist/esm/server/index.js.map +0 -1
- package/dist/esm/transport/DirectTransport.js +0 -21
- package/dist/esm/transport/DirectTransport.js.map +0 -1
- package/dist/esm/utils/connectionMonitor.js +0 -75
- package/dist/esm/utils/connectionMonitor.js.map +0 -1
- package/dist/esm/utils/preloadAssets.js +0 -63
- package/dist/esm/utils/preloadAssets.js.map +0 -1
- package/dist/esm/utils/serverTime.js +0 -41
- package/dist/esm/utils/serverTime.js.map +0 -1
- package/dist/types/SmoreHost.d.ts +0 -187
- package/dist/types/SmoreHost.d.ts.map +0 -1
- package/dist/types/SmorePlayer.d.ts +0 -146
- package/dist/types/SmorePlayer.d.ts.map +0 -1
- package/dist/types/components/DirectionPad.d.ts +0 -21
- package/dist/types/components/DirectionPad.d.ts.map +0 -1
- package/dist/types/components/HoldButton.d.ts +0 -22
- package/dist/types/components/HoldButton.d.ts.map +0 -1
- package/dist/types/components/IframeGameBridge.d.ts +0 -38
- package/dist/types/components/IframeGameBridge.d.ts.map +0 -1
- package/dist/types/components/SwipeArea.d.ts +0 -19
- package/dist/types/components/SwipeArea.d.ts.map +0 -1
- package/dist/types/components/TapButton.d.ts +0 -19
- package/dist/types/components/TapButton.d.ts.map +0 -1
- package/dist/types/components/index.d.ts +0 -6
- package/dist/types/components/index.d.ts.map +0 -1
- package/dist/types/context/RoomProvider.d.ts +0 -69
- package/dist/types/context/RoomProvider.d.ts.map +0 -1
- package/dist/types/context/index.d.ts +0 -3
- package/dist/types/context/index.d.ts.map +0 -1
- package/dist/types/dev/DevSimulator.d.ts +0 -31
- package/dist/types/dev/DevSimulator.d.ts.map +0 -1
- package/dist/types/dev/index.d.ts +0 -2
- package/dist/types/dev/index.d.ts.map +0 -1
- package/dist/types/hooks/index.d.ts +0 -7
- package/dist/types/hooks/index.d.ts.map +0 -1
- package/dist/types/hooks/useExternalGames.d.ts +0 -32
- package/dist/types/hooks/useExternalGames.d.ts.map +0 -1
- package/dist/types/hooks/useGameHost.d.ts +0 -67
- package/dist/types/hooks/useGameHost.d.ts.map +0 -1
- package/dist/types/hooks/useGamePlayer.d.ts +0 -55
- package/dist/types/hooks/useGamePlayer.d.ts.map +0 -1
- package/dist/types/iframe/IframeRoomProvider.d.ts +0 -31
- package/dist/types/iframe/IframeRoomProvider.d.ts.map +0 -1
- package/dist/types/iframe/index.d.ts +0 -18
- package/dist/types/iframe/index.d.ts.map +0 -1
- package/dist/types/iframe/vanilla-entry.d.ts +0 -7
- package/dist/types/iframe/vanilla-entry.d.ts.map +0 -1
- package/dist/types/iframe/vanilla.d.ts +0 -49
- package/dist/types/iframe/vanilla.d.ts.map +0 -1
- package/dist/types/server/createGameRelay.d.ts +0 -26
- package/dist/types/server/createGameRelay.d.ts.map +0 -1
- package/dist/types/server/index.d.ts +0 -3
- package/dist/types/server/index.d.ts.map +0 -1
- package/dist/types/utils/connectionMonitor.d.ts +0 -57
- package/dist/types/utils/connectionMonitor.d.ts.map +0 -1
- package/dist/types/utils/index.d.ts +0 -7
- package/dist/types/utils/index.d.ts.map +0 -1
- package/dist/types/utils/preloadAssets.d.ts +0 -29
- package/dist/types/utils/preloadAssets.d.ts.map +0 -1
- package/dist/types/utils/serverTime.d.ts +0 -28
- package/dist/types/utils/serverTime.d.ts.map +0 -1
- package/dist/umd/smore-sdk-iframe.umd.js +0 -266
- package/dist/umd/smore-sdk-iframe.umd.js.map +0 -1
- package/dist/umd/smore-sdk-iframe.umd.min.js +0 -2
- package/dist/umd/smore-sdk-iframe.umd.min.js.map +0 -1
- package/dist/umd/smore-sdk-vanilla.umd.js +0 -1275
- package/dist/umd/smore-sdk-vanilla.umd.js.map +0 -1
- package/dist/umd/smore-sdk-vanilla.umd.min.js +0 -2
- package/dist/umd/smore-sdk-vanilla.umd.min.js.map +0 -1
package/dist/esm/screen.js
CHANGED
|
@@ -3,6 +3,7 @@ import { isBridgeMessage, validateInitPayload } from './transport/protocol.js';
|
|
|
3
3
|
import { SmoreSDKError } from './errors.js';
|
|
4
4
|
import { validateEventName, SMORE_EVENTS } from './events.js';
|
|
5
5
|
import { DebugLogger } from './logger.js';
|
|
6
|
+
import { getGlobalConfig } from './config.js';
|
|
6
7
|
|
|
7
8
|
const DEFAULT_TIMEOUT = 1e4;
|
|
8
9
|
function validatePlayerIndex(playerIndex, controllers) {
|
|
@@ -102,8 +103,8 @@ class ScreenImpl {
|
|
|
102
103
|
roomCode: this._roomCode,
|
|
103
104
|
controllers: this._controllers.length
|
|
104
105
|
});
|
|
105
|
-
|
|
106
|
-
if (
|
|
106
|
+
const autoReady = getGlobalConfig().autoReady ?? true;
|
|
107
|
+
if (autoReady) {
|
|
107
108
|
this.logger.lifecycle("Auto-signaling ready (autoReady enabled)");
|
|
108
109
|
this.signalReady();
|
|
109
110
|
}
|
|
@@ -227,7 +228,7 @@ class ScreenImpl {
|
|
|
227
228
|
});
|
|
228
229
|
this.registerTransportHandler(SMORE_EVENTS.ALL_READY, () => {
|
|
229
230
|
this.logger.lifecycle("All participants ready");
|
|
230
|
-
this.config.
|
|
231
|
+
this.config.onReady?.();
|
|
231
232
|
});
|
|
232
233
|
if (this.config.listeners) {
|
|
233
234
|
for (const [event, handler] of Object.entries(this.config.listeners)) {
|
package/dist/esm/screen.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen.js","sources":["../../src/screen.ts"],"sourcesContent":["/**\n * createScreen - Factory function for Screen instances (Host/TV side)\n *\n * Promise-based factory with type-safe event handling and comprehensive error management.\n *\n * @example Promise-based (recommended)\n * ```ts\n * interface MyEvents {\n * tap: { x: number; y: number };\n * 'phase-update': { phase: 'lobby' | 'playing' | 'results' };\n * }\n *\n * const screen = await createScreen<MyEvents>({\n * listeners: {\n * tap: (playerIndex, data) => console.log(`Player ${playerIndex} tapped at`, data.x, data.y),\n * },\n * });\n *\n * screen.broadcast('phase-update', { phase: 'playing' });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const screen = createScreen<MyEvents>({\n * onReady: () => screen.broadcast('ready', {}),\n * listeners: { ... },\n * });\n * // Use screen.instance for immediate access\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} from './types';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport { isBridgeMessage, validateInitPayload, type BridgeInitMessage, type BridgeUpdateMessage } from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName } from './events';\nimport { DebugLogger } from './logger';\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<TEvents>;\n private logger: DebugLogger;\n\n private _controllers: ControllerInfo[] = [];\n private _roomCode: RoomCode = '';\n private _isReady = false;\n private _isDestroyed = false;\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 // Tracks event names registered via config.listeners so off(event) without handler won't remove them\n private _configListenerEvents = new Set<string>();\n\n constructor(config: ScreenConfig<TEvents> = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreScreen]');\n\n // Validate event names in initial listeners\n if (config.listeners) {\n for (const event of Object.keys(config.listeners)) {\n validateEventName(event);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Initialization (called by factory)\n // ---------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n this.logger.lifecycle('Initializing screen...');\n\n const parentOrigin = this.config.parentOrigin ?? '*';\n const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;\n\n return new Promise<void>((resolve, reject) => {\n const timeoutId = 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 reject(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(timeoutId);\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 reject(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 reject(error);\n return;\n }\n\n // Initialize transport\n this.transport = new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\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 this._isReady = true;\n\n this.logger.lifecycle('Screen ready', {\n roomCode: this._roomCode,\n controllers: this._controllers.length,\n });\n\n this.config.onReady?.();\n\n // Auto-signal ready unless explicitly disabled\n if (this.config.autoReady !== false) {\n this.logger.lifecycle('Auto-signaling ready (autoReady enabled)');\n this.signalReady();\n }\n\n resolve();\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.config.onControllerJoin?.(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.config.onControllerLeave?.(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' }, parentOrigin);\n this.logger.lifecycle('Sent _bridge:ready to parent');\n });\n }\n\n private mapControllersFromInit(players: unknown[]): ControllerInfo[] {\n return (players as Record<string, unknown>[]).map((p, index) => ({\n playerIndex: (p.playerIndex as number) ?? index,\n // Fallback to `nickname` for defensive compatibility (server currently always sends `name`)\n nickname: (p.nickname as string) || (p.name as string) || `Player ${index + 1}`,\n connected: p.connected !== false,\n // Fallback to `appearance` for defensive compatibility (server currently always sends `character`)\n appearance: (p.appearance ?? p.character) as ControllerInfo['appearance'],\n }));\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: ControllerInfo = {\n playerIndex: playerData.playerIndex as number,\n nickname: (playerData.nickname as string) || (playerData.name as string) || `Player ${(playerData.playerIndex as number) + 1}`,\n connected: playerData.connected !== false,\n appearance: (playerData.appearance ?? playerData.character) as ControllerInfo['appearance'],\n };\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.config.onControllerJoin?.(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.config.onControllerLeave?.(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.config.onControllerDisconnect?.(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: ControllerInfo = {\n playerIndex: playerData.playerIndex as number,\n nickname: (playerData.nickname as string) || (playerData.name as string) || `Player ${(playerData.playerIndex as number) + 1}`,\n connected: true,\n appearance: (playerData.appearance ?? playerData.character) as ControllerInfo['appearance'],\n };\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.config.onControllerReconnect?.(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 appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerData.playerIndex ? { ...c, appearance } : c\n );\n this.logger.lifecycle('Player character updated', { playerIndex: playerData.playerIndex });\n this.config.onCharacterUpdated?.(playerData.playerIndex, appearance ?? null);\n }\n });\n\n // Rate limited: notify when server rate-limits an event\n this.registerTransportHandler(SMORE_EVENTS.RATE_LIMITED, (data: unknown) => {\n const payload = data as { event?: string };\n const event = payload?.event ?? 'unknown';\n this.logger.warn(`Rate limited: ${event}`);\n this.config.onRateLimited?.(event);\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.config.onAllReady?.();\n });\n\n // User event listeners from config\n // These are tracked in _configListenerEvents so off(event) without handler won't remove them.\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n this._configListenerEvents.add(event);\n this.setupUserEventHandler(event, handler as ScreenEventHandler<unknown>);\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\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 // ---------------------------------------------------------------------------\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 * Runtime behavior is identical to broadcastRaw - both call validateEventName at runtime.\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 * @see broadcastRaw for bypassing TypeScript type checking (runtime behavior identical)\n */\n broadcast<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void {\n this.ensureReady('broadcast');\n validateEventName(event);\n this.logger.send(event, data);\n this.transport!.emit(event, data);\n }\n\n /**\n * Send events to all controllers without TypeScript type checking.\n *\n * Bypasses EventMap generic type checks at compile time.\n * Runtime behavior is identical to broadcast - both call validateEventName at runtime.\n *\n * Use this when you need dynamic event names or when working without a predefined EventMap.\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 * @see broadcast for type-safe version using EventMap generic\n */\n broadcastRaw(event: string, data?: unknown): void {\n this.ensureReady('broadcastRaw');\n validateEventName(event);\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 this.ensureReady('sendToController');\n validateEventName(event);\n validatePlayerIndex(playerIndex, this._controllers);\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 sendToControllerRaw(playerIndex: PlayerIndex, event: string, data?: unknown): void {\n this.ensureReady('sendToControllerRaw');\n validateEventName(event);\n validatePlayerIndex(playerIndex, this._controllers);\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 * **Important:** If called before the Screen is ready (i.e., before `await createScreen()`\n * resolves or before the `onReady` callback fires), the handler is stored locally but\n * will NOT be registered with the transport layer. This means the handler will never\n * fire for events received during the pre-ready window. Always call `on()` after\n * initialization completes, or use `config.listeners` for handlers needed from the start.\n */\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\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 // Also register on transport if ready\n let wrappedHandler: TransportEventHandler | null = null;\n if (this.transport) {\n 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 }\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 if (wrappedHandler) {\n this.transport?.off(event, wrappedHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== wrappedHandler\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 * @example\n * ```ts\n * const unsubscribe = screen.once('ready', (playerIndex, data) => {\n * console.log('Ready event received');\n * });\n *\n * // To remove before the event fires:\n * unsubscribe();\n * ```\n */\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\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 (!handler) {\n // Remove all dynamically-added handlers for this event, but preserve\n // config listeners (registered via ScreenConfig.listeners) which are\n // permanent for the lifetime of the Screen instance.\n if (this._configListenerEvents.has(event)) {\n // Only remove handlers that were added via on(), not config listeners.\n // Remove handlerToTransport entries (these are from on() calls only).\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) {\n this.transport?.off(event, val.transportHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== val.transportHandler\n );\n this.handlerToTransport.delete(key);\n }\n }\n // Remove dynamically-added entries from eventHandlers but don't delete the Set\n // (config handler is still in it)\n } else {\n // No config listener for this event -- safe to remove everything\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 }\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 }\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 * Check if there is at least one connected controller.\n * Useful for detecting when all players have disconnected\n * (e.g., to pause the game or show a waiting screen).\n *\n * Use this in onControllerDisconnect callback to detect when all controllers have disconnected.\n *\n * @example\n * ```ts\n * const screen = await createScreen<MyEvents>({\n * onControllerDisconnect: (playerIndex) => {\n * if (!screen.hasAnyConnectedControllers()) {\n * console.log('All controllers disconnected!');\n * screen.broadcast('waiting-for-players', {});\n * }\n * },\n * });\n * ```\n */\n hasAnyConnectedControllers(): boolean {\n return this._controllers.some((c) => c.connected);\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 // 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\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.config.onError) {\n this.config.onError(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 createScreen() or onReady callback.`,\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 Promise that resolves when the screen is ready.\n * The promise also has an `instance` property for immediate access (use with onReady callback).\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Screen configuration\n * @returns Promise that resolves to the Screen instance when ready\n *\n * @example Promise-based (recommended)\n * ```ts\n * const screen = await createScreen<MyEvents>({\n * listeners: {\n * tap: (playerIndex, data) => handleTap(playerIndex, data),\n * },\n * });\n *\n * screen.broadcast('game-start', { countdown: 3 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const { instance: screen } = createScreen<MyEvents>({\n * onReady: () => {\n * screen.broadcast('game-start', { countdown: 3 });\n * },\n * listeners: { ... },\n * });\n * ```\n */\nexport function createScreen<TEvents extends EventMap = EventMap>(\n config?: ScreenConfig<TEvents>\n): Promise<Screen<TEvents>> & { instance: Screen<TEvents> } {\n const screen = new ScreenImpl<TEvents>(config);\n\n const promise = screen.initialize().then(() => screen as Screen<TEvents>);\n\n // Attach instance for callback-based usage\n (promise as Promise<Screen<TEvents>> & { instance: Screen<TEvents> }).instance =\n screen as Screen<TEvents>;\n\n return promise as Promise<Screen<TEvents>> & { instance: Screen<TEvents> };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"screen.js","sources":["../../src/screen.ts"],"sourcesContent":["/**\n * createScreen - Factory function for Screen instances (Host/TV side)\n *\n * Promise-based factory with type-safe event handling and comprehensive error management.\n *\n * @example Promise-based (recommended)\n * ```ts\n * interface MyEvents {\n * tap: { x: number; y: number };\n * 'phase-update': { phase: 'lobby' | 'playing' | 'results' };\n * }\n *\n * const screen = await createScreen<MyEvents>({\n * listeners: {\n * tap: (playerIndex, data) => console.log(`Player ${playerIndex} tapped at`, data.x, data.y),\n * },\n * });\n *\n * screen.broadcast('phase-update', { phase: 'playing' });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const screen = createScreen<MyEvents>({\n * onReady: () => screen.broadcast('ready', {}),\n * listeners: { ... },\n * });\n * // Use screen.instance for immediate access\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} from './types';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport { isBridgeMessage, validateInitPayload, type BridgeInitMessage, type BridgeUpdateMessage } from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName } from './events';\nimport { DebugLogger } from './logger';\nimport { getGlobalConfig } from './config';\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<TEvents>;\n private logger: DebugLogger;\n\n private _controllers: ControllerInfo[] = [];\n private _roomCode: RoomCode = '';\n private _isReady = false;\n private _isDestroyed = false;\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 // Tracks event names registered via config.listeners so off(event) without handler won't remove them\n private _configListenerEvents = new Set<string>();\n\n constructor(config: ScreenConfig<TEvents> = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreScreen]');\n\n // Validate event names in initial listeners\n if (config.listeners) {\n for (const event of Object.keys(config.listeners)) {\n validateEventName(event);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Initialization (called by factory)\n // ---------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n this.logger.lifecycle('Initializing screen...');\n\n const parentOrigin = this.config.parentOrigin ?? '*';\n const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;\n\n return new Promise<void>((resolve, reject) => {\n const timeoutId = 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 reject(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(timeoutId);\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 reject(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 reject(error);\n return;\n }\n\n // Initialize transport\n this.transport = new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\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 this._isReady = true;\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 configure()\n const autoReady = getGlobalConfig().autoReady ?? true;\n if (autoReady) {\n this.logger.lifecycle('Auto-signaling ready (autoReady enabled)');\n this.signalReady();\n }\n\n resolve();\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.config.onControllerJoin?.(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.config.onControllerLeave?.(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' }, parentOrigin);\n this.logger.lifecycle('Sent _bridge:ready to parent');\n });\n }\n\n private mapControllersFromInit(players: unknown[]): ControllerInfo[] {\n return (players as Record<string, unknown>[]).map((p, index) => ({\n playerIndex: (p.playerIndex as number) ?? index,\n // Fallback to `nickname` for defensive compatibility (server currently always sends `name`)\n nickname: (p.nickname as string) || (p.name as string) || `Player ${index + 1}`,\n connected: p.connected !== false,\n // Fallback to `appearance` for defensive compatibility (server currently always sends `character`)\n appearance: (p.appearance ?? p.character) as ControllerInfo['appearance'],\n }));\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: ControllerInfo = {\n playerIndex: playerData.playerIndex as number,\n nickname: (playerData.nickname as string) || (playerData.name as string) || `Player ${(playerData.playerIndex as number) + 1}`,\n connected: playerData.connected !== false,\n appearance: (playerData.appearance ?? playerData.character) as ControllerInfo['appearance'],\n };\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.config.onControllerJoin?.(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.config.onControllerLeave?.(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.config.onControllerDisconnect?.(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: ControllerInfo = {\n playerIndex: playerData.playerIndex as number,\n nickname: (playerData.nickname as string) || (playerData.name as string) || `Player ${(playerData.playerIndex as number) + 1}`,\n connected: true,\n appearance: (playerData.appearance ?? playerData.character) as ControllerInfo['appearance'],\n };\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.config.onControllerReconnect?.(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 appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerData.playerIndex ? { ...c, appearance } : c\n );\n this.logger.lifecycle('Player character updated', { playerIndex: playerData.playerIndex });\n this.config.onCharacterUpdated?.(playerData.playerIndex, appearance ?? null);\n }\n });\n\n // Rate limited: notify when server rate-limits an event\n this.registerTransportHandler(SMORE_EVENTS.RATE_LIMITED, (data: unknown) => {\n const payload = data as { event?: string };\n const event = payload?.event ?? 'unknown';\n this.logger.warn(`Rate limited: ${event}`);\n this.config.onRateLimited?.(event);\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.config.onReady?.();\n });\n\n // User event listeners from config\n // These are tracked in _configListenerEvents so off(event) without handler won't remove them.\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n this._configListenerEvents.add(event);\n this.setupUserEventHandler(event, handler as ScreenEventHandler<unknown>);\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\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 // ---------------------------------------------------------------------------\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 * Runtime behavior is identical to broadcastRaw - both call validateEventName at runtime.\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 * @see broadcastRaw for bypassing TypeScript type checking (runtime behavior identical)\n */\n broadcast<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void {\n this.ensureReady('broadcast');\n validateEventName(event);\n this.logger.send(event, data);\n this.transport!.emit(event, data);\n }\n\n /**\n * Send events to all controllers without TypeScript type checking.\n *\n * Bypasses EventMap generic type checks at compile time.\n * Runtime behavior is identical to broadcast - both call validateEventName at runtime.\n *\n * Use this when you need dynamic event names or when working without a predefined EventMap.\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 * @see broadcast for type-safe version using EventMap generic\n */\n broadcastRaw(event: string, data?: unknown): void {\n this.ensureReady('broadcastRaw');\n validateEventName(event);\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 this.ensureReady('sendToController');\n validateEventName(event);\n validatePlayerIndex(playerIndex, this._controllers);\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 sendToControllerRaw(playerIndex: PlayerIndex, event: string, data?: unknown): void {\n this.ensureReady('sendToControllerRaw');\n validateEventName(event);\n validatePlayerIndex(playerIndex, this._controllers);\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 * **Important:** If called before the Screen is ready (i.e., before `await createScreen()`\n * resolves or before the `onReady` callback fires), the handler is stored locally but\n * will NOT be registered with the transport layer. This means the handler will never\n * fire for events received during the pre-ready window. Always call `on()` after\n * initialization completes, or use `config.listeners` for handlers needed from the start.\n */\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\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 // Also register on transport if ready\n let wrappedHandler: TransportEventHandler | null = null;\n if (this.transport) {\n 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 }\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 if (wrappedHandler) {\n this.transport?.off(event, wrappedHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== wrappedHandler\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 * @example\n * ```ts\n * const unsubscribe = screen.once('ready', (playerIndex, data) => {\n * console.log('Ready event received');\n * });\n *\n * // To remove before the event fires:\n * unsubscribe();\n * ```\n */\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>\n ): () => void {\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 (!handler) {\n // Remove all dynamically-added handlers for this event, but preserve\n // config listeners (registered via ScreenConfig.listeners) which are\n // permanent for the lifetime of the Screen instance.\n if (this._configListenerEvents.has(event)) {\n // Only remove handlers that were added via on(), not config listeners.\n // Remove handlerToTransport entries (these are from on() calls only).\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) {\n this.transport?.off(event, val.transportHandler);\n this.registeredTransportHandlers = this.registeredTransportHandlers.filter(\n h => h.handler !== val.transportHandler\n );\n this.handlerToTransport.delete(key);\n }\n }\n // Remove dynamically-added entries from eventHandlers but don't delete the Set\n // (config handler is still in it)\n } else {\n // No config listener for this event -- safe to remove everything\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 }\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 }\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 * Check if there is at least one connected controller.\n * Useful for detecting when all players have disconnected\n * (e.g., to pause the game or show a waiting screen).\n *\n * Use this in onControllerDisconnect callback to detect when all controllers have disconnected.\n *\n * @example\n * ```ts\n * const screen = await createScreen<MyEvents>({\n * onControllerDisconnect: (playerIndex) => {\n * if (!screen.hasAnyConnectedControllers()) {\n * console.log('All controllers disconnected!');\n * screen.broadcast('waiting-for-players', {});\n * }\n * },\n * });\n * ```\n */\n hasAnyConnectedControllers(): boolean {\n return this._controllers.some((c) => c.connected);\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 // 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\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.config.onError) {\n this.config.onError(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 createScreen() or onReady callback.`,\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 Promise that resolves when the screen is ready.\n * The promise also has an `instance` property for immediate access (use with onReady callback).\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Screen configuration\n * @returns Promise that resolves to the Screen instance when ready\n *\n * @example Promise-based (recommended)\n * ```ts\n * const screen = await createScreen<MyEvents>({\n * listeners: {\n * tap: (playerIndex, data) => handleTap(playerIndex, data),\n * },\n * });\n *\n * screen.broadcast('game-start', { countdown: 3 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const { instance: screen } = createScreen<MyEvents>({\n * onReady: () => {\n * screen.broadcast('game-start', { countdown: 3 });\n * },\n * listeners: { ... },\n * });\n * ```\n */\nexport function createScreen<TEvents extends EventMap = EventMap>(\n config?: ScreenConfig<TEvents>\n): Promise<Screen<TEvents>> & { instance: Screen<TEvents> } {\n const screen = new ScreenImpl<TEvents>(config);\n\n const promise = screen.initialize().then(() => screen as Screen<TEvents>);\n\n // Attach instance for callback-based usage\n (promise as Promise<Screen<TEvents>> & { instance: Screen<TEvents> }).instance =\n screen as Screen<TEvents>;\n\n return promise as Promise<Screen<TEvents>> & { instance: Screen<TEvents> };\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/testing.js
CHANGED
|
@@ -5,37 +5,34 @@ function createMockScreen(options = {}) {
|
|
|
5
5
|
roomCode = "TEST",
|
|
6
6
|
controllers: initialControllers = [],
|
|
7
7
|
autoReady = true,
|
|
8
|
-
onReady: onReadyCb,
|
|
9
8
|
onControllerJoin: onJoinCb,
|
|
10
9
|
onControllerLeave: onLeaveCb,
|
|
11
10
|
onControllerDisconnect: onDisconnectCb,
|
|
12
11
|
onControllerReconnect: onReconnectCb,
|
|
13
12
|
onCharacterUpdated: onCharacterUpdatedCb,
|
|
14
13
|
onRateLimited: onRateLimitedCb,
|
|
15
|
-
|
|
14
|
+
onReady: onReadyCb,
|
|
16
15
|
onError: onErrorCb
|
|
17
16
|
} = options;
|
|
18
17
|
let _controllers = [...initialControllers];
|
|
19
18
|
let _isReady = false;
|
|
20
19
|
let _isDestroyed = false;
|
|
21
20
|
const listeners = /* @__PURE__ */ new Map();
|
|
22
|
-
let onReadyCallback;
|
|
23
21
|
let onControllerJoinCallback;
|
|
24
22
|
let onControllerLeaveCallback;
|
|
25
23
|
let onControllerDisconnectCallback;
|
|
26
24
|
let onControllerReconnectCallback;
|
|
27
25
|
let onCharacterUpdatedCallback;
|
|
28
26
|
let onRateLimitedCallback;
|
|
29
|
-
let
|
|
27
|
+
let onReadyCallback;
|
|
30
28
|
let onErrorCallback;
|
|
31
|
-
onReadyCallback = onReadyCb;
|
|
32
29
|
onControllerJoinCallback = onJoinCb;
|
|
33
30
|
onControllerLeaveCallback = onLeaveCb;
|
|
34
31
|
onControllerDisconnectCallback = onDisconnectCb;
|
|
35
32
|
onControllerReconnectCallback = onReconnectCb;
|
|
36
33
|
onCharacterUpdatedCallback = onCharacterUpdatedCb;
|
|
37
34
|
onRateLimitedCallback = onRateLimitedCb;
|
|
38
|
-
|
|
35
|
+
onReadyCallback = onReadyCb;
|
|
39
36
|
onErrorCallback = onErrorCb;
|
|
40
37
|
const broadcasts = [];
|
|
41
38
|
const sends = [];
|
|
@@ -252,8 +249,8 @@ function createMockScreen(options = {}) {
|
|
|
252
249
|
}
|
|
253
250
|
},
|
|
254
251
|
simulateAllReady() {
|
|
255
|
-
if (
|
|
256
|
-
|
|
252
|
+
if (onReadyCallback) {
|
|
253
|
+
onReadyCallback();
|
|
257
254
|
}
|
|
258
255
|
},
|
|
259
256
|
simulateError(error) {
|
|
@@ -273,9 +270,6 @@ function createMockScreen(options = {}) {
|
|
|
273
270
|
},
|
|
274
271
|
triggerReady() {
|
|
275
272
|
_isReady = true;
|
|
276
|
-
if (onReadyCallback) {
|
|
277
|
-
onReadyCallback();
|
|
278
|
-
}
|
|
279
273
|
}
|
|
280
274
|
};
|
|
281
275
|
if (autoReady) {
|
|
@@ -288,37 +282,34 @@ function createMockController(options = {}) {
|
|
|
288
282
|
roomCode = "TEST",
|
|
289
283
|
myIndex = 0,
|
|
290
284
|
autoReady = true,
|
|
291
|
-
onReady: onReadyCb,
|
|
292
285
|
onControllerJoin: onJoinCb,
|
|
293
286
|
onControllerLeave: onLeaveCb,
|
|
294
287
|
onControllerDisconnect: onDisconnectCb,
|
|
295
288
|
onControllerReconnect: onReconnectCb,
|
|
296
289
|
onCharacterUpdated: onCharacterUpdatedCb,
|
|
297
290
|
onRateLimited: onRateLimitedCb,
|
|
298
|
-
|
|
291
|
+
onReady: onReadyCb,
|
|
299
292
|
onError: onErrorCb
|
|
300
293
|
} = options;
|
|
301
294
|
let _isReady = false;
|
|
302
295
|
let _isDestroyed = false;
|
|
303
296
|
let _controllers = options.controllers ?? [];
|
|
304
297
|
const listeners = /* @__PURE__ */ new Map();
|
|
305
|
-
let onReadyCallback;
|
|
306
298
|
let onControllerJoinCallback;
|
|
307
299
|
let onControllerLeaveCallback;
|
|
308
300
|
let onControllerDisconnectCallback;
|
|
309
301
|
let onControllerReconnectCallback;
|
|
310
302
|
let onCharacterUpdatedCallback;
|
|
311
303
|
let onRateLimitedCallback;
|
|
312
|
-
let
|
|
304
|
+
let onReadyCallback;
|
|
313
305
|
let onErrorCallback;
|
|
314
|
-
onReadyCallback = onReadyCb;
|
|
315
306
|
onControllerJoinCallback = onJoinCb;
|
|
316
307
|
onControllerLeaveCallback = onLeaveCb;
|
|
317
308
|
onControllerDisconnectCallback = onDisconnectCb;
|
|
318
309
|
onControllerReconnectCallback = onReconnectCb;
|
|
319
310
|
onCharacterUpdatedCallback = onCharacterUpdatedCb;
|
|
320
311
|
onRateLimitedCallback = onRateLimitedCb;
|
|
321
|
-
|
|
312
|
+
onReadyCallback = onReadyCb;
|
|
322
313
|
onErrorCallback = onErrorCb;
|
|
323
314
|
const sentEvents = [];
|
|
324
315
|
const controller = {
|
|
@@ -418,9 +409,6 @@ function createMockController(options = {}) {
|
|
|
418
409
|
},
|
|
419
410
|
triggerReady() {
|
|
420
411
|
_isReady = true;
|
|
421
|
-
if (onReadyCallback) {
|
|
422
|
-
onReadyCallback();
|
|
423
|
-
}
|
|
424
412
|
},
|
|
425
413
|
/**
|
|
426
414
|
* Simulate a new player joining the room.
|
|
@@ -509,8 +497,8 @@ function createMockController(options = {}) {
|
|
|
509
497
|
}
|
|
510
498
|
},
|
|
511
499
|
simulateAllReady() {
|
|
512
|
-
if (
|
|
513
|
-
|
|
500
|
+
if (onReadyCallback) {
|
|
501
|
+
onReadyCallback();
|
|
514
502
|
}
|
|
515
503
|
},
|
|
516
504
|
simulateError(error) {
|
package/dist/esm/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sources":["../../src/testing.ts"],"sourcesContent":["/**\n * @smoregg/sdk - Testing Utilities\n *\n * Mock implementations of Screen and Controller for unit testing.\n * All methods work synchronously for predictable test execution.\n *\n * @packageDocumentation\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n CharacterAppearance,\n ControllerInfo,\n PlayerIndex,\n GameResults,\n ScreenEventHandler,\n ControllerEventHandler,\n MockScreen,\n MockController,\n MockOptions,\n} from './types';\nimport { validateEventName } from './events';\n\n// =============================================================================\n// MOCK SCREEN IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedBroadcast {\n event: string;\n data: unknown;\n}\n\ninterface RecordedSend {\n playerIndex: PlayerIndex;\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Screen for testing game logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * @example\n * ```ts\n * const screen = createMockScreen<MyEvents>({\n * controllers: [\n * { playerIndex: 0, nickname: 'Player 1', connected: true },\n * { playerIndex: 1, nickname: 'Player 2', connected: true },\n * ],\n * });\n *\n * // Simulate player input\n * screen.simulateEvent(0, 'tap', { x: 100, y: 200 });\n *\n * // Check what was broadcast\n * expect(screen.getBroadcasts()).toContainEqual({\n * event: 'score-update',\n * data: { scores: { 0: 10 } },\n * });\n * ```\n */\nexport function createMockScreen<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockScreen<TEvents> {\n const {\n roomCode = 'TEST',\n controllers: initialControllers = [],\n autoReady = true,\n onReady: onReadyCb,\n onControllerJoin: onJoinCb,\n onControllerLeave: onLeaveCb,\n onControllerDisconnect: onDisconnectCb,\n onControllerReconnect: onReconnectCb,\n onCharacterUpdated: onCharacterUpdatedCb,\n onRateLimited: onRateLimitedCb,\n onAllReady: onAllReadyCb,\n onError: onErrorCb,\n } = options;\n\n // Internal state\n let _controllers: ControllerInfo[] = [...initialControllers];\n let _isReady = false;\n let _isDestroyed = false;\n\n // Event listeners\n const listeners = new Map<string, Set<ScreenEventHandler>>();\n\n // Lifecycle callbacks\n let onReadyCallback: (() => void) | undefined;\n let onControllerJoinCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onControllerLeaveCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerDisconnectCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerReconnectCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onCharacterUpdatedCallback:\n | ((index: PlayerIndex, appearance: CharacterAppearance | null) => void)\n | undefined;\n let onRateLimitedCallback: ((event: string) => void) | undefined;\n let onAllReadyCallback: (() => void) | undefined;\n let onErrorCallback: ((error: any) => void) | undefined;\n\n // Assign callbacks from options\n onReadyCallback = onReadyCb;\n onControllerJoinCallback = onJoinCb;\n onControllerLeaveCallback = onLeaveCb;\n onControllerDisconnectCallback = onDisconnectCb;\n onControllerReconnectCallback = onReconnectCb;\n onCharacterUpdatedCallback = onCharacterUpdatedCb;\n onRateLimitedCallback = onRateLimitedCb;\n onAllReadyCallback = onAllReadyCb;\n onErrorCallback = onErrorCb;\n\n // Recorded events for testing\n const broadcasts: RecordedBroadcast[] = [];\n const sends: RecordedSend[] = [];\n\n // Screen implementation\n const screen: MockScreen<TEvents> = {\n // === Properties ===\n get controllers() {\n return [..._controllers];\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n // === Communication Methods ===\n broadcast<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event as string);\n broadcasts.push({ event: event as string, data });\n },\n\n broadcastRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event);\n broadcasts.push({ event, data });\n },\n\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event as string);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event: event as string, data });\n },\n\n sendToControllerRaw(\n playerIndex: PlayerIndex,\n event: string,\n data?: unknown,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event, data });\n },\n\n // === Game Lifecycle ===\n gameOver(results?: GameResults): void {\n if (_isDestroyed) {\n throw new Error('Cannot call gameOver: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call gameOver: screen is not ready');\n }\n broadcasts.push({ event: 'smore:game-over', data: { results } });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: screen is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ScreenEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n handler(playerIndex, data);\n screen.off(event, wrapper);\n };\n return screen.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n }\n },\n\n // === Utilities ===\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n hasAnyConnectedControllers(): boolean {\n return _controllers.some(c => c.connected);\n },\n\n // === Cleanup ===\n /**\n * Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.\n */\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(playerIndex, data);\n });\n }\n },\n\n simulateControllerJoin(info: ControllerInfo): void {\n _controllers.push(info);\n if (onControllerJoinCallback) {\n onControllerJoinCallback(info.playerIndex, info);\n }\n },\n\n simulateControllerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);\n if (onControllerLeaveCallback) {\n onControllerLeaveCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a controller network disconnect (player still in room but unreachable).\n *\n * @example\n * ```ts\n * screen.simulateControllerDisconnect(0);\n * expect(screen.getController(0)?.connected).toBe(false);\n * ```\n */\n simulateControllerDisconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as disconnected (need to create new object since ControllerInfo is readonly)\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex\n ? { ...c, connected: false }\n : c\n );\n if (onControllerDisconnectCallback) {\n onControllerDisconnectCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a controller network reconnect after disconnect.\n *\n * @example\n * ```ts\n * screen.simulateControllerDisconnect(0);\n * screen.simulateControllerReconnect(0);\n * expect(screen.getController(0)?.connected).toBe(true);\n * ```\n */\n simulateControllerReconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as connected (need to create new object since ControllerInfo is readonly)\n const reconnectedController = { ...controller, connected: true };\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? reconnectedController : c\n );\n if (onControllerReconnectCallback) {\n onControllerReconnectCallback(playerIndex, reconnectedController);\n }\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n if (onCharacterUpdatedCallback) {\n onCharacterUpdatedCallback(playerIndex, appearance);\n }\n },\n\n simulateRateLimited(event: string): void {\n if (onRateLimitedCallback) {\n onRateLimitedCallback(event);\n }\n },\n\n simulateAllReady(): void {\n if (onAllReadyCallback) {\n onAllReadyCallback();\n }\n },\n\n simulateError(error: any): void {\n if (onErrorCallback) {\n onErrorCallback(error);\n }\n },\n\n getBroadcasts(): Array<{ event: string; data: unknown }> {\n return [...broadcasts];\n },\n\n getSentToController(\n playerIndex: PlayerIndex,\n ): Array<{ event: string; data: unknown }> {\n return sends\n .filter((s) => s.playerIndex === playerIndex)\n .map((s) => ({ event: s.event, data: s.data }));\n },\n\n clearRecordedEvents(): void {\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n if (onReadyCallback) {\n onReadyCallback();\n }\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => screen.triggerReady(), 0);\n }\n\n return screen;\n}\n\n// =============================================================================\n// MOCK CONTROLLER IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedEvent {\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Controller for testing player input logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * @example\n * ```ts\n * const controller = createMockController<MyEvents>({\n * myIndex: 0,\n * });\n *\n * // Simulate receiving from screen\n * controller.simulateEvent('your-turn', { timeLimit: 30 });\n *\n * // Check what was sent\n * expect(controller.getSentEvents()).toContainEqual({\n * event: 'answer',\n * data: { choice: 2 },\n * });\n * ```\n */\nexport function createMockController<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockController<TEvents> {\n const {\n roomCode = 'TEST',\n myIndex = 0,\n autoReady = true,\n onReady: onReadyCb,\n onControllerJoin: onJoinCb,\n onControllerLeave: onLeaveCb,\n onControllerDisconnect: onDisconnectCb,\n onControllerReconnect: onReconnectCb,\n onCharacterUpdated: onCharacterUpdatedCb,\n onRateLimited: onRateLimitedCb,\n onAllReady: onAllReadyCb,\n onError: onErrorCb,\n } = options;\n\n // Internal state -- uses a full ControllerInfo[] array (matching MockScreen pattern)\n // to preserve nickname/appearance data from simulatePlayerJoin()\n let _isReady = false;\n let _isDestroyed = false;\n let _controllers: ControllerInfo[] = options.controllers ?? [];\n\n // Event listeners\n const listeners = new Map<string, Set<ControllerEventHandler>>();\n\n // Lifecycle callbacks\n let onReadyCallback: (() => void) | undefined;\n let onControllerJoinCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onControllerLeaveCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerDisconnectCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerReconnectCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onCharacterUpdatedCallback:\n | ((index: PlayerIndex, appearance: CharacterAppearance | null) => void)\n | undefined;\n let onRateLimitedCallback: ((event: string) => void) | undefined;\n let onAllReadyCallback: (() => void) | undefined;\n let onErrorCallback: ((error: any) => void) | undefined;\n\n // Assign callbacks from options\n onReadyCallback = onReadyCb;\n onControllerJoinCallback = onJoinCb;\n onControllerLeaveCallback = onLeaveCb;\n onControllerDisconnectCallback = onDisconnectCb;\n onControllerReconnectCallback = onReconnectCb;\n onCharacterUpdatedCallback = onCharacterUpdatedCb;\n onRateLimitedCallback = onRateLimitedCb;\n onAllReadyCallback = onAllReadyCb;\n onErrorCallback = onErrorCb;\n\n // Recorded events for testing\n const sentEvents: RecordedEvent[] = [];\n\n // Controller implementation\n const controller: MockController<TEvents> = {\n // === Properties ===\n get myIndex() {\n return myIndex;\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n get controllers(): readonly ControllerInfo[] {\n return [..._controllers];\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n // === Communication Methods ===\n send<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event as string);\n sentEvents.push({ event: event as string, data });\n },\n\n sendRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event);\n sentEvents.push({ event, data });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: controller is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: controller is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ControllerEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ControllerEventHandler<EventData<TEvents, K>> = (data) => {\n handler(data);\n controller.off(event, wrapper);\n };\n return controller.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n }\n },\n\n // === Cleanup ===\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n sentEvents.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(data);\n });\n }\n },\n\n getSentEvents(): Array<{ event: string; data: unknown }> {\n return [...sentEvents];\n },\n\n clearRecordedEvents(): void {\n sentEvents.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n if (onReadyCallback) {\n onReadyCallback();\n }\n },\n\n /**\n * Simulate a new player joining the room.\n * Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.\n *\n * @example\n * ```ts\n * controller.simulatePlayerJoin(2, { playerIndex: 2, nickname: 'Alice', connected: true });\n * expect(controller.getControllerCount()).toBe(3);\n * expect(controller.controllers.find(c => c.playerIndex === 2)?.nickname).toBe('Alice');\n * ```\n */\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void {\n if (!_controllers.some(c => c.playerIndex === playerIndex)) {\n _controllers = [..._controllers, { ...info, connected: info.connected ?? true }];\n }\n if (onControllerJoinCallback) {\n onControllerJoinCallback(playerIndex, info);\n }\n },\n\n /**\n * Simulate a player leaving the room (fully removed).\n *\n * @example\n * ```ts\n * controller.simulatePlayerLeave(1);\n * expect(controller.controllers.some(c => c.playerIndex === 1)).toBe(false);\n * ```\n */\n simulatePlayerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter(c => c.playerIndex !== playerIndex);\n if (onControllerLeaveCallback) {\n onControllerLeaveCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a player network disconnect (player still in room but unreachable).\n *\n * @example\n * ```ts\n * controller.simulatePlayerDisconnect(1);\n * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(false);\n * ```\n */\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n if (onControllerDisconnectCallback) {\n onControllerDisconnectCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a player network reconnect after disconnect.\n *\n * @example\n * ```ts\n * controller.simulatePlayerDisconnect(1);\n * controller.simulatePlayerReconnect(1, { playerIndex: 1, nickname: 'Bob', connected: true });\n * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(true);\n * ```\n */\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...info, connected: true } : c\n );\n if (onControllerReconnectCallback) {\n onControllerReconnectCallback(playerIndex, info);\n }\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n if (onCharacterUpdatedCallback) {\n onCharacterUpdatedCallback(playerIndex, appearance);\n }\n },\n\n simulateRateLimited(event: string): void {\n if (onRateLimitedCallback) {\n onRateLimitedCallback(event);\n }\n },\n\n simulateAllReady(): void {\n if (onAllReadyCallback) {\n onAllReadyCallback();\n }\n },\n\n simulateError(error: any): void {\n if (onErrorCallback) {\n onErrorCallback(error);\n }\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => controller.triggerReady(), 0);\n }\n\n return controller;\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport type { MockScreen, MockController, MockOptions };\n"],"names":["controller"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.js","sources":["../../src/testing.ts"],"sourcesContent":["/**\n * @smoregg/sdk - Testing Utilities\n *\n * Mock implementations of Screen and Controller for unit testing.\n * All methods work synchronously for predictable test execution.\n *\n * @packageDocumentation\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n CharacterAppearance,\n ControllerInfo,\n PlayerIndex,\n GameResults,\n ScreenEventHandler,\n ControllerEventHandler,\n MockScreen,\n MockController,\n MockOptions,\n} from './types';\nimport { validateEventName } from './events';\n\n// =============================================================================\n// MOCK SCREEN IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedBroadcast {\n event: string;\n data: unknown;\n}\n\ninterface RecordedSend {\n playerIndex: PlayerIndex;\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Screen for testing game logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * @example\n * ```ts\n * const screen = createMockScreen<MyEvents>({\n * controllers: [\n * { playerIndex: 0, nickname: 'Player 1', connected: true },\n * { playerIndex: 1, nickname: 'Player 2', connected: true },\n * ],\n * });\n *\n * // Simulate player input\n * screen.simulateEvent(0, 'tap', { x: 100, y: 200 });\n *\n * // Check what was broadcast\n * expect(screen.getBroadcasts()).toContainEqual({\n * event: 'score-update',\n * data: { scores: { 0: 10 } },\n * });\n * ```\n */\nexport function createMockScreen<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockScreen<TEvents> {\n const {\n roomCode = 'TEST',\n controllers: initialControllers = [],\n autoReady = true,\n onControllerJoin: onJoinCb,\n onControllerLeave: onLeaveCb,\n onControllerDisconnect: onDisconnectCb,\n onControllerReconnect: onReconnectCb,\n onCharacterUpdated: onCharacterUpdatedCb,\n onRateLimited: onRateLimitedCb,\n onReady: onReadyCb,\n onError: onErrorCb,\n } = options;\n\n // Internal state\n let _controllers: ControllerInfo[] = [...initialControllers];\n let _isReady = false;\n let _isDestroyed = false;\n\n // Event listeners\n const listeners = new Map<string, Set<ScreenEventHandler>>();\n\n // Lifecycle callbacks\n let onControllerJoinCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onControllerLeaveCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerDisconnectCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerReconnectCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onCharacterUpdatedCallback:\n | ((index: PlayerIndex, appearance: CharacterAppearance | null) => void)\n | undefined;\n let onRateLimitedCallback: ((event: string) => void) | undefined;\n let onReadyCallback: (() => void) | undefined;\n let onErrorCallback: ((error: any) => void) | undefined;\n\n // Assign callbacks from options\n onControllerJoinCallback = onJoinCb;\n onControllerLeaveCallback = onLeaveCb;\n onControllerDisconnectCallback = onDisconnectCb;\n onControllerReconnectCallback = onReconnectCb;\n onCharacterUpdatedCallback = onCharacterUpdatedCb;\n onRateLimitedCallback = onRateLimitedCb;\n onReadyCallback = onReadyCb;\n onErrorCallback = onErrorCb;\n\n // Recorded events for testing\n const broadcasts: RecordedBroadcast[] = [];\n const sends: RecordedSend[] = [];\n\n // Screen implementation\n const screen: MockScreen<TEvents> = {\n // === Properties ===\n get controllers() {\n return [..._controllers];\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n // === Communication Methods ===\n broadcast<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event as string);\n broadcasts.push({ event: event as string, data });\n },\n\n broadcastRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event);\n broadcasts.push({ event, data });\n },\n\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event as string);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event: event as string, data });\n },\n\n sendToControllerRaw(\n playerIndex: PlayerIndex,\n event: string,\n data?: unknown,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event, data });\n },\n\n // === Game Lifecycle ===\n gameOver(results?: GameResults): void {\n if (_isDestroyed) {\n throw new Error('Cannot call gameOver: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call gameOver: screen is not ready');\n }\n broadcasts.push({ event: 'smore:game-over', data: { results } });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: screen is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ScreenEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n handler(playerIndex, data);\n screen.off(event, wrapper);\n };\n return screen.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n }\n },\n\n // === Utilities ===\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n hasAnyConnectedControllers(): boolean {\n return _controllers.some(c => c.connected);\n },\n\n // === Cleanup ===\n /**\n * Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.\n */\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(playerIndex, data);\n });\n }\n },\n\n simulateControllerJoin(info: ControllerInfo): void {\n _controllers.push(info);\n if (onControllerJoinCallback) {\n onControllerJoinCallback(info.playerIndex, info);\n }\n },\n\n simulateControllerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);\n if (onControllerLeaveCallback) {\n onControllerLeaveCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a controller network disconnect (player still in room but unreachable).\n *\n * @example\n * ```ts\n * screen.simulateControllerDisconnect(0);\n * expect(screen.getController(0)?.connected).toBe(false);\n * ```\n */\n simulateControllerDisconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as disconnected (need to create new object since ControllerInfo is readonly)\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex\n ? { ...c, connected: false }\n : c\n );\n if (onControllerDisconnectCallback) {\n onControllerDisconnectCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a controller network reconnect after disconnect.\n *\n * @example\n * ```ts\n * screen.simulateControllerDisconnect(0);\n * screen.simulateControllerReconnect(0);\n * expect(screen.getController(0)?.connected).toBe(true);\n * ```\n */\n simulateControllerReconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as connected (need to create new object since ControllerInfo is readonly)\n const reconnectedController = { ...controller, connected: true };\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? reconnectedController : c\n );\n if (onControllerReconnectCallback) {\n onControllerReconnectCallback(playerIndex, reconnectedController);\n }\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n if (onCharacterUpdatedCallback) {\n onCharacterUpdatedCallback(playerIndex, appearance);\n }\n },\n\n simulateRateLimited(event: string): void {\n if (onRateLimitedCallback) {\n onRateLimitedCallback(event);\n }\n },\n\n simulateAllReady(): void {\n if (onReadyCallback) {\n onReadyCallback();\n }\n },\n\n simulateError(error: any): void {\n if (onErrorCallback) {\n onErrorCallback(error);\n }\n },\n\n getBroadcasts(): Array<{ event: string; data: unknown }> {\n return [...broadcasts];\n },\n\n getSentToController(\n playerIndex: PlayerIndex,\n ): Array<{ event: string; data: unknown }> {\n return sends\n .filter((s) => s.playerIndex === playerIndex)\n .map((s) => ({ event: s.event, data: s.data }));\n },\n\n clearRecordedEvents(): void {\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => screen.triggerReady(), 0);\n }\n\n return screen;\n}\n\n// =============================================================================\n// MOCK CONTROLLER IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedEvent {\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Controller for testing player input logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * @example\n * ```ts\n * const controller = createMockController<MyEvents>({\n * myIndex: 0,\n * });\n *\n * // Simulate receiving from screen\n * controller.simulateEvent('your-turn', { timeLimit: 30 });\n *\n * // Check what was sent\n * expect(controller.getSentEvents()).toContainEqual({\n * event: 'answer',\n * data: { choice: 2 },\n * });\n * ```\n */\nexport function createMockController<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockController<TEvents> {\n const {\n roomCode = 'TEST',\n myIndex = 0,\n autoReady = true,\n onControllerJoin: onJoinCb,\n onControllerLeave: onLeaveCb,\n onControllerDisconnect: onDisconnectCb,\n onControllerReconnect: onReconnectCb,\n onCharacterUpdated: onCharacterUpdatedCb,\n onRateLimited: onRateLimitedCb,\n onReady: onReadyCb,\n onError: onErrorCb,\n } = options;\n\n // Internal state -- uses a full ControllerInfo[] array (matching MockScreen pattern)\n // to preserve nickname/appearance data from simulatePlayerJoin()\n let _isReady = false;\n let _isDestroyed = false;\n let _controllers: ControllerInfo[] = options.controllers ?? [];\n\n // Event listeners\n const listeners = new Map<string, Set<ControllerEventHandler>>();\n\n // Lifecycle callbacks\n let onControllerJoinCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onControllerLeaveCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerDisconnectCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerReconnectCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onCharacterUpdatedCallback:\n | ((index: PlayerIndex, appearance: CharacterAppearance | null) => void)\n | undefined;\n let onRateLimitedCallback: ((event: string) => void) | undefined;\n let onReadyCallback: (() => void) | undefined;\n let onErrorCallback: ((error: any) => void) | undefined;\n\n // Assign callbacks from options\n onControllerJoinCallback = onJoinCb;\n onControllerLeaveCallback = onLeaveCb;\n onControllerDisconnectCallback = onDisconnectCb;\n onControllerReconnectCallback = onReconnectCb;\n onCharacterUpdatedCallback = onCharacterUpdatedCb;\n onRateLimitedCallback = onRateLimitedCb;\n onReadyCallback = onReadyCb;\n onErrorCallback = onErrorCb;\n\n // Recorded events for testing\n const sentEvents: RecordedEvent[] = [];\n\n // Controller implementation\n const controller: MockController<TEvents> = {\n // === Properties ===\n get myIndex() {\n return myIndex;\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n get controllers(): readonly ControllerInfo[] {\n return [..._controllers];\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n // === Communication Methods ===\n send<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event as string);\n sentEvents.push({ event: event as string, data });\n },\n\n sendRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event);\n sentEvents.push({ event, data });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: controller is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: controller is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ControllerEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ControllerEventHandler<EventData<TEvents, K>> = (data) => {\n handler(data);\n controller.off(event, wrapper);\n };\n return controller.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n }\n },\n\n // === Cleanup ===\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n sentEvents.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(data);\n });\n }\n },\n\n getSentEvents(): Array<{ event: string; data: unknown }> {\n return [...sentEvents];\n },\n\n clearRecordedEvents(): void {\n sentEvents.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n },\n\n /**\n * Simulate a new player joining the room.\n * Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.\n *\n * @example\n * ```ts\n * controller.simulatePlayerJoin(2, { playerIndex: 2, nickname: 'Alice', connected: true });\n * expect(controller.getControllerCount()).toBe(3);\n * expect(controller.controllers.find(c => c.playerIndex === 2)?.nickname).toBe('Alice');\n * ```\n */\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void {\n if (!_controllers.some(c => c.playerIndex === playerIndex)) {\n _controllers = [..._controllers, { ...info, connected: info.connected ?? true }];\n }\n if (onControllerJoinCallback) {\n onControllerJoinCallback(playerIndex, info);\n }\n },\n\n /**\n * Simulate a player leaving the room (fully removed).\n *\n * @example\n * ```ts\n * controller.simulatePlayerLeave(1);\n * expect(controller.controllers.some(c => c.playerIndex === 1)).toBe(false);\n * ```\n */\n simulatePlayerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter(c => c.playerIndex !== playerIndex);\n if (onControllerLeaveCallback) {\n onControllerLeaveCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a player network disconnect (player still in room but unreachable).\n *\n * @example\n * ```ts\n * controller.simulatePlayerDisconnect(1);\n * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(false);\n * ```\n */\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n if (onControllerDisconnectCallback) {\n onControllerDisconnectCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a player network reconnect after disconnect.\n *\n * @example\n * ```ts\n * controller.simulatePlayerDisconnect(1);\n * controller.simulatePlayerReconnect(1, { playerIndex: 1, nickname: 'Bob', connected: true });\n * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(true);\n * ```\n */\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...info, connected: true } : c\n );\n if (onControllerReconnectCallback) {\n onControllerReconnectCallback(playerIndex, info);\n }\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n if (onCharacterUpdatedCallback) {\n onCharacterUpdatedCallback(playerIndex, appearance);\n }\n },\n\n simulateRateLimited(event: string): void {\n if (onRateLimitedCallback) {\n onRateLimitedCallback(event);\n }\n },\n\n simulateAllReady(): void {\n if (onReadyCallback) {\n onReadyCallback();\n }\n },\n\n simulateError(error: any): void {\n if (onErrorCallback) {\n onErrorCallback(error);\n }\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => controller.triggerReady(), 0);\n }\n\n return controller;\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport type { MockScreen, MockController, MockOptions };\n"],"names":["controller"],"mappings":"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"}
|