@smoregg/sdk 0.4.1 → 0.5.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 (89) hide show
  1. package/dist/cjs/SmoreHost.cjs +306 -0
  2. package/dist/cjs/SmoreHost.cjs.map +1 -0
  3. package/dist/cjs/SmorePlayer.cjs +229 -0
  4. package/dist/cjs/SmorePlayer.cjs.map +1 -0
  5. package/dist/cjs/components/IframeGameBridge.cjs +115 -0
  6. package/dist/cjs/components/IframeGameBridge.cjs.map +1 -0
  7. package/dist/cjs/context/RoomProvider.cjs +3 -3
  8. package/dist/cjs/context/RoomProvider.cjs.map +1 -1
  9. package/dist/cjs/hooks/useGameHost.cjs +86 -13
  10. package/dist/cjs/hooks/useGameHost.cjs.map +1 -1
  11. package/dist/cjs/hooks/useGamePlayer.cjs +60 -4
  12. package/dist/cjs/hooks/useGamePlayer.cjs.map +1 -1
  13. package/dist/cjs/iframe/index.cjs +50 -316
  14. package/dist/cjs/iframe/index.cjs.map +1 -1
  15. package/dist/cjs/index.cjs +4 -22
  16. package/dist/cjs/index.cjs.map +1 -1
  17. package/dist/cjs/transport/protocol.cjs.map +1 -1
  18. package/dist/cjs/utils/connectionMonitor.cjs +77 -0
  19. package/dist/cjs/utils/connectionMonitor.cjs.map +1 -0
  20. package/dist/cjs/utils/preloadAssets.cjs +66 -0
  21. package/dist/cjs/utils/preloadAssets.cjs.map +1 -0
  22. package/dist/cjs/utils/serverTime.cjs +43 -0
  23. package/dist/cjs/utils/serverTime.cjs.map +1 -0
  24. package/dist/esm/SmoreHost.js +304 -0
  25. package/dist/esm/SmoreHost.js.map +1 -0
  26. package/dist/esm/SmorePlayer.js +227 -0
  27. package/dist/esm/SmorePlayer.js.map +1 -0
  28. package/dist/esm/components/IframeGameBridge.js +113 -0
  29. package/dist/esm/components/IframeGameBridge.js.map +1 -0
  30. package/dist/esm/context/RoomProvider.js +3 -3
  31. package/dist/esm/context/RoomProvider.js.map +1 -1
  32. package/dist/esm/hooks/useGameHost.js +87 -14
  33. package/dist/esm/hooks/useGameHost.js.map +1 -1
  34. package/dist/esm/hooks/useGamePlayer.js +61 -5
  35. package/dist/esm/hooks/useGamePlayer.js.map +1 -1
  36. package/dist/esm/iframe/index.js +51 -314
  37. package/dist/esm/iframe/index.js.map +1 -1
  38. package/dist/esm/index.js +2 -8
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/transport/protocol.js.map +1 -1
  41. package/dist/esm/utils/connectionMonitor.js +75 -0
  42. package/dist/esm/utils/connectionMonitor.js.map +1 -0
  43. package/dist/esm/utils/preloadAssets.js +63 -0
  44. package/dist/esm/utils/preloadAssets.js.map +1 -0
  45. package/dist/esm/utils/serverTime.js +41 -0
  46. package/dist/esm/utils/serverTime.js.map +1 -0
  47. package/dist/types/SmoreHost.d.ts +187 -0
  48. package/dist/types/SmoreHost.d.ts.map +1 -0
  49. package/dist/types/SmorePlayer.d.ts +146 -0
  50. package/dist/types/SmorePlayer.d.ts.map +1 -0
  51. package/dist/types/components/IframeGameBridge.d.ts +2 -2
  52. package/dist/types/components/IframeGameBridge.d.ts.map +1 -1
  53. package/dist/types/components/index.d.ts +2 -4
  54. package/dist/types/components/index.d.ts.map +1 -1
  55. package/dist/types/context/RoomProvider.d.ts +3 -3
  56. package/dist/types/context/RoomProvider.d.ts.map +1 -1
  57. package/dist/types/hooks/useGameHost.d.ts +33 -7
  58. package/dist/types/hooks/useGameHost.d.ts.map +1 -1
  59. package/dist/types/hooks/useGamePlayer.d.ts +29 -3
  60. package/dist/types/hooks/useGamePlayer.d.ts.map +1 -1
  61. package/dist/types/iframe/index.d.ts +10 -10
  62. package/dist/types/iframe/index.d.ts.map +1 -1
  63. package/dist/types/iframe/vanilla.d.ts +12 -4
  64. package/dist/types/iframe/vanilla.d.ts.map +1 -1
  65. package/dist/types/index.d.ts +36 -20
  66. package/dist/types/index.d.ts.map +1 -1
  67. package/dist/types/transport/protocol.d.ts +1 -1
  68. package/dist/types/transport/protocol.d.ts.map +1 -1
  69. package/dist/types/utils/connectionMonitor.d.ts +57 -0
  70. package/dist/types/utils/connectionMonitor.d.ts.map +1 -0
  71. package/dist/types/utils/index.d.ts +7 -0
  72. package/dist/types/utils/index.d.ts.map +1 -0
  73. package/dist/types/utils/preloadAssets.d.ts +29 -0
  74. package/dist/types/utils/preloadAssets.d.ts.map +1 -0
  75. package/dist/types/utils/serverTime.d.ts +28 -0
  76. package/dist/types/utils/serverTime.d.ts.map +1 -0
  77. package/dist/umd/smore-sdk-iframe.umd.js +54 -317
  78. package/dist/umd/smore-sdk-iframe.umd.js.map +1 -1
  79. package/dist/umd/smore-sdk-iframe.umd.min.js +1 -1
  80. package/dist/umd/smore-sdk-iframe.umd.min.js.map +1 -1
  81. package/dist/umd/smore-sdk-vanilla.umd.js +550 -126
  82. package/dist/umd/smore-sdk-vanilla.umd.js.map +1 -1
  83. package/dist/umd/smore-sdk-vanilla.umd.min.js +1 -1
  84. package/dist/umd/smore-sdk-vanilla.umd.min.js.map +1 -1
  85. package/dist/umd/smore-sdk.umd.js +488 -576
  86. package/dist/umd/smore-sdk.umd.js.map +1 -1
  87. package/dist/umd/smore-sdk.umd.min.js +1 -1
  88. package/dist/umd/smore-sdk.umd.min.js.map +1 -1
  89. package/package.json +1 -26
@@ -1 +1 @@
1
- {"version":3,"file":"smore-sdk-vanilla.umd.js","sources":["../../src/transport/protocol.ts","../../src/transport/PostMessageTransport.ts","../../src/iframe/vanilla.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 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 * 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 * Vanilla JS bridge API for iframe-hosted games.\n * No React - pure JavaScript with PostMessage transport.\n */\n\nimport { PostMessageTransport } from '../transport/PostMessageTransport';\nimport { isSmoreMessage, type SmoreInitMessage } from '../transport/protocol';\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): boolean {\n if (!EVENT_NAME_REGEX.test(event)) {\n console.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 return false;\n }\n return true;\n}\n\nexport interface HostBridgeOptions {\n parentOrigin?: string;\n gameId: string;\n listeners?: Record<string, (data: any) => void>;\n onPlayerJoin?: (playerId: string) => void;\n onPlayerLeave?: (playerId: string) => void;\n onReady?: (room: { roomCode: string; players: any[]; leaderId: string | null }) => void;\n}\n\nexport interface HostBridge {\n broadcast: (event: string, data?: any) => void;\n sendToPlayer: (playerId: string, event: string, data?: any) => void;\n emitGameOver: (results: any) => void;\n setLoaded: () => void;\n destroy: () => void;\n}\n\nexport interface PlayerBridgeOptions {\n parentOrigin?: string;\n gameId: string;\n listeners?: Record<string, (data: any) => void>;\n onReady?: (room: { roomCode: string; isLeader: boolean; mySessionId: string }) => void;\n}\n\nexport interface PlayerBridge {\n emit: (event: string, data?: any) => void;\n onEvent: (event: string, handler: (data: any) => void) => () => void;\n destroy: () => void;\n}\n\nexport function createHostBridge(options: HostBridgeOptions): HostBridge {\n const { parentOrigin = '*', gameId, listeners, onPlayerJoin, onPlayerLeave, onReady } = options;\n\n let transport: PostMessageTransport | null = null;\n let isInitialized = false;\n\n // Immediately signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\n\n const messageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:init') {\n const { side, roomCode, players, leaderId } = (msg as SmoreInitMessage).payload;\n\n if (side !== 'host') {\n console.error('[HostBridge] Received init for wrong side:', side);\n return;\n }\n\n // Create transport and wire up event handlers\n transport = new PostMessageTransport(parentOrigin);\n\n // Handle input events\n if (listeners) {\n Object.keys(listeners).forEach((event) => {\n validateEventName(event);\n const handler = listeners[event];\n transport!.on(event, handler);\n });\n }\n\n // Handle player join/leave events\n if (onPlayerJoin) {\n transport.on('player:joined', (payload: any) => {\n onPlayerJoin(payload.sessionId);\n });\n }\n if (onPlayerLeave) {\n transport.on('player:left', (payload: any) => {\n onPlayerLeave(payload.sessionId);\n });\n }\n\n isInitialized = true;\n\n // Call onReady callback\n if (onReady) {\n onReady({ roomCode, players, leaderId });\n }\n }\n };\n\n window.addEventListener('message', messageHandler);\n\n return {\n broadcast: (event: string, data?: any) => {\n if (!transport) {\n console.warn('[HostBridge] Cannot broadcast before init');\n return;\n }\n validateEventName(event);\n transport.emit(event, data);\n },\n\n sendToPlayer: (playerId: string, event: string, data?: any) => {\n if (!transport) {\n console.warn('[HostBridge] Cannot sendToPlayer before init');\n return;\n }\n transport.emit(event, { targetSessionId: playerId, ...data });\n },\n\n emitGameOver: (results: any) => {\n if (!transport) {\n console.warn('[HostBridge] Cannot emitGameOver before init');\n return;\n }\n transport.emit('game-over', results);\n },\n\n setLoaded: () => {\n window.parent.postMessage({ type: 'smore:loaded' }, parentOrigin);\n },\n\n destroy: () => {\n window.removeEventListener('message', messageHandler);\n transport?.destroy();\n transport = null;\n isInitialized = false;\n },\n };\n}\n\nexport function createPlayerBridge(options: PlayerBridgeOptions): PlayerBridge {\n const { parentOrigin = '*', gameId, listeners, onReady } = options;\n\n let transport: PostMessageTransport | null = null;\n let isInitialized = false;\n\n // Immediately signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\n\n const messageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:init') {\n const { side, roomCode, mySessionId, isLeader } = (msg as SmoreInitMessage).payload;\n\n if (side !== 'player') {\n console.error('[PlayerBridge] Received init for wrong side:', side);\n return;\n }\n\n if (!mySessionId) {\n console.error('[PlayerBridge] Missing mySessionId in init payload');\n return;\n }\n\n // Create transport and wire up listeners\n transport = new PostMessageTransport(parentOrigin);\n\n // Wire up game-specific event listeners\n if (listeners) {\n Object.keys(listeners).forEach((event) => {\n validateEventName(event);\n const handler = listeners[event];\n transport!.on(event, handler);\n });\n }\n\n isInitialized = true;\n\n // Call onReady callback\n if (onReady) {\n onReady({ roomCode, isLeader: !!isLeader, mySessionId });\n }\n }\n };\n\n window.addEventListener('message', messageHandler);\n\n return {\n emit: (event: string, data?: any) => {\n if (!transport) {\n console.warn('[PlayerBridge] Cannot emit before init');\n return;\n }\n validateEventName(event);\n transport.emit(event, data);\n },\n\n onEvent: (event: string, handler: (data: any) => void) => {\n if (!transport) {\n console.warn('[PlayerBridge] Cannot onEvent before init');\n return () => {};\n }\n validateEventName(event);\n transport.on(event, handler);\n return () => {\n transport?.off(event, handler);\n };\n },\n\n destroy: () => {\n window.removeEventListener('message', messageHandler);\n transport?.destroy();\n transport = null;\n isInitialized = false;\n },\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"smore-sdk-vanilla.umd.js","sources":["../../src/transport/DirectTransport.ts","../../src/transport/protocol.ts","../../src/transport/PostMessageTransport.ts","../../src/SmoreHost.ts","../../src/SmorePlayer.ts","../../src/events.ts"],"sourcesContent":["/**\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 * 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\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 * 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 * SmoreHost - Unified Host-side class for the S'MORE SDK (AirConsole style)\n *\n * Works in any environment: React, Phaser, Vanilla JS.\n * Automatically detects iframe vs bundled environment.\n *\n * @example Iframe game (auto-detection)\n * ```ts\n * const host = new SmoreHost({\n * onPlayerJoin: (playerIndex) => console.log('Player joined:', playerIndex),\n * listeners: {\n * tap: (playerIndex, data) => handleTap(playerIndex, data),\n * },\n * });\n *\n * // Later\n * host.broadcast('phase-update', { phase: 'playing' });\n * host.gameOver({ scores: { 0: 100, 1: 50 } });\n * ```\n *\n * @example Bundled game (direct socket)\n * ```ts\n * const host = new SmoreHost({\n * socket,\n * roomCode: 'ABCD',\n * players: [...],\n * leaderIndex: 0,\n * listeners: { ... },\n * });\n * ```\n */\n\nimport type { Socket } from 'socket.io-client';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { DirectTransport } from './transport/DirectTransport';\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 GAME_OVER: `${SYSTEM_PREFIX}game-over`,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\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 `[SmoreHost] 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\n/**\n * Player information exposed to game developers.\n * Uses playerIndex (0, 1, 2, ...) instead of internal sessionId.\n */\nexport interface SmorePlayer {\n /** Player index (0, 1, 2, ...) */\n playerIndex: number;\n /** Player's chosen nickname */\n nickname: string;\n /** Whether player is currently connected */\n connected: boolean;\n /** Player's character appearance (optional) */\n appearance?: {\n skinColor?: string;\n hairColor?: string;\n shirtColor?: string;\n pantsColor?: string;\n };\n}\n\n/**\n * Configuration for SmoreHost constructor.\n */\nexport interface SmoreHostConfig {\n // === Callbacks ===\n\n /** Called when the host is initialized and ready (iframe games only) */\n onReady?: () => void;\n\n /** Called when a player joins the room */\n onPlayerJoin?: (playerIndex: number) => void;\n\n /** Called when a player leaves the room */\n onPlayerLeave?: (playerIndex: number) => void;\n\n /**\n * Event listeners for specific events.\n * Keys are event names (no colons), values are handler functions.\n * Handler receives (playerIndex, data).\n */\n listeners?: Record<string, (playerIndex: number, data: any) => void>;\n\n // === Bundled game options (skip iframe detection) ===\n\n /** Socket.IO socket instance (bundled games only) */\n socket?: Socket;\n\n /** Room code (bundled games only) */\n roomCode?: string;\n\n /** Initial players array (bundled games only) */\n players?: SmorePlayer[];\n\n /** Leader player index (bundled games only) */\n leaderIndex?: number;\n\n // === Iframe game options ===\n\n /** Parent window origin for postMessage validation (iframe games) */\n parentOrigin?: string;\n}\n\n// ---------------------------------------------------------------------------\n// SmoreHost Class\n// ---------------------------------------------------------------------------\n\n/**\n * SmoreHost - Main host-side class for game development.\n *\n * Automatically detects iframe vs bundled environment:\n * - Iframe: Uses PostMessageTransport, waits for smore:init from parent\n * - Bundled: Uses DirectTransport with provided socket\n */\nexport class SmoreHost {\n private transport: Transport | null = null;\n private config: SmoreHostConfig;\n private _players: SmorePlayer[] = [];\n private _roomCode: string = '';\n private _leaderIndex: number = -1;\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\n constructor(config: SmoreHostConfig = {}) {\n this.config = config;\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 // Detect environment and initialize\n if (config.socket) {\n // Bundled game mode: use DirectTransport\n this.initBundled(config);\n } else {\n // Iframe game mode: use PostMessageTransport\n this.initIframe(config);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n private initBundled(config: SmoreHostConfig): void {\n if (!config.socket) {\n throw new Error('[SmoreHost] socket is required for bundled games');\n }\n\n this.transport = new DirectTransport(config.socket);\n this._roomCode = config.roomCode || '';\n this._players = config.players || [];\n this._leaderIndex = config.leaderIndex ?? -1;\n\n this.setupEventHandlers();\n\n // Mark as ready immediately for bundled games\n this._isReady = true;\n this.config.onReady?.();\n }\n\n private initIframe(config: SmoreHostConfig): void {\n const parentOrigin = config.parentOrigin || '*';\n\n // Signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\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 const initData = (msg as SmoreInitMessage).payload;\n\n if (initData.side !== 'host') {\n console.error('[SmoreHost] Received init for wrong side:', initData.side);\n return;\n }\n\n // Initialize transport\n this.transport = new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n this._players = this.mapPlayersFromInit(initData.players);\n this._leaderIndex = this.findLeaderIndex(initData.players, initData.leaderId);\n\n this.setupEventHandlers();\n\n this._isReady = true;\n this.config.onReady?.();\n } else if (msg.type === 'smore:update') {\n const updateData = (msg as SmoreUpdateMessage).payload;\n\n if (updateData.players) {\n this._players = this.mapPlayersFromInit(updateData.players);\n }\n if (updateData.leaderId !== undefined) {\n this._leaderIndex = this.findLeaderIndex(this._players as any[], updateData.leaderId);\n }\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n private mapPlayersFromInit(players: any[]): SmorePlayer[] {\n return players.map((p, index) => ({\n playerIndex: p.playerIndex ?? index,\n nickname: p.nickname || `Player ${index + 1}`,\n connected: p.connected !== false,\n appearance: p.appearance,\n }));\n }\n\n private findLeaderIndex(players: any[], leaderId: string | null): number {\n if (!leaderId) return -1;\n const idx = players.findIndex((p) => p.sessionId === leaderId);\n return idx >= 0 ? idx : -1;\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave\n this.registerHandler(SYSTEM_EVENTS.PLAYER_JOIN, (data: { player: SmorePlayer }) => {\n const playerIndex = data.player?.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerJoin?.(playerIndex);\n }\n });\n\n this.registerHandler(SYSTEM_EVENTS.PLAYER_LEAVE, (data: { playerIndex: number }) => {\n if (data.playerIndex !== undefined) {\n this.config.onPlayerLeave?.(data.playerIndex);\n }\n });\n\n // Legacy room events (backward compatibility)\n this.registerHandler('room:player-joined', (data: any) => {\n const playerIndex = data?.player?.playerIndex ?? data?.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerJoin?.(playerIndex);\n }\n });\n\n this.registerHandler('room:player-left', (data: any) => {\n const playerIndex = data?.playerIndex ?? data?.player?.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerLeave?.(playerIndex);\n }\n });\n\n // User event listeners\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: { playerIndex: number; [key: string]: any }) => {\n const { playerIndex, ...rest } = data;\n if (playerIndex !== undefined) {\n handler(playerIndex, rest);\n }\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 // Public Properties\n // ---------------------------------------------------------------------------\n\n /**\n * Get all players in the room.\n * Returns a copy to prevent external mutation.\n */\n get players(): SmorePlayer[] {\n return [...this._players];\n }\n\n /**\n * Get the room code.\n */\n get roomCode(): string {\n return this._roomCode;\n }\n\n /**\n * Get the leader's player index (-1 if no leader).\n */\n get leaderIndex(): number {\n return this._leaderIndex;\n }\n\n /**\n * Check if the host is initialized and ready.\n */\n get isReady(): boolean {\n return this._isReady;\n }\n\n // ---------------------------------------------------------------------------\n // Public Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Broadcast an event to all players.\n *\n * @param event - Event name (no colons allowed)\n * @param data - Optional data payload\n *\n * @example\n * ```ts\n * host.broadcast('phase-update', { phase: 'playing' });\n * host.broadcast('timer-tick', { remaining: 30 });\n * ```\n */\n broadcast(event: string, data?: any): void {\n this.ensureReady('broadcast');\n validateEventName(event);\n // Emit user event directly - genericRelay handles Host → Room broadcast\n this.transport!.emit(event, data);\n }\n\n /**\n * Send an event to a specific player.\n *\n * @param playerIndex - Target player index (0, 1, 2, ...)\n * @param event - Event name (no colons allowed)\n * @param data - Optional data payload\n *\n * @example\n * ```ts\n * host.sendToPlayer(0, 'your-turn', { timeLimit: 30 });\n * host.sendToPlayer(1, 'wait', { message: 'Not your turn' });\n * ```\n */\n sendToPlayer(playerIndex: number, event: string, data?: any): void {\n this.ensureReady('sendToPlayer');\n validateEventName(event);\n // Emit user event with targetPlayerIndex - genericRelay handles Host → Player\n this.transport!.emit(event, {\n targetPlayerIndex: playerIndex,\n ...(data && typeof data === 'object' ? data : { data }),\n });\n }\n\n /**\n * Signal game over with results.\n * This will broadcast the game over event to all players.\n *\n * @param results - Game results (scores, winner, etc.)\n *\n * @example\n * ```ts\n * host.gameOver({\n * scores: { 0: 100, 1: 75, 2: 50 },\n * winner: 0,\n * });\n * ```\n */\n gameOver(results?: any): void {\n this.ensureReady('gameOver');\n this.transport!.emit(SYSTEM_EVENTS.GAME_OVER, { results });\n }\n\n /**\n * Add a listener for a specific event after construction.\n *\n * @param event - Event name (no colons allowed)\n * @param handler - Handler function (playerIndex, data) => void\n * @returns Cleanup function to remove the listener\n *\n * @example\n * ```ts\n * const cleanup = host.on('tap', (playerIndex, data) => {\n * console.log(`Player ${playerIndex} tapped`);\n * });\n *\n * // Later\n * cleanup();\n * ```\n */\n on(event: string, handler: (playerIndex: number, data: any) => void): () => void {\n validateEventName(event);\n\n const wrappedHandler = (data: { playerIndex: number; [key: string]: any }) => {\n const { playerIndex, ...rest } = data;\n if (playerIndex !== undefined) {\n handler(playerIndex, rest);\n }\n };\n\n if (this.transport) {\n this.transport.on(event, wrappedHandler);\n this.registeredHandlers.push({ event, handler: wrappedHandler });\n }\n\n return () => {\n this.transport?.off(event, wrappedHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.event !== event || h.handler !== wrappedHandler\n );\n };\n }\n\n /**\n * Clean up all resources.\n * Call this when unmounting/destroying the game.\n */\n destroy(): void {\n if (this._isDestroyed) return;\n\n this._isDestroyed = true;\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 // Destroy transport\n if (this.transport instanceof PostMessageTransport) {\n (this.transport as PostMessageTransport).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 // Private Helpers\n // ---------------------------------------------------------------------------\n\n private ensureReady(method: string): void {\n if (!this._isReady || !this.transport) {\n throw new Error(`[SmoreHost] Cannot call ${method}() before host is ready. Wait for onReady callback.`);\n }\n if (this._isDestroyed) {\n throw new Error(`[SmoreHost] Cannot call ${method}() after destroy()`);\n }\n }\n}\n","/**\n * SmorePlayer - Unified Player-side class for the S'MORE SDK (AirConsole style)\n *\n * Works in any environment: React, Phaser, Vanilla JS.\n * Automatically detects iframe vs bundled environment.\n *\n * @example Iframe game (auto-detection)\n * ```ts\n * const player = new SmorePlayer({\n * onReady: () => console.log('Ready! My index:', player.myIndex),\n * listeners: {\n * 'phase-update': (data) => handlePhaseUpdate(data),\n * },\n * });\n *\n * // Later\n * player.send('tap', { timestamp: Date.now() });\n * ```\n *\n * @example Bundled game (direct socket)\n * ```ts\n * const player = new SmorePlayer({\n * socket,\n * roomCode: 'ABCD',\n * myIndex: 0,\n * isLeader: true,\n * listeners: { ... },\n * });\n * ```\n */\n\nimport type { Socket } from 'socket.io-client';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { DirectTransport } from './transport/DirectTransport';\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} as const;\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\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 `[SmorePlayer] 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\n/**\n * Player information.\n */\nexport interface SmorePlayerInfo {\n /** Player index (0, 1, 2, ...) */\n playerIndex: number;\n /** Player's chosen nickname */\n nickname: string;\n /** Whether player is currently connected */\n connected: boolean;\n}\n\n/**\n * Configuration for SmorePlayer constructor.\n */\nexport interface SmorePlayerConfig {\n // === Callbacks ===\n\n /** Called when the player is ready and initialized (iframe games only) */\n onReady?: () => void;\n\n /** Called when another player joins the room */\n onPlayerJoin?: (playerIndex: number) => void;\n\n /** Called when another player leaves the room */\n onPlayerLeave?: (playerIndex: number) => void;\n\n /**\n * Event listeners for specific events.\n * Keys are event names (no colons), values are handler functions.\n * Handler receives (data) only - player side doesn't need playerIndex.\n */\n listeners?: Record<string, (data: any) => void>;\n\n // === Bundled game options (skip iframe detection) ===\n\n /** Socket.IO socket instance (bundled games only) */\n socket?: Socket;\n\n /** Room code (bundled games only) */\n roomCode?: string;\n\n /** My player index (bundled games only) */\n myIndex?: number;\n\n /** Am I the leader? (bundled games only) */\n isLeader?: boolean;\n\n // === Iframe game options ===\n\n /** Parent window origin for postMessage validation (iframe games) */\n parentOrigin?: string;\n}\n\n// ---------------------------------------------------------------------------\n// SmorePlayer Class\n// ---------------------------------------------------------------------------\n\n/**\n * SmorePlayer - Main player-side class for game development.\n *\n * Automatically detects iframe vs bundled environment:\n * - Iframe: Uses PostMessageTransport, waits for smore:init from parent\n * - Bundled: Uses DirectTransport with provided socket\n */\nexport class SmorePlayer {\n private transport: Transport | null = null;\n private config: SmorePlayerConfig;\n private _roomCode: string = '';\n private _myIndex: number = -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\n constructor(config: SmorePlayerConfig = {}) {\n this.config = config;\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 // Detect environment and initialize\n if (config.socket) {\n // Bundled game mode: use DirectTransport\n this.initBundled(config);\n } else {\n // Iframe game mode: use PostMessageTransport\n this.initIframe(config);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n private initBundled(config: SmorePlayerConfig): void {\n if (!config.socket) {\n throw new Error('[SmorePlayer] socket is required for bundled games');\n }\n\n this.transport = new DirectTransport(config.socket);\n this._roomCode = config.roomCode || '';\n this._myIndex = config.myIndex ?? -1;\n this._isLeader = config.isLeader ?? false;\n\n this.setupEventHandlers();\n\n // Mark as ready immediately for bundled games\n this._isReady = true;\n this.config.onReady?.();\n }\n\n private initIframe(config: SmorePlayerConfig): void {\n const parentOrigin = config.parentOrigin || '*';\n\n // Signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\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 const initData = (msg as SmoreInitMessage).payload;\n\n if (initData.side !== 'player') {\n console.error('[SmorePlayer] Received init for wrong side:', initData.side);\n return;\n }\n\n if (initData.myIndex === undefined) {\n console.error('[SmorePlayer] Missing myIndex in init payload');\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.config.onReady?.();\n } else if (msg.type === 'smore:update') {\n const updateData = (msg as SmoreUpdateMessage).payload;\n\n // Update leader status if changed\n if (updateData.leaderId !== undefined) {\n // Note: Without players array, we can't determine isLeader change\n // This would require the parent to send myIndex in update\n }\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave\n this.registerHandler(SYSTEM_EVENTS.PLAYER_JOIN, (data: { player?: SmorePlayerInfo; playerIndex?: number }) => {\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerJoin?.(playerIndex);\n }\n });\n\n this.registerHandler(SYSTEM_EVENTS.PLAYER_LEAVE, (data: { player?: { playerIndex?: number }; playerIndex?: number }) => {\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerLeave?.(playerIndex);\n }\n });\n\n // User event listeners\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n\n // Player side receives data directly (no playerIndex unwrapping)\n this.registerHandler(event, handler);\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 // Public Properties\n // ---------------------------------------------------------------------------\n\n /**\n * Get my player index (0, 1, 2, ...).\n */\n get myIndex(): number {\n return this._myIndex;\n }\n\n /**\n * Check if I am the room leader.\n */\n get isLeader(): boolean {\n return this._isLeader;\n }\n\n /**\n * Get the room code.\n */\n get roomCode(): string {\n return this._roomCode;\n }\n\n /**\n * Check if the player is initialized and ready.\n */\n get isReady(): boolean {\n return this._isReady;\n }\n\n // ---------------------------------------------------------------------------\n // Public Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Send an event to the host.\n *\n * @param event - Event name (no colons allowed)\n * @param data - Optional data payload\n *\n * @example\n * ```ts\n * player.send('tap', { timestamp: Date.now() });\n * player.send('answer', { choice: 2 });\n * ```\n */\n send(event: string, data?: any): void {\n this.ensureReady('send');\n validateEventName(event);\n this.transport!.emit(event, data);\n }\n\n /**\n * Add a listener for a specific event after construction.\n *\n * @param event - Event name (no colons allowed)\n * @param handler - Handler function (data) => void\n * @returns Cleanup function to remove the listener\n *\n * @example\n * ```ts\n * const cleanup = player.on('phase-update', (data) => {\n * console.log('New phase:', data.phase);\n * });\n *\n * // Later\n * cleanup();\n * ```\n */\n on(event: string, handler: (data: any) => void): () => void {\n validateEventName(event);\n\n if (this.transport) {\n this.transport.on(event, handler);\n this.registeredHandlers.push({ event, handler });\n }\n\n return () => {\n this.transport?.off(event, handler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.event !== event || h.handler !== handler\n );\n };\n }\n\n /**\n * Clean up all resources.\n * Call this when unmounting/destroying the game.\n */\n destroy(): void {\n if (this._isDestroyed) return;\n\n this._isDestroyed = true;\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 // Destroy transport\n if (this.transport instanceof PostMessageTransport) {\n (this.transport as PostMessageTransport).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 // Private Helpers\n // ---------------------------------------------------------------------------\n\n private ensureReady(method: string): void {\n if (!this._isReady || !this.transport) {\n throw new Error(`[SmorePlayer] Cannot call ${method}() before player is ready. Wait for onReady callback.`);\n }\n if (this._isDestroyed) {\n throw new Error(`[SmorePlayer] Cannot call ${method}() after destroy()`);\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"],"names":["SYSTEM_PREFIX","SYSTEM_EVENTS","EVENT_NAME_REGEX","validateEventName"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).SmoreSDKIframe={})}(this,function(e){"use strict";function n(e){return e&&"object"==typeof e&&"string"==typeof e.type&&e.type.startsWith("smore:")}class t{handlers=new Map;ackCallbacks=new Map;ackCounter=0;parentOrigin;boundMessageHandler;constructor(e="*"){this.parentOrigin=e,this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}emit(e,...n){let t,o=n[0];if(n.length>=2&&"function"==typeof n[n.length-1]){n.length,o=n[0];const e=n[n.length-1];t="ack_"+ ++this.ackCounter,this.ackCallbacks.set(t,e)}window.parent.postMessage({type:"smore:emit",payload:{event:e,data:o,ackId:t}},this.parentOrigin)}on(e,n){let t=this.handlers.get(e);t||(t=new Set,this.handlers.set(e,t)),t.add(n)}off(e,n){n?this.handlers.get(e)?.delete(n):this.handlers.delete(e)}destroy(){window.removeEventListener("message",this.boundMessageHandler),this.handlers.clear(),this.ackCallbacks.clear()}handleMessage(e){if("*"!==this.parentOrigin&&e.origin!==this.parentOrigin)return;const t=e.data;if(n(t))if("smore:event"===t.type){const{event:e,data:n}=t.payload,o=this.handlers.get(e);o&&o.forEach(e=>e(n))}else if("smore:ack"===t.type){const{ackId:e,data:n}=t.payload,o=this.ackCallbacks.get(e);o&&(this.ackCallbacks.delete(e),o(n))}}}const o=/^[a-zA-Z]([a-zA-Z_-]*[a-zA-Z])?$/;function s(e){return!!o.test(e)||(console.error(`[SDK] Invalid event name "${e}". Event names must:\n - Only contain letters (a-z, A-Z), hyphens (-), and underscores (_)\n - Start and end with a letter (no leading/trailing - or _)`),!1)}e.createHostBridge=function(e){const{parentOrigin:o="*",gameId:r,listeners:a,onPlayerJoin:i,onPlayerLeave:d,onReady:l}=e;let c=null;window.parent.postMessage({type:"smore:ready"},o);const g=e=>{if("*"!==o&&e.origin!==o)return;const r=e.data;if(n(r)&&"smore:init"===r.type){const{side:e,roomCode:n,players:g,leaderId:y}=r.payload;if("host"!==e)return void console.error("[HostBridge] Received init for wrong side:",e);c=new t(o),a&&Object.keys(a).forEach(e=>{s(e);const n=a[e];c.on(e,n)}),i&&c.on("player:joined",e=>{i(e.sessionId)}),d&&c.on("player:left",e=>{d(e.sessionId)}),l&&l({roomCode:n,players:g,leaderId:y})}};return window.addEventListener("message",g),{broadcast:(e,n)=>{c?(s(e),c.emit(e,n)):console.warn("[HostBridge] Cannot broadcast before init")},sendToPlayer:(e,n,t)=>{c?c.emit(n,{targetSessionId:e,...t}):console.warn("[HostBridge] Cannot sendToPlayer before init")},emitGameOver:e=>{c?c.emit("game-over",e):console.warn("[HostBridge] Cannot emitGameOver before init")},setLoaded:()=>{window.parent.postMessage({type:"smore:loaded"},o)},destroy:()=>{window.removeEventListener("message",g),c?.destroy(),c=null}}},e.createPlayerBridge=function(e){const{parentOrigin:o="*",gameId:r,listeners:a,onReady:i}=e;let d=null;window.parent.postMessage({type:"smore:ready"},o);const l=e=>{if("*"!==o&&e.origin!==o)return;const r=e.data;if(n(r)&&"smore:init"===r.type){const{side:e,roomCode:n,mySessionId:l,isLeader:c}=r.payload;if("player"!==e)return void console.error("[PlayerBridge] Received init for wrong side:",e);if(!l)return void console.error("[PlayerBridge] Missing mySessionId in init payload");d=new t(o),a&&Object.keys(a).forEach(e=>{s(e);const n=a[e];d.on(e,n)}),i&&i({roomCode:n,isLeader:!!c,mySessionId:l})}};return window.addEventListener("message",l),{emit:(e,n)=>{d?(s(e),d.emit(e,n)):console.warn("[PlayerBridge] Cannot emit before init")},onEvent:(e,n)=>d?(s(e),d.on(e,n),()=>{d?.off(e,n)}):(console.warn("[PlayerBridge] Cannot onEvent before init"),()=>{}),destroy:()=>{window.removeEventListener("message",l),d?.destroy(),d=null}}}});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).SmoreSDK={})}(this,function(e){"use strict";class t{constructor(e){this.socket=e}emit(e,...t){this.socket.emit(e,...t)}on(e,t){this.socket.on(e,t)}off(e,t){t?this.socket.off(e,t):this.socket.off(e)}}function s(e){return e&&"object"==typeof e&&"string"==typeof e.type&&e.type.startsWith("smore:")}class r{handlers=new Map;ackCallbacks=new Map;ackCounter=0;parentOrigin;boundMessageHandler;constructor(e="*"){this.parentOrigin=e,this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}emit(e,...t){let s,r=t[0];if(t.length>=2&&"function"==typeof t[t.length-1]){t.length,r=t[0];const e=t[t.length-1];s="ack_"+ ++this.ackCounter,this.ackCallbacks.set(s,e)}window.parent.postMessage({type:"smore:emit",payload:{event:e,data:r,ackId:s}},this.parentOrigin)}on(e,t){let s=this.handlers.get(e);s||(s=new Set,this.handlers.set(e,s)),s.add(t)}off(e,t){t?this.handlers.get(e)?.delete(t):this.handlers.delete(e)}destroy(){window.removeEventListener("message",this.boundMessageHandler),this.handlers.clear(),this.ackCallbacks.clear()}handleMessage(e){if("*"!==this.parentOrigin&&e.origin!==this.parentOrigin)return;const t=e.data;if(s(t))if("smore:event"===t.type){const{event:e,data:s}=t.payload,r=this.handlers.get(e);r&&r.forEach(e=>e(s))}else if("smore:ack"===t.type){const{ackId:e,data:s}=t.payload,r=this.ackCallbacks.get(e);r&&(this.ackCallbacks.delete(e),r(s))}}}const n="smore:",i={PLAYER_JOIN:`${n}player-join`,PLAYER_LEAVE:`${n}player-leave`,GAME_OVER:`${n}game-over`},a=/^[a-zA-Z]([a-zA-Z_-]*[a-zA-Z])?$/;function o(e){if(!a.test(e))throw new Error(`[SmoreHost] Invalid event name "${e}". Event names must:\n - Only contain letters (a-z, A-Z), hyphens (-), and underscores (_)\n - Start and end with a letter (no leading/trailing - or _)`)}const d="smore:",l={PLAYER_JOIN:`${d}player-join`,PLAYER_LEAVE:`${d}player-leave`},h=/^[a-zA-Z]([a-zA-Z_-]*[a-zA-Z])?$/;function y(e){if(!h.test(e))throw new Error(`[SmorePlayer] Invalid event name "${e}". Event names must:\n - Only contain letters (a-z, A-Z), hyphens (-), and underscores (_)\n - Start and end with a letter (no leading/trailing - or _)`)}e.DirectTransport=t,e.PostMessageTransport=r,e.SMORE_EVENTS={READY:"smore:ready",GAME_OVER:"smore:game-over",RETURN_TO_LOBBY:"smore:return-to-lobby",PLAYER_JOIN:"smore:player-join",PLAYER_LEAVE:"smore:player-leave",SEND_TO_PLAYER:"smore:send-to-player",INIT:"smore:init",UPDATE:"smore:update"},e.SmoreHost=class{transport=null;config;_players=[];_roomCode="";_leaderIndex=-1;_isReady=!1;_isDestroyed=!1;boundMessageHandler=null;registeredHandlers=[];constructor(e={}){if(this.config=e,e.listeners)for(const t of Object.keys(e.listeners))o(t);e.socket?this.initBundled(e):this.initIframe(e)}initBundled(e){if(!e.socket)throw new Error("[SmoreHost] socket is required for bundled games");this.transport=new t(e.socket),this._roomCode=e.roomCode||"",this._players=e.players||[],this._leaderIndex=e.leaderIndex??-1,this.setupEventHandlers(),this._isReady=!0,this.config.onReady?.()}initIframe(e){const t=e.parentOrigin||"*";window.parent.postMessage({type:"smore:ready"},t),this.boundMessageHandler=e=>{if("*"!==t&&e.origin!==t)return;const n=e.data;if(s(n))if("smore:init"===n.type){const e=n.payload;if("host"!==e.side)return void console.error("[SmoreHost] Received init for wrong side:",e.side);this.transport=new r(t),this._roomCode=e.roomCode,this._players=this.mapPlayersFromInit(e.players),this._leaderIndex=this.findLeaderIndex(e.players,e.leaderId),this.setupEventHandlers(),this._isReady=!0,this.config.onReady?.()}else if("smore:update"===n.type){const e=n.payload;e.players&&(this._players=this.mapPlayersFromInit(e.players)),void 0!==e.leaderId&&(this._leaderIndex=this.findLeaderIndex(this._players,e.leaderId))}},window.addEventListener("message",this.boundMessageHandler)}mapPlayersFromInit(e){return e.map((e,t)=>({playerIndex:e.playerIndex??t,nickname:e.nickname||`Player ${t+1}`,connected:!1!==e.connected,appearance:e.appearance}))}findLeaderIndex(e,t){if(!t)return-1;const s=e.findIndex(e=>e.sessionId===t);return s>=0?s:-1}setupEventHandlers(){if(this.transport&&(this.registerHandler(i.PLAYER_JOIN,e=>{const t=e.player?.playerIndex;void 0!==t&&this.config.onPlayerJoin?.(t)}),this.registerHandler(i.PLAYER_LEAVE,e=>{void 0!==e.playerIndex&&this.config.onPlayerLeave?.(e.playerIndex)}),this.registerHandler("room:player-joined",e=>{const t=e?.player?.playerIndex??e?.playerIndex;void 0!==t&&this.config.onPlayerJoin?.(t)}),this.registerHandler("room:player-left",e=>{const t=e?.playerIndex??e?.player?.playerIndex;void 0!==t&&this.config.onPlayerLeave?.(t)}),this.config.listeners))for(const[e,t]of Object.entries(this.config.listeners))t&&this.registerHandler(e,e=>{const{playerIndex:s,...r}=e;void 0!==s&&t(s,r)})}registerHandler(e,t){this.transport&&(this.transport.on(e,t),this.registeredHandlers.push({event:e,handler:t}))}get players(){return[...this._players]}get roomCode(){return this._roomCode}get leaderIndex(){return this._leaderIndex}get isReady(){return this._isReady}broadcast(e,t){this.ensureReady("broadcast"),o(e),this.transport.emit(e,t)}sendToPlayer(e,t,s){this.ensureReady("sendToPlayer"),o(t),this.transport.emit(t,{targetPlayerIndex:e,...s&&"object"==typeof s?s:{data:s}})}gameOver(e){this.ensureReady("gameOver"),this.transport.emit(i.GAME_OVER,{results:e})}on(e,t){o(e);const s=e=>{const{playerIndex:s,...r}=e;void 0!==s&&t(s,r)};return this.transport&&(this.transport.on(e,s),this.registeredHandlers.push({event:e,handler:s})),()=>{this.transport?.off(e,s),this.registeredHandlers=this.registeredHandlers.filter(t=>t.event!==e||t.handler!==s)}}destroy(){if(!this._isDestroyed){this._isDestroyed=!0,this._isReady=!1;for(const{event:e,handler:t}of this.registeredHandlers)this.transport?.off(e,t);this.registeredHandlers=[],this.transport instanceof r&&this.transport.destroy(),this.transport=null,this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null)}}ensureReady(e){if(!this._isReady||!this.transport)throw new Error(`[SmoreHost] Cannot call ${e}() before host is ready. Wait for onReady callback.`);if(this._isDestroyed)throw new Error(`[SmoreHost] Cannot call ${e}() after destroy()`)}},e.SmorePlayer=class{transport=null;config;_roomCode="";_myIndex=-1;_isLeader=!1;_isReady=!1;_isDestroyed=!1;boundMessageHandler=null;registeredHandlers=[];constructor(e={}){if(this.config=e,e.listeners)for(const t of Object.keys(e.listeners))y(t);e.socket?this.initBundled(e):this.initIframe(e)}initBundled(e){if(!e.socket)throw new Error("[SmorePlayer] socket is required for bundled games");this.transport=new t(e.socket),this._roomCode=e.roomCode||"",this._myIndex=e.myIndex??-1,this._isLeader=e.isLeader??!1,this.setupEventHandlers(),this._isReady=!0,this.config.onReady?.()}initIframe(e){const t=e.parentOrigin||"*";window.parent.postMessage({type:"smore:ready"},t),this.boundMessageHandler=e=>{if("*"!==t&&e.origin!==t)return;const n=e.data;if(s(n))if("smore:init"===n.type){const e=n.payload;if("player"!==e.side)return void console.error("[SmorePlayer] Received init for wrong side:",e.side);if(void 0===e.myIndex)return void console.error("[SmorePlayer] Missing myIndex in init payload");this.transport=new r(t),this._roomCode=e.roomCode,this._myIndex=e.myIndex,this._isLeader=e.isLeader??!1,this.setupEventHandlers(),this._isReady=!0,this.config.onReady?.()}else if("smore:update"===n.type){n.payload.leaderId}},window.addEventListener("message",this.boundMessageHandler)}setupEventHandlers(){if(this.transport&&(this.registerHandler(l.PLAYER_JOIN,e=>{const t=e.player?.playerIndex??e.playerIndex;void 0!==t&&this.config.onPlayerJoin?.(t)}),this.registerHandler(l.PLAYER_LEAVE,e=>{const t=e.player?.playerIndex??e.playerIndex;void 0!==t&&this.config.onPlayerLeave?.(t)}),this.config.listeners))for(const[e,t]of Object.entries(this.config.listeners))t&&this.registerHandler(e,t)}registerHandler(e,t){this.transport&&(this.transport.on(e,t),this.registeredHandlers.push({event:e,handler:t}))}get myIndex(){return this._myIndex}get isLeader(){return this._isLeader}get roomCode(){return this._roomCode}get isReady(){return this._isReady}send(e,t){this.ensureReady("send"),y(e),this.transport.emit(e,t)}on(e,t){return y(e),this.transport&&(this.transport.on(e,t),this.registeredHandlers.push({event:e,handler:t})),()=>{this.transport?.off(e,t),this.registeredHandlers=this.registeredHandlers.filter(s=>s.event!==e||s.handler!==t)}}destroy(){if(!this._isDestroyed){this._isDestroyed=!0,this._isReady=!1;for(const{event:e,handler:t}of this.registeredHandlers)this.transport?.off(e,t);this.registeredHandlers=[],this.transport instanceof r&&this.transport.destroy(),this.transport=null,this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null)}}ensureReady(e){if(!this._isReady||!this.transport)throw new Error(`[SmorePlayer] Cannot call ${e}() before player is ready. Wait for onReady callback.`);if(this._isDestroyed)throw new Error(`[SmorePlayer] Cannot call ${e}() after destroy()`)}},e.isSystemEvent=function(e){return e.startsWith("smore:")},e.validateUserEvent=function(e){if(e.includes(":"))throw new Error(`Invalid event name "${e}": User events cannot contain ':'. Use '_' or '-' instead. System events use 'smore:' prefix.`)}});
2
2
  //# sourceMappingURL=smore-sdk-vanilla.umd.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"smore-sdk-vanilla.umd.min.js","sources":["../../src/transport/protocol.ts","../../src/transport/PostMessageTransport.ts","../../src/iframe/vanilla.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 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 * 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 * Vanilla JS bridge API for iframe-hosted games.\n * No React - pure JavaScript with PostMessage transport.\n */\n\nimport { PostMessageTransport } from '../transport/PostMessageTransport';\nimport { isSmoreMessage, type SmoreInitMessage } from '../transport/protocol';\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): boolean {\n if (!EVENT_NAME_REGEX.test(event)) {\n console.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 return false;\n }\n return true;\n}\n\nexport interface HostBridgeOptions {\n parentOrigin?: string;\n gameId: string;\n listeners?: Record<string, (data: any) => void>;\n onPlayerJoin?: (playerId: string) => void;\n onPlayerLeave?: (playerId: string) => void;\n onReady?: (room: { roomCode: string; players: any[]; leaderId: string | null }) => void;\n}\n\nexport interface HostBridge {\n broadcast: (event: string, data?: any) => void;\n sendToPlayer: (playerId: string, event: string, data?: any) => void;\n emitGameOver: (results: any) => void;\n setLoaded: () => void;\n destroy: () => void;\n}\n\nexport interface PlayerBridgeOptions {\n parentOrigin?: string;\n gameId: string;\n listeners?: Record<string, (data: any) => void>;\n onReady?: (room: { roomCode: string; isLeader: boolean; mySessionId: string }) => void;\n}\n\nexport interface PlayerBridge {\n emit: (event: string, data?: any) => void;\n onEvent: (event: string, handler: (data: any) => void) => () => void;\n destroy: () => void;\n}\n\nexport function createHostBridge(options: HostBridgeOptions): HostBridge {\n const { parentOrigin = '*', gameId, listeners, onPlayerJoin, onPlayerLeave, onReady } = options;\n\n let transport: PostMessageTransport | null = null;\n let isInitialized = false;\n\n // Immediately signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\n\n const messageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:init') {\n const { side, roomCode, players, leaderId } = (msg as SmoreInitMessage).payload;\n\n if (side !== 'host') {\n console.error('[HostBridge] Received init for wrong side:', side);\n return;\n }\n\n // Create transport and wire up event handlers\n transport = new PostMessageTransport(parentOrigin);\n\n // Handle input events\n if (listeners) {\n Object.keys(listeners).forEach((event) => {\n validateEventName(event);\n const handler = listeners[event];\n transport!.on(event, handler);\n });\n }\n\n // Handle player join/leave events\n if (onPlayerJoin) {\n transport.on('player:joined', (payload: any) => {\n onPlayerJoin(payload.sessionId);\n });\n }\n if (onPlayerLeave) {\n transport.on('player:left', (payload: any) => {\n onPlayerLeave(payload.sessionId);\n });\n }\n\n isInitialized = true;\n\n // Call onReady callback\n if (onReady) {\n onReady({ roomCode, players, leaderId });\n }\n }\n };\n\n window.addEventListener('message', messageHandler);\n\n return {\n broadcast: (event: string, data?: any) => {\n if (!transport) {\n console.warn('[HostBridge] Cannot broadcast before init');\n return;\n }\n validateEventName(event);\n transport.emit(event, data);\n },\n\n sendToPlayer: (playerId: string, event: string, data?: any) => {\n if (!transport) {\n console.warn('[HostBridge] Cannot sendToPlayer before init');\n return;\n }\n transport.emit(event, { targetSessionId: playerId, ...data });\n },\n\n emitGameOver: (results: any) => {\n if (!transport) {\n console.warn('[HostBridge] Cannot emitGameOver before init');\n return;\n }\n transport.emit('game-over', results);\n },\n\n setLoaded: () => {\n window.parent.postMessage({ type: 'smore:loaded' }, parentOrigin);\n },\n\n destroy: () => {\n window.removeEventListener('message', messageHandler);\n transport?.destroy();\n transport = null;\n isInitialized = false;\n },\n };\n}\n\nexport function createPlayerBridge(options: PlayerBridgeOptions): PlayerBridge {\n const { parentOrigin = '*', gameId, listeners, onReady } = options;\n\n let transport: PostMessageTransport | null = null;\n let isInitialized = false;\n\n // Immediately signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\n\n const messageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:init') {\n const { side, roomCode, mySessionId, isLeader } = (msg as SmoreInitMessage).payload;\n\n if (side !== 'player') {\n console.error('[PlayerBridge] Received init for wrong side:', side);\n return;\n }\n\n if (!mySessionId) {\n console.error('[PlayerBridge] Missing mySessionId in init payload');\n return;\n }\n\n // Create transport and wire up listeners\n transport = new PostMessageTransport(parentOrigin);\n\n // Wire up game-specific event listeners\n if (listeners) {\n Object.keys(listeners).forEach((event) => {\n validateEventName(event);\n const handler = listeners[event];\n transport!.on(event, handler);\n });\n }\n\n isInitialized = true;\n\n // Call onReady callback\n if (onReady) {\n onReady({ roomCode, isLeader: !!isLeader, mySessionId });\n }\n }\n };\n\n window.addEventListener('message', messageHandler);\n\n return {\n emit: (event: string, data?: any) => {\n if (!transport) {\n console.warn('[PlayerBridge] Cannot emit before init');\n return;\n }\n validateEventName(event);\n transport.emit(event, data);\n },\n\n onEvent: (event: string, handler: (data: any) => void) => {\n if (!transport) {\n console.warn('[PlayerBridge] Cannot onEvent before init');\n return () => {};\n }\n validateEventName(event);\n transport.on(event, handler);\n return () => {\n transport?.off(event, handler);\n };\n },\n\n destroy: () => {\n window.removeEventListener('message', messageHandler);\n transport?.destroy();\n transport = null;\n isInitialized = false;\n },\n };\n}\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","EVENT_NAME_REGEX","validateEventName","test","console","error","options","gameId","listeners","onPlayerJoin","onPlayerLeave","onReady","transport","messageHandler","side","roomCode","players","leaderId","Object","keys","sessionId","broadcast","warn","sendToPlayer","playerId","targetSessionId","emitGameOver","results","setLoaded","mySessionId","isLeader","onEvent"],"mappings":"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"}
1
+ {"version":3,"file":"smore-sdk-vanilla.umd.min.js","sources":["../../src/transport/DirectTransport.ts","../../src/transport/protocol.ts","../../src/transport/PostMessageTransport.ts","../../src/SmoreHost.ts","../../src/SmorePlayer.ts","../../src/events.ts"],"sourcesContent":["/**\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 * 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\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 * 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 * SmoreHost - Unified Host-side class for the S'MORE SDK (AirConsole style)\n *\n * Works in any environment: React, Phaser, Vanilla JS.\n * Automatically detects iframe vs bundled environment.\n *\n * @example Iframe game (auto-detection)\n * ```ts\n * const host = new SmoreHost({\n * onPlayerJoin: (playerIndex) => console.log('Player joined:', playerIndex),\n * listeners: {\n * tap: (playerIndex, data) => handleTap(playerIndex, data),\n * },\n * });\n *\n * // Later\n * host.broadcast('phase-update', { phase: 'playing' });\n * host.gameOver({ scores: { 0: 100, 1: 50 } });\n * ```\n *\n * @example Bundled game (direct socket)\n * ```ts\n * const host = new SmoreHost({\n * socket,\n * roomCode: 'ABCD',\n * players: [...],\n * leaderIndex: 0,\n * listeners: { ... },\n * });\n * ```\n */\n\nimport type { Socket } from 'socket.io-client';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { DirectTransport } from './transport/DirectTransport';\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 GAME_OVER: `${SYSTEM_PREFIX}game-over`,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\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 `[SmoreHost] 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\n/**\n * Player information exposed to game developers.\n * Uses playerIndex (0, 1, 2, ...) instead of internal sessionId.\n */\nexport interface SmorePlayer {\n /** Player index (0, 1, 2, ...) */\n playerIndex: number;\n /** Player's chosen nickname */\n nickname: string;\n /** Whether player is currently connected */\n connected: boolean;\n /** Player's character appearance (optional) */\n appearance?: {\n skinColor?: string;\n hairColor?: string;\n shirtColor?: string;\n pantsColor?: string;\n };\n}\n\n/**\n * Configuration for SmoreHost constructor.\n */\nexport interface SmoreHostConfig {\n // === Callbacks ===\n\n /** Called when the host is initialized and ready (iframe games only) */\n onReady?: () => void;\n\n /** Called when a player joins the room */\n onPlayerJoin?: (playerIndex: number) => void;\n\n /** Called when a player leaves the room */\n onPlayerLeave?: (playerIndex: number) => void;\n\n /**\n * Event listeners for specific events.\n * Keys are event names (no colons), values are handler functions.\n * Handler receives (playerIndex, data).\n */\n listeners?: Record<string, (playerIndex: number, data: any) => void>;\n\n // === Bundled game options (skip iframe detection) ===\n\n /** Socket.IO socket instance (bundled games only) */\n socket?: Socket;\n\n /** Room code (bundled games only) */\n roomCode?: string;\n\n /** Initial players array (bundled games only) */\n players?: SmorePlayer[];\n\n /** Leader player index (bundled games only) */\n leaderIndex?: number;\n\n // === Iframe game options ===\n\n /** Parent window origin for postMessage validation (iframe games) */\n parentOrigin?: string;\n}\n\n// ---------------------------------------------------------------------------\n// SmoreHost Class\n// ---------------------------------------------------------------------------\n\n/**\n * SmoreHost - Main host-side class for game development.\n *\n * Automatically detects iframe vs bundled environment:\n * - Iframe: Uses PostMessageTransport, waits for smore:init from parent\n * - Bundled: Uses DirectTransport with provided socket\n */\nexport class SmoreHost {\n private transport: Transport | null = null;\n private config: SmoreHostConfig;\n private _players: SmorePlayer[] = [];\n private _roomCode: string = '';\n private _leaderIndex: number = -1;\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\n constructor(config: SmoreHostConfig = {}) {\n this.config = config;\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 // Detect environment and initialize\n if (config.socket) {\n // Bundled game mode: use DirectTransport\n this.initBundled(config);\n } else {\n // Iframe game mode: use PostMessageTransport\n this.initIframe(config);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n private initBundled(config: SmoreHostConfig): void {\n if (!config.socket) {\n throw new Error('[SmoreHost] socket is required for bundled games');\n }\n\n this.transport = new DirectTransport(config.socket);\n this._roomCode = config.roomCode || '';\n this._players = config.players || [];\n this._leaderIndex = config.leaderIndex ?? -1;\n\n this.setupEventHandlers();\n\n // Mark as ready immediately for bundled games\n this._isReady = true;\n this.config.onReady?.();\n }\n\n private initIframe(config: SmoreHostConfig): void {\n const parentOrigin = config.parentOrigin || '*';\n\n // Signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\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 const initData = (msg as SmoreInitMessage).payload;\n\n if (initData.side !== 'host') {\n console.error('[SmoreHost] Received init for wrong side:', initData.side);\n return;\n }\n\n // Initialize transport\n this.transport = new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n this._players = this.mapPlayersFromInit(initData.players);\n this._leaderIndex = this.findLeaderIndex(initData.players, initData.leaderId);\n\n this.setupEventHandlers();\n\n this._isReady = true;\n this.config.onReady?.();\n } else if (msg.type === 'smore:update') {\n const updateData = (msg as SmoreUpdateMessage).payload;\n\n if (updateData.players) {\n this._players = this.mapPlayersFromInit(updateData.players);\n }\n if (updateData.leaderId !== undefined) {\n this._leaderIndex = this.findLeaderIndex(this._players as any[], updateData.leaderId);\n }\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n private mapPlayersFromInit(players: any[]): SmorePlayer[] {\n return players.map((p, index) => ({\n playerIndex: p.playerIndex ?? index,\n nickname: p.nickname || `Player ${index + 1}`,\n connected: p.connected !== false,\n appearance: p.appearance,\n }));\n }\n\n private findLeaderIndex(players: any[], leaderId: string | null): number {\n if (!leaderId) return -1;\n const idx = players.findIndex((p) => p.sessionId === leaderId);\n return idx >= 0 ? idx : -1;\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave\n this.registerHandler(SYSTEM_EVENTS.PLAYER_JOIN, (data: { player: SmorePlayer }) => {\n const playerIndex = data.player?.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerJoin?.(playerIndex);\n }\n });\n\n this.registerHandler(SYSTEM_EVENTS.PLAYER_LEAVE, (data: { playerIndex: number }) => {\n if (data.playerIndex !== undefined) {\n this.config.onPlayerLeave?.(data.playerIndex);\n }\n });\n\n // Legacy room events (backward compatibility)\n this.registerHandler('room:player-joined', (data: any) => {\n const playerIndex = data?.player?.playerIndex ?? data?.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerJoin?.(playerIndex);\n }\n });\n\n this.registerHandler('room:player-left', (data: any) => {\n const playerIndex = data?.playerIndex ?? data?.player?.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerLeave?.(playerIndex);\n }\n });\n\n // User event listeners\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: { playerIndex: number; [key: string]: any }) => {\n const { playerIndex, ...rest } = data;\n if (playerIndex !== undefined) {\n handler(playerIndex, rest);\n }\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 // Public Properties\n // ---------------------------------------------------------------------------\n\n /**\n * Get all players in the room.\n * Returns a copy to prevent external mutation.\n */\n get players(): SmorePlayer[] {\n return [...this._players];\n }\n\n /**\n * Get the room code.\n */\n get roomCode(): string {\n return this._roomCode;\n }\n\n /**\n * Get the leader's player index (-1 if no leader).\n */\n get leaderIndex(): number {\n return this._leaderIndex;\n }\n\n /**\n * Check if the host is initialized and ready.\n */\n get isReady(): boolean {\n return this._isReady;\n }\n\n // ---------------------------------------------------------------------------\n // Public Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Broadcast an event to all players.\n *\n * @param event - Event name (no colons allowed)\n * @param data - Optional data payload\n *\n * @example\n * ```ts\n * host.broadcast('phase-update', { phase: 'playing' });\n * host.broadcast('timer-tick', { remaining: 30 });\n * ```\n */\n broadcast(event: string, data?: any): void {\n this.ensureReady('broadcast');\n validateEventName(event);\n // Emit user event directly - genericRelay handles Host → Room broadcast\n this.transport!.emit(event, data);\n }\n\n /**\n * Send an event to a specific player.\n *\n * @param playerIndex - Target player index (0, 1, 2, ...)\n * @param event - Event name (no colons allowed)\n * @param data - Optional data payload\n *\n * @example\n * ```ts\n * host.sendToPlayer(0, 'your-turn', { timeLimit: 30 });\n * host.sendToPlayer(1, 'wait', { message: 'Not your turn' });\n * ```\n */\n sendToPlayer(playerIndex: number, event: string, data?: any): void {\n this.ensureReady('sendToPlayer');\n validateEventName(event);\n // Emit user event with targetPlayerIndex - genericRelay handles Host → Player\n this.transport!.emit(event, {\n targetPlayerIndex: playerIndex,\n ...(data && typeof data === 'object' ? data : { data }),\n });\n }\n\n /**\n * Signal game over with results.\n * This will broadcast the game over event to all players.\n *\n * @param results - Game results (scores, winner, etc.)\n *\n * @example\n * ```ts\n * host.gameOver({\n * scores: { 0: 100, 1: 75, 2: 50 },\n * winner: 0,\n * });\n * ```\n */\n gameOver(results?: any): void {\n this.ensureReady('gameOver');\n this.transport!.emit(SYSTEM_EVENTS.GAME_OVER, { results });\n }\n\n /**\n * Add a listener for a specific event after construction.\n *\n * @param event - Event name (no colons allowed)\n * @param handler - Handler function (playerIndex, data) => void\n * @returns Cleanup function to remove the listener\n *\n * @example\n * ```ts\n * const cleanup = host.on('tap', (playerIndex, data) => {\n * console.log(`Player ${playerIndex} tapped`);\n * });\n *\n * // Later\n * cleanup();\n * ```\n */\n on(event: string, handler: (playerIndex: number, data: any) => void): () => void {\n validateEventName(event);\n\n const wrappedHandler = (data: { playerIndex: number; [key: string]: any }) => {\n const { playerIndex, ...rest } = data;\n if (playerIndex !== undefined) {\n handler(playerIndex, rest);\n }\n };\n\n if (this.transport) {\n this.transport.on(event, wrappedHandler);\n this.registeredHandlers.push({ event, handler: wrappedHandler });\n }\n\n return () => {\n this.transport?.off(event, wrappedHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.event !== event || h.handler !== wrappedHandler\n );\n };\n }\n\n /**\n * Clean up all resources.\n * Call this when unmounting/destroying the game.\n */\n destroy(): void {\n if (this._isDestroyed) return;\n\n this._isDestroyed = true;\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 // Destroy transport\n if (this.transport instanceof PostMessageTransport) {\n (this.transport as PostMessageTransport).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 // Private Helpers\n // ---------------------------------------------------------------------------\n\n private ensureReady(method: string): void {\n if (!this._isReady || !this.transport) {\n throw new Error(`[SmoreHost] Cannot call ${method}() before host is ready. Wait for onReady callback.`);\n }\n if (this._isDestroyed) {\n throw new Error(`[SmoreHost] Cannot call ${method}() after destroy()`);\n }\n }\n}\n","/**\n * SmorePlayer - Unified Player-side class for the S'MORE SDK (AirConsole style)\n *\n * Works in any environment: React, Phaser, Vanilla JS.\n * Automatically detects iframe vs bundled environment.\n *\n * @example Iframe game (auto-detection)\n * ```ts\n * const player = new SmorePlayer({\n * onReady: () => console.log('Ready! My index:', player.myIndex),\n * listeners: {\n * 'phase-update': (data) => handlePhaseUpdate(data),\n * },\n * });\n *\n * // Later\n * player.send('tap', { timestamp: Date.now() });\n * ```\n *\n * @example Bundled game (direct socket)\n * ```ts\n * const player = new SmorePlayer({\n * socket,\n * roomCode: 'ABCD',\n * myIndex: 0,\n * isLeader: true,\n * listeners: { ... },\n * });\n * ```\n */\n\nimport type { Socket } from 'socket.io-client';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport { DirectTransport } from './transport/DirectTransport';\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} as const;\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\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 `[SmorePlayer] 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\n/**\n * Player information.\n */\nexport interface SmorePlayerInfo {\n /** Player index (0, 1, 2, ...) */\n playerIndex: number;\n /** Player's chosen nickname */\n nickname: string;\n /** Whether player is currently connected */\n connected: boolean;\n}\n\n/**\n * Configuration for SmorePlayer constructor.\n */\nexport interface SmorePlayerConfig {\n // === Callbacks ===\n\n /** Called when the player is ready and initialized (iframe games only) */\n onReady?: () => void;\n\n /** Called when another player joins the room */\n onPlayerJoin?: (playerIndex: number) => void;\n\n /** Called when another player leaves the room */\n onPlayerLeave?: (playerIndex: number) => void;\n\n /**\n * Event listeners for specific events.\n * Keys are event names (no colons), values are handler functions.\n * Handler receives (data) only - player side doesn't need playerIndex.\n */\n listeners?: Record<string, (data: any) => void>;\n\n // === Bundled game options (skip iframe detection) ===\n\n /** Socket.IO socket instance (bundled games only) */\n socket?: Socket;\n\n /** Room code (bundled games only) */\n roomCode?: string;\n\n /** My player index (bundled games only) */\n myIndex?: number;\n\n /** Am I the leader? (bundled games only) */\n isLeader?: boolean;\n\n // === Iframe game options ===\n\n /** Parent window origin for postMessage validation (iframe games) */\n parentOrigin?: string;\n}\n\n// ---------------------------------------------------------------------------\n// SmorePlayer Class\n// ---------------------------------------------------------------------------\n\n/**\n * SmorePlayer - Main player-side class for game development.\n *\n * Automatically detects iframe vs bundled environment:\n * - Iframe: Uses PostMessageTransport, waits for smore:init from parent\n * - Bundled: Uses DirectTransport with provided socket\n */\nexport class SmorePlayer {\n private transport: Transport | null = null;\n private config: SmorePlayerConfig;\n private _roomCode: string = '';\n private _myIndex: number = -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\n constructor(config: SmorePlayerConfig = {}) {\n this.config = config;\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 // Detect environment and initialize\n if (config.socket) {\n // Bundled game mode: use DirectTransport\n this.initBundled(config);\n } else {\n // Iframe game mode: use PostMessageTransport\n this.initIframe(config);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n private initBundled(config: SmorePlayerConfig): void {\n if (!config.socket) {\n throw new Error('[SmorePlayer] socket is required for bundled games');\n }\n\n this.transport = new DirectTransport(config.socket);\n this._roomCode = config.roomCode || '';\n this._myIndex = config.myIndex ?? -1;\n this._isLeader = config.isLeader ?? false;\n\n this.setupEventHandlers();\n\n // Mark as ready immediately for bundled games\n this._isReady = true;\n this.config.onReady?.();\n }\n\n private initIframe(config: SmorePlayerConfig): void {\n const parentOrigin = config.parentOrigin || '*';\n\n // Signal ready to parent\n window.parent.postMessage({ type: 'smore:ready' }, parentOrigin);\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 const initData = (msg as SmoreInitMessage).payload;\n\n if (initData.side !== 'player') {\n console.error('[SmorePlayer] Received init for wrong side:', initData.side);\n return;\n }\n\n if (initData.myIndex === undefined) {\n console.error('[SmorePlayer] Missing myIndex in init payload');\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.config.onReady?.();\n } else if (msg.type === 'smore:update') {\n const updateData = (msg as SmoreUpdateMessage).payload;\n\n // Update leader status if changed\n if (updateData.leaderId !== undefined) {\n // Note: Without players array, we can't determine isLeader change\n // This would require the parent to send myIndex in update\n }\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave\n this.registerHandler(SYSTEM_EVENTS.PLAYER_JOIN, (data: { player?: SmorePlayerInfo; playerIndex?: number }) => {\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerJoin?.(playerIndex);\n }\n });\n\n this.registerHandler(SYSTEM_EVENTS.PLAYER_LEAVE, (data: { player?: { playerIndex?: number }; playerIndex?: number }) => {\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n this.config.onPlayerLeave?.(playerIndex);\n }\n });\n\n // User event listeners\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n\n // Player side receives data directly (no playerIndex unwrapping)\n this.registerHandler(event, handler);\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 // Public Properties\n // ---------------------------------------------------------------------------\n\n /**\n * Get my player index (0, 1, 2, ...).\n */\n get myIndex(): number {\n return this._myIndex;\n }\n\n /**\n * Check if I am the room leader.\n */\n get isLeader(): boolean {\n return this._isLeader;\n }\n\n /**\n * Get the room code.\n */\n get roomCode(): string {\n return this._roomCode;\n }\n\n /**\n * Check if the player is initialized and ready.\n */\n get isReady(): boolean {\n return this._isReady;\n }\n\n // ---------------------------------------------------------------------------\n // Public Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Send an event to the host.\n *\n * @param event - Event name (no colons allowed)\n * @param data - Optional data payload\n *\n * @example\n * ```ts\n * player.send('tap', { timestamp: Date.now() });\n * player.send('answer', { choice: 2 });\n * ```\n */\n send(event: string, data?: any): void {\n this.ensureReady('send');\n validateEventName(event);\n this.transport!.emit(event, data);\n }\n\n /**\n * Add a listener for a specific event after construction.\n *\n * @param event - Event name (no colons allowed)\n * @param handler - Handler function (data) => void\n * @returns Cleanup function to remove the listener\n *\n * @example\n * ```ts\n * const cleanup = player.on('phase-update', (data) => {\n * console.log('New phase:', data.phase);\n * });\n *\n * // Later\n * cleanup();\n * ```\n */\n on(event: string, handler: (data: any) => void): () => void {\n validateEventName(event);\n\n if (this.transport) {\n this.transport.on(event, handler);\n this.registeredHandlers.push({ event, handler });\n }\n\n return () => {\n this.transport?.off(event, handler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.event !== event || h.handler !== handler\n );\n };\n }\n\n /**\n * Clean up all resources.\n * Call this when unmounting/destroying the game.\n */\n destroy(): void {\n if (this._isDestroyed) return;\n\n this._isDestroyed = true;\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 // Destroy transport\n if (this.transport instanceof PostMessageTransport) {\n (this.transport as PostMessageTransport).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 // Private Helpers\n // ---------------------------------------------------------------------------\n\n private ensureReady(method: string): void {\n if (!this._isReady || !this.transport) {\n throw new Error(`[SmorePlayer] Cannot call ${method}() before player is ready. Wait for onReady callback.`);\n }\n if (this._isDestroyed) {\n throw new Error(`[SmorePlayer] Cannot call ${method}() after destroy()`);\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"],"names":["DirectTransport","constructor","socket","this","emit","event","args","on","handler","off","isSmoreMessage","data","type","startsWith","PostMessageTransport","handlers","Map","ackCallbacks","ackCounter","parentOrigin","boundMessageHandler","handleMessage","bind","window","addEventListener","ackId","length","callback","set","parent","postMessage","payload","get","Set","add","delete","destroy","removeEventListener","clear","e","origin","msg","forEach","cb","SYSTEM_PREFIX","SYSTEM_EVENTS","PLAYER_JOIN","PLAYER_LEAVE","GAME_OVER","EVENT_NAME_REGEX","validateEventName","test","Error","READY","RETURN_TO_LOBBY","SEND_TO_PLAYER","INIT","UPDATE","transport","config","_players","_roomCode","_leaderIndex","_isReady","_isDestroyed","registeredHandlers","listeners","Object","keys","initBundled","initIframe","roomCode","players","leaderIndex","setupEventHandlers","onReady","initData","side","console","error","mapPlayersFromInit","findLeaderIndex","leaderId","updateData","map","p","index","playerIndex","nickname","connected","appearance","idx","findIndex","sessionId","registerHandler","player","onPlayerJoin","onPlayerLeave","entries","rest","push","isReady","broadcast","ensureReady","sendToPlayer","targetPlayerIndex","gameOver","results","wrappedHandler","filter","h","method","_myIndex","_isLeader","myIndex","isLeader","send","includes"],"mappings":"+OAQO,MAAMA,EACX,WAAAC,CAAoBC,GAAAC,KAAAD,OAAAA,CAAiB,CAErC,IAAAE,CAAKC,KAAkBC,GACrBH,KAAKD,OAAOE,KAAKC,KAAUC,EAC7B,CAEA,EAAAC,CAAGF,EAAeG,GAChBL,KAAKD,OAAOK,GAAGF,EAAOG,EACxB,CAEA,GAAAC,CAAIJ,EAAeG,GACbA,EACFL,KAAKD,OAAOO,IAAIJ,EAAOG,GAEvBL,KAAKD,OAAOO,IAAIJ,EAEpB,EC2CK,SAASK,EAAeC,GAC7B,OAAOA,GAAwB,iBAATA,GAA0C,iBAAdA,EAAKC,MAAqBD,EAAKC,KAAKC,WAjExD,SAkEhC,CC5DO,MAAMC,EACHC,aAAeC,IACfC,iBAAmBD,IACnBE,WAAa,EACbC,aACAC,oBAER,WAAAnB,CAAYkB,EAAuB,KACjChB,KAAKgB,aAAeA,EACpBhB,KAAKiB,oBAAsBjB,KAAKkB,cAAcC,KAAKnB,MACnDoB,OAAOC,iBAAiB,UAAWrB,KAAKiB,oBAC1C,CAEA,IAAAhB,CAAKC,KAAkBC,GAErB,IACImB,EADAd,EAAYL,EAAK,GAGrB,GAAIA,EAAKoB,QAAU,GAAsC,mBAA1BpB,EAAKA,EAAKoB,OAAS,GAAmB,CAC5DpB,EAAKoB,OAAZf,EAA2BL,EAAK,GAChC,MAAMqB,EAAWrB,EAAKA,EAAKoB,OAAS,GACpCD,EAAQ,UAAStB,KAAKe,WACtBf,KAAKc,aAAaW,IAAIH,EAAOE,EAC/B,CAEAJ,OAAOM,OAAOC,YACZ,CAAElB,KAAM,aAAcmB,QAAS,CAAE1B,QAAOM,OAAMc,UAC9CtB,KAAKgB,aAET,CAEA,EAAAZ,CAAGF,EAAeG,GAChB,IAAIoB,EAAMzB,KAAKY,SAASiB,IAAI3B,GACvBuB,IACHA,MAAUK,IACV9B,KAAKY,SAASa,IAAIvB,EAAOuB,IAE3BA,EAAIM,IAAI1B,EACV,CAEA,GAAAC,CAAIJ,EAAeG,GACZA,EAILL,KAAKY,SAASiB,IAAI3B,IAAQ8B,OAAO3B,GAH/BL,KAAKY,SAASoB,OAAO9B,EAIzB,CAEA,OAAA+B,GACEb,OAAOc,oBAAoB,UAAWlC,KAAKiB,qBAC3CjB,KAAKY,SAASuB,QACdnC,KAAKc,aAAaqB,OACpB,CAEQ,aAAAjB,CAAckB,GAEpB,GAA0B,MAAtBpC,KAAKgB,cAAwBoB,EAAEC,SAAWrC,KAAKgB,aAAc,OAEjE,MAAMsB,EAAMF,EAAE5B,KACd,GAAKD,EAAe+B,GAEpB,GAAiB,gBAAbA,EAAI7B,KAAwB,CAC9B,MAAMP,MAAEA,EAAAM,KAAOA,GAAU8B,EAA0BV,QAC7CH,EAAMzB,KAAKY,SAASiB,IAAI3B,GAC1BuB,GACFA,EAAIc,QAASlC,GAAYA,EAAQG,GAErC,MAAA,GAAwB,cAAb8B,EAAI7B,KAAsB,CACnC,MAAMa,MAAEA,EAAAd,KAAOA,GAAU8B,EAAwBV,QAC3CY,EAAKxC,KAAKc,aAAae,IAAIP,GAC7BkB,IACFxC,KAAKc,aAAakB,OAAOV,GACzBkB,EAAGhC,GAEP,CACF,EC3CF,MAAMiC,EAAgB,SAEhBC,EAAgB,CAEpBC,YAAa,GAAGF,eAChBG,aAAc,GAAGH,gBACjBI,UAAW,GAAGJ,cAOVK,EAAmB,mCAEzB,SAASC,EAAkB7C,GACzB,IAAK4C,EAAiBE,KAAK9C,GACzB,MAAM,IAAI+C,MACR,mCAAmC/C,6JAKzC,CCxBA,MAAMuC,EAAgB,SAEhBC,EAAgB,CAEpBC,YAAa,GAAGF,eAChBG,aAAc,GAAGH,iBAObK,EAAmB,mCAEzB,SAASC,EAAkB7C,GACzB,IAAK4C,EAAiBE,KAAK9C,GACzB,MAAM,IAAI+C,MACR,qCAAqC/C,6JAK3C,6DCzD4B,CAE1BgD,MAAO,cACPL,UAAW,kBACXM,gBAAiB,wBAGjBR,YAAa,oBACbC,aAAc,qBAGdQ,eAAgB,uBAGhBC,KAAM,aACNC,OAAQ,4BF2HH,MACGC,UAA8B,KAC9BC,OACAC,SAA0B,GAC1BC,UAAoB,GACpBC,cAAuB,EACvBC,UAAoB,EACpBC,cAAwB,EACxB5C,oBAA0D,KAC1D6C,mBAA+E,GAEvF,WAAAhE,CAAY0D,EAA0B,IAIpC,GAHAxD,KAAKwD,OAASA,EAGVA,EAAOO,UACT,IAAA,MAAW7D,KAAS8D,OAAOC,KAAKT,EAAOO,WACrChB,EAAkB7C,GAKlBsD,EAAOzD,OAETC,KAAKkE,YAAYV,GAGjBxD,KAAKmE,WAAWX,EAEpB,CAMQ,WAAAU,CAAYV,GAClB,IAAKA,EAAOzD,OACV,MAAM,IAAIkD,MAAM,oDAGlBjD,KAAKuD,UAAY,IAAI1D,EAAgB2D,EAAOzD,QAC5CC,KAAK0D,UAAYF,EAAOY,UAAY,GACpCpE,KAAKyD,SAAWD,EAAOa,SAAW,GAClCrE,KAAK2D,aAAeH,EAAOc,cAAe,EAE1CtE,KAAKuE,qBAGLvE,KAAK4D,UAAW,EAChB5D,KAAKwD,OAAOgB,WACd,CAEQ,UAAAL,CAAWX,GACjB,MAAMxC,EAAewC,EAAOxC,cAAgB,IAG5CI,OAAOM,OAAOC,YAAY,CAAElB,KAAM,eAAiBO,GAGnDhB,KAAKiB,oBAAuBmB,IAC1B,GAAqB,MAAjBpB,GAAwBoB,EAAEC,SAAWrB,EAAc,OAEvD,MAAMsB,EAAMF,EAAE5B,KACd,GAAKD,EAAe+B,GAEpB,GAAiB,eAAbA,EAAI7B,KAAuB,CAC7B,MAAMgE,EAAYnC,EAAyBV,QAE3C,GAAsB,SAAlB6C,EAASC,KAEX,YADAC,QAAQC,MAAM,4CAA6CH,EAASC,MAKtE1E,KAAKuD,UAAY,IAAI5C,EAAqBK,GAC1ChB,KAAK0D,UAAYe,EAASL,SAC1BpE,KAAKyD,SAAWzD,KAAK6E,mBAAmBJ,EAASJ,SACjDrE,KAAK2D,aAAe3D,KAAK8E,gBAAgBL,EAASJ,QAASI,EAASM,UAEpE/E,KAAKuE,qBAELvE,KAAK4D,UAAW,EAChB5D,KAAKwD,OAAOgB,WACd,MAAA,GAAwB,iBAAblC,EAAI7B,KAAyB,CACtC,MAAMuE,EAAc1C,EAA2BV,QAE3CoD,EAAWX,UACbrE,KAAKyD,SAAWzD,KAAK6E,mBAAmBG,EAAWX,mBAEjDW,EAAWD,WACb/E,KAAK2D,aAAe3D,KAAK8E,gBAAgB9E,KAAKyD,SAAmBuB,EAAWD,UAEhF,GAGF3D,OAAOC,iBAAiB,UAAWrB,KAAKiB,oBAC1C,CAEQ,kBAAA4D,CAAmBR,GACzB,OAAOA,EAAQY,IAAI,CAACC,EAAGC,KAAA,CACrBC,YAAaF,EAAEE,aAAeD,EAC9BE,SAAUH,EAAEG,UAAY,UAAUF,EAAQ,IAC1CG,WAA2B,IAAhBJ,EAAEI,UACbC,WAAYL,EAAEK,aAElB,CAEQ,eAAAT,CAAgBT,EAAgBU,GACtC,IAAKA,EAAU,OAAO,EACtB,MAAMS,EAAMnB,EAAQoB,UAAWP,GAAMA,EAAEQ,YAAcX,GACrD,OAAOS,GAAO,EAAIA,GAAM,CAC1B,CAEQ,kBAAAjB,GACN,GAAKvE,KAAKuD,YAGVvD,KAAK2F,gBAAgBjD,EAAcC,YAAcnC,IAC/C,MAAM4E,EAAc5E,EAAKoF,QAAQR,iBACb,IAAhBA,GACFpF,KAAKwD,OAAOqC,eAAeT,KAI/BpF,KAAK2F,gBAAgBjD,EAAcE,aAAepC,aAC5CA,EAAK4E,aACPpF,KAAKwD,OAAOsC,gBAAgBtF,EAAK4E,eAKrCpF,KAAK2F,gBAAgB,qBAAuBnF,IAC1C,MAAM4E,EAAc5E,GAAMoF,QAAQR,aAAe5E,GAAM4E,iBACnC,IAAhBA,GACFpF,KAAKwD,OAAOqC,eAAeT,KAI/BpF,KAAK2F,gBAAgB,mBAAqBnF,IACxC,MAAM4E,EAAc5E,GAAM4E,aAAe5E,GAAMoF,QAAQR,iBACnC,IAAhBA,GACFpF,KAAKwD,OAAOsC,gBAAgBV,KAK5BpF,KAAKwD,OAAOO,WACd,IAAA,MAAY7D,EAAOG,KAAY2D,OAAO+B,QAAQ/F,KAAKwD,OAAOO,WACnD1D,GAELL,KAAK2F,gBAAgBzF,EAAQM,IAC3B,MAAM4E,YAAEA,KAAgBY,GAASxF,OACb,IAAhB4E,GACF/E,EAAQ+E,EAAaY,IAK/B,CAEQ,eAAAL,CAAgBzF,EAAeG,GAChCL,KAAKuD,YACVvD,KAAKuD,UAAUnD,GAAGF,EAAOG,GACzBL,KAAK8D,mBAAmBmC,KAAK,CAAE/F,QAAOG,YACxC,CAUA,WAAIgE,GACF,MAAO,IAAIrE,KAAKyD,SAClB,CAKA,YAAIW,GACF,OAAOpE,KAAK0D,SACd,CAKA,eAAIY,GACF,OAAOtE,KAAK2D,YACd,CAKA,WAAIuC,GACF,OAAOlG,KAAK4D,QACd,CAkBA,SAAAuC,CAAUjG,EAAeM,GACvBR,KAAKoG,YAAY,aACjBrD,EAAkB7C,GAElBF,KAAKuD,UAAWtD,KAAKC,EAAOM,EAC9B,CAeA,YAAA6F,CAAajB,EAAqBlF,EAAeM,GAC/CR,KAAKoG,YAAY,gBACjBrD,EAAkB7C,GAElBF,KAAKuD,UAAWtD,KAAKC,EAAO,CAC1BoG,kBAAmBlB,KACf5E,GAAwB,iBAATA,EAAoBA,EAAO,CAAEA,SAEpD,CAgBA,QAAA+F,CAASC,GACPxG,KAAKoG,YAAY,YACjBpG,KAAKuD,UAAWtD,KAAKyC,EAAcG,UAAW,CAAE2D,WAClD,CAmBA,EAAApG,CAAGF,EAAeG,GAChB0C,EAAkB7C,GAElB,MAAMuG,EAAkBjG,IACtB,MAAM4E,YAAEA,KAAgBY,GAASxF,OACb,IAAhB4E,GACF/E,EAAQ+E,EAAaY,IASzB,OALIhG,KAAKuD,YACPvD,KAAKuD,UAAUnD,GAAGF,EAAOuG,GACzBzG,KAAK8D,mBAAmBmC,KAAK,CAAE/F,QAAOG,QAASoG,KAG1C,KACLzG,KAAKuD,WAAWjD,IAAIJ,EAAOuG,GAC3BzG,KAAK8D,mBAAqB9D,KAAK8D,mBAAmB4C,OAC/CC,GAAMA,EAAEzG,QAAUA,GAASyG,EAAEtG,UAAYoG,GAGhD,CAMA,OAAAxE,GACE,IAAIjC,KAAK6D,aAAT,CAEA7D,KAAK6D,cAAe,EACpB7D,KAAK4D,UAAW,EAGhB,IAAA,MAAW1D,MAAEA,EAAAG,QAAOA,KAAaL,KAAK8D,mBACpC9D,KAAKuD,WAAWjD,IAAIJ,EAAOG,GAE7BL,KAAK8D,mBAAqB,GAGtB9D,KAAKuD,qBAAqB5C,GAC3BX,KAAKuD,UAAmCtB,UAE3CjC,KAAKuD,UAAY,KAGbvD,KAAKiB,sBACPG,OAAOc,oBAAoB,UAAWlC,KAAKiB,qBAC3CjB,KAAKiB,oBAAsB,KApBN,CAsBzB,CAMQ,WAAAmF,CAAYQ,GAClB,IAAK5G,KAAK4D,WAAa5D,KAAKuD,UAC1B,MAAM,IAAIN,MAAM,2BAA2B2D,wDAE7C,GAAI5G,KAAK6D,aACP,MAAM,IAAIZ,MAAM,2BAA2B2D,sBAE/C,iBClWK,MACGrD,UAA8B,KAC9BC,OACAE,UAAoB,GACpBmD,UAAmB,EACnBC,WAAqB,EACrBlD,UAAoB,EACpBC,cAAwB,EACxB5C,oBAA0D,KAC1D6C,mBAA+E,GAEvF,WAAAhE,CAAY0D,EAA4B,IAItC,GAHAxD,KAAKwD,OAASA,EAGVA,EAAOO,UACT,IAAA,MAAW7D,KAAS8D,OAAOC,KAAKT,EAAOO,WACrChB,EAAkB7C,GAKlBsD,EAAOzD,OAETC,KAAKkE,YAAYV,GAGjBxD,KAAKmE,WAAWX,EAEpB,CAMQ,WAAAU,CAAYV,GAClB,IAAKA,EAAOzD,OACV,MAAM,IAAIkD,MAAM,sDAGlBjD,KAAKuD,UAAY,IAAI1D,EAAgB2D,EAAOzD,QAC5CC,KAAK0D,UAAYF,EAAOY,UAAY,GACpCpE,KAAK6G,SAAWrD,EAAOuD,UAAW,EAClC/G,KAAK8G,UAAYtD,EAAOwD,WAAY,EAEpChH,KAAKuE,qBAGLvE,KAAK4D,UAAW,EAChB5D,KAAKwD,OAAOgB,WACd,CAEQ,UAAAL,CAAWX,GACjB,MAAMxC,EAAewC,EAAOxC,cAAgB,IAG5CI,OAAOM,OAAOC,YAAY,CAAElB,KAAM,eAAiBO,GAGnDhB,KAAKiB,oBAAuBmB,IAC1B,GAAqB,MAAjBpB,GAAwBoB,EAAEC,SAAWrB,EAAc,OAEvD,MAAMsB,EAAMF,EAAE5B,KACd,GAAKD,EAAe+B,GAEpB,GAAiB,eAAbA,EAAI7B,KAAuB,CAC7B,MAAMgE,EAAYnC,EAAyBV,QAE3C,GAAsB,WAAlB6C,EAASC,KAEX,YADAC,QAAQC,MAAM,8CAA+CH,EAASC,MAIxE,YAAID,EAASsC,QAEX,YADApC,QAAQC,MAAM,iDAKhB5E,KAAKuD,UAAY,IAAI5C,EAAqBK,GAC1ChB,KAAK0D,UAAYe,EAASL,SAC1BpE,KAAK6G,SAAWpC,EAASsC,QACzB/G,KAAK8G,UAAYrC,EAASuC,WAAY,EAEtChH,KAAKuE,qBAELvE,KAAK4D,UAAW,EAChB5D,KAAKwD,OAAOgB,WACd,MAAA,GAAwB,iBAAblC,EAAI7B,KAAyB,CAClB6B,EAA2BV,QAGhCmD,QAIjB,GAGF3D,OAAOC,iBAAiB,UAAWrB,KAAKiB,oBAC1C,CAEQ,kBAAAsD,GACN,GAAKvE,KAAKuD,YAGVvD,KAAK2F,gBAAgBjD,EAAcC,YAAcnC,IAC/C,MAAM4E,EAAc5E,EAAKoF,QAAQR,aAAe5E,EAAK4E,iBACjC,IAAhBA,GACFpF,KAAKwD,OAAOqC,eAAeT,KAI/BpF,KAAK2F,gBAAgBjD,EAAcE,aAAepC,IAChD,MAAM4E,EAAc5E,EAAKoF,QAAQR,aAAe5E,EAAK4E,iBACjC,IAAhBA,GACFpF,KAAKwD,OAAOsC,gBAAgBV,KAK5BpF,KAAKwD,OAAOO,WACd,IAAA,MAAY7D,EAAOG,KAAY2D,OAAO+B,QAAQ/F,KAAKwD,OAAOO,WACnD1D,GAGLL,KAAK2F,gBAAgBzF,EAAOG,EAGlC,CAEQ,eAAAsF,CAAgBzF,EAAeG,GAChCL,KAAKuD,YACVvD,KAAKuD,UAAUnD,GAAGF,EAAOG,GACzBL,KAAK8D,mBAAmBmC,KAAK,CAAE/F,QAAOG,YACxC,CASA,WAAI0G,GACF,OAAO/G,KAAK6G,QACd,CAKA,YAAIG,GACF,OAAOhH,KAAK8G,SACd,CAKA,YAAI1C,GACF,OAAOpE,KAAK0D,SACd,CAKA,WAAIwC,GACF,OAAOlG,KAAK4D,QACd,CAkBA,IAAAqD,CAAK/G,EAAeM,GAClBR,KAAKoG,YAAY,QACjBrD,EAAkB7C,GAClBF,KAAKuD,UAAWtD,KAAKC,EAAOM,EAC9B,CAmBA,EAAAJ,CAAGF,EAAeG,GAQhB,OAPA0C,EAAkB7C,GAEdF,KAAKuD,YACPvD,KAAKuD,UAAUnD,GAAGF,EAAOG,GACzBL,KAAK8D,mBAAmBmC,KAAK,CAAE/F,QAAOG,aAGjC,KACLL,KAAKuD,WAAWjD,IAAIJ,EAAOG,GAC3BL,KAAK8D,mBAAqB9D,KAAK8D,mBAAmB4C,OAC/CC,GAAMA,EAAEzG,QAAUA,GAASyG,EAAEtG,UAAYA,GAGhD,CAMA,OAAA4B,GACE,IAAIjC,KAAK6D,aAAT,CAEA7D,KAAK6D,cAAe,EACpB7D,KAAK4D,UAAW,EAGhB,IAAA,MAAW1D,MAAEA,EAAAG,QAAOA,KAAaL,KAAK8D,mBACpC9D,KAAKuD,WAAWjD,IAAIJ,EAAOG,GAE7BL,KAAK8D,mBAAqB,GAGtB9D,KAAKuD,qBAAqB5C,GAC3BX,KAAKuD,UAAmCtB,UAE3CjC,KAAKuD,UAAY,KAGbvD,KAAKiB,sBACPG,OAAOc,oBAAoB,UAAWlC,KAAKiB,qBAC3CjB,KAAKiB,oBAAsB,KApBN,CAsBzB,CAMQ,WAAAmF,CAAYQ,GAClB,IAAK5G,KAAK4D,WAAa5D,KAAKuD,UAC1B,MAAM,IAAIN,MAAM,6BAA6B2D,0DAE/C,GAAI5G,KAAK6D,aACP,MAAM,IAAIZ,MAAM,6BAA6B2D,sBAEjD,mBCpWK,SAAuB1G,GAC5B,OAAOA,EAAMQ,WAAW,SAC1B,sBAdO,SAA2BR,GAChC,GAAIA,EAAMgH,SAAS,KACjB,MAAM,IAAIjE,MACR,uBAAuB/C,iGAI7B"}