@smoregg/sdk 0.4.1 → 0.6.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.
Files changed (110) hide show
  1. package/README.md +199 -0
  2. package/dist/cjs/SmoreHost.cjs +306 -0
  3. package/dist/cjs/SmoreHost.cjs.map +1 -0
  4. package/dist/cjs/SmorePlayer.cjs +229 -0
  5. package/dist/cjs/SmorePlayer.cjs.map +1 -0
  6. package/dist/cjs/components/IframeGameBridge.cjs +115 -0
  7. package/dist/cjs/components/IframeGameBridge.cjs.map +1 -0
  8. package/dist/cjs/context/RoomProvider.cjs +3 -3
  9. package/dist/cjs/context/RoomProvider.cjs.map +1 -1
  10. package/dist/cjs/controller.cjs +379 -0
  11. package/dist/cjs/controller.cjs.map +1 -0
  12. package/dist/cjs/hooks/useGameHost.cjs +86 -13
  13. package/dist/cjs/hooks/useGameHost.cjs.map +1 -1
  14. package/dist/cjs/hooks/useGamePlayer.cjs +60 -4
  15. package/dist/cjs/hooks/useGamePlayer.cjs.map +1 -1
  16. package/dist/cjs/iframe/index.cjs +50 -316
  17. package/dist/cjs/iframe/index.cjs.map +1 -1
  18. package/dist/cjs/index.cjs +8 -22
  19. package/dist/cjs/index.cjs.map +1 -1
  20. package/dist/cjs/screen.cjs +526 -0
  21. package/dist/cjs/screen.cjs.map +1 -0
  22. package/dist/cjs/testing.cjs +257 -0
  23. package/dist/cjs/testing.cjs.map +1 -0
  24. package/dist/cjs/transport/protocol.cjs.map +1 -1
  25. package/dist/cjs/utils/connectionMonitor.cjs +77 -0
  26. package/dist/cjs/utils/connectionMonitor.cjs.map +1 -0
  27. package/dist/cjs/utils/preloadAssets.cjs +66 -0
  28. package/dist/cjs/utils/preloadAssets.cjs.map +1 -0
  29. package/dist/cjs/utils/serverTime.cjs +43 -0
  30. package/dist/cjs/utils/serverTime.cjs.map +1 -0
  31. package/dist/esm/SmoreHost.js +304 -0
  32. package/dist/esm/SmoreHost.js.map +1 -0
  33. package/dist/esm/SmorePlayer.js +227 -0
  34. package/dist/esm/SmorePlayer.js.map +1 -0
  35. package/dist/esm/components/IframeGameBridge.js +113 -0
  36. package/dist/esm/components/IframeGameBridge.js.map +1 -0
  37. package/dist/esm/context/RoomProvider.js +3 -3
  38. package/dist/esm/context/RoomProvider.js.map +1 -1
  39. package/dist/esm/controller.js +376 -0
  40. package/dist/esm/controller.js.map +1 -0
  41. package/dist/esm/hooks/useGameHost.js +87 -14
  42. package/dist/esm/hooks/useGameHost.js.map +1 -1
  43. package/dist/esm/hooks/useGamePlayer.js +61 -5
  44. package/dist/esm/hooks/useGamePlayer.js.map +1 -1
  45. package/dist/esm/iframe/index.js +51 -314
  46. package/dist/esm/iframe/index.js.map +1 -1
  47. package/dist/esm/index.js +3 -8
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/screen.js +523 -0
  50. package/dist/esm/screen.js.map +1 -0
  51. package/dist/esm/testing.js +254 -0
  52. package/dist/esm/testing.js.map +1 -0
  53. package/dist/esm/transport/protocol.js.map +1 -1
  54. package/dist/esm/utils/connectionMonitor.js +75 -0
  55. package/dist/esm/utils/connectionMonitor.js.map +1 -0
  56. package/dist/esm/utils/preloadAssets.js +63 -0
  57. package/dist/esm/utils/preloadAssets.js.map +1 -0
  58. package/dist/esm/utils/serverTime.js +41 -0
  59. package/dist/esm/utils/serverTime.js.map +1 -0
  60. package/dist/types/SmoreHost.d.ts +187 -0
  61. package/dist/types/SmoreHost.d.ts.map +1 -0
  62. package/dist/types/SmorePlayer.d.ts +146 -0
  63. package/dist/types/SmorePlayer.d.ts.map +1 -0
  64. package/dist/types/components/IframeGameBridge.d.ts +2 -2
  65. package/dist/types/components/IframeGameBridge.d.ts.map +1 -1
  66. package/dist/types/components/index.d.ts +2 -4
  67. package/dist/types/components/index.d.ts.map +1 -1
  68. package/dist/types/context/RoomProvider.d.ts +3 -3
  69. package/dist/types/context/RoomProvider.d.ts.map +1 -1
  70. package/dist/types/controller.d.ts +78 -0
  71. package/dist/types/controller.d.ts.map +1 -0
  72. package/dist/types/hooks/useGameHost.d.ts +33 -7
  73. package/dist/types/hooks/useGameHost.d.ts.map +1 -1
  74. package/dist/types/hooks/useGamePlayer.d.ts +29 -3
  75. package/dist/types/hooks/useGamePlayer.d.ts.map +1 -1
  76. package/dist/types/iframe/index.d.ts +10 -10
  77. package/dist/types/iframe/index.d.ts.map +1 -1
  78. package/dist/types/iframe/vanilla.d.ts +12 -4
  79. package/dist/types/iframe/vanilla.d.ts.map +1 -1
  80. package/dist/types/index.d.ts +36 -21
  81. package/dist/types/index.d.ts.map +1 -1
  82. package/dist/types/screen.d.ts +79 -0
  83. package/dist/types/screen.d.ts.map +1 -0
  84. package/dist/types/testing.d.ts +61 -0
  85. package/dist/types/testing.d.ts.map +1 -0
  86. package/dist/types/transport/protocol.d.ts +2 -5
  87. package/dist/types/transport/protocol.d.ts.map +1 -1
  88. package/dist/types/types.d.ts +869 -4
  89. package/dist/types/types.d.ts.map +1 -1
  90. package/dist/types/utils/connectionMonitor.d.ts +57 -0
  91. package/dist/types/utils/connectionMonitor.d.ts.map +1 -0
  92. package/dist/types/utils/index.d.ts +7 -0
  93. package/dist/types/utils/index.d.ts.map +1 -0
  94. package/dist/types/utils/preloadAssets.d.ts +29 -0
  95. package/dist/types/utils/preloadAssets.d.ts.map +1 -0
  96. package/dist/types/utils/serverTime.d.ts +28 -0
  97. package/dist/types/utils/serverTime.d.ts.map +1 -0
  98. package/dist/umd/smore-sdk-iframe.umd.js +54 -317
  99. package/dist/umd/smore-sdk-iframe.umd.js.map +1 -1
  100. package/dist/umd/smore-sdk-iframe.umd.min.js +1 -1
  101. package/dist/umd/smore-sdk-iframe.umd.min.js.map +1 -1
  102. package/dist/umd/smore-sdk-vanilla.umd.js +1166 -117
  103. package/dist/umd/smore-sdk-vanilla.umd.js.map +1 -1
  104. package/dist/umd/smore-sdk-vanilla.umd.min.js +1 -1
  105. package/dist/umd/smore-sdk-vanilla.umd.min.js.map +1 -1
  106. package/dist/umd/smore-sdk.umd.js +1139 -602
  107. package/dist/umd/smore-sdk.umd.js.map +1 -1
  108. package/dist/umd/smore-sdk.umd.min.js +1 -1
  109. package/dist/umd/smore-sdk.umd.min.js.map +1 -1
  110. package/package.json +1 -26
@@ -1 +1 @@
1
- {"version":3,"file":"smore-sdk.umd.min.js","sources":["../../../../node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.js","../../src/transport/DirectTransport.ts","../../src/context/RoomProvider.tsx","../../src/hooks/useGameHost.ts","../../src/hooks/useGamePlayer.ts","../../src/components/DirectionPad.tsx","../../src/components/HoldButton.tsx","../../src/transport/PostMessageTransport.ts","../../src/transport/protocol.ts","../../src/events.ts","../../src/components/SwipeArea.tsx","../../src/components/TapButton.tsx","../../src/hooks/useExternalGames.ts"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n","/**\n * DirectTransport - Wraps a Socket.IO socket as a Transport.\n * Used by bundled (internal) games. Behaviour is identical to using socket directly.\n */\n\nimport type { Socket } from 'socket.io-client';\nimport type { Transport, TransportEventHandler } from './types';\n\nexport class DirectTransport implements Transport {\n constructor(private socket: Socket) {}\n\n emit(event: string, ...args: any[]): void {\n this.socket.emit(event, ...args);\n }\n\n on(event: string, handler: TransportEventHandler): void {\n this.socket.on(event, handler);\n }\n\n off(event: string, handler?: TransportEventHandler): void {\n if (handler) {\n this.socket.off(event, handler);\n } else {\n this.socket.off(event);\n }\n }\n}\n","/**\n * RoomProvider - SDK Room Context\n *\n * Foundation context that all other SDK hooks depend on.\n * Provides room state (players, roomCode, leaderId) for both host and player sides.\n *\n * Also provides a Transport abstraction via TransportContext.\n * - HostRoomProvider / PlayerRoomProvider create a DirectTransport from the socket.\n * - IframeRoomProvider (external) provides a PostMessageTransport.\n *\n * Usage:\n * - Host: <HostRoomProvider roomCode={...} players={...} leaderId={...} socket={...}>\n * - Player: <PlayerRoomProvider roomCode={...} players={...} leaderId={...} mySessionId={...} isLeader={...} socket={...} isConnected={...}>\n */\n\nimport React, { createContext, useContext, useMemo } from 'react';\nimport type { Player } from '@smoregg/shared';\nimport type { Socket } from 'socket.io-client';\nimport type { Transport } from '../transport/types';\nimport { DirectTransport } from '../transport/DirectTransport';\n\n// ===== Transport Context =====\n\nconst TransportContext = createContext<Transport | null>(null);\n\nexport function useTransport(): Transport {\n const transport = useContext(TransportContext);\n if (!transport) {\n throw new Error('useTransport must be used within a RoomProvider that supplies a Transport');\n }\n return transport;\n}\n\nexport { TransportContext };\n\n// ===== State Types =====\n\nexport interface RoomState {\n roomCode: string;\n players: Player[];\n connectedPlayers: Player[];\n leaderId: string | null;\n}\n\nexport interface HostRoomState extends RoomState {\n socket: Socket;\n}\n\nexport interface PlayerRoomState extends RoomState {\n mySessionId: string;\n isLeader: boolean;\n socket: Socket;\n isConnected: boolean;\n}\n\nexport interface RoomContextValue {\n roomCode: string;\n players: Player[];\n connectedPlayers: Player[];\n leaderId: string | null;\n side: 'host' | 'player';\n host: HostRoomState | null;\n player: PlayerRoomState | null;\n}\n\n// ===== Context =====\n\nexport const RoomContext = createContext<RoomContextValue | null>(null);\n\n// ===== Host Provider =====\n\ninterface HostRoomProviderProps {\n roomCode: string;\n players: Player[];\n leaderId: string | null;\n socket: Socket;\n children: React.ReactNode;\n}\n\nexport const HostRoomProvider: React.FC<HostRoomProviderProps> = ({\n roomCode,\n players,\n leaderId,\n socket,\n children,\n}) => {\n const connectedPlayers = useMemo(\n () => players.filter((p) => p.connected !== false),\n [players]\n );\n\n const hostState: HostRoomState = useMemo(\n () => ({ roomCode, players, connectedPlayers, leaderId, socket }),\n [roomCode, players, connectedPlayers, leaderId, socket]\n );\n\n const value: RoomContextValue = useMemo(\n () => ({\n roomCode,\n players,\n connectedPlayers,\n leaderId,\n side: 'host' as const,\n host: hostState,\n player: null,\n }),\n [roomCode, players, connectedPlayers, leaderId, hostState]\n );\n\n const transport = useMemo(() => new DirectTransport(socket), [socket]);\n\n return (\n <TransportContext.Provider value={transport}>\n <RoomContext.Provider value={value}>{children}</RoomContext.Provider>\n </TransportContext.Provider>\n );\n};\n\n// ===== Player Provider =====\n\ninterface PlayerRoomProviderProps {\n roomCode: string;\n players: Player[];\n leaderId: string | null;\n mySessionId: string;\n isLeader: boolean;\n socket: Socket;\n isConnected: boolean;\n children: React.ReactNode;\n}\n\nexport const PlayerRoomProvider: React.FC<PlayerRoomProviderProps> = ({\n roomCode,\n players,\n leaderId,\n mySessionId,\n isLeader,\n socket,\n isConnected,\n children,\n}) => {\n const connectedPlayers = useMemo(\n () => players.filter((p) => p.connected !== false),\n [players]\n );\n\n const playerState: PlayerRoomState = useMemo(\n () => ({\n roomCode,\n players,\n connectedPlayers,\n leaderId,\n mySessionId,\n isLeader,\n socket,\n isConnected,\n }),\n [roomCode, players, connectedPlayers, leaderId, mySessionId, isLeader, socket, isConnected]\n );\n\n const value: RoomContextValue = useMemo(\n () => ({\n roomCode,\n players,\n connectedPlayers,\n leaderId,\n side: 'player' as const,\n host: null,\n player: playerState,\n }),\n [roomCode, players, connectedPlayers, leaderId, playerState]\n );\n\n const transport = useMemo(() => new DirectTransport(socket), [socket]);\n\n return (\n <TransportContext.Provider value={transport}>\n <RoomContext.Provider value={value}>{children}</RoomContext.Provider>\n </TransportContext.Provider>\n );\n};\n\n// ===== Hooks =====\n\nexport function useRoom(): RoomContextValue {\n const context = useContext(RoomContext);\n if (!context) {\n throw new Error('useRoom must be used within HostRoomProvider or PlayerRoomProvider');\n }\n return context;\n}\n\nexport function useHostRoom(): HostRoomState {\n const context = useRoom();\n if (context.side !== 'host' || !context.host) {\n throw new Error('useHostRoom must be used within HostRoomProvider');\n }\n return context.host;\n}\n\nexport function usePlayerRoom(): PlayerRoomState {\n const context = useRoom();\n if (context.side !== 'player' || !context.player) {\n throw new Error('usePlayerRoom must be used within PlayerRoomProvider');\n }\n return context.player;\n}\n","/**\n * useGameHost - Host-side game hook for the S'MORE SDK\n *\n * Provides host game developers with:\n * - Room state access (players, leaderId, roomCode)\n * - Event listeners for player inputs\n * - Broadcasting to all/specific players\n * - Game lifecycle management\n *\n * Internally uses Transport abstraction so the same API works over\n * Socket.IO (bundled games) or postMessage (iframe games).\n */\nimport { useEffect, useCallback, useRef } from 'react';\nimport { useHostRoom, useTransport } from '../context/RoomProvider';\nimport type { Player } from '../types';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SYSTEM_PREFIX = 'smore:';\n\n// System events (internal use only)\nconst SYSTEM_EVENTS = {\n READY: `${SYSTEM_PREFIX}ready`,\n PLAYER_JOIN: `${SYSTEM_PREFIX}player-join`,\n PLAYER_LEAVE: `${SYSTEM_PREFIX}player-leave`,\n GAME_OVER: `${SYSTEM_PREFIX}game-over`,\n RETURN_TO_LOBBY: `${SYSTEM_PREFIX}return-to-lobby`,\n SEND_TO_PLAYER: `${SYSTEM_PREFIX}send-to-player`,\n BROADCAST: `${SYSTEM_PREFIX}broadcast`,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validates event name format.\n * Rules:\n * - Only English letters (a-z, A-Z), hyphens (-), and underscores (_) allowed\n * - Must start and end with a letter (no leading/trailing - or _)\n * - Colons are reserved for system prefix\n */\nconst EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z_-]*[a-zA-Z])?$/;\n\nfunction validateEventName(event: string): void {\n if (!EVENT_NAME_REGEX.test(event)) {\n throw new Error(\n `[SDK] Invalid event name \"${event}\". Event names must:\\n` +\n ` - Only contain letters (a-z, A-Z), hyphens (-), and underscores (_)\\n` +\n ` - Start and end with a letter (no leading/trailing - or _)`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UseGameHostConfig<T extends Record<string, any> = Record<string, any>> {\n /** Called when the host is ready and connected. */\n onReady?: () => void;\n\n /** Called when a player joins the room. */\n onPlayerJoin?: (player: Player) => void;\n\n /** Called when a player leaves the room. */\n onPlayerLeave?: (sessionId: string) => void;\n\n /**\n * Event listeners for player inputs.\n * Keys are event names (without prefix), values are handler functions.\n * Handler receives (sessionId, data) where sessionId identifies the player.\n */\n listeners?: { [K in keyof T]?: (sessionId: string, data: T[K]) => void };\n}\n\nexport interface UseGameHostReturn {\n /** List of all players in the room. */\n players: Player[];\n\n /** The session ID of the room leader (host). */\n leaderId: string | null;\n\n /** The room code. */\n roomCode: string;\n\n /**\n * Emit an event to all players.\n * Event name must not contain colons.\n */\n emit: (event: string, data?: any) => void;\n\n /**\n * Send an event to a specific player.\n * Event name must not contain colons.\n */\n sendToPlayer: (sessionId: string, event: string, data?: any) => void;\n\n /** Signal game over with optional results. */\n gameOver: (results?: any) => void;\n\n /** Return all players to lobby. */\n returnToLobby: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useGameHost<T extends Record<string, any> = Record<string, any>>(\n config: UseGameHostConfig<T> = {}\n): UseGameHostReturn {\n const { onReady, onPlayerJoin, onPlayerLeave, listeners } = config;\n\n const hostRoom = useHostRoom();\n const transport = useTransport();\n\n // Stable refs to avoid stale closures in listeners\n const onReadyRef = useRef(onReady);\n const onPlayerJoinRef = useRef(onPlayerJoin);\n const onPlayerLeaveRef = useRef(onPlayerLeave);\n const listenersRef = useRef(listeners);\n\n useEffect(() => {\n onReadyRef.current = onReady;\n }, [onReady]);\n useEffect(() => {\n onPlayerJoinRef.current = onPlayerJoin;\n }, [onPlayerJoin]);\n useEffect(() => {\n onPlayerLeaveRef.current = onPlayerLeave;\n }, [onPlayerLeave]);\n useEffect(() => {\n listenersRef.current = listeners;\n }, [listeners]);\n\n // -------------------------------------------------------------------------\n // System event listeners (player lifecycle)\n // -------------------------------------------------------------------------\n useEffect(() => {\n if (!transport) return;\n\n const handleReady = () => {\n onReadyRef.current?.();\n };\n\n const handlePlayerJoin = (data: { player: Player }) => {\n onPlayerJoinRef.current?.(data.player);\n };\n\n const handlePlayerLeave = (data: { sessionId: string }) => {\n onPlayerLeaveRef.current?.(data.sessionId);\n };\n\n transport.on(SYSTEM_EVENTS.READY, handleReady);\n transport.on(SYSTEM_EVENTS.PLAYER_JOIN, handlePlayerJoin);\n transport.on(SYSTEM_EVENTS.PLAYER_LEAVE, handlePlayerLeave);\n\n // Also listen to legacy room events for backward compatibility\n transport.on('room:player-left', (data: any) => {\n onPlayerLeaveRef.current?.(data?.sessionId ?? data?.playerId);\n });\n transport.on('room:player-joined', (data: any) => {\n if (data?.player) {\n onPlayerJoinRef.current?.(data.player);\n }\n });\n\n return () => {\n transport.off(SYSTEM_EVENTS.READY, handleReady);\n transport.off(SYSTEM_EVENTS.PLAYER_JOIN, handlePlayerJoin);\n transport.off(SYSTEM_EVENTS.PLAYER_LEAVE, handlePlayerLeave);\n transport.off('room:player-left');\n transport.off('room:player-joined');\n };\n }, [transport]);\n\n // -------------------------------------------------------------------------\n // User event listeners\n // -------------------------------------------------------------------------\n useEffect(() => {\n if (!transport || !listeners) return;\n\n const entries = Object.entries(listeners);\n const cleanups: (() => void)[] = [];\n\n for (const [event, handler] of entries) {\n if (!handler) continue;\n\n // Validate event name (no colons allowed)\n validateEventName(event);\n\n // Listen for the event directly\n // Server sends: { sessionId, ...playerData }\n const wrappedHandler = (data: { sessionId: string; [key: string]: any }) => {\n const { sessionId, ...rest } = data;\n (handler as (sessionId: string, data: any) => void)(sessionId, rest);\n };\n\n transport.on(event, wrappedHandler);\n cleanups.push(() => transport.off(event, wrappedHandler));\n }\n\n return () => {\n cleanups.forEach((cleanup) => cleanup());\n };\n }, [transport, listeners]);\n\n // -------------------------------------------------------------------------\n // Actions\n // -------------------------------------------------------------------------\n\n const emit = useCallback(\n (event: string, data?: any) => {\n validateEventName(event);\n // Broadcast to all players via system event\n transport?.emit(SYSTEM_EVENTS.BROADCAST, { event, data });\n },\n [transport]\n );\n\n const sendToPlayer = useCallback(\n (sessionId: string, event: string, data?: any) => {\n validateEventName(event);\n // Send to specific player via system event\n // The server will unwrap and send the inner event directly to the player\n transport?.emit(SYSTEM_EVENTS.SEND_TO_PLAYER, {\n targetSessionId: sessionId,\n event,\n data,\n });\n },\n [transport]\n );\n\n const gameOver = useCallback(\n (results?: any) => {\n transport?.emit(SYSTEM_EVENTS.GAME_OVER, { results });\n },\n [transport]\n );\n\n const returnToLobby = useCallback(() => {\n transport?.emit(SYSTEM_EVENTS.RETURN_TO_LOBBY, {});\n }, [transport]);\n\n return {\n players: hostRoom.players,\n leaderId: hostRoom.leaderId,\n roomCode: hostRoom.roomCode,\n emit,\n sendToPlayer,\n gameOver,\n returnToLobby,\n };\n}\n","import { useEffect, useCallback, useRef } from 'react';\nimport { usePlayerRoom } from '../context/RoomProvider';\nimport { useTransport } from '../context/RoomProvider';\nimport type { Player } from '@smoregg/shared';\n\n/**\n * Validates event name format.\n * Rules:\n * - Only English letters (a-z, A-Z), hyphens (-), and underscores (_) allowed\n * - Must start and end with a letter (no leading/trailing - or _)\n * - Colons are reserved for system prefix\n */\nconst EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z_-]*[a-zA-Z])?$/;\n\nfunction validateEventName(event: string): void {\n if (!EVENT_NAME_REGEX.test(event)) {\n throw new Error(\n `[SDK] Invalid event name \"${event}\". Event names must:\\n` +\n ` - Only contain letters (a-z, A-Z), hyphens (-), and underscores (_)\\n` +\n ` - Start and end with a letter (no leading/trailing - or _)`\n );\n }\n}\n\n// ===== Types =====\n\nexport interface UseGamePlayerConfig<T = Record<string, any>> {\n /** Called when smore:ready is received */\n onReady?: () => void;\n /** Called when a player joins */\n onPlayerJoin?: (player: Player) => void;\n /** Called when a player leaves */\n onPlayerLeave?: (sessionId: string) => void;\n /** Custom event listeners (event name -> handler) */\n listeners?: { [K in keyof T]?: (data: T[K]) => void };\n}\n\nexport interface UseGamePlayerReturn {\n /** All players in the room */\n players: Player[];\n /** Leader player's session ID */\n leaderId: string | null;\n /** Room code */\n roomCode: string;\n /** My session ID */\n mySessionId: string;\n /** Am I the leader? */\n isLeader: boolean;\n /** Emit event to host (event name must not contain ':') */\n emit: (event: string, data?: any) => void;\n}\n\n// ===== Hook =====\n\nexport function useGamePlayer<T = Record<string, any>>(\n config: UseGamePlayerConfig<T> = {}\n): UseGamePlayerReturn {\n const room = usePlayerRoom();\n const transport = useTransport();\n const { onReady, onPlayerJoin, onPlayerLeave, listeners } = config;\n\n // Use refs to avoid re-subscribing on every render\n const onReadyRef = useRef(onReady);\n const onPlayerJoinRef = useRef(onPlayerJoin);\n const onPlayerLeaveRef = useRef(onPlayerLeave);\n const listenersRef = useRef(listeners);\n\n onReadyRef.current = onReady;\n onPlayerJoinRef.current = onPlayerJoin;\n onPlayerLeaveRef.current = onPlayerLeave;\n listenersRef.current = listeners;\n\n // System event listeners (smore: prefix)\n useEffect(() => {\n if (!transport) return;\n\n const handleReady = () => {\n onReadyRef.current?.();\n };\n\n const handlePlayerJoin = (player: Player) => {\n onPlayerJoinRef.current?.(player);\n };\n\n const handlePlayerLeave = (data: { sessionId: string }) => {\n onPlayerLeaveRef.current?.(data.sessionId);\n };\n\n transport.on('smore:ready', handleReady);\n transport.on('smore:player-join', handlePlayerJoin);\n transport.on('smore:player-leave', handlePlayerLeave);\n\n return () => {\n transport.off('smore:ready', handleReady);\n transport.off('smore:player-join', handlePlayerJoin);\n transport.off('smore:player-leave', handlePlayerLeave);\n };\n }, [transport]);\n\n // User event listeners (passed through directly, no wrapping)\n useEffect(() => {\n if (!transport || !listenersRef.current) return;\n\n const cleanups: (() => void)[] = [];\n\n Object.entries(listenersRef.current).forEach(([event, handler]) => {\n if (handler) {\n transport.on(event, handler as (data: any) => void);\n cleanups.push(() => transport.off(event, handler as (data: any) => void));\n }\n });\n\n return () => cleanups.forEach((fn) => fn());\n }, [transport, listeners]);\n\n // Emit function with validation\n const emit = useCallback(\n (event: string, data?: any) => {\n if (!transport) return;\n validateEventName(event);\n transport.emit(event, data);\n },\n [transport]\n );\n\n return {\n players: room.players,\n leaderId: room.leaderId,\n roomCode: room.roomCode,\n mySessionId: room.mySessionId,\n isLeader: room.isLeader,\n emit,\n };\n}\n","/**\n * S'MORE Game SDK - DirectionPad Component\n *\n * 4-direction or 2-direction control pad\n */\n\nimport { useCallback, useRef } from 'react';\nimport type { DirectionPadProps } from '../types';\nimport styles from './DirectionPad.module.css';\n\n/**\n * DirectionPad - For directional input\n *\n * @example\n * ```tsx\n * // 4 directions\n * <DirectionPad onDirection={(dir) => send('move', { direction: dir })} />\n *\n * // Left/Right only\n * <DirectionPad leftRightOnly onDirection={(dir) => send('move', { direction: dir })} />\n * ```\n */\nexport function DirectionPad({\n onDirection,\n leftRightOnly = false,\n upDownOnly = false,\n className,\n}: DirectionPadProps) {\n const pressedRef = useRef<Set<string>>(new Set());\n\n const handlePress = useCallback((direction: 'up' | 'down' | 'left' | 'right') => {\n if (pressedRef.current.has(direction)) return;\n pressedRef.current.add(direction);\n\n // Haptic feedback\n if (navigator.vibrate) {\n navigator.vibrate(10);\n }\n\n onDirection(direction);\n }, [onDirection]);\n\n const handleRelease = useCallback((direction: string) => {\n pressedRef.current.delete(direction);\n }, []);\n\n const createButton = (direction: 'up' | 'down' | 'left' | 'right', label: string) => (\n <button\n key={direction}\n className={`${styles.dirButton} ${styles[direction]}`}\n onTouchStart={(e) => {\n e.preventDefault();\n handlePress(direction);\n }}\n onTouchEnd={(e) => {\n e.preventDefault();\n handleRelease(direction);\n }}\n onTouchCancel={() => handleRelease(direction)}\n onMouseDown={() => handlePress(direction)}\n onMouseUp={() => handleRelease(direction)}\n onMouseLeave={() => handleRelease(direction)}\n >\n {label}\n </button>\n );\n\n if (leftRightOnly) {\n return (\n <div className={`${styles.padContainer} ${styles.horizontal} ${className || ''}`}>\n {createButton('left', '◀')}\n {createButton('right', '▶')}\n </div>\n );\n }\n\n if (upDownOnly) {\n return (\n <div className={`${styles.padContainer} ${styles.vertical} ${className || ''}`}>\n {createButton('up', '▲')}\n {createButton('down', '▼')}\n </div>\n );\n }\n\n return (\n <div className={`${styles.padContainer} ${styles.full} ${className || ''}`}>\n <div className={styles.row}>\n {createButton('up', '▲')}\n </div>\n <div className={styles.row}>\n {createButton('left', '◀')}\n {createButton('right', '▶')}\n </div>\n <div className={styles.row}>\n {createButton('down', '▼')}\n </div>\n </div>\n );\n}\n\nexport default DirectionPad;\n","/**\n * S'MORE Game SDK - HoldButton Component\n *\n * Button that detects hold start/end events\n */\n\nimport { useRef, useCallback } from 'react';\nimport type React from 'react';\nimport type { HoldButtonProps } from '../types';\nimport styles from './HoldButton.module.css';\n\n/**\n * HoldButton - For hold/release input patterns\n *\n * @example\n * ```tsx\n * <HoldButton\n * onHoldStart={() => send('charge', { type: 'start' })}\n * onHoldEnd={() => send('charge', { type: 'end' })}\n * >\n * HOLD\n * </HoldButton>\n * ```\n */\nexport function HoldButton({\n onHoldStart,\n onHoldEnd,\n children,\n className,\n disabled = false,\n}: HoldButtonProps) {\n const isHolding = useRef(false);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const startHold = useCallback(() => {\n if (disabled || isHolding.current) return;\n isHolding.current = true;\n buttonRef.current?.classList.add(styles.pressed || '');\n\n // Haptic feedback\n if (navigator.vibrate) {\n navigator.vibrate(10);\n }\n\n onHoldStart();\n }, [onHoldStart, disabled]);\n\n const endHold = useCallback(() => {\n if (!isHolding.current) return;\n isHolding.current = false;\n buttonRef.current?.classList.remove(styles.pressed || '');\n onHoldEnd();\n }, [onHoldEnd]);\n\n const handleTouchStart = useCallback((e: React.TouchEvent) => {\n e.preventDefault();\n startHold();\n }, [startHold]);\n\n const handleTouchEnd = useCallback((e: React.TouchEvent) => {\n e.preventDefault();\n endHold();\n }, [endHold]);\n\n return (\n <button\n ref={buttonRef}\n className={`${styles.holdButton} ${className || ''} ${disabled ? styles.disabled : ''}`}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n onTouchCancel={handleTouchEnd}\n onMouseDown={startHold}\n onMouseUp={endHold}\n onMouseLeave={endHold}\n disabled={disabled}\n >\n {children}\n </button>\n );\n}\n\nexport default HoldButton;\n","/**\n * PostMessageTransport - Transport over window.postMessage for iframe-hosted games.\n *\n * Used inside an iframe. Sends `smore:emit` to parent and listens for `smore:event` from parent.\n */\n\nimport type { Transport, TransportEventHandler } from './types';\nimport type { SmoreEventMessage, SmoreAckMessage } from './protocol';\nimport { isSmoreMessage } from './protocol';\n\nexport class PostMessageTransport implements Transport {\n private handlers = new Map<string, Set<TransportEventHandler>>();\n private ackCallbacks = new Map<string, (...args: any[]) => void>();\n private ackCounter = 0;\n private parentOrigin: string;\n private boundMessageHandler: (e: MessageEvent) => void;\n\n constructor(parentOrigin: string = '*') {\n this.parentOrigin = parentOrigin;\n this.boundMessageHandler = this.handleMessage.bind(this);\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n emit(event: string, ...args: any[]): void {\n // Detect if last arg is a callback (ack pattern)\n let data: any = args[0];\n let ackId: string | undefined;\n\n if (args.length >= 2 && typeof args[args.length - 1] === 'function') {\n data = args.length === 2 ? args[0] : args[0];\n const callback = args[args.length - 1];\n ackId = `ack_${++this.ackCounter}`;\n this.ackCallbacks.set(ackId, callback);\n }\n\n window.parent.postMessage(\n { type: 'smore:emit', payload: { event, data, ackId } },\n this.parentOrigin,\n );\n }\n\n on(event: string, handler: TransportEventHandler): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler);\n }\n\n off(event: string, handler?: TransportEventHandler): void {\n if (!handler) {\n this.handlers.delete(event);\n return;\n }\n this.handlers.get(event)?.delete(handler);\n }\n\n destroy(): void {\n window.removeEventListener('message', this.boundMessageHandler);\n this.handlers.clear();\n this.ackCallbacks.clear();\n }\n\n private handleMessage(e: MessageEvent): void {\n // Origin validation: only accept messages from the expected parent\n if (this.parentOrigin !== '*' && e.origin !== this.parentOrigin) return;\n\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:event') {\n const { event, data } = (msg as SmoreEventMessage).payload;\n const set = this.handlers.get(event);\n if (set) {\n set.forEach((handler) => handler(data));\n }\n } else if (msg.type === 'smore:ack') {\n const { ackId, data } = (msg as SmoreAckMessage).payload;\n const cb = this.ackCallbacks.get(ackId);\n if (cb) {\n this.ackCallbacks.delete(ackId);\n cb(data);\n }\n }\n }\n}\n","/**\n * postMessage protocol types for iframe ↔ parent communication.\n */\n\nexport const SMORE_MSG_PREFIX = 'smore:' as const;\n\nexport interface SmoreReadyMessage {\n type: 'smore:ready';\n}\n\nexport interface SmoreInitMessage {\n type: 'smore:init';\n payload: {\n side: 'host' | 'player';\n roomCode: string;\n players: any[];\n leaderId: string | null;\n mySessionId?: string;\n isLeader?: boolean;\n };\n}\n\nexport interface SmoreEmitMessage {\n type: 'smore:emit';\n payload: {\n event: string;\n data?: any;\n ackId?: string;\n };\n}\n\nexport interface SmoreEventMessage {\n type: 'smore:event';\n payload: {\n event: string;\n data?: any;\n };\n}\n\nexport interface SmoreAckMessage {\n type: 'smore:ack';\n payload: {\n ackId: string;\n data?: any;\n };\n}\n\nexport interface SmoreUpdateMessage {\n type: 'smore:update';\n payload: {\n players?: any[];\n leaderId?: string | null;\n };\n}\n\nexport interface SmoreLoadedMessage {\n type: 'smore:loaded';\n}\n\nexport type SmoreMessage =\n | SmoreReadyMessage\n | SmoreInitMessage\n | SmoreEmitMessage\n | SmoreEventMessage\n | SmoreAckMessage\n | SmoreUpdateMessage\n | SmoreLoadedMessage;\n\nexport function isSmoreMessage(data: any): data is SmoreMessage {\n return data && typeof data === 'object' && typeof data.type === 'string' && data.type.startsWith(SMORE_MSG_PREFIX);\n}\n","/**\n * SDK 시스템 이벤트 상수\n * 모든 시스템 이벤트는 'smore:' prefix 사용\n * 유저 이벤트는 ':' 사용 불가\n */\n\nexport const SMORE_EVENTS = {\n // 게임 lifecycle\n READY: 'smore:ready',\n GAME_OVER: 'smore:game-over',\n RETURN_TO_LOBBY: 'smore:return-to-lobby',\n\n // 플레이어 관리\n PLAYER_JOIN: 'smore:player-join',\n PLAYER_LEAVE: 'smore:player-leave',\n\n // 특정 플레이어에게 전송 (내부용)\n SEND_TO_PLAYER: 'smore:send-to-player',\n\n // 초기화\n INIT: 'smore:init',\n UPDATE: 'smore:update',\n} as const;\n\nexport type SmoreEvent = typeof SMORE_EVENTS[keyof typeof SMORE_EVENTS];\n\n/**\n * 유저 이벤트명 검증\n * ':' 포함 시 에러, '_'와 '-'는 허용\n */\nexport function validateUserEvent(event: string): void {\n if (event.includes(':')) {\n throw new Error(\n `Invalid event name \"${event}\": User events cannot contain ':'. ` +\n `Use '_' or '-' instead. System events use 'smore:' prefix.`\n );\n }\n}\n\n/**\n * 시스템 이벤트인지 확인\n */\nexport function isSystemEvent(event: string): boolean {\n return event.startsWith('smore:');\n}\n","/**\n * S'MORE Game SDK - SwipeArea Component\n *\n * Detects swipe gestures\n */\n\nimport { useRef, useCallback } from 'react';\nimport type React from 'react';\nimport type { SwipeAreaProps } from '../types';\nimport styles from './SwipeArea.module.css';\n\n/**\n * SwipeArea - For swipe gesture input\n *\n * @example\n * ```tsx\n * <SwipeArea onSwipe={(dir) => send('swipe', { direction: dir })}>\n * <div>Swipe here!</div>\n * </SwipeArea>\n * ```\n */\nexport function SwipeArea({\n onSwipe,\n threshold = 50,\n children,\n className,\n}: SwipeAreaProps) {\n const touchStart = useRef<{ x: number; y: number } | null>(null);\n\n const handleTouchStart = useCallback((e: React.TouchEvent) => {\n const touch = e.touches[0];\n if (!touch) return;\n touchStart.current = { x: touch.clientX, y: touch.clientY };\n }, []);\n\n const handleTouchEnd = useCallback((e: React.TouchEvent) => {\n if (!touchStart.current) return;\n\n const touch = e.changedTouches[0];\n if (!touch) return;\n const deltaX = touch.clientX - touchStart.current.x;\n const deltaY = touch.clientY - touchStart.current.y;\n\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n // Check if swipe exceeds threshold\n if (absX < threshold && absY < threshold) {\n touchStart.current = null;\n return;\n }\n\n // Determine direction\n let direction: 'up' | 'down' | 'left' | 'right';\n if (absX > absY) {\n direction = deltaX > 0 ? 'right' : 'left';\n } else {\n direction = deltaY > 0 ? 'down' : 'up';\n }\n\n // Haptic feedback\n if (navigator.vibrate) {\n navigator.vibrate(15);\n }\n\n onSwipe(direction);\n touchStart.current = null;\n }, [onSwipe, threshold]);\n\n return (\n <div\n className={`${styles.swipeArea} ${className || ''}`}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n onTouchCancel={() => { touchStart.current = null; }}\n >\n {children}\n </div>\n );\n}\n\nexport default SwipeArea;\n","/**\n * S'MORE Game SDK - TapButton Component\n *\n * Optimized touch button with haptic feedback\n */\n\nimport { useRef, useCallback } from 'react';\nimport type React from 'react';\nimport type { TapButtonProps } from '../types';\nimport styles from './TapButton.module.css';\n\n/**\n * TapButton - Optimized for mobile touch input\n *\n * @example\n * ```tsx\n * <TapButton onTap={() => send('tap')}>\n * TAP!\n * </TapButton>\n * ```\n */\nexport function TapButton({\n onTap,\n children,\n className,\n disabled = false,\n}: TapButtonProps) {\n const isPressed = useRef(false);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const handleTouchStart = useCallback((e: React.TouchEvent) => {\n if (disabled) return;\n e.preventDefault();\n isPressed.current = true;\n buttonRef.current?.classList.add(styles.pressed || '');\n\n // Haptic feedback\n if (navigator.vibrate) {\n navigator.vibrate(10);\n }\n\n onTap();\n }, [onTap, disabled]);\n\n const handleTouchEnd = useCallback((e: React.TouchEvent) => {\n e.preventDefault();\n isPressed.current = false;\n buttonRef.current?.classList.remove(styles.pressed || '');\n }, []);\n\n const handleMouseDown = useCallback(() => {\n if (disabled) return;\n isPressed.current = true;\n buttonRef.current?.classList.add(styles.pressed || '');\n onTap();\n }, [onTap, disabled]);\n\n const handleMouseUp = useCallback(() => {\n isPressed.current = false;\n buttonRef.current?.classList.remove(styles.pressed || '');\n }, []);\n\n return (\n <button\n ref={buttonRef}\n className={`${styles.tapButton} ${className || ''} ${disabled ? styles.disabled : ''}`}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n onTouchCancel={handleTouchEnd}\n onMouseDown={handleMouseDown}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n disabled={disabled}\n >\n {children}\n </button>\n );\n}\n\nexport default TapButton;\n","/**\n * useExternalGames - Fetches external games from the server API.\n *\n * Returns a list of GameMetadata-compatible objects for external (iframe) games.\n * Consumers merge this with their local GAMES array.\n */\n\nimport { useState, useEffect } from 'react';\n\nexport interface ExternalGameMetadata {\n id: string;\n title: string;\n description: string;\n minPlayers: number;\n maxPlayers: number;\n thumbnail: string;\n categories: string[];\n type: 'external';\n hostUrl: string | null;\n playerUrl: string | null;\n available: boolean;\n rating: number;\n heroRequired: boolean;\n}\n\nexport interface UseExternalGamesConfig {\n serverUrl: string;\n enabled?: boolean;\n}\n\nexport function useExternalGames(config: UseExternalGamesConfig): {\n games: ExternalGameMetadata[];\n loading: boolean;\n error: string | null;\n refresh: () => void;\n} {\n const { serverUrl, enabled = true } = config;\n const [games, setGames] = useState<ExternalGameMetadata[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [refreshKey, setRefreshKey] = useState(0);\n\n useEffect(() => {\n if (!enabled) return;\n\n let cancelled = false;\n setLoading(true);\n\n fetch(`${serverUrl}/api/games`)\n .then((res) => res.json())\n .then((data) => {\n if (cancelled) return;\n const external: ExternalGameMetadata[] = (data.games || []).map((g: any) => ({\n id: g.id,\n title: g.title,\n description: g.description || '',\n minPlayers: g.minPlayers || 2,\n maxPlayers: g.maxPlayers || 8,\n thumbnail: g.thumbnail || '/thumbnails/g8.jpeg',\n categories: g.categories || ['party'],\n type: 'external' as const,\n hostUrl: g.hostUrl,\n playerUrl: g.playerUrl,\n available: !!(g.hostUrl && g.playerUrl),\n rating: 4.0,\n heroRequired: false,\n }));\n setGames(external);\n setError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setError(err.message);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => { cancelled = true; };\n }, [serverUrl, enabled, refreshKey]);\n\n const refresh = () => setRefreshKey((k) => k + 1);\n\n return { games, loading, error, refresh };\n}\n"],"names":["styleInject","css","ref","insertAt","document","head","getElementsByTagName","style","createElement","type","firstChild","insertBefore","appendChild","styleSheet","cssText","createTextNode","DirectTransport","constructor","socket","this","emit","event","args","on","handler","off","TransportContext","createContext","useTransport","transport","useContext","Error","RoomContext","useRoom","context","useHostRoom","side","host","usePlayerRoom","player","SYSTEM_PREFIX","SYSTEM_EVENTS","READY","PLAYER_JOIN","PLAYER_LEAVE","GAME_OVER","RETURN_TO_LOBBY","SEND_TO_PLAYER","BROADCAST","EVENT_NAME_REGEX","validateEventName","test","onDirection","leftRightOnly","upDownOnly","className","pressedRef","useRef","Set","handlePress","useCallback","direction","current","has","add","navigator","vibrate","handleRelease","delete","createButton","label","jsx","styles","dirButton","onTouchStart","e","preventDefault","onTouchEnd","onTouchCancel","onMouseDown","onMouseUp","onMouseLeave","children","jsxs","padContainer","horizontal","vertical","full","row","onHoldStart","onHoldEnd","disabled","isHolding","buttonRef","startHold","classList","endHold","remove","handleTouchStart","handleTouchEnd","roomCode","players","leaderId","connectedPlayers","useMemo","filter","p","connected","hostState","value","Provider","mySessionId","isLeader","isConnected","playerState","handlers","Map","ackCallbacks","ackCounter","parentOrigin","boundMessageHandler","handleMessage","bind","window","addEventListener","ackId","data","length","callback","set","parent","postMessage","payload","get","destroy","removeEventListener","clear","origin","msg","startsWith","forEach","cb","INIT","UPDATE","onSwipe","threshold","touchStart","touch","touches","x","clientX","y","clientY","changedTouches","deltaX","deltaY","absX","Math","abs","absY","onTap","isPressed","handleMouseDown","handleMouseUp","config","serverUrl","enabled","games","setGames","useState","loading","setLoading","error","setError","refreshKey","setRefreshKey","useEffect","cancelled","fetch","then","res","json","external","map","g","id","title","description","minPlayers","maxPlayers","thumbnail","categories","hostUrl","playerUrl","available","rating","heroRequired","catch","err","message","finally","refresh","k","onReady","onPlayerJoin","onPlayerLeave","listeners","hostRoom","onReadyRef","onPlayerJoinRef","onPlayerLeaveRef","listenersRef","handleReady","handlePlayerJoin","handlePlayerLeave","sessionId","playerId","entries","Object","cleanups","wrappedHandler","rest","push","cleanup","sendToPlayer","targetSessionId","gameOver","results","returnToLobby","room","fn","includes"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"smore-sdk.umd.min.js","sources":["../../src/transport/protocol.ts","../../src/transport/PostMessageTransport.ts","../../src/screen.ts","../../src/controller.ts","../../src/transport/DirectTransport.ts","../../src/events.ts","../../src/testing.ts"],"sourcesContent":["/**\n * postMessage protocol types for iframe ↔ parent communication.\n */\n\nexport const SMORE_MSG_PREFIX = 'smore:' as const;\n\nexport interface SmoreReadyMessage {\n type: 'smore:ready';\n}\n\nexport interface SmoreInitMessage {\n type: 'smore:init';\n payload: {\n side: 'host' | 'player';\n roomCode: string;\n players: any[];\n leaderId: string | null;\n myIndex?: number;\n isLeader?: boolean;\n };\n}\n\nexport interface SmoreEmitMessage {\n type: 'smore:emit';\n payload: {\n event: string;\n data?: any;\n ackId?: string;\n };\n}\n\nexport interface SmoreEventMessage {\n type: 'smore:event';\n payload: {\n event: string;\n data?: any;\n };\n}\n\nexport interface SmoreAckMessage {\n type: 'smore:ack';\n payload: {\n ackId: string;\n data?: any;\n };\n}\n\nexport interface SmoreUpdateMessage {\n type: 'smore:update';\n payload: {\n players?: any[];\n leaderId?: string | null;\n };\n}\n\n// DEPRECATED: SmoreLoadedMessage removed - no longer used in protocol\n// Previously: interface SmoreLoadedMessage { type: 'smore:loaded' }\n\nexport type SmoreMessage =\n | SmoreReadyMessage\n | SmoreInitMessage\n | SmoreEmitMessage\n | SmoreEventMessage\n | SmoreAckMessage\n | SmoreUpdateMessage;\n\nexport function isSmoreMessage(data: any): data is SmoreMessage {\n return data && typeof data === 'object' && typeof data.type === 'string' && data.type.startsWith(SMORE_MSG_PREFIX);\n}\n","/**\n * PostMessageTransport - Transport over window.postMessage for iframe-hosted games.\n *\n * Used inside an iframe. Sends `smore:emit` to parent and listens for `smore:event` from parent.\n */\n\nimport type { Transport, TransportEventHandler } from './types';\nimport type { SmoreEventMessage, SmoreAckMessage } from './protocol';\nimport { isSmoreMessage } from './protocol';\n\nexport class PostMessageTransport implements Transport {\n private handlers = new Map<string, Set<TransportEventHandler>>();\n private ackCallbacks = new Map<string, (...args: any[]) => void>();\n private ackCounter = 0;\n private parentOrigin: string;\n private boundMessageHandler: (e: MessageEvent) => void;\n\n constructor(parentOrigin: string = '*') {\n this.parentOrigin = parentOrigin;\n this.boundMessageHandler = this.handleMessage.bind(this);\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n emit(event: string, ...args: any[]): void {\n // Detect if last arg is a callback (ack pattern)\n let data: any = args[0];\n let ackId: string | undefined;\n\n if (args.length >= 2 && typeof args[args.length - 1] === 'function') {\n data = args.length === 2 ? args[0] : args[0];\n const callback = args[args.length - 1];\n ackId = `ack_${++this.ackCounter}`;\n this.ackCallbacks.set(ackId, callback);\n }\n\n window.parent.postMessage(\n { type: 'smore:emit', payload: { event, data, ackId } },\n this.parentOrigin,\n );\n }\n\n on(event: string, handler: TransportEventHandler): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler);\n }\n\n off(event: string, handler?: TransportEventHandler): void {\n if (!handler) {\n this.handlers.delete(event);\n return;\n }\n this.handlers.get(event)?.delete(handler);\n }\n\n destroy(): void {\n window.removeEventListener('message', this.boundMessageHandler);\n this.handlers.clear();\n this.ackCallbacks.clear();\n }\n\n private handleMessage(e: MessageEvent): void {\n // Origin validation: only accept messages from the expected parent\n if (this.parentOrigin !== '*' && e.origin !== this.parentOrigin) return;\n\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:event') {\n const { event, data } = (msg as SmoreEventMessage).payload;\n const set = this.handlers.get(event);\n if (set) {\n set.forEach((handler) => handler(data));\n }\n } else if (msg.type === 'smore:ack') {\n const { ackId, data } = (msg as SmoreAckMessage).payload;\n const cb = this.ackCallbacks.get(ackId);\n if (cb) {\n this.ackCallbacks.delete(ackId);\n cb(data);\n }\n }\n }\n}\n","/**\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 SmoreError,\n SmoreErrorCode,\n DebugOptions,\n LogLevel,\n} from './types';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport { isSmoreMessage, type SmoreInitMessage, type SmoreUpdateMessage } from './transport/protocol';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst SYSTEM_PREFIX = 'smore:';\n\nconst SYSTEM_EVENTS = {\n READY: `${SYSTEM_PREFIX}ready`,\n PLAYER_JOIN: `${SYSTEM_PREFIX}player-join`,\n PLAYER_LEAVE: `${SYSTEM_PREFIX}player-leave`,\n PLAYER_RECONNECT: `${SYSTEM_PREFIX}player-reconnect`,\n GAME_OVER: `${SYSTEM_PREFIX}game-over`,\n} as const;\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// ERROR CLASS\n// =============================================================================\n\n/**\n * Custom error class for SDK errors with structured error handling.\n */\nexport class SmoreSDKError extends Error {\n readonly code: SmoreErrorCode;\n readonly cause?: Error;\n readonly details?: Record<string, unknown>;\n\n constructor(\n code: SmoreErrorCode,\n message: string,\n options?: { cause?: Error; details?: Record<string, unknown> }\n ) {\n super(message);\n this.name = 'SmoreSDKError';\n this.code = code;\n this.cause = options?.cause;\n this.details = options?.details;\n\n // Maintain proper stack trace in V8 environments\n const ErrorWithCapture = Error as typeof Error & {\n captureStackTrace?: (target: object, constructor?: Function) => void;\n };\n if (typeof ErrorWithCapture.captureStackTrace === 'function') {\n ErrorWithCapture.captureStackTrace(this, SmoreSDKError);\n }\n }\n\n toSmoreError(): SmoreError {\n return {\n code: this.code,\n message: this.message,\n cause: this.cause,\n details: this.details,\n };\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\nconst EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;\n\nfunction validateEventName(event: string): void {\n if (!event || typeof event !== 'string') {\n throw new SmoreSDKError('INVALID_EVENT', 'Event name must be a non-empty string');\n }\n if (!EVENT_NAME_REGEX.test(event)) {\n throw new SmoreSDKError(\n 'INVALID_EVENT',\n `Invalid event name \"${event}\". Event names must start with a letter, ` +\n `contain only letters, numbers, hyphens, or underscores, and end with a letter or number.`,\n { details: { event } }\n );\n }\n}\n\nfunction validatePlayerIndex(playerIndex: PlayerIndex, controllersCount: number): void {\n if (typeof playerIndex !== 'number' || !Number.isInteger(playerIndex)) {\n throw new SmoreSDKError('INVALID_PLAYER', 'Player index must be an integer');\n }\n if (playerIndex < 0 || playerIndex >= controllersCount) {\n throw new SmoreSDKError(\n 'INVALID_PLAYER',\n `Invalid player index ${playerIndex}. Valid range: 0-${controllersCount - 1}`,\n { details: { playerIndex, controllersCount } }\n );\n }\n}\n\n// =============================================================================\n// DEBUG LOGGER\n// =============================================================================\n\nclass DebugLogger {\n private enabled: boolean;\n private level: LogLevel;\n private prefix: string;\n private logSend: boolean;\n private logReceive: boolean;\n private logLifecycle: boolean;\n private customLogger?: (level: LogLevel, message: string, data?: unknown) => void;\n\n private static levelOrder: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n };\n\n constructor(options?: boolean | DebugOptions) {\n if (typeof options === 'boolean') {\n this.enabled = options;\n this.level = 'debug';\n this.prefix = '[SmoreScreen]';\n this.logSend = true;\n this.logReceive = true;\n this.logLifecycle = true;\n } else if (options) {\n this.enabled = options.enabled ?? false;\n this.level = options.level ?? 'debug';\n this.prefix = options.prefix ?? '[SmoreScreen]';\n this.logSend = options.logSend ?? true;\n this.logReceive = options.logReceive ?? true;\n this.logLifecycle = options.logLifecycle ?? true;\n this.customLogger = options.logger;\n } else {\n this.enabled = false;\n this.level = 'debug';\n this.prefix = '[SmoreScreen]';\n this.logSend = true;\n this.logReceive = true;\n this.logLifecycle = true;\n }\n }\n\n private shouldLog(level: LogLevel): boolean {\n return this.enabled && DebugLogger.levelOrder[level] >= DebugLogger.levelOrder[this.level];\n }\n\n private log(level: LogLevel, message: string, data?: unknown): void {\n if (!this.shouldLog(level)) return;\n\n if (this.customLogger) {\n this.customLogger(level, `${this.prefix} ${message}`, data);\n return;\n }\n\n const consoleMethod = level === 'error' ? 'error' : level === 'warn' ? 'warn' : 'log';\n if (data !== undefined) {\n console[consoleMethod](`${this.prefix} ${message}`, data);\n } else {\n console[consoleMethod](`${this.prefix} ${message}`);\n }\n }\n\n debug(message: string, data?: unknown): void {\n this.log('debug', message, data);\n }\n\n info(message: string, data?: unknown): void {\n this.log('info', message, data);\n }\n\n warn(message: string, data?: unknown): void {\n this.log('warn', message, data);\n }\n\n error(message: string, data?: unknown): void {\n this.log('error', message, data);\n }\n\n send(event: string, data?: unknown): void {\n if (this.logSend) {\n this.debug(`-> SEND: ${event}`, data);\n }\n }\n\n receive(event: string, data?: unknown): void {\n if (this.logReceive) {\n this.debug(`<- RECV: ${event}`, data);\n }\n }\n\n lifecycle(message: string, data?: unknown): void {\n if (this.logLifecycle) {\n this.info(`[Lifecycle] ${message}`, data);\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 _leaderIndex: PlayerIndex = -1;\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\n constructor(config: ScreenConfig<TEvents> = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug);\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. Make sure the parent frame sends smore:init.`,\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 (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:init') {\n clearTimeout(timeoutId);\n const initData = (msg as SmoreInitMessage).payload;\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 this._leaderIndex = this.findLeaderIndex(initData.players, initData.leaderId);\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 leaderIndex: this._leaderIndex,\n });\n\n this.config.onReady?.();\n resolve();\n } else if (msg.type === 'smore:update') {\n const updateData = (msg as SmoreUpdateMessage).payload;\n\n if (updateData.players) {\n this._controllers = this.mapControllersFromInit(updateData.players);\n }\n if (updateData.leaderId !== undefined) {\n this._leaderIndex = this.findLeaderIndex(\n updateData.players ?? [],\n updateData.leaderId\n );\n }\n\n this.logger.lifecycle('Room updated', {\n controllers: this._controllers.length,\n leaderIndex: this._leaderIndex,\n });\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n\n // Signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\n this.logger.lifecycle('Sent smore: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 nickname: (p.nickname as string) || `Player ${index + 1}`,\n connected: p.connected !== false,\n appearance: p.appearance as ControllerInfo['appearance'],\n }));\n }\n\n private findLeaderIndex(players: unknown[], leaderId: string | null): PlayerIndex {\n if (!leaderId) return -1;\n const idx = (players as Record<string, unknown>[]).findIndex(\n (p) => p.sessionId === leaderId\n );\n return idx >= 0 ? idx : -1;\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave/reconnect\n this.registerTransportHandler(SYSTEM_EVENTS.PLAYER_JOIN, (data: unknown) => {\n const payload = data as { player?: ControllerInfo };\n const player = payload?.player;\n if (player && typeof player.playerIndex === 'number') {\n this.logger.lifecycle('Controller joined', { playerIndex: player.playerIndex });\n this.config.onControllerJoin?.(player.playerIndex, player);\n }\n });\n\n this.registerTransportHandler(SYSTEM_EVENTS.PLAYER_LEAVE, (data: unknown) => {\n const payload = data as { playerIndex?: number };\n if (typeof payload?.playerIndex === 'number') {\n this.logger.lifecycle('Controller left', { playerIndex: payload.playerIndex });\n this.config.onControllerLeave?.(payload.playerIndex);\n }\n });\n\n this.registerTransportHandler(SYSTEM_EVENTS.PLAYER_RECONNECT, (data: unknown) => {\n const payload = data as { player?: ControllerInfo };\n const player = payload?.player;\n if (player && typeof player.playerIndex === 'number') {\n this.logger.lifecycle('Controller reconnected', { playerIndex: player.playerIndex });\n this.config.onControllerReconnect?.(player.playerIndex, player);\n }\n });\n\n // Legacy room events (backward compatibility)\n this.registerTransportHandler('room:player-joined', (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.config.onControllerJoin?.(playerIndex, {\n playerIndex,\n nickname: `Player ${playerIndex + 1}`,\n connected: true,\n });\n }\n });\n\n this.registerTransportHandler('room:player-left', (data: unknown) => {\n const payload = data as { playerIndex?: number; player?: { playerIndex?: number } };\n const playerIndex = payload?.playerIndex ?? payload?.player?.playerIndex;\n if (typeof playerIndex === 'number') {\n this.config.onControllerLeave?.(playerIndex);\n }\n });\n\n // User event listeners from config\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n this.setupUserEventHandler(event, handler as ScreenEventHandler<unknown>);\n }\n }\n }\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 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 }\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 get controllers(): readonly ControllerInfo[] {\n return [...this._controllers];\n }\n\n get roomCode(): RoomCode {\n return this._roomCode;\n }\n\n get leaderIndex(): PlayerIndex {\n return this._leaderIndex;\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 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 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 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.length);\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.length);\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(SYSTEM_EVENTS.GAME_OVER, { results });\n }\n\n // ---------------------------------------------------------------------------\n // Event Subscription\n // ---------------------------------------------------------------------------\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 if (this.transport) {\n const wrappedHandler = (data: unknown) => {\n this.logger.receive(event, data);\n const payload = data as { playerIndex?: number };\n const { playerIndex, ...rest } = payload as { playerIndex?: number; [key: string]: unknown };\n if (typeof playerIndex === 'number') {\n try {\n handler(playerIndex, rest as EventData<TEvents, K>);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n }\n };\n this.registerTransportHandler(event, 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 };\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 handlers for this event\n this.eventHandlers.delete(event);\n this.transport?.off(event);\n } else {\n const handlers = this.eventHandlers.get(event);\n handlers?.delete(handler as ScreenEventHandler<unknown>);\n if (handlers?.size === 0) {\n this.eventHandlers.delete(event);\n }\n }\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 isLeader(playerIndex: PlayerIndex): boolean {\n return playerIndex === this._leaderIndex;\n }\n\n getControllerCount(): number {\n return this._controllers.filter((c) => c.connected).length;\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n destroy(): void {\n if (this._isDestroyed) return;\n\n this.logger.lifecycle('Destroying screen...');\n this._isDestroyed = true;\n this._isReady = false;\n\n this.cleanup();\n this.logger.lifecycle('Screen destroyed');\n }\n\n private cleanup(): void {\n // 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\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 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","/**\n * createController - Factory function for creating a Controller instance.\n *\n * Returns a Promise that resolves when the controller is ready and initialized.\n * Uses PostMessageTransport for iframe communication with parent window.\n *\n * @example Promise-based (recommended)\n * ```ts\n * const controller = await createController<MyEvents>({\n * listeners: {\n * 'phase-update': (data) => setPhase(data.phase),\n * },\n * });\n *\n * console.log(`Ready! My index: ${controller.myIndex}`);\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const controller = createController<MyEvents>({\n * onReady: () => {\n * console.log('Ready!');\n * },\n * listeners: { ... },\n * });\n * // Use controller.instance for immediate access\n * ```\n */\n\nimport type {\n Controller,\n ControllerConfig,\n ControllerEventHandler,\n ControllerInfo,\n DebugOptions,\n EventData,\n EventMap,\n EventNames,\n LogLevel,\n PlayerIndex,\n RoomCode,\n SmoreError,\n SmoreErrorCode,\n} from './types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport type { TransportEventHandler } from './transport/types';\nimport {\n isSmoreMessage,\n type SmoreInitMessage,\n type SmoreUpdateMessage,\n} from './transport/protocol';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst SYSTEM_PREFIX = 'smore:';\n\nconst SYSTEM_EVENTS = {\n READY: `${SYSTEM_PREFIX}ready`,\n PLAYER_JOIN: `${SYSTEM_PREFIX}player-join`,\n PLAYER_LEAVE: `${SYSTEM_PREFIX}player-leave`,\n} as const;\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// ERROR CLASS\n// =============================================================================\n\n/**\n * Custom error class for SDK errors.\n */\nexport class SmoreSDKError extends Error {\n readonly code: SmoreErrorCode;\n readonly cause?: Error;\n readonly details?: Record<string, unknown>;\n\n constructor(\n code: SmoreErrorCode,\n message: string,\n options?: {\n cause?: Error;\n details?: Record<string, unknown>;\n },\n ) {\n super(message);\n this.name = 'SmoreSDKError';\n this.code = code;\n this.cause = options?.cause;\n this.details = options?.details;\n\n // Maintain proper stack trace in V8 engines\n const ErrorWithCapture = Error as typeof Error & {\n captureStackTrace?: (target: object, constructor?: Function) => void;\n };\n if (typeof ErrorWithCapture.captureStackTrace === 'function') {\n ErrorWithCapture.captureStackTrace(this, SmoreSDKError);\n }\n }\n\n toSmoreError(): SmoreError {\n return {\n code: this.code,\n message: this.message,\n cause: this.cause,\n details: this.details,\n };\n }\n}\n\n// =============================================================================\n// VALIDATION\n// =============================================================================\n\nconst EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;\n\nfunction validateEventName(event: string): void {\n if (!event || typeof event !== 'string') {\n throw new SmoreSDKError('INVALID_EVENT', 'Event name must be a non-empty string');\n }\n if (!EVENT_NAME_REGEX.test(event)) {\n throw new SmoreSDKError(\n 'INVALID_EVENT',\n `Invalid event name \"${event}\". Event names must:\\n` +\n ` - Start with a letter (a-z, A-Z)\\n` +\n ` - Only contain letters, numbers, hyphens (-), and underscores (_)\\n` +\n ` - End with a letter or number`,\n { details: { event } },\n );\n }\n}\n\n// =============================================================================\n// DEBUG LOGGER\n// =============================================================================\n\ninterface Logger {\n debug(message: string, data?: unknown): void;\n info(message: string, data?: unknown): void;\n warn(message: string, data?: unknown): void;\n error(message: string, data?: unknown): void;\n}\n\nfunction createLogger(options: boolean | DebugOptions | undefined): Logger {\n const enabled = typeof options === 'boolean' ? options : options?.enabled ?? false;\n const level = (typeof options === 'object' ? options.level : undefined) ?? 'debug';\n const prefix = (typeof options === 'object' ? options.prefix : undefined) ?? '[SmoreController]';\n const customLogger = typeof options === 'object' ? options.logger : undefined;\n\n const levelPriority: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n };\n\n const shouldLog = (msgLevel: LogLevel): boolean => {\n if (!enabled) return false;\n return levelPriority[msgLevel] >= levelPriority[level];\n };\n\n const log = (msgLevel: LogLevel, message: string, data?: unknown): void => {\n if (!shouldLog(msgLevel)) return;\n\n if (customLogger) {\n customLogger(msgLevel, message, data);\n return;\n }\n\n const fullMessage = `${prefix} ${message}`;\n const consoleFn = console[msgLevel] ?? console.log;\n\n if (data !== undefined) {\n consoleFn(fullMessage, data);\n } else {\n consoleFn(fullMessage);\n }\n };\n\n return {\n debug: (msg, data) => log('debug', msg, data),\n info: (msg, data) => log('info', msg, data),\n warn: (msg, data) => log('warn', msg, data),\n error: (msg, data) => log('error', msg, data),\n };\n}\n\n// =============================================================================\n// CONTROLLER IMPLEMENTATION\n// =============================================================================\n\nclass ControllerImpl<TEvents extends EventMap> implements Controller<TEvents> {\n private transport: PostMessageTransport | null = null;\n private config: ControllerConfig<TEvents>;\n private logger: Logger;\n private _roomCode: RoomCode = '';\n private _myIndex: PlayerIndex = -1;\n private _isLeader: boolean = false;\n private _isReady: boolean = false;\n private _isDestroyed: boolean = false;\n private boundMessageHandler: ((e: MessageEvent) => void) | null = null;\n private registeredHandlers: Array<{ event: string; handler: TransportEventHandler }> = [];\n private eventListeners = new Map<string, Set<ControllerEventHandler<unknown>>>();\n\n constructor(config: ControllerConfig<TEvents> = {}) {\n this.config = config;\n this.logger = createLogger(config.debug);\n\n // Validate event names in listeners\n if (config.listeners) {\n for (const event of Object.keys(config.listeners)) {\n validateEventName(event);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Properties (readonly)\n // ---------------------------------------------------------------------------\n\n get myIndex(): PlayerIndex {\n return this._myIndex;\n }\n\n get isLeader(): boolean {\n return this._isLeader;\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 // Initialization\n // ---------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const parentOrigin = this.config.parentOrigin ?? '*';\n const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;\n\n this.logger.debug('Initializing controller...', { parentOrigin, timeout });\n\n return new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.cleanup();\n const error = new SmoreSDKError(\n 'TIMEOUT',\n `Controller initialization timed out after ${timeout}ms. ` +\n `Make sure the parent window sends smore:init message.`,\n { details: { timeout } },\n );\n this.handleError(error);\n reject(error);\n }, timeout);\n\n // Listen for init message from parent\n this.boundMessageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:init') {\n clearTimeout(timeoutId);\n this.handleInit(msg as SmoreInitMessage, parentOrigin, resolve, reject);\n } else if (msg.type === 'smore:update') {\n this.handleUpdate(msg as SmoreUpdateMessage);\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n\n // Signal ready to parent\n this.logger.debug('Sending smore:ready to parent');\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\n });\n }\n\n private handleInit(\n msg: SmoreInitMessage,\n parentOrigin: string,\n resolve: () => void,\n reject: (err: Error) => void,\n ): void {\n const initData = msg.payload;\n\n this.logger.debug('Received smore:init', initData);\n\n if (initData.side !== 'player') {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Controller received init for wrong side: ${initData.side}`,\n { details: { side: initData.side } },\n );\n this.handleError(error);\n reject(error);\n return;\n }\n\n if (initData.myIndex === undefined) {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n 'Missing myIndex in init payload',\n { details: initData },\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._myIndex = initData.myIndex;\n this._isLeader = initData.isLeader ?? false;\n\n this.setupEventHandlers();\n\n this._isReady = true;\n this.logger.info('Controller ready', {\n roomCode: this._roomCode,\n myIndex: this._myIndex,\n isLeader: this._isLeader,\n });\n\n this.config.onReady?.();\n resolve();\n }\n\n private handleUpdate(msg: SmoreUpdateMessage): void {\n const updateData = msg.payload;\n this.logger.debug('Received smore:update', updateData);\n\n // Future: handle leader changes, player list updates, etc.\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave\n this.registerHandler(\n SYSTEM_EVENTS.PLAYER_JOIN,\n (data: { player?: ControllerInfo; playerIndex?: number }) => {\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n this.logger.debug('Player joined', { playerIndex });\n this.config.onControllerJoin?.(playerIndex, data.player as ControllerInfo);\n }\n },\n );\n\n this.registerHandler(\n SYSTEM_EVENTS.PLAYER_LEAVE,\n (data: { player?: { playerIndex?: number }; playerIndex?: number }) => {\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n this.logger.debug('Player left', { playerIndex });\n this.config.onControllerLeave?.(playerIndex);\n }\n },\n );\n\n // User event listeners from config\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n\n this.registerHandler(event, (data: unknown) => {\n this.logReceive(event, data);\n (handler as ControllerEventHandler<unknown>)(data);\n });\n }\n }\n }\n\n private registerHandler(event: string, handler: TransportEventHandler): void {\n if (!this.transport) return;\n this.transport.on(event, handler);\n this.registeredHandlers.push({ event, handler });\n }\n\n // ---------------------------------------------------------------------------\n // Communication Methods\n // ---------------------------------------------------------------------------\n\n send<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void {\n this.ensureReady('send');\n validateEventName(event);\n\n this.logSend(event, data);\n this.transport!.emit(event, data);\n }\n\n sendRaw(event: string, data?: unknown): void {\n this.ensureReady('sendRaw');\n validateEventName(event);\n\n this.logSend(event, data);\n this.transport!.emit(event, data);\n }\n\n // ---------------------------------------------------------------------------\n // Event Subscription\n // ---------------------------------------------------------------------------\n\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event);\n\n // Add to local listeners map\n let listeners = this.eventListeners.get(event);\n if (!listeners) {\n listeners = new Set();\n this.eventListeners.set(event, listeners);\n }\n listeners.add(handler as ControllerEventHandler<unknown>);\n\n // Register with transport if ready\n const transportHandler: TransportEventHandler = (data: unknown) => {\n this.logReceive(event, data);\n (handler as ControllerEventHandler<unknown>)(data);\n };\n\n if (this.transport) {\n this.transport.on(event, transportHandler);\n this.registeredHandlers.push({ event, handler: transportHandler });\n }\n\n // Return unsubscribe function\n return () => {\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n this.transport?.off(event, transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== transportHandler,\n );\n };\n }\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n const unsubscribe = this.on(event, ((data: EventData<TEvents, K>) => {\n unsubscribe();\n handler(data);\n }) as ControllerEventHandler<EventData<TEvents, K>>);\n return unsubscribe;\n }\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n if (!handler) {\n // Remove all listeners for this event\n this.eventListeners.delete(event);\n this.transport?.off(event);\n this.registeredHandlers = this.registeredHandlers.filter((h) => h.event !== event);\n } else {\n // Remove specific handler\n const listeners = this.eventListeners.get(event);\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n // Note: Can't easily remove specific handler from transport without tracking\n // This is a limitation of the current transport interface\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n destroy(): void {\n if (this._isDestroyed) return;\n\n this.logger.info('Destroying controller');\n this.cleanup();\n this._isDestroyed = true;\n }\n\n private cleanup(): void {\n this._isReady = false;\n\n // Remove all registered handlers\n for (const { event, handler } of this.registeredHandlers) {\n this.transport?.off(event, handler);\n }\n this.registeredHandlers = [];\n\n // Clear event listeners\n this.eventListeners.clear();\n\n // Destroy transport\n if (this.transport) {\n this.transport.destroy();\n this.transport = null;\n }\n\n // Remove message listener\n if (this.boundMessageHandler) {\n window.removeEventListener('message', this.boundMessageHandler);\n this.boundMessageHandler = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private Helpers\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 controller is ready. ` +\n `Use await createController() or wait for onReady callback.`,\n { details: { method, isReady: this._isReady } },\n );\n }\n }\n\n private handleError(error: SmoreSDKError): void {\n if (this.config.onError) {\n this.config.onError(error.toSmoreError());\n } else {\n this.logger.error(error.message, error.details);\n }\n }\n\n private logSend(event: string, data?: unknown): void {\n const options = this.config.debug;\n const shouldLog =\n typeof options === 'object' ? (options.logSend ?? true) : Boolean(options);\n if (shouldLog) {\n this.logger.debug(`→ SEND [${event}]`, data);\n }\n }\n\n private logReceive(event: string, data?: unknown): void {\n const options = this.config.debug;\n const shouldLog =\n typeof options === 'object' ? (options.logReceive ?? true) : Boolean(options);\n if (shouldLog) {\n this.logger.debug(`← RECV [${event}]`, data);\n }\n }\n}\n\n// =============================================================================\n// FACTORY FUNCTION\n// =============================================================================\n\n/**\n * Create a Controller instance for the player/phone side of your game.\n *\n * Returns a Promise that resolves when the controller is ready.\n * The returned object also has an `instance` property for immediate access.\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Controller configuration\n * @returns Promise that resolves to the Controller instance when ready\n *\n * @example Promise-based (recommended)\n * ```ts\n * const controller = await createController<MyEvents>({\n * listeners: {\n * 'phase-update': (data) => setPhase(data.phase),\n * },\n * });\n *\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const result = createController<MyEvents>({\n * onReady: () => {\n * result.instance.send('ready', {});\n * },\n * listeners: { ... },\n * });\n * ```\n */\nexport function createController<TEvents extends EventMap = EventMap>(\n config?: ControllerConfig<TEvents>,\n): Promise<Controller<TEvents>> & { instance: Controller<TEvents> } {\n const controller = new ControllerImpl<TEvents>(config ?? {});\n\n const promise = controller.initialize().then(() => controller as Controller<TEvents>);\n\n // Attach instance property for immediate access\n (promise as Promise<Controller<TEvents>> & { instance: Controller<TEvents> }).instance =\n controller;\n\n return promise as Promise<Controller<TEvents>> & { instance: Controller<TEvents> };\n}\n","/**\n * DirectTransport - Wraps a Socket.IO socket as a Transport.\n * Used by bundled (internal) games. Behaviour is identical to using socket directly.\n */\n\nimport type { Socket } from 'socket.io-client';\nimport type { Transport, TransportEventHandler } from './types';\n\nexport class DirectTransport implements Transport {\n constructor(private socket: Socket) {}\n\n emit(event: string, ...args: any[]): void {\n this.socket.emit(event, ...args);\n }\n\n on(event: string, handler: TransportEventHandler): void {\n this.socket.on(event, handler);\n }\n\n off(event: string, handler?: TransportEventHandler): void {\n if (handler) {\n this.socket.off(event, handler);\n } else {\n this.socket.off(event);\n }\n }\n}\n","/**\n * SDK 시스템 이벤트 상수\n * 모든 시스템 이벤트는 'smore:' prefix 사용\n * 유저 이벤트는 ':' 사용 불가\n */\n\nexport const SMORE_EVENTS = {\n // 게임 lifecycle\n READY: 'smore:ready',\n GAME_OVER: 'smore:game-over',\n RETURN_TO_LOBBY: 'smore:return-to-lobby',\n\n // 플레이어 관리\n PLAYER_JOIN: 'smore:player-join',\n PLAYER_LEAVE: 'smore:player-leave',\n\n // 특정 플레이어에게 전송 (내부용)\n SEND_TO_PLAYER: 'smore:send-to-player',\n\n // 초기화\n INIT: 'smore:init',\n UPDATE: 'smore:update',\n} as const;\n\nexport type SmoreEvent = typeof SMORE_EVENTS[keyof typeof SMORE_EVENTS];\n\n/**\n * 유저 이벤트명 검증\n * ':' 포함 시 에러, '_'와 '-'는 허용\n */\nexport function validateUserEvent(event: string): void {\n if (event.includes(':')) {\n throw new Error(\n `Invalid event name \"${event}\": User events cannot contain ':'. ` +\n `Use '_' or '-' instead. System events use 'smore:' prefix.`\n );\n }\n}\n\n/**\n * 시스템 이벤트인지 확인\n */\nexport function isSystemEvent(event: string): boolean {\n return event.startsWith('smore:');\n}\n","/**\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 ControllerInfo,\n PlayerIndex,\n GameResults,\n ScreenEventHandler,\n ControllerEventHandler,\n MockScreen,\n MockController,\n MockOptions,\n} from './types';\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 } = options;\n\n // Internal state\n let _controllers: ControllerInfo[] = [...initialControllers];\n let _isReady = false;\n let _isDestroyed = false;\n let _leaderIndex: PlayerIndex = initialControllers[0]?.playerIndex ?? -1;\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\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 leaderIndex() {\n return _leaderIndex;\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 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 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 (!_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 (!_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 screen.broadcastRaw('game-over', results);\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\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 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 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 isLeader(playerIndex: PlayerIndex): boolean {\n return playerIndex === _leaderIndex;\n },\n\n getControllerCount(): number {\n return _controllers.length;\n },\n\n // === Cleanup ===\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 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 (_leaderIndex === -1) {\n _leaderIndex = info.playerIndex;\n }\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 (_leaderIndex === playerIndex) {\n _leaderIndex = _controllers[0]?.playerIndex ?? -1;\n }\n if (onControllerLeaveCallback) {\n onControllerLeaveCallback(playerIndex);\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 // Auto-trigger ready if enabled\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 * isLeader: true,\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 isLeader: initialIsLeader = false,\n autoReady = true,\n } = options;\n\n // Internal state\n let _isReady = false;\n let _isDestroyed = false;\n let _isLeader = initialIsLeader;\n\n // Event listeners\n const listeners = new Map<string, Set<ControllerEventHandler>>();\n\n // Lifecycle callbacks\n let onReadyCallback: (() => void) | undefined;\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 isLeader() {\n return _isLeader;\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 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 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 sentEvents.push({ event, data });\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\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 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 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 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 setLeader(isLeader: boolean): void {\n _isLeader = isLeader;\n },\n };\n\n // Auto-trigger ready if enabled\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":["isSmoreMessage","data","type","startsWith","PostMessageTransport","handlers","Map","ackCallbacks","ackCounter","parentOrigin","boundMessageHandler","constructor","this","handleMessage","bind","window","addEventListener","emit","event","args","ackId","length","callback","set","parent","postMessage","payload","on","handler","get","Set","add","off","delete","destroy","removeEventListener","clear","e","origin","msg","forEach","cb","SYSTEM_PREFIX","SYSTEM_EVENTS","PLAYER_JOIN","PLAYER_LEAVE","PLAYER_RECONNECT","GAME_OVER","SmoreSDKError","Error","code","cause","details","message","options","super","name","ErrorWithCapture","captureStackTrace","toSmoreError","EVENT_NAME_REGEX","validateEventName","test","validatePlayerIndex","playerIndex","controllersCount","Number","isInteger","DebugLogger","enabled","level","prefix","logSend","logReceive","logLifecycle","customLogger","static","debug","info","warn","error","logger","shouldLog","levelOrder","log","consoleMethod","console","send","receive","lifecycle","ScreenImpl","transport","config","_controllers","_roomCode","_leaderIndex","_isReady","_isDestroyed","eventHandlers","registeredTransportHandlers","listeners","Object","keys","initialize","timeout","Promise","resolve","reject","timeoutId","setTimeout","cleanup","handleError","clearTimeout","initData","side","roomCode","mapControllersFromInit","players","findLeaderIndex","leaderId","setupEventHandlers","controllers","leaderIndex","onReady","updateData","map","p","index","nickname","connected","appearance","idx","findIndex","sessionId","registerTransportHandler","player","onControllerJoin","onControllerLeave","onControllerReconnect","entries","setupUserEventHandler","rest","err","push","isReady","isDestroyed","broadcast","ensureReady","broadcastRaw","sendToController","targetPlayerIndex","sendToControllerRaw","gameOver","results","wrappedHandler","size","once","unsubscribe","getController","find","c","isLeader","getControllerCount","filter","smoreError","onError","method","createLogger","levelPriority","msgLevel","fullMessage","consoleFn","ControllerImpl","_myIndex","_isLeader","registeredHandlers","eventListeners","myIndex","handleInit","handleUpdate","registerHandler","sendRaw","transportHandler","h","Boolean","socket","READY","RETURN_TO_LOBBY","SEND_TO_PLAYER","INIT","UPDATE","controller","promise","then","instance","initialIsLeader","autoReady","sentEvents","eventStr","has","wrapper","simulateEvent","getSentEvents","clearRecordedEvents","triggerReady","setLeader","initialControllers","broadcasts","sends","screen","some","simulateControllerJoin","simulateControllerLeave","getBroadcasts","getSentToController","s","includes"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smoregg/sdk",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "S'MORE Game SDK - Simplified interface for building party games",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",
@@ -11,31 +11,6 @@
11
11
  "types": "./dist/types/index.d.ts",
12
12
  "import": "./dist/esm/index.js",
13
13
  "require": "./dist/cjs/index.cjs"
14
- },
15
- "./iframe": {
16
- "types": "./dist/types/iframe/index.d.ts",
17
- "import": "./dist/esm/iframe/index.js",
18
- "require": "./dist/cjs/iframe/index.cjs"
19
- },
20
- "./server": {
21
- "types": "./dist/types/server/index.d.ts",
22
- "import": "./dist/esm/server/index.js",
23
- "require": "./dist/cjs/server/index.cjs"
24
- },
25
- "./dev": {
26
- "types": "./dist/types/dev/index.d.ts",
27
- "import": "./dist/esm/dev/index.js",
28
- "require": "./dist/cjs/dev/index.cjs"
29
- },
30
- "./components/*": {
31
- "types": "./dist/types/components/*.d.ts",
32
- "import": "./dist/esm/components/*.js",
33
- "require": "./dist/cjs/components/*.cjs"
34
- },
35
- "./transport": {
36
- "types": "./dist/types/transport/index.d.ts",
37
- "import": "./dist/esm/transport/index.js",
38
- "require": "./dist/cjs/transport/index.cjs"
39
14
  }
40
15
  },
41
16
  "files": [