@smoregg/sdk 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/cjs/config.cjs +13 -0
  2. package/dist/cjs/config.cjs.map +1 -0
  3. package/dist/cjs/controller.cjs +15 -0
  4. package/dist/cjs/controller.cjs.map +1 -1
  5. package/dist/cjs/events.cjs +3 -0
  6. package/dist/cjs/events.cjs.map +1 -1
  7. package/dist/cjs/index.cjs +2 -0
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/screen.cjs +15 -0
  10. package/dist/cjs/screen.cjs.map +1 -1
  11. package/dist/cjs/testing.cjs +32 -0
  12. package/dist/cjs/testing.cjs.map +1 -1
  13. package/dist/esm/config.js +10 -0
  14. package/dist/esm/config.js.map +1 -0
  15. package/dist/esm/controller.js +15 -0
  16. package/dist/esm/controller.js.map +1 -1
  17. package/dist/esm/events.js +3 -0
  18. package/dist/esm/events.js.map +1 -1
  19. package/dist/esm/index.js +1 -0
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/screen.js +15 -0
  22. package/dist/esm/screen.js.map +1 -1
  23. package/dist/esm/testing.js +32 -0
  24. package/dist/esm/testing.js.map +1 -1
  25. package/dist/types/config.d.ts +36 -0
  26. package/dist/types/config.d.ts.map +1 -0
  27. package/dist/types/controller.d.ts.map +1 -1
  28. package/dist/types/events.d.ts +2 -0
  29. package/dist/types/events.d.ts.map +1 -1
  30. package/dist/types/index.d.ts +2 -0
  31. package/dist/types/index.d.ts.map +1 -1
  32. package/dist/types/screen.d.ts.map +1 -1
  33. package/dist/types/testing.d.ts.map +1 -1
  34. package/dist/types/types.d.ts +75 -0
  35. package/dist/types/types.d.ts.map +1 -1
  36. package/dist/umd/smore-sdk.umd.js +72 -0
  37. package/dist/umd/smore-sdk.umd.js.map +1 -1
  38. package/dist/umd/smore-sdk.umd.min.js +1 -1
  39. package/dist/umd/smore-sdk.umd.min.js.map +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ let globalConfig = {};
4
+ function configure(config) {
5
+ globalConfig = { ...globalConfig, ...config };
6
+ }
7
+ function getGlobalConfig() {
8
+ return globalConfig;
9
+ }
10
+
11
+ exports.configure = configure;
12
+ exports.getGlobalConfig = getGlobalConfig;
13
+ //# sourceMappingURL=config.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.cjs","sources":["../../src/config.ts"],"sourcesContent":["/**\n * Global SDK configuration.\n * Settings here apply to both Screen and Controller unless overridden per-instance.\n */\n\nexport interface SmoreGlobalConfig {\n /**\n * Automatically signal ready after initialization.\n * When true (default), the SDK calls signalReady() 3 seconds after init.\n * Set to false if your game needs to load resources before signaling ready.\n * Applies to both Screen and Controller.\n * Can be overridden per-instance via ScreenConfig.autoReady / ControllerConfig.autoReady.\n * @default true\n */\n autoReady?: boolean;\n}\n\nlet globalConfig: SmoreGlobalConfig = {};\n\n/**\n * Set global SDK configuration.\n * Call this before createScreen() / createController().\n *\n * @example\n * ```ts\n * import { configure, createScreen, createController } from '@smoregg/sdk';\n *\n * // Set once, applies to both screen and controller\n * configure({ autoReady: false });\n *\n * const screen = await createScreen({ ... });\n * const controller = await createController({ ... });\n * ```\n */\nexport function configure(config: SmoreGlobalConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get current global config. Used internally by Screen and Controller.\n */\nexport function getGlobalConfig(): Readonly<SmoreGlobalConfig> {\n return globalConfig;\n}\n"],"names":[],"mappings":";;AAiBA,IAAI,eAAkC,EAAC;AAiBhC,SAAS,UAAU,MAAA,EAAiC;AACzD,EAAA,YAAA,GAAe,EAAE,GAAG,YAAA,EAAc,GAAG,MAAA,EAAO;AAC9C;AAKO,SAAS,eAAA,GAA+C;AAC7D,EAAA,OAAO,YAAA;AACT;;;;;"}
@@ -5,6 +5,7 @@ var protocol = require('./transport/protocol.cjs');
5
5
  var errors = require('./errors.cjs');
6
6
  var events = require('./events.cjs');
7
7
  var logger = require('./logger.cjs');
8
+ var config = require('./config.cjs');
8
9
 
9
10
  const DEFAULT_TIMEOUT = 1e4;
10
11
  class ControllerImpl {
@@ -151,6 +152,11 @@ class ControllerImpl {
151
152
  myIndex: this._myIndex
152
153
  });
153
154
  this.config.onReady?.();
155
+ const autoReady = this.config.autoReady ?? config.getGlobalConfig().autoReady ?? true;
156
+ if (autoReady) {
157
+ this.logger.lifecycle("Auto-signaling ready (autoReady enabled)");
158
+ this.signalReady();
159
+ }
154
160
  resolve();
155
161
  }
156
162
  handleUpdate(msg) {
@@ -280,6 +286,10 @@ class ControllerImpl {
280
286
  this.logger.warn(`Rate limited: ${event}`);
281
287
  this.config.onRateLimited?.(event);
282
288
  });
289
+ this.registerHandler(events.SMORE_EVENTS.ALL_READY, () => {
290
+ this.logger.lifecycle("All participants ready");
291
+ this.config.onAllReady?.();
292
+ });
283
293
  if (this.config.onHostDisconnect) {
284
294
  this.logger.warn("onHostDisconnect is reserved for future use and currently non-functional");
285
295
  }
@@ -341,6 +351,11 @@ class ControllerImpl {
341
351
  this.logSend(event, data);
342
352
  this.transport.emit(event, data);
343
353
  }
354
+ signalReady() {
355
+ this.ensureReady("signalReady");
356
+ this.logSend(events.SMORE_EVENTS.GAME_READY, {});
357
+ this.transport.emit(events.SMORE_EVENTS.GAME_READY, {});
358
+ }
344
359
  // ---------------------------------------------------------------------------
345
360
  // Event Subscription
346
361
  // ---------------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"controller.cjs","sources":["../../src/controller.ts"],"sourcesContent":["/**\n * createController - Factory function for creating a Controller instance.\n *\n * Returns a Promise that resolves when the controller is ready and initialized.\n * Uses PostMessageTransport for iframe communication with parent window.\n *\n * @example Promise-based (recommended)\n * ```ts\n * const controller = await createController<MyEvents>({\n * listeners: {\n * 'phase-update': (data) => setPhase(data.phase),\n * },\n * });\n *\n * console.log(`Ready! My index: ${controller.myIndex}`);\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const controller = createController<MyEvents>({\n * onReady: () => {\n * console.log('Ready!');\n * },\n * listeners: { ... },\n * });\n * // Use controller.instance for immediate access\n * ```\n */\n\nimport type {\n Controller,\n ControllerConfig,\n ControllerEventHandler,\n ControllerInfo,\n EventData,\n EventMap,\n EventNames,\n PlayerIndex,\n RoomCode,\n} from './types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport type { TransportEventHandler } from './transport/types';\nimport {\n isBridgeMessage,\n validateInitPayload,\n type BridgeInitMessage,\n type BridgeUpdateMessage,\n} from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName } from './events';\nimport { DebugLogger } from './logger';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// CONTROLLER IMPLEMENTATION\n// =============================================================================\n\nclass ControllerImpl<TEvents extends EventMap> implements Controller<TEvents> {\n private transport: PostMessageTransport | null = null;\n private config: ControllerConfig<TEvents>;\n private logger: DebugLogger;\n private _roomCode: RoomCode = '';\n private _myIndex: PlayerIndex = -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 private eventListeners = new Map<string, Set<ControllerEventHandler<unknown>>>();\n // Maps user-facing handler -> transport wrappedHandler for proper cleanup in on()/off()\n private handlerToTransport = new Map<Function, { event: string; transportHandler: TransportEventHandler }>();\n private _controllers: ControllerInfo[] = [];\n // Tracks event names registered via config.listeners so off(event) without handler won't remove them\n private _configListenerEvents = new Set<string>();\n\n constructor(config: ControllerConfig<TEvents> = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreController]');\n\n // Validate event names in listeners\n if (config.listeners) {\n for (const event of Object.keys(config.listeners)) {\n validateEventName(event);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Properties (readonly)\n // ---------------------------------------------------------------------------\n\n get myIndex(): PlayerIndex {\n return this._myIndex;\n }\n\n get roomCode(): RoomCode {\n return this._roomCode;\n }\n\n get isReady(): boolean {\n return this._isReady;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Read-only list of all known controllers (players) in the room.\n * Returns full ControllerInfo including playerIndex, nickname, connected status, and appearance.\n *\n * Returns a new shallow copy on every access. Cache the result if accessing\n * repeatedly in the same frame/tick.\n */\n get controllers(): readonly ControllerInfo[] {\n return [...this._controllers];\n }\n\n /**\n * Returns the number of currently connected players.\n */\n getControllerCount(): number {\n return this._controllers.filter(c => c.connected).length;\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const parentOrigin = this.config.parentOrigin ?? '*';\n const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;\n\n this.logger.lifecycle('Initializing controller...', { parentOrigin, timeout });\n\n return new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.cleanup();\n const error = new SmoreSDKError(\n 'TIMEOUT',\n `Controller initialization timed out after ${timeout}ms. ` +\n `Make sure the parent window sends _bridge:init message. ` +\n `Check that the iframe has correct sandbox attributes (allow-scripts required) and same-origin/cross-origin settings. ` +\n `Create a new Controller instance to retry (this instance has been cleaned up).`,\n { details: { timeout } },\n );\n this.handleError(error);\n reject(error);\n }, timeout);\n\n // Listen for init message from parent\n this.boundMessageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n\n const msg = e.data;\n if (!isBridgeMessage(msg)) return;\n\n if (msg.type === '_bridge:init') {\n clearTimeout(timeoutId);\n this.handleInit(msg as BridgeInitMessage, parentOrigin, resolve, reject);\n } else if (msg.type === '_bridge:update') {\n this.handleUpdate(msg as BridgeUpdateMessage);\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n\n // Signal ready to parent\n this.logger.lifecycle('Sending _bridge:ready to parent');\n window.parent.postMessage({ type: '_bridge:ready' }, parentOrigin);\n });\n }\n\n private handleInit(\n msg: BridgeInitMessage,\n parentOrigin: string,\n resolve: () => void,\n reject: (err: Error) => void,\n ): void {\n const initPayload = msg.payload;\n\n this.logger.debug('Received _bridge:init', initPayload);\n\n // MIN-A1-1: Runtime validation of _bridge:init payload structure\n try {\n validateInitPayload(initPayload);\n } catch (err) {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Invalid _bridge:init payload: ${err instanceof Error ? err.message : String(err)}`,\n { details: { payload: initPayload } }\n );\n this.logger.warn('_bridge:init validation failed', error);\n this.handleError(error);\n reject(error);\n return;\n }\n\n const initData = initPayload;\n\n if (initData.side !== 'player') {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Controller received init for wrong side: ${initData.side}`,\n { details: { side: initData.side } },\n );\n this.handleError(error);\n reject(error);\n return;\n }\n\n if (initData.myIndex === undefined) {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n 'Missing myIndex in init payload',\n { details: initData },\n );\n this.handleError(error);\n reject(error);\n return;\n }\n\n // Initialize transport\n this.transport = new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n this._myIndex = initData.myIndex;\n\n // Track known players for join/leave detection (full ControllerInfo)\n const initPlayers = initData.players as Record<string, unknown>[];\n this._controllers = initPlayers\n .filter(p => typeof p.playerIndex === 'number')\n .map((p) => ({\n playerIndex: (p.playerIndex as number),\n nickname: (p.nickname as string) || (p.name as string) || `Player ${(p.playerIndex as number) + 1}`,\n connected: p.connected !== false,\n appearance: (p.appearance ?? p.character) as ControllerInfo['appearance'],\n }));\n\n this.setupEventHandlers();\n\n this._isReady = true;\n this.logger.lifecycle('Controller ready', {\n roomCode: this._roomCode,\n myIndex: this._myIndex,\n });\n\n this.config.onReady?.();\n resolve();\n }\n\n private handleUpdate(msg: BridgeUpdateMessage): void {\n if (!this._isReady) {\n this.logger.debug('Ignoring _bridge:update before init completes');\n return;\n }\n const updateData = msg.payload;\n this.logger.debug('Received _bridge:update', updateData);\n\n if (updateData.players && Array.isArray(updateData.players)) {\n const players = updateData.players as Record<string, unknown>[];\n const newControllers: ControllerInfo[] = players\n .filter(p => typeof p.playerIndex === 'number')\n .map(p => ({\n playerIndex: p.playerIndex as number,\n nickname: (p.nickname as string) || (p.name as string) || `Player ${(p.playerIndex as number) + 1}`,\n connected: p.connected !== false,\n appearance: (p.appearance ?? p.character) as ControllerInfo['appearance'],\n }));\n\n const oldControllers = this._controllers;\n\n // Detect joins\n for (const nc of newControllers) {\n if (!oldControllers.some(oc => oc.playerIndex === nc.playerIndex)) {\n this.config.onControllerJoin?.(nc.playerIndex, nc);\n }\n }\n\n // Detect leaves\n for (const oc of oldControllers) {\n if (!newControllers.some(nc => nc.playerIndex === oc.playerIndex)) {\n this.config.onControllerLeave?.(oc.playerIndex);\n }\n }\n\n // Update connected state for existing players and fire disconnect/reconnect callbacks\n for (const nc of newControllers) {\n const oc = oldControllers.find(c => c.playerIndex === nc.playerIndex);\n if (oc) {\n // Detect disconnect (was connected, now not)\n if (oc.connected && !nc.connected) {\n this.logger.debug('Player disconnected (via update)', { playerIndex: nc.playerIndex });\n this.config.onControllerDisconnect?.(nc.playerIndex);\n }\n // Detect reconnect (was disconnected, now connected)\n if (!oc.connected && nc.connected) {\n this.logger.debug('Player reconnected (via update)', { playerIndex: nc.playerIndex });\n this.config.onControllerReconnect?.(nc.playerIndex, nc);\n }\n }\n }\n\n this._controllers = newControllers;\n }\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave\n // These smore:* events are forwarded by IframeGameBridge's GAME_FACING_EVENTS allowlist.\n // Each handler updates _controllers to stay consistent and prevent duplicate\n // callbacks if _bridge:update also fires with the same data.\n this.registerHandler(SMORE_EVENTS.PLAYER_JOINED, (raw: unknown) => {\n const data = raw as { player?: Record<string, unknown>; playerIndex?: number };\n const playerInfo = data.player as Record<string, unknown> | undefined;\n const playerIndex = playerInfo?.playerIndex as number | undefined ?? data.playerIndex;\n if (playerIndex !== undefined) {\n if (this._controllers.some(c => c.playerIndex === playerIndex)) return;\n const controllerInfo: ControllerInfo = playerInfo ? {\n playerIndex,\n nickname: (playerInfo.nickname as string) || (playerInfo.name as string) || `Player ${playerIndex + 1}`,\n connected: playerInfo.connected !== false,\n appearance: (playerInfo.appearance ?? playerInfo.character) as ControllerInfo['appearance'],\n } : {\n playerIndex,\n nickname: `Player ${playerIndex + 1}`,\n connected: true,\n };\n this._controllers = [...this._controllers, controllerInfo];\n this.logger.debug('Player joined', { playerIndex });\n this.config.onControllerJoin?.(playerIndex, controllerInfo);\n }\n });\n\n this.registerHandler(SMORE_EVENTS.PLAYER_LEFT, (raw: unknown) => {\n const data = raw as { player?: { playerIndex?: number }; playerIndex?: number };\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n if (!this._controllers.some(c => c.playerIndex === playerIndex)) return;\n this._controllers = this._controllers.filter(c => c.playerIndex !== playerIndex);\n this.logger.debug('Player left', { playerIndex });\n this.config.onControllerLeave?.(playerIndex);\n }\n });\n\n this.registerHandler(SMORE_EVENTS.PLAYER_DISCONNECTED, (raw: unknown) => {\n const data = raw as { player?: Record<string, unknown>; playerIndex?: number };\n const playerData = data.player;\n const playerIndex = (playerData?.playerIndex as number | undefined) ?? data.playerIndex;\n if (playerIndex !== undefined) {\n // Update connected state in _controllers\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n this.logger.debug('Player disconnected', { playerIndex });\n this.config.onControllerDisconnect?.(playerIndex);\n }\n });\n\n this.registerHandler(SMORE_EVENTS.PLAYER_RECONNECTED, (raw: unknown) => {\n const data = raw as { player?: Record<string, unknown>; playerIndex?: number };\n const playerData = data.player;\n const playerIndex = (playerData?.playerIndex as number | undefined) ?? data.playerIndex;\n if (playerIndex !== undefined) {\n // Build ControllerInfo to match Screen SDK behavior\n const controllerInfo: ControllerInfo = playerData ? {\n playerIndex,\n nickname: (playerData.nickname as string) || (playerData.name as string) || `Player ${playerIndex + 1}`,\n connected: true,\n appearance: (playerData.appearance ?? playerData.character) as ControllerInfo['appearance'],\n } : {\n playerIndex,\n nickname: `Player ${playerIndex + 1}`,\n connected: true,\n };\n // Update full ControllerInfo in _controllers (reconnect may carry updated data)\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerIndex ? controllerInfo : c\n );\n this.logger.debug('Player reconnected', { playerIndex });\n this.config.onControllerReconnect?.(playerIndex, controllerInfo);\n }\n });\n\n // Character updated: update appearance in _controllers\n // Server emits { player: player.toDTO(), room: room.toDTO() }\n this.registerHandler(SMORE_EVENTS.PLAYER_CHARACTER_UPDATED, (raw: unknown) => {\n const payload = raw as { player?: { playerIndex?: number; character?: Record<string, unknown> | null; name?: string; nickname?: string } };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerData.playerIndex ? { ...c, appearance } : c\n );\n this.logger.debug('Player character updated', { playerIndex: playerData.playerIndex });\n this.config.onCharacterUpdated?.(playerData.playerIndex, appearance ?? null);\n }\n });\n\n // Rate limited: notify when server rate-limits an event\n this.registerHandler(SMORE_EVENTS.RATE_LIMITED, (raw: unknown) => {\n const data = raw as { event?: string };\n const event = data?.event ?? 'unknown';\n this.logger.warn(`Rate limited: ${event}`);\n this.config.onRateLimited?.(event);\n });\n\n // Host disconnect/reconnect callbacks (@experimental)\n // The server emits 'smore:host-disconnected' and 'smore:host-reconnected' events,\n // but they are not in IframeGameBridge's GAME_FACING_EVENTS allowlist, so they\n // won't reach SDK games. Once GAME_FACING_EVENTS is updated to include these,\n // register handlers here:\n // this.registerHandler('smore:host-disconnected', () => this.config.onHostDisconnect?.());\n // this.registerHandler('smore:host-reconnected', () => this.config.onHostReconnect?.());\n if (this.config.onHostDisconnect) {\n this.logger.warn('onHostDisconnect is reserved for future use and currently non-functional');\n }\n if (this.config.onHostReconnect) {\n this.logger.warn('onHostReconnect is reserved for future use and currently non-functional');\n }\n\n // User event listeners from config\n // These are tracked in _configListenerEvents so off(event) without handler won't remove them.\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n this._configListenerEvents.add(event);\n\n this.registerHandler(event, (data: unknown) => {\n this.logReceive(event, data);\n try {\n (handler as ControllerEventHandler<unknown>)(data);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\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 // Communication Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Send an event to the Screen. Controller-to-Controller direct communication\n * is not supported; all messages must go through the Screen.\n *\n * Data is sent to the Screen only (not to other controllers). For Screen→Controller communication,\n * Screen uses broadcast() or sendToController().\n *\n * @note Fire-and-forget sends (no callback) will silently fail if rate-limited.\n * Use the onError callback or smore:rate-limited event to detect rate limiting.\n */\n send<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void {\n this.ensureReady('send');\n validateEventName(event);\n\n if (typeof data !== 'object' || data === null) {\n this.logger.warn(\n 'Event data should be an object. Primitive values will be wrapped as { data: value } by the relay server. ' +\n 'To avoid confusion, wrap explicitly: send(\"event\", { value: 42 }) instead of send(\"event\", 42).'\n );\n }\n\n this.logSend(event, data);\n this.transport!.emit(event, data);\n }\n\n sendRaw(event: string, data?: unknown): void {\n this.ensureReady('sendRaw');\n validateEventName(event);\n\n this.logSend(event, data);\n this.transport!.emit(event, data);\n }\n\n // ---------------------------------------------------------------------------\n // Event Subscription\n // ---------------------------------------------------------------------------\n\n /**\n * Register a handler for custom events.\n *\n * When receiving events from Screen's `broadcast()`:\n * handler receives `(data)` — no playerIndex included.\n *\n * When receiving events from Screen's `sendToController()`:\n * handler receives `(data)` — targeted to this specific controller.\n *\n * @note Unlike Screen's `on()` which receives `(playerIndex, data)`,\n * Controller's `on()` receives only `(data)` since there's only one player per controller.\n *\n * Controller's on() handler signature: (data) => void\n * Unlike Screen's (playerIndex, data) => void, Controller doesn't receive playerIndex\n * because Controller only receives events from Screen, not from other controllers.\n * The sender is always the Screen, so playerIndex is not applicable.\n *\n * **Important:** If called before the Controller is ready (i.e., before `await createController()`\n * resolves or before the `onReady` callback fires), the handler is stored locally but\n * will NOT receive events until the transport is initialized. Always call `on()` after\n * initialization completes, or use `config.listeners` for handlers needed from the start.\n */\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event);\n\n // Add to local listeners map\n let listeners = this.eventListeners.get(event);\n if (!listeners) {\n listeners = new Set();\n this.eventListeners.set(event, listeners);\n }\n listeners.add(handler as ControllerEventHandler<unknown>);\n\n // Register with transport if ready\n const transportHandler: TransportEventHandler = (data: unknown) => {\n this.logReceive(event, data);\n try {\n (handler as ControllerEventHandler<unknown>)(data);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\n }\n };\n\n if (this.transport) {\n this.transport.on(event, transportHandler);\n this.registeredHandlers.push({ event, handler: transportHandler });\n this.handlerToTransport.set(handler as Function, { event, transportHandler });\n }\n\n // Return unsubscribe function\n return () => {\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n this.transport?.off(event, transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== transportHandler,\n );\n this.handlerToTransport.delete(handler as Function);\n };\n }\n\n /**\n * Add a one-time listener that auto-removes after first call.\n *\n * @note The handler is internally wrapped, so it cannot be removed via\n * `off(event, originalHandler)`. Use the returned unsubscribe function instead.\n *\n * @example\n * ```ts\n * const unsubscribe = controller.once('game-start', (data) => {\n * console.log('Game started!', data);\n * });\n *\n * // To cancel before it fires:\n * unsubscribe();\n * ```\n */\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n const unsubscribe = this.on(event, ((data: EventData<TEvents, K>) => {\n unsubscribe();\n handler(data);\n }) as ControllerEventHandler<EventData<TEvents, K>>);\n return unsubscribe;\n }\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n if (!handler) {\n // Remove all dynamically-added listeners for this event, but preserve\n // config listeners (registered via ControllerConfig.listeners) which are\n // permanent for the lifetime of the Controller instance.\n if (this._configListenerEvents.has(event)) {\n // Only remove handlers that were added via on(), not config listeners.\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) {\n this.transport?.off(event, val.transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== val.transportHandler,\n );\n this.handlerToTransport.delete(key);\n }\n }\n } else {\n // No config listener for this event -- safe to remove everything\n this.eventListeners.delete(event);\n this.transport?.off(event);\n this.registeredHandlers = this.registeredHandlers.filter((h) => h.event !== event);\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) this.handlerToTransport.delete(key);\n }\n }\n } else {\n // Remove specific handler\n const listeners = this.eventListeners.get(event);\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n // Remove specific transport handler via handlerToTransport map\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== entry.transportHandler,\n );\n this.handlerToTransport.delete(handler as Function);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n destroy(): void {\n if (this._isDestroyed) return;\n\n this.logger.lifecycle('Destroying controller');\n this.cleanup();\n this._isDestroyed = true;\n }\n\n private cleanup(): void {\n this._isReady = false;\n\n // Remove all registered handlers\n for (const { event, handler } of this.registeredHandlers) {\n this.transport?.off(event, handler);\n }\n this.registeredHandlers = [];\n\n // Clear event listeners\n this.eventListeners.clear();\n this.handlerToTransport.clear();\n\n // Destroy transport\n if (this.transport) {\n this.transport.destroy();\n this.transport = null;\n }\n\n // Remove message listener\n if (this.boundMessageHandler) {\n window.removeEventListener('message', this.boundMessageHandler);\n this.boundMessageHandler = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private Helpers\n // ---------------------------------------------------------------------------\n\n private ensureReady(method: string): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError(\n 'DESTROYED',\n `Cannot call ${method}() after destroy()`,\n { details: { method } },\n );\n }\n if (!this._isReady || !this.transport) {\n throw new SmoreSDKError(\n 'NOT_READY',\n `Cannot call ${method}() before controller is ready. ` +\n `Use await createController() or wait for onReady callback.`,\n { details: { method, isReady: this._isReady } },\n );\n }\n }\n\n private handleError(error: SmoreSDKError): void {\n // Always log at warn level so errors are never completely silent\n this.logger.warn(`Error in handler: ${error.message}`);\n if (this.config.onError) {\n this.config.onError(error.toSmoreError());\n } else {\n this.logger.error(error.message, error.details);\n }\n }\n\n private logSend(event: string, data?: unknown): void {\n this.logger.send(event, data);\n }\n\n private logReceive(event: string, data?: unknown): void {\n this.logger.receive(event, data);\n }\n}\n\n// =============================================================================\n// FACTORY FUNCTION\n// =============================================================================\n\n/**\n * Create a Controller instance for the player/phone side of your game.\n *\n * Returns a Promise that resolves when the controller is ready.\n * The returned object also has an `instance` property for immediate access.\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Controller configuration\n * @returns Promise that resolves to the Controller instance when ready\n *\n * @example Promise-based (recommended)\n * ```ts\n * const controller = await createController<MyEvents>({\n * listeners: {\n * 'phase-update': (data) => setPhase(data.phase),\n * },\n * });\n *\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const result = createController<MyEvents>({\n * onReady: () => {\n * result.instance.send('ready', {});\n * },\n * listeners: { ... },\n * });\n * ```\n */\nexport function createController<TEvents extends EventMap = EventMap>(\n config?: ControllerConfig<TEvents>,\n): Promise<Controller<TEvents>> & { instance: Controller<TEvents> } {\n const controller = new ControllerImpl<TEvents>(config ?? {});\n\n const promise = controller.initialize().then(() => controller as Controller<TEvents>);\n\n // Attach instance property for immediate access\n (promise as Promise<Controller<TEvents>> & { instance: Controller<TEvents> }).instance =\n controller;\n\n return promise as Promise<Controller<TEvents>> & { instance: Controller<TEvents> };\n}\n"],"names":["DebugLogger","validateEventName","SmoreSDKError","isBridgeMessage","validateInitPayload","PostMessageTransport","SMORE_EVENTS"],"mappings":";;;;;;;;AAyDA,MAAM,eAAA,GAAkB,GAAA;AAMxB,MAAM,cAAA,CAAwE;AAAA,EACpE,SAAA,GAAyC,IAAA;AAAA,EACzC,MAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA,GAAsB,EAAA;AAAA,EACtB,QAAA,GAAwB,EAAA;AAAA,EACxB,QAAA,GAAoB,KAAA;AAAA,EACpB,YAAA,GAAwB,KAAA;AAAA,EACxB,mBAAA,GAA0D,IAAA;AAAA,EAC1D,qBAA+E,EAAC;AAAA,EAChF,cAAA,uBAAqB,GAAA,EAAkD;AAAA;AAAA,EAEvE,kBAAA,uBAAyB,GAAA,EAA0E;AAAA,EACnG,eAAiC,EAAC;AAAA;AAAA,EAElC,qBAAA,uBAA4B,GAAA,EAAY;AAAA,EAEhD,WAAA,CAAY,MAAA,GAAoC,EAAC,EAAG;AAClD,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,IAAIA,kBAAA,CAAY,MAAA,CAAO,OAAO,mBAAmB,CAAA;AAG/D,IAAA,IAAI,OAAO,SAAA,EAAW;AACpB,MAAA,KAAA,MAAW,KAAA,IAAS,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AACjD,QAAAC,wBAAA,CAAkB,KAAK,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,OAAA,GAAuB;AACzB,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA,EAEA,IAAI,QAAA,GAAqB;AACvB,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA,EAEA,IAAI,OAAA,GAAmB;AACrB,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA,EAEA,IAAI,WAAA,GAAuB;AACzB,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,IAAI,WAAA,GAAyC;AAC3C,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,YAAY,CAAA;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAA,GAA6B;AAC3B,IAAA,OAAO,KAAK,YAAA,CAAa,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,CAAA,CAAE,MAAA;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAA,GAA4B;AAChC,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,MAAA,CAAO,YAAA,IAAgB,GAAA;AACjD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,OAAA,IAAW,eAAA;AAEvC,IAAA,IAAA,CAAK,OAAO,SAAA,CAAU,4BAAA,EAA8B,EAAE,YAAA,EAAc,SAAS,CAAA;AAE7E,IAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC5C,MAAA,MAAM,SAAA,GAAY,WAAW,MAAM;AACjC,QAAA,IAAA,CAAK,OAAA,EAAQ;AACb,QAAA,MAAM,QAAQ,IAAIC,oBAAA;AAAA,UAChB,SAAA;AAAA,UACA,6CAA6C,OAAO,CAAA,+PAAA,CAAA;AAAA,UAIpD,EAAE,OAAA,EAAS,EAAE,OAAA,EAAQ;AAAE,SACzB;AACA,QAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,QAAA,MAAA,CAAO,KAAK,CAAA;AAAA,MACd,GAAG,OAAO,CAAA;AAGV,MAAA,IAAA,CAAK,mBAAA,GAAsB,CAAC,CAAA,KAAoB;AAC9C,QAAA,IAAI,YAAA,KAAiB,GAAA,IAAO,CAAA,CAAE,MAAA,KAAW,YAAA,EAAc;AAEvD,QAAA,MAAM,MAAM,CAAA,CAAE,IAAA;AACd,QAAA,IAAI,CAACC,wBAAA,CAAgB,GAAG,CAAA,EAAG;AAE3B,QAAA,IAAI,GAAA,CAAI,SAAS,cAAA,EAAgB;AAC/B,UAAA,YAAA,CAAa,SAAS,CAAA;AACtB,UAAA,IAAA,CAAK,UAAA,CAAW,GAAA,EAA0B,YAAA,EAAc,OAAA,EAAS,MAAM,CAAA;AAAA,QACzE,CAAA,MAAA,IAAW,GAAA,CAAI,IAAA,KAAS,gBAAA,EAAkB;AACxC,UAAA,IAAA,CAAK,aAAa,GAA0B,CAAA;AAAA,QAC9C;AAAA,MACF,CAAA;AAEA,MAAA,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,mBAAmB,CAAA;AAG3D,MAAA,IAAA,CAAK,MAAA,CAAO,UAAU,iCAAiC,CAAA;AACvD,MAAA,MAAA,CAAO,OAAO,WAAA,CAAY,EAAE,IAAA,EAAM,eAAA,IAAmB,YAAY,CAAA;AAAA,IACnE,CAAC,CAAA;AAAA,EACH;AAAA,EAEQ,UAAA,CACN,GAAA,EACA,YAAA,EACA,OAAA,EACA,MAAA,EACM;AACN,IAAA,MAAM,cAAc,GAAA,CAAI,OAAA;AAExB,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,uBAAA,EAAyB,WAAW,CAAA;AAGtD,IAAA,IAAI;AACF,MAAAC,4BAAA,CAAoB,WAAW,CAAA;AAAA,IACjC,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,QAAQ,IAAIF,oBAAA;AAAA,QAChB,aAAA;AAAA,QACA,iCAAiC,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA,CAAA;AAAA,QACjF,EAAE,OAAA,EAAS,EAAE,OAAA,EAAS,aAAY;AAAE,OACtC;AACA,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,gCAAA,EAAkC,KAAK,CAAA;AACxD,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,MAAA,CAAO,KAAK,CAAA;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,WAAA;AAEjB,IAAA,IAAI,QAAA,CAAS,SAAS,QAAA,EAAU;AAC9B,MAAA,MAAM,QAAQ,IAAIA,oBAAA;AAAA,QAChB,aAAA;AAAA,QACA,CAAA,yCAAA,EAA4C,SAAS,IAAI,CAAA,CAAA;AAAA,QACzD,EAAE,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,CAAS,MAAK;AAAE,OACrC;AACA,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,MAAA,CAAO,KAAK,CAAA;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,QAAA,CAAS,YAAY,MAAA,EAAW;AAClC,MAAA,MAAM,QAAQ,IAAIA,oBAAA;AAAA,QAChB,aAAA;AAAA,QACA,iCAAA;AAAA,QACA,EAAE,SAAS,QAAA;AAAS,OACtB;AACA,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,MAAA,MAAA,CAAO,KAAK,CAAA;AACZ,MAAA;AAAA,IACF;AAGA,IAAA,IAAA,CAAK,SAAA,GAAY,IAAIG,yCAAA,CAAqB,YAAY,CAAA;AACtD,IAAA,IAAA,CAAK,YAAY,QAAA,CAAS,QAAA;AAC1B,IAAA,IAAA,CAAK,WAAW,QAAA,CAAS,OAAA;AAGzB,IAAA,MAAM,cAAc,QAAA,CAAS,OAAA;AAC7B,IAAA,IAAA,CAAK,YAAA,GAAe,WAAA,CACjB,MAAA,CAAO,CAAA,CAAA,KAAK,OAAO,CAAA,CAAE,WAAA,KAAgB,QAAQ,CAAA,CAC7C,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MACX,aAAc,CAAA,CAAE,WAAA;AAAA,MAChB,QAAA,EAAW,EAAE,QAAA,IAAwB,CAAA,CAAE,QAAmB,CAAA,OAAA,EAAW,CAAA,CAAE,cAAyB,CAAC,CAAA,CAAA;AAAA,MACjG,SAAA,EAAW,EAAE,SAAA,KAAc,KAAA;AAAA,MAC3B,UAAA,EAAa,CAAA,CAAE,UAAA,IAAc,CAAA,CAAE;AAAA,KACjC,CAAE,CAAA;AAEJ,IAAA,IAAA,CAAK,kBAAA,EAAmB;AAExB,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,kBAAA,EAAoB;AAAA,MACxC,UAAU,IAAA,CAAK,SAAA;AAAA,MACf,SAAS,IAAA,CAAK;AAAA,KACf,CAAA;AAED,IAAA,IAAA,CAAK,OAAO,OAAA,IAAU;AACtB,IAAA,OAAA,EAAQ;AAAA,EACV;AAAA,EAEQ,aAAa,GAAA,EAAgC;AACnD,IAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AAClB,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,+CAA+C,CAAA;AACjE,MAAA;AAAA,IACF;AACA,IAAA,MAAM,aAAa,GAAA,CAAI,OAAA;AACvB,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,yBAAA,EAA2B,UAAU,CAAA;AAEvD,IAAA,IAAI,WAAW,OAAA,IAAW,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW,OAAO,CAAA,EAAG;AAC3D,MAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,MAAA,MAAM,cAAA,GAAmC,OAAA,CACtC,MAAA,CAAO,CAAA,CAAA,KAAK,OAAO,EAAE,WAAA,KAAgB,QAAQ,CAAA,CAC7C,GAAA,CAAI,CAAA,CAAA,MAAM;AAAA,QACT,aAAa,CAAA,CAAE,WAAA;AAAA,QACf,QAAA,EAAW,EAAE,QAAA,IAAwB,CAAA,CAAE,QAAmB,CAAA,OAAA,EAAW,CAAA,CAAE,cAAyB,CAAC,CAAA,CAAA;AAAA,QACjG,SAAA,EAAW,EAAE,SAAA,KAAc,KAAA;AAAA,QAC3B,UAAA,EAAa,CAAA,CAAE,UAAA,IAAc,CAAA,CAAE;AAAA,OACjC,CAAE,CAAA;AAEJ,MAAA,MAAM,iBAAiB,IAAA,CAAK,YAAA;AAG5B,MAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,QAAA,IAAI,CAAC,eAAe,IAAA,CAAK,CAAA,EAAA,KAAM,GAAG,WAAA,KAAgB,EAAA,CAAG,WAAW,CAAA,EAAG;AACjE,UAAA,IAAA,CAAK,MAAA,CAAO,gBAAA,GAAmB,EAAA,CAAG,WAAA,EAAa,EAAE,CAAA;AAAA,QACnD;AAAA,MACF;AAGA,MAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,QAAA,IAAI,CAAC,eAAe,IAAA,CAAK,CAAA,EAAA,KAAM,GAAG,WAAA,KAAgB,EAAA,CAAG,WAAW,CAAA,EAAG;AACjE,UAAA,IAAA,CAAK,MAAA,CAAO,iBAAA,GAAoB,EAAA,CAAG,WAAW,CAAA;AAAA,QAChD;AAAA,MACF;AAGA,MAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,QAAA,MAAM,KAAK,cAAA,CAAe,IAAA,CAAK,OAAK,CAAA,CAAE,WAAA,KAAgB,GAAG,WAAW,CAAA;AACpE,QAAA,IAAI,EAAA,EAAI;AAEN,UAAA,IAAI,EAAA,CAAG,SAAA,IAAa,CAAC,EAAA,CAAG,SAAA,EAAW;AACjC,YAAA,IAAA,CAAK,OAAO,KAAA,CAAM,kCAAA,EAAoC,EAAE,WAAA,EAAa,EAAA,CAAG,aAAa,CAAA;AACrF,YAAA,IAAA,CAAK,MAAA,CAAO,sBAAA,GAAyB,EAAA,CAAG,WAAW,CAAA;AAAA,UACrD;AAEA,UAAA,IAAI,CAAC,EAAA,CAAG,SAAA,IAAa,EAAA,CAAG,SAAA,EAAW;AACjC,YAAA,IAAA,CAAK,OAAO,KAAA,CAAM,iCAAA,EAAmC,EAAE,WAAA,EAAa,EAAA,CAAG,aAAa,CAAA;AACpF,YAAA,IAAA,CAAK,MAAA,CAAO,qBAAA,GAAwB,EAAA,CAAG,WAAA,EAAa,EAAE,CAAA;AAAA,UACxD;AAAA,QACF;AAAA,MACF;AAEA,MAAA,IAAA,CAAK,YAAA,GAAe,cAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEQ,kBAAA,GAA2B;AACjC,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AAMrB,IAAA,IAAA,CAAK,eAAA,CAAgBC,mBAAA,CAAa,aAAA,EAAe,CAAC,GAAA,KAAiB;AACjE,MAAA,MAAM,IAAA,GAAO,GAAA;AACb,MAAA,MAAM,aAAa,IAAA,CAAK,MAAA;AACxB,MAAA,MAAM,WAAA,GAAc,UAAA,EAAY,WAAA,IAAqC,IAAA,CAAK,WAAA;AAC1E,MAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,QAAA,IAAI,KAAK,YAAA,CAAa,IAAA,CAAK,OAAK,CAAA,CAAE,WAAA,KAAgB,WAAW,CAAA,EAAG;AAChE,QAAA,MAAM,iBAAiC,UAAA,GAAa;AAAA,UAClD,WAAA;AAAA,UACA,UAAW,UAAA,CAAW,QAAA,IAAwB,WAAW,IAAA,IAAmB,CAAA,OAAA,EAAU,cAAc,CAAC,CAAA,CAAA;AAAA,UACrG,SAAA,EAAW,WAAW,SAAA,KAAc,KAAA;AAAA,UACpC,UAAA,EAAa,UAAA,CAAW,UAAA,IAAc,UAAA,CAAW;AAAA,SACnD,GAAI;AAAA,UACF,WAAA;AAAA,UACA,QAAA,EAAU,CAAA,OAAA,EAAU,WAAA,GAAc,CAAC,CAAA,CAAA;AAAA,UACnC,SAAA,EAAW;AAAA,SACb;AACA,QAAA,IAAA,CAAK,YAAA,GAAe,CAAC,GAAG,IAAA,CAAK,cAAc,cAAc,CAAA;AACzD,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,eAAA,EAAiB,EAAE,aAAa,CAAA;AAClD,QAAA,IAAA,CAAK,MAAA,CAAO,gBAAA,GAAmB,WAAA,EAAa,cAAc,CAAA;AAAA,MAC5D;AAAA,IACF,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,eAAA,CAAgBA,mBAAA,CAAa,WAAA,EAAa,CAAC,GAAA,KAAiB;AAC/D,MAAA,MAAM,IAAA,GAAO,GAAA;AACb,MAAA,MAAM,WAAA,GAAc,IAAA,CAAK,MAAA,EAAQ,WAAA,IAAe,IAAA,CAAK,WAAA;AACrD,MAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,QAAA,IAAI,CAAC,KAAK,YAAA,CAAa,IAAA,CAAK,OAAK,CAAA,CAAE,WAAA,KAAgB,WAAW,CAAA,EAAG;AACjE,QAAA,IAAA,CAAK,eAAe,IAAA,CAAK,YAAA,CAAa,OAAO,CAAA,CAAA,KAAK,CAAA,CAAE,gBAAgB,WAAW,CAAA;AAC/E,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,aAAA,EAAe,EAAE,aAAa,CAAA;AAChD,QAAA,IAAA,CAAK,MAAA,CAAO,oBAAoB,WAAW,CAAA;AAAA,MAC7C;AAAA,IACF,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,eAAA,CAAgBA,mBAAA,CAAa,mBAAA,EAAqB,CAAC,GAAA,KAAiB;AACvE,MAAA,MAAM,IAAA,GAAO,GAAA;AACb,MAAA,MAAM,aAAa,IAAA,CAAK,MAAA;AACxB,MAAA,MAAM,WAAA,GAAe,UAAA,EAAY,WAAA,IAAsC,IAAA,CAAK,WAAA;AAC5E,MAAA,IAAI,gBAAgB,MAAA,EAAW;AAE7B,QAAA,IAAA,CAAK,YAAA,GAAe,KAAK,YAAA,CAAa,GAAA;AAAA,UAAI,CAAA,CAAA,KACxC,EAAE,WAAA,KAAgB,WAAA,GAAc,EAAE,GAAG,CAAA,EAAG,SAAA,EAAW,KAAA,EAAM,GAAI;AAAA,SAC/D;AACA,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,qBAAA,EAAuB,EAAE,aAAa,CAAA;AACxD,QAAA,IAAA,CAAK,MAAA,CAAO,yBAAyB,WAAW,CAAA;AAAA,MAClD;AAAA,IACF,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,eAAA,CAAgBA,mBAAA,CAAa,kBAAA,EAAoB,CAAC,GAAA,KAAiB;AACtE,MAAA,MAAM,IAAA,GAAO,GAAA;AACb,MAAA,MAAM,aAAa,IAAA,CAAK,MAAA;AACxB,MAAA,MAAM,WAAA,GAAe,UAAA,EAAY,WAAA,IAAsC,IAAA,CAAK,WAAA;AAC5E,MAAA,IAAI,gBAAgB,MAAA,EAAW;AAE7B,QAAA,MAAM,iBAAiC,UAAA,GAAa;AAAA,UAClD,WAAA;AAAA,UACA,UAAW,UAAA,CAAW,QAAA,IAAwB,WAAW,IAAA,IAAmB,CAAA,OAAA,EAAU,cAAc,CAAC,CAAA,CAAA;AAAA,UACrG,SAAA,EAAW,IAAA;AAAA,UACX,UAAA,EAAa,UAAA,CAAW,UAAA,IAAc,UAAA,CAAW;AAAA,SACnD,GAAI;AAAA,UACF,WAAA;AAAA,UACA,QAAA,EAAU,CAAA,OAAA,EAAU,WAAA,GAAc,CAAC,CAAA,CAAA;AAAA,UACnC,SAAA,EAAW;AAAA,SACb;AAEA,QAAA,IAAA,CAAK,YAAA,GAAe,KAAK,YAAA,CAAa,GAAA;AAAA,UAAI,CAAA,CAAA,KACxC,CAAA,CAAE,WAAA,KAAgB,WAAA,GAAc,cAAA,GAAiB;AAAA,SACnD;AACA,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,oBAAA,EAAsB,EAAE,aAAa,CAAA;AACvD,QAAA,IAAA,CAAK,MAAA,CAAO,qBAAA,GAAwB,WAAA,EAAa,cAAc,CAAA;AAAA,MACjE;AAAA,IACF,CAAC,CAAA;AAID,IAAA,IAAA,CAAK,eAAA,CAAgBA,mBAAA,CAAa,wBAAA,EAA0B,CAAC,GAAA,KAAiB;AAC5E,MAAA,MAAM,OAAA,GAAU,GAAA;AAChB,MAAA,MAAM,aAAa,OAAA,EAAS,MAAA;AAC5B,MAAA,IAAI,UAAA,IAAc,OAAO,UAAA,CAAW,WAAA,KAAgB,QAAA,EAAU;AAC5D,QAAA,MAAM,UAAA,GAAc,WAAW,SAAA,IAAa,IAAA;AAC5C,QAAA,IAAA,CAAK,YAAA,GAAe,KAAK,YAAA,CAAa,GAAA;AAAA,UAAI,CAAA,CAAA,KACxC,EAAE,WAAA,KAAgB,UAAA,CAAW,cAAc,EAAE,GAAG,CAAA,EAAG,UAAA,EAAW,GAAI;AAAA,SACpE;AACA,QAAA,IAAA,CAAK,OAAO,KAAA,CAAM,0BAAA,EAA4B,EAAE,WAAA,EAAa,UAAA,CAAW,aAAa,CAAA;AACrF,QAAA,IAAA,CAAK,MAAA,CAAO,kBAAA,GAAqB,UAAA,CAAW,WAAA,EAAa,cAAc,IAAI,CAAA;AAAA,MAC7E;AAAA,IACF,CAAC,CAAA;AAGD,IAAA,IAAA,CAAK,eAAA,CAAgBA,mBAAA,CAAa,YAAA,EAAc,CAAC,GAAA,KAAiB;AAChE,MAAA,MAAM,IAAA,GAAO,GAAA;AACb,MAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,IAAS,SAAA;AAC7B,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAE,CAAA;AACzC,MAAA,IAAA,CAAK,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAAA,IACnC,CAAC,CAAA;AASD,IAAA,IAAI,IAAA,CAAK,OAAO,gBAAA,EAAkB;AAChC,MAAA,IAAA,CAAK,MAAA,CAAO,KAAK,0EAA0E,CAAA;AAAA,IAC7F;AACA,IAAA,IAAI,IAAA,CAAK,OAAO,eAAA,EAAiB;AAC/B,MAAA,IAAA,CAAK,MAAA,CAAO,KAAK,yEAAyE,CAAA;AAAA,IAC5F;AAIA,IAAA,IAAI,IAAA,CAAK,OAAO,SAAA,EAAW;AACzB,MAAA,KAAA,MAAW,CAAC,OAAO,OAAO,CAAA,IAAK,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AACpE,QAAA,IAAI,CAAC,OAAA,EAAS;AACd,QAAA,IAAA,CAAK,qBAAA,CAAsB,IAAI,KAAK,CAAA;AAEpC,QAAA,IAAA,CAAK,eAAA,CAAgB,KAAA,EAAO,CAAC,IAAA,KAAkB;AAC7C,UAAA,IAAA,CAAK,UAAA,CAAW,OAAO,IAAI,CAAA;AAC3B,UAAA,IAAI;AACF,YAAC,QAA4C,IAAI,CAAA;AAAA,UACnD,SAAS,GAAA,EAAK;AACZ,YAAA,IAAA,CAAK,WAAA;AAAA,cACH,IAAIJ,oBAAA,CAAc,SAAA,EAAW,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAA,CAAA,EAAK;AAAA,gBACpE,KAAA,EAAO,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,MAAA;AAAA,gBACpC,OAAA,EAAS,EAAE,KAAA;AAAM,eAClB;AAAA,aACH;AAAA,UACF;AAAA,QACF,CAAC,CAAA;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,eAAA,CAAgB,OAAe,OAAA,EAAsC;AAC3E,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AACrB,IAAA,IAAA,CAAK,SAAA,CAAU,EAAA,CAAG,KAAA,EAAO,OAAO,CAAA;AAChC,IAAA,IAAA,CAAK,kBAAA,CAAmB,IAAA,CAAK,EAAE,KAAA,EAAO,SAAS,CAAA;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,IAAA,CAAoC,OAAU,IAAA,EAAmC;AAC/E,IAAA,IAAA,CAAK,YAAY,MAAM,CAAA;AACvB,IAAAD,wBAAA,CAAkB,KAAK,CAAA;AAEvB,IAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,EAAM;AAC7C,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV;AAAA,OAEF;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,SAAA,CAAW,IAAA,CAAK,KAAA,EAAO,IAAI,CAAA;AAAA,EAClC;AAAA,EAEA,OAAA,CAAQ,OAAe,IAAA,EAAsB;AAC3C,IAAA,IAAA,CAAK,YAAY,SAAS,CAAA;AAC1B,IAAAA,wBAAA,CAAkB,KAAK,CAAA;AAEvB,IAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,SAAA,CAAW,IAAA,CAAK,KAAA,EAAO,IAAI,CAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,EAAA,CACE,OACA,OAAA,EACY;AACZ,IAAAA,wBAAA,CAAkB,KAAK,CAAA;AAGvB,IAAA,IAAI,SAAA,GAAY,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,KAAK,CAAA;AAC7C,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,SAAA,uBAAgB,GAAA,EAAI;AACpB,MAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,KAAA,EAAO,SAAS,CAAA;AAAA,IAC1C;AACA,IAAA,SAAA,CAAU,IAAI,OAA0C,CAAA;AAGxD,IAAA,MAAM,gBAAA,GAA0C,CAAC,IAAA,KAAkB;AACjE,MAAA,IAAA,CAAK,UAAA,CAAW,OAAO,IAAI,CAAA;AAC3B,MAAA,IAAI;AACF,QAAC,QAA4C,IAAI,CAAA;AAAA,MACnD,SAAS,GAAA,EAAK;AACZ,QAAA,IAAA,CAAK,WAAA;AAAA,UACH,IAAIC,oBAAA,CAAc,SAAA,EAAW,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAA,CAAA,EAAK;AAAA,YACpE,KAAA,EAAO,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,MAAA;AAAA,YACpC,OAAA,EAAS,EAAE,KAAA;AAAM,WAClB;AAAA,SACH;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,IAAA,CAAK,SAAA,CAAU,EAAA,CAAG,KAAA,EAAO,gBAAgB,CAAA;AACzC,MAAA,IAAA,CAAK,mBAAmB,IAAA,CAAK,EAAE,KAAA,EAAO,OAAA,EAAS,kBAAkB,CAAA;AACjE,MAAA,IAAA,CAAK,mBAAmB,GAAA,CAAI,OAAA,EAAqB,EAAE,KAAA,EAAO,kBAAkB,CAAA;AAAA,IAC9E;AAGA,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,EAAW,OAAO,OAA0C,CAAA;AAC5D,MAAA,IAAI,SAAA,EAAW,SAAS,CAAA,EAAG;AACzB,QAAA,IAAA,CAAK,cAAA,CAAe,OAAO,KAAK,CAAA;AAAA,MAClC;AACA,MAAA,IAAA,CAAK,SAAA,EAAW,GAAA,CAAI,KAAA,EAAO,gBAAgB,CAAA;AAC3C,MAAA,IAAA,CAAK,kBAAA,GAAqB,KAAK,kBAAA,CAAmB,MAAA;AAAA,QAChD,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY;AAAA,OACvB;AACA,MAAA,IAAA,CAAK,kBAAA,CAAmB,OAAO,OAAmB,CAAA;AAAA,IACpD,CAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,IAAA,CACE,OACA,OAAA,EACY;AACZ,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,EAAA,CAAG,KAAA,GAAQ,CAAC,IAAA,KAAgC;AACnE,MAAA,WAAA,EAAY;AACZ,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAA,EAAmD;AACnD,IAAA,OAAO,WAAA;AAAA,EACT;AAAA,EAEA,GAAA,CACE,OACA,OAAA,EACM;AACN,IAAA,IAAI,CAAC,OAAA,EAAS;AAIZ,MAAA,IAAI,IAAA,CAAK,qBAAA,CAAsB,GAAA,CAAI,KAAK,CAAA,EAAG;AAEzC,QAAA,KAAA,MAAW,CAAC,GAAA,EAAK,GAAG,CAAA,IAAK,KAAK,kBAAA,EAAoB;AAChD,UAAA,IAAI,GAAA,CAAI,UAAU,KAAA,EAAO;AACvB,YAAA,IAAA,CAAK,SAAA,EAAW,GAAA,CAAI,KAAA,EAAO,GAAA,CAAI,gBAAgB,CAAA;AAC/C,YAAA,IAAA,CAAK,kBAAA,GAAqB,KAAK,kBAAA,CAAmB,MAAA;AAAA,cAChD,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,GAAA,CAAI;AAAA,aAC3B;AACA,YAAA,IAAA,CAAK,kBAAA,CAAmB,OAAO,GAAG,CAAA;AAAA,UACpC;AAAA,QACF;AAAA,MACF,CAAA,MAAO;AAEL,QAAA,IAAA,CAAK,cAAA,CAAe,OAAO,KAAK,CAAA;AAChC,QAAA,IAAA,CAAK,SAAA,EAAW,IAAI,KAAK,CAAA;AACzB,QAAA,IAAA,CAAK,kBAAA,GAAqB,KAAK,kBAAA,CAAmB,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,KAAK,CAAA;AACjF,QAAA,KAAA,MAAW,CAAC,GAAA,EAAK,GAAG,CAAA,IAAK,KAAK,kBAAA,EAAoB;AAChD,UAAA,IAAI,IAAI,KAAA,KAAU,KAAA,EAAO,IAAA,CAAK,kBAAA,CAAmB,OAAO,GAAG,CAAA;AAAA,QAC7D;AAAA,MACF;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,KAAK,CAAA;AAC/C,MAAA,SAAA,EAAW,OAAO,OAA0C,CAAA;AAC5D,MAAA,IAAI,SAAA,EAAW,SAAS,CAAA,EAAG;AACzB,QAAA,IAAA,CAAK,cAAA,CAAe,OAAO,KAAK,CAAA;AAAA,MAClC;AAEA,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,kBAAA,CAAmB,GAAA,CAAI,OAAmB,CAAA;AAC7D,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,IAAA,CAAK,SAAA,EAAW,GAAA,CAAI,KAAA,EAAO,KAAA,CAAM,gBAAgB,CAAA;AACjD,QAAA,IAAA,CAAK,kBAAA,GAAqB,KAAK,kBAAA,CAAmB,MAAA;AAAA,UAChD,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,KAAA,CAAM;AAAA,SAC7B;AACA,QAAA,IAAA,CAAK,kBAAA,CAAmB,OAAO,OAAmB,CAAA;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,YAAA,EAAc;AAEvB,IAAA,IAAA,CAAK,MAAA,CAAO,UAAU,uBAAuB,CAAA;AAC7C,IAAA,IAAA,CAAK,OAAA,EAAQ;AACb,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAAA,EACtB;AAAA,EAEQ,OAAA,GAAgB;AACtB,IAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAGhB,IAAA,KAAA,MAAW,EAAE,KAAA,EAAO,OAAA,EAAQ,IAAK,KAAK,kBAAA,EAAoB;AACxD,MAAA,IAAA,CAAK,SAAA,EAAW,GAAA,CAAI,KAAA,EAAO,OAAO,CAAA;AAAA,IACpC;AACA,IAAA,IAAA,CAAK,qBAAqB,EAAC;AAG3B,IAAA,IAAA,CAAK,eAAe,KAAA,EAAM;AAC1B,IAAA,IAAA,CAAK,mBAAmB,KAAA,EAAM;AAG9B,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,IAAA,CAAK,UAAU,OAAA,EAAQ;AACvB,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,IACnB;AAGA,IAAA,IAAI,KAAK,mBAAA,EAAqB;AAC5B,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,IAAA,CAAK,mBAAmB,CAAA;AAC9D,MAAA,IAAA,CAAK,mBAAA,GAAsB,IAAA;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAY,MAAA,EAAsB;AACxC,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,MAAM,IAAIA,oBAAA;AAAA,QACR,WAAA;AAAA,QACA,eAAe,MAAM,CAAA,kBAAA,CAAA;AAAA,QACrB,EAAE,OAAA,EAAS,EAAE,MAAA,EAAO;AAAE,OACxB;AAAA,IACF;AACA,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,IAAY,CAAC,KAAK,SAAA,EAAW;AACrC,MAAA,MAAM,IAAIA,oBAAA;AAAA,QACR,WAAA;AAAA,QACA,eAAe,MAAM,CAAA,yFAAA,CAAA;AAAA,QAErB,EAAE,OAAA,EAAS,EAAE,QAAQ,OAAA,EAAS,IAAA,CAAK,UAAS;AAAE,OAChD;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,YAAY,KAAA,EAA4B;AAE9C,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,kBAAA,EAAqB,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AACrD,IAAA,IAAI,IAAA,CAAK,OAAO,OAAA,EAAS;AACvB,MAAA,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,YAAA,EAAc,CAAA;AAAA,IAC1C,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,OAAA,EAAS,MAAM,OAAO,CAAA;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,OAAA,CAAQ,OAAe,IAAA,EAAsB;AACnD,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,IAAI,CAAA;AAAA,EAC9B;AAAA,EAEQ,UAAA,CAAW,OAAe,IAAA,EAAsB;AACtD,IAAA,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,IAAI,CAAA;AAAA,EACjC;AACF;AAqCO,SAAS,iBACd,MAAA,EACkE;AAClE,EAAA,MAAM,UAAA,GAAa,IAAI,cAAA,CAAwB,MAAA,IAAU,EAAE,CAAA;AAE3D,EAAA,MAAM,UAAU,UAAA,CAAW,UAAA,EAAW,CAAE,IAAA,CAAK,MAAM,UAAiC,CAAA;AAGpF,EAAC,QAA6E,QAAA,GAC5E,UAAA;AAEF,EAAA,OAAO,OAAA;AACT;;;;"}
1
+ {"version":3,"file":"controller.cjs","sources":["../../src/controller.ts"],"sourcesContent":["/**\n * createController - Factory function for creating a Controller instance.\n *\n * Returns a Promise that resolves when the controller is ready and initialized.\n * Uses PostMessageTransport for iframe communication with parent window.\n *\n * @example Promise-based (recommended)\n * ```ts\n * const controller = await createController<MyEvents>({\n * listeners: {\n * 'phase-update': (data) => setPhase(data.phase),\n * },\n * });\n *\n * console.log(`Ready! My index: ${controller.myIndex}`);\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const controller = createController<MyEvents>({\n * onReady: () => {\n * console.log('Ready!');\n * },\n * listeners: { ... },\n * });\n * // Use controller.instance for immediate access\n * ```\n */\n\nimport type {\n Controller,\n ControllerConfig,\n ControllerEventHandler,\n ControllerInfo,\n EventData,\n EventMap,\n EventNames,\n PlayerIndex,\n RoomCode,\n} from './types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport type { TransportEventHandler } from './transport/types';\nimport {\n isBridgeMessage,\n validateInitPayload,\n type BridgeInitMessage,\n type BridgeUpdateMessage,\n} from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName } from './events';\nimport { DebugLogger } from './logger';\nimport { getGlobalConfig } from './config';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// CONTROLLER IMPLEMENTATION\n// =============================================================================\n\nclass ControllerImpl<TEvents extends EventMap> implements Controller<TEvents> {\n private transport: PostMessageTransport | null = null;\n private config: ControllerConfig<TEvents>;\n private logger: DebugLogger;\n private _roomCode: RoomCode = '';\n private _myIndex: PlayerIndex = -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 private eventListeners = new Map<string, Set<ControllerEventHandler<unknown>>>();\n // Maps user-facing handler -> transport wrappedHandler for proper cleanup in on()/off()\n private handlerToTransport = new Map<Function, { event: string; transportHandler: TransportEventHandler }>();\n private _controllers: ControllerInfo[] = [];\n // Tracks event names registered via config.listeners so off(event) without handler won't remove them\n private _configListenerEvents = new Set<string>();\n\n constructor(config: ControllerConfig<TEvents> = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreController]');\n\n // Validate event names in listeners\n if (config.listeners) {\n for (const event of Object.keys(config.listeners)) {\n validateEventName(event);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Properties (readonly)\n // ---------------------------------------------------------------------------\n\n get myIndex(): PlayerIndex {\n return this._myIndex;\n }\n\n get roomCode(): RoomCode {\n return this._roomCode;\n }\n\n get isReady(): boolean {\n return this._isReady;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Read-only list of all known controllers (players) in the room.\n * Returns full ControllerInfo including playerIndex, nickname, connected status, and appearance.\n *\n * Returns a new shallow copy on every access. Cache the result if accessing\n * repeatedly in the same frame/tick.\n */\n get controllers(): readonly ControllerInfo[] {\n return [...this._controllers];\n }\n\n /**\n * Returns the number of currently connected players.\n */\n getControllerCount(): number {\n return this._controllers.filter(c => c.connected).length;\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const parentOrigin = this.config.parentOrigin ?? '*';\n const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;\n\n this.logger.lifecycle('Initializing controller...', { parentOrigin, timeout });\n\n return new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.cleanup();\n const error = new SmoreSDKError(\n 'TIMEOUT',\n `Controller initialization timed out after ${timeout}ms. ` +\n `Make sure the parent window sends _bridge:init message. ` +\n `Check that the iframe has correct sandbox attributes (allow-scripts required) and same-origin/cross-origin settings. ` +\n `Create a new Controller instance to retry (this instance has been cleaned up).`,\n { details: { timeout } },\n );\n this.handleError(error);\n reject(error);\n }, timeout);\n\n // Listen for init message from parent\n this.boundMessageHandler = (e: MessageEvent) => {\n if (parentOrigin !== '*' && e.origin !== parentOrigin) return;\n\n const msg = e.data;\n if (!isBridgeMessage(msg)) return;\n\n if (msg.type === '_bridge:init') {\n clearTimeout(timeoutId);\n this.handleInit(msg as BridgeInitMessage, parentOrigin, resolve, reject);\n } else if (msg.type === '_bridge:update') {\n this.handleUpdate(msg as BridgeUpdateMessage);\n }\n };\n\n window.addEventListener('message', this.boundMessageHandler);\n\n // Signal ready to parent\n this.logger.lifecycle('Sending _bridge:ready to parent');\n window.parent.postMessage({ type: '_bridge:ready' }, parentOrigin);\n });\n }\n\n private handleInit(\n msg: BridgeInitMessage,\n parentOrigin: string,\n resolve: () => void,\n reject: (err: Error) => void,\n ): void {\n const initPayload = msg.payload;\n\n this.logger.debug('Received _bridge:init', initPayload);\n\n // MIN-A1-1: Runtime validation of _bridge:init payload structure\n try {\n validateInitPayload(initPayload);\n } catch (err) {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Invalid _bridge:init payload: ${err instanceof Error ? err.message : String(err)}`,\n { details: { payload: initPayload } }\n );\n this.logger.warn('_bridge:init validation failed', error);\n this.handleError(error);\n reject(error);\n return;\n }\n\n const initData = initPayload;\n\n if (initData.side !== 'player') {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n `Controller received init for wrong side: ${initData.side}`,\n { details: { side: initData.side } },\n );\n this.handleError(error);\n reject(error);\n return;\n }\n\n if (initData.myIndex === undefined) {\n const error = new SmoreSDKError(\n 'INIT_FAILED',\n 'Missing myIndex in init payload',\n { details: initData },\n );\n this.handleError(error);\n reject(error);\n return;\n }\n\n // Initialize transport\n this.transport = new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n this._myIndex = initData.myIndex;\n\n // Track known players for join/leave detection (full ControllerInfo)\n const initPlayers = initData.players as Record<string, unknown>[];\n this._controllers = initPlayers\n .filter(p => typeof p.playerIndex === 'number')\n .map((p) => ({\n playerIndex: (p.playerIndex as number),\n nickname: (p.nickname as string) || (p.name as string) || `Player ${(p.playerIndex as number) + 1}`,\n connected: p.connected !== false,\n appearance: (p.appearance ?? p.character) as ControllerInfo['appearance'],\n }));\n\n this.setupEventHandlers();\n\n this._isReady = true;\n this.logger.lifecycle('Controller ready', {\n roomCode: this._roomCode,\n myIndex: this._myIndex,\n });\n\n this.config.onReady?.();\n\n // Auto-signal ready unless explicitly disabled\n // Check: instance config > global config > default (true)\n const autoReady = this.config.autoReady ?? getGlobalConfig().autoReady ?? true;\n if (autoReady) {\n this.logger.lifecycle('Auto-signaling ready (autoReady enabled)');\n this.signalReady();\n }\n\n resolve();\n }\n\n private handleUpdate(msg: BridgeUpdateMessage): void {\n if (!this._isReady) {\n this.logger.debug('Ignoring _bridge:update before init completes');\n return;\n }\n const updateData = msg.payload;\n this.logger.debug('Received _bridge:update', updateData);\n\n if (updateData.players && Array.isArray(updateData.players)) {\n const players = updateData.players as Record<string, unknown>[];\n const newControllers: ControllerInfo[] = players\n .filter(p => typeof p.playerIndex === 'number')\n .map(p => ({\n playerIndex: p.playerIndex as number,\n nickname: (p.nickname as string) || (p.name as string) || `Player ${(p.playerIndex as number) + 1}`,\n connected: p.connected !== false,\n appearance: (p.appearance ?? p.character) as ControllerInfo['appearance'],\n }));\n\n const oldControllers = this._controllers;\n\n // Detect joins\n for (const nc of newControllers) {\n if (!oldControllers.some(oc => oc.playerIndex === nc.playerIndex)) {\n this.config.onControllerJoin?.(nc.playerIndex, nc);\n }\n }\n\n // Detect leaves\n for (const oc of oldControllers) {\n if (!newControllers.some(nc => nc.playerIndex === oc.playerIndex)) {\n this.config.onControllerLeave?.(oc.playerIndex);\n }\n }\n\n // Update connected state for existing players and fire disconnect/reconnect callbacks\n for (const nc of newControllers) {\n const oc = oldControllers.find(c => c.playerIndex === nc.playerIndex);\n if (oc) {\n // Detect disconnect (was connected, now not)\n if (oc.connected && !nc.connected) {\n this.logger.debug('Player disconnected (via update)', { playerIndex: nc.playerIndex });\n this.config.onControllerDisconnect?.(nc.playerIndex);\n }\n // Detect reconnect (was disconnected, now connected)\n if (!oc.connected && nc.connected) {\n this.logger.debug('Player reconnected (via update)', { playerIndex: nc.playerIndex });\n this.config.onControllerReconnect?.(nc.playerIndex, nc);\n }\n }\n }\n\n this._controllers = newControllers;\n }\n }\n\n private setupEventHandlers(): void {\n if (!this.transport) return;\n\n // System events: player join/leave\n // These smore:* events are forwarded by IframeGameBridge's GAME_FACING_EVENTS allowlist.\n // Each handler updates _controllers to stay consistent and prevent duplicate\n // callbacks if _bridge:update also fires with the same data.\n this.registerHandler(SMORE_EVENTS.PLAYER_JOINED, (raw: unknown) => {\n const data = raw as { player?: Record<string, unknown>; playerIndex?: number };\n const playerInfo = data.player as Record<string, unknown> | undefined;\n const playerIndex = playerInfo?.playerIndex as number | undefined ?? data.playerIndex;\n if (playerIndex !== undefined) {\n if (this._controllers.some(c => c.playerIndex === playerIndex)) return;\n const controllerInfo: ControllerInfo = playerInfo ? {\n playerIndex,\n nickname: (playerInfo.nickname as string) || (playerInfo.name as string) || `Player ${playerIndex + 1}`,\n connected: playerInfo.connected !== false,\n appearance: (playerInfo.appearance ?? playerInfo.character) as ControllerInfo['appearance'],\n } : {\n playerIndex,\n nickname: `Player ${playerIndex + 1}`,\n connected: true,\n };\n this._controllers = [...this._controllers, controllerInfo];\n this.logger.debug('Player joined', { playerIndex });\n this.config.onControllerJoin?.(playerIndex, controllerInfo);\n }\n });\n\n this.registerHandler(SMORE_EVENTS.PLAYER_LEFT, (raw: unknown) => {\n const data = raw as { player?: { playerIndex?: number }; playerIndex?: number };\n const playerIndex = data.player?.playerIndex ?? data.playerIndex;\n if (playerIndex !== undefined) {\n if (!this._controllers.some(c => c.playerIndex === playerIndex)) return;\n this._controllers = this._controllers.filter(c => c.playerIndex !== playerIndex);\n this.logger.debug('Player left', { playerIndex });\n this.config.onControllerLeave?.(playerIndex);\n }\n });\n\n this.registerHandler(SMORE_EVENTS.PLAYER_DISCONNECTED, (raw: unknown) => {\n const data = raw as { player?: Record<string, unknown>; playerIndex?: number };\n const playerData = data.player;\n const playerIndex = (playerData?.playerIndex as number | undefined) ?? data.playerIndex;\n if (playerIndex !== undefined) {\n // Update connected state in _controllers\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n this.logger.debug('Player disconnected', { playerIndex });\n this.config.onControllerDisconnect?.(playerIndex);\n }\n });\n\n this.registerHandler(SMORE_EVENTS.PLAYER_RECONNECTED, (raw: unknown) => {\n const data = raw as { player?: Record<string, unknown>; playerIndex?: number };\n const playerData = data.player;\n const playerIndex = (playerData?.playerIndex as number | undefined) ?? data.playerIndex;\n if (playerIndex !== undefined) {\n // Build ControllerInfo to match Screen SDK behavior\n const controllerInfo: ControllerInfo = playerData ? {\n playerIndex,\n nickname: (playerData.nickname as string) || (playerData.name as string) || `Player ${playerIndex + 1}`,\n connected: true,\n appearance: (playerData.appearance ?? playerData.character) as ControllerInfo['appearance'],\n } : {\n playerIndex,\n nickname: `Player ${playerIndex + 1}`,\n connected: true,\n };\n // Update full ControllerInfo in _controllers (reconnect may carry updated data)\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerIndex ? controllerInfo : c\n );\n this.logger.debug('Player reconnected', { playerIndex });\n this.config.onControllerReconnect?.(playerIndex, controllerInfo);\n }\n });\n\n // Character updated: update appearance in _controllers\n // Server emits { player: player.toDTO(), room: room.toDTO() }\n this.registerHandler(SMORE_EVENTS.PLAYER_CHARACTER_UPDATED, (raw: unknown) => {\n const payload = raw as { player?: { playerIndex?: number; character?: Record<string, unknown> | null; name?: string; nickname?: string } };\n const playerData = payload?.player;\n if (playerData && typeof playerData.playerIndex === 'number') {\n const appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === playerData.playerIndex ? { ...c, appearance } : c\n );\n this.logger.debug('Player character updated', { playerIndex: playerData.playerIndex });\n this.config.onCharacterUpdated?.(playerData.playerIndex, appearance ?? null);\n }\n });\n\n // Rate limited: notify when server rate-limits an event\n this.registerHandler(SMORE_EVENTS.RATE_LIMITED, (raw: unknown) => {\n const data = raw as { event?: string };\n const event = data?.event ?? 'unknown';\n this.logger.warn(`Rate limited: ${event}`);\n this.config.onRateLimited?.(event);\n });\n\n // All ready: all participants have signaled ready\n this.registerHandler(SMORE_EVENTS.ALL_READY, () => {\n this.logger.lifecycle('All participants ready');\n this.config.onAllReady?.();\n });\n\n // Host disconnect/reconnect callbacks (@experimental)\n // The server emits 'smore:host-disconnected' and 'smore:host-reconnected' events,\n // but they are not in IframeGameBridge's GAME_FACING_EVENTS allowlist, so they\n // won't reach SDK games. Once GAME_FACING_EVENTS is updated to include these,\n // register handlers here:\n // this.registerHandler('smore:host-disconnected', () => this.config.onHostDisconnect?.());\n // this.registerHandler('smore:host-reconnected', () => this.config.onHostReconnect?.());\n if (this.config.onHostDisconnect) {\n this.logger.warn('onHostDisconnect is reserved for future use and currently non-functional');\n }\n if (this.config.onHostReconnect) {\n this.logger.warn('onHostReconnect is reserved for future use and currently non-functional');\n }\n\n // User event listeners from config\n // These are tracked in _configListenerEvents so off(event) without handler won't remove them.\n if (this.config.listeners) {\n for (const [event, handler] of Object.entries(this.config.listeners)) {\n if (!handler) continue;\n this._configListenerEvents.add(event);\n\n this.registerHandler(event, (data: unknown) => {\n this.logReceive(event, data);\n try {\n (handler as ControllerEventHandler<unknown>)(data);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\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 // Communication Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Send an event to the Screen. Controller-to-Controller direct communication\n * is not supported; all messages must go through the Screen.\n *\n * Data is sent to the Screen only (not to other controllers). For Screen→Controller communication,\n * Screen uses broadcast() or sendToController().\n *\n * @note Fire-and-forget sends (no callback) will silently fail if rate-limited.\n * Use the onError callback or smore:rate-limited event to detect rate limiting.\n */\n send<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void {\n this.ensureReady('send');\n validateEventName(event);\n\n if (typeof data !== 'object' || data === null) {\n this.logger.warn(\n 'Event data should be an object. Primitive values will be wrapped as { data: value } by the relay server. ' +\n 'To avoid confusion, wrap explicitly: send(\"event\", { value: 42 }) instead of send(\"event\", 42).'\n );\n }\n\n this.logSend(event, data);\n this.transport!.emit(event, data);\n }\n\n sendRaw(event: string, data?: unknown): void {\n this.ensureReady('sendRaw');\n validateEventName(event);\n\n this.logSend(event, data);\n this.transport!.emit(event, data);\n }\n\n signalReady(): void {\n this.ensureReady('signalReady');\n this.logSend(SMORE_EVENTS.GAME_READY, {});\n this.transport!.emit(SMORE_EVENTS.GAME_READY, {});\n }\n\n // ---------------------------------------------------------------------------\n // Event Subscription\n // ---------------------------------------------------------------------------\n\n /**\n * Register a handler for custom events.\n *\n * When receiving events from Screen's `broadcast()`:\n * handler receives `(data)` — no playerIndex included.\n *\n * When receiving events from Screen's `sendToController()`:\n * handler receives `(data)` — targeted to this specific controller.\n *\n * @note Unlike Screen's `on()` which receives `(playerIndex, data)`,\n * Controller's `on()` receives only `(data)` since there's only one player per controller.\n *\n * Controller's on() handler signature: (data) => void\n * Unlike Screen's (playerIndex, data) => void, Controller doesn't receive playerIndex\n * because Controller only receives events from Screen, not from other controllers.\n * The sender is always the Screen, so playerIndex is not applicable.\n *\n * **Important:** If called before the Controller is ready (i.e., before `await createController()`\n * resolves or before the `onReady` callback fires), the handler is stored locally but\n * will NOT receive events until the transport is initialized. Always call `on()` after\n * initialization completes, or use `config.listeners` for handlers needed from the start.\n */\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event);\n\n // Add to local listeners map\n let listeners = this.eventListeners.get(event);\n if (!listeners) {\n listeners = new Set();\n this.eventListeners.set(event, listeners);\n }\n listeners.add(handler as ControllerEventHandler<unknown>);\n\n // Register with transport if ready\n const transportHandler: TransportEventHandler = (data: unknown) => {\n this.logReceive(event, data);\n try {\n (handler as ControllerEventHandler<unknown>)(data);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in handler for event \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\n }\n };\n\n if (this.transport) {\n this.transport.on(event, transportHandler);\n this.registeredHandlers.push({ event, handler: transportHandler });\n this.handlerToTransport.set(handler as Function, { event, transportHandler });\n }\n\n // Return unsubscribe function\n return () => {\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n this.transport?.off(event, transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== transportHandler,\n );\n this.handlerToTransport.delete(handler as Function);\n };\n }\n\n /**\n * Add a one-time listener that auto-removes after first call.\n *\n * @note The handler is internally wrapped, so it cannot be removed via\n * `off(event, originalHandler)`. Use the returned unsubscribe function instead.\n *\n * @example\n * ```ts\n * const unsubscribe = controller.once('game-start', (data) => {\n * console.log('Game started!', data);\n * });\n *\n * // To cancel before it fires:\n * unsubscribe();\n * ```\n */\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n const unsubscribe = this.on(event, ((data: EventData<TEvents, K>) => {\n unsubscribe();\n handler(data);\n }) as ControllerEventHandler<EventData<TEvents, K>>);\n return unsubscribe;\n }\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n if (!handler) {\n // Remove all dynamically-added listeners for this event, but preserve\n // config listeners (registered via ControllerConfig.listeners) which are\n // permanent for the lifetime of the Controller instance.\n if (this._configListenerEvents.has(event)) {\n // Only remove handlers that were added via on(), not config listeners.\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) {\n this.transport?.off(event, val.transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== val.transportHandler,\n );\n this.handlerToTransport.delete(key);\n }\n }\n } else {\n // No config listener for this event -- safe to remove everything\n this.eventListeners.delete(event);\n this.transport?.off(event);\n this.registeredHandlers = this.registeredHandlers.filter((h) => h.event !== event);\n for (const [key, val] of this.handlerToTransport) {\n if (val.event === event) this.handlerToTransport.delete(key);\n }\n }\n } else {\n // Remove specific handler\n const listeners = this.eventListeners.get(event);\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n // Remove specific transport handler via handlerToTransport map\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== entry.transportHandler,\n );\n this.handlerToTransport.delete(handler as Function);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n destroy(): void {\n if (this._isDestroyed) return;\n\n this.logger.lifecycle('Destroying controller');\n this.cleanup();\n this._isDestroyed = true;\n }\n\n private cleanup(): void {\n this._isReady = false;\n\n // Remove all registered handlers\n for (const { event, handler } of this.registeredHandlers) {\n this.transport?.off(event, handler);\n }\n this.registeredHandlers = [];\n\n // Clear event listeners\n this.eventListeners.clear();\n this.handlerToTransport.clear();\n\n // Destroy transport\n if (this.transport) {\n this.transport.destroy();\n this.transport = null;\n }\n\n // Remove message listener\n if (this.boundMessageHandler) {\n window.removeEventListener('message', this.boundMessageHandler);\n this.boundMessageHandler = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private Helpers\n // ---------------------------------------------------------------------------\n\n private ensureReady(method: string): void {\n if (this._isDestroyed) {\n throw new SmoreSDKError(\n 'DESTROYED',\n `Cannot call ${method}() after destroy()`,\n { details: { method } },\n );\n }\n if (!this._isReady || !this.transport) {\n throw new SmoreSDKError(\n 'NOT_READY',\n `Cannot call ${method}() before controller is ready. ` +\n `Use await createController() or wait for onReady callback.`,\n { details: { method, isReady: this._isReady } },\n );\n }\n }\n\n private handleError(error: SmoreSDKError): void {\n // Always log at warn level so errors are never completely silent\n this.logger.warn(`Error in handler: ${error.message}`);\n if (this.config.onError) {\n this.config.onError(error.toSmoreError());\n } else {\n this.logger.error(error.message, error.details);\n }\n }\n\n private logSend(event: string, data?: unknown): void {\n this.logger.send(event, data);\n }\n\n private logReceive(event: string, data?: unknown): void {\n this.logger.receive(event, data);\n }\n}\n\n// =============================================================================\n// FACTORY FUNCTION\n// =============================================================================\n\n/**\n * Create a Controller instance for the player/phone side of your game.\n *\n * Returns a Promise that resolves when the controller is ready.\n * The returned object also has an `instance` property for immediate access.\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Controller configuration\n * @returns Promise that resolves to the Controller instance when ready\n *\n * @example Promise-based (recommended)\n * ```ts\n * const controller = await createController<MyEvents>({\n * listeners: {\n * 'phase-update': (data) => setPhase(data.phase),\n * },\n * });\n *\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n *\n * @example Callback-based\n * ```ts\n * const result = createController<MyEvents>({\n * onReady: () => {\n * result.instance.send('ready', {});\n * },\n * listeners: { ... },\n * });\n * ```\n */\nexport function createController<TEvents extends EventMap = EventMap>(\n config?: ControllerConfig<TEvents>,\n): Promise<Controller<TEvents>> & { instance: Controller<TEvents> } {\n const controller = new ControllerImpl<TEvents>(config ?? {});\n\n const promise = controller.initialize().then(() => controller as Controller<TEvents>);\n\n // Attach instance property for immediate access\n (promise as Promise<Controller<TEvents>> & { instance: Controller<TEvents> }).instance =\n controller;\n\n return promise as Promise<Controller<TEvents>> & { instance: Controller<TEvents> };\n}\n"],"names":["DebugLogger","validateEventName","SmoreSDKError","isBridgeMessage","validateInitPayload","PostMessageTransport","getGlobalConfig","SMORE_EVENTS"],"mappings":"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"}
@@ -16,6 +16,9 @@ const SMORE_EVENTS = {
16
16
  PLAYER_CHARACTER_UPDATED: "smore:player-character-updated",
17
17
  // Rate limiting
18
18
  RATE_LIMITED: "smore:rate-limited",
19
+ // Game ready sync
20
+ GAME_READY: "smore:game-ready",
21
+ ALL_READY: "smore:all-ready",
19
22
  // Send to specific player (internal use)
20
23
  SEND_TO_PLAYER: "smore:send-to-player"
21
24
  // Used internally by platform, not handled by SDK
@@ -1 +1 @@
1
- {"version":3,"file":"events.cjs","sources":["../../src/events.ts"],"sourcesContent":["/**\n * SDK system event constants (socket level)\n *\n * smore:* prefix = platform service events\n * User events are validated to prevent ':' usage via validateEventName()\n *\n * Note: iframe ↔ parent internal communication uses _bridge:* prefix (transport/protocol.ts)\n */\n\nimport { SmoreSDKError } from './errors';\n\n/**\n * Platform system event names (internal use only).\n *\n * These events are reserved by the S'MORE platform and cannot be used by game code.\n * All platform events use the `smore:` prefix to avoid conflicts with user events.\n *\n * User-defined events are validated to prevent `:` usage via validateEventName().\n *\n * @internal Not part of the public SDK API. Do not import directly.\n */\nexport const SMORE_EVENTS = {\n // Game lifecycle\n GAME_OVER: 'smore:game-over',\n RETURN_TO_LOBBY: 'smore:return-to-lobby', // Used internally by platform, not handled by SDK\n\n // Player management\n PLAYER_JOINED: 'smore:player-joined',\n PLAYER_LEFT: 'smore:player-left',\n PLAYER_DISCONNECTED: 'smore:player-disconnected',\n PLAYER_RECONNECTED: 'smore:player-reconnected',\n\n // Character change\n PLAYER_CHARACTER_UPDATED: 'smore:player-character-updated',\n\n // Rate limiting\n RATE_LIMITED: 'smore:rate-limited',\n\n // Send to specific player (internal use)\n SEND_TO_PLAYER: 'smore:send-to-player', // Used internally by platform, not handled by SDK\n} as const;\n\nexport type SmoreEvent = typeof SMORE_EVENTS[keyof typeof SMORE_EVENTS];\n\nexport const SYSTEM_EVENTS: ReadonlySet<string> = new Set(\n Object.values(SMORE_EVENTS)\n);\n\n\nexport const EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;\n\nexport const EVENT_NAME_MAX_LENGTH = 128;\n\n/**\n * Validate a user-defined event name.\n *\n * Enforces naming rules to prevent conflicts with platform system events:\n * - Must start with a letter\n * - Can contain letters, numbers, hyphens, underscores\n * - Must end with a letter or number\n * - Cannot contain `:` (reserved for platform events like `smore:*`)\n * - Maximum length: 128 characters\n *\n * @throws {SmoreSDKError} INVALID_EVENT if validation fails\n *\n * @example\n * ```ts\n * validateEventName('player-ready'); // OK\n * validateEventName('score_update'); // OK\n * validateEventName('tap123'); // OK\n * validateEventName('smore:internal'); // Throws: colon not allowed\n * validateEventName('123start'); // Throws: must start with letter\n * ```\n */\nexport function validateEventName(event: string): void {\n if (!event || typeof event !== 'string') {\n throw new SmoreSDKError('INVALID_EVENT', 'Event name must be a non-empty string');\n }\n if (event.length > EVENT_NAME_MAX_LENGTH) {\n throw new SmoreSDKError(\n 'INVALID_EVENT',\n `Event name exceeds maximum length of ${EVENT_NAME_MAX_LENGTH} characters (got ${event.length}).`,\n { details: { event: event.slice(0, 50) + '...' } }\n );\n }\n if (!EVENT_NAME_REGEX.test(event)) {\n throw new SmoreSDKError(\n 'INVALID_EVENT',\n `Invalid event name \"${event}\". Event names must start with a letter, ` +\n `contain only letters, numbers, hyphens, or underscores, and end with a letter or number.`,\n { details: { event } }\n );\n }\n}\n\n/**\n * Check if an event name is a system event.\n *\n * System events use the `smore:` prefix and are reserved for platform use.\n * Prefix-based check is intentional for forward-compatibility with new system events.\n *\n * @param event - Event name to check\n * @returns true if the event is a system event\n */\nexport function isSystemEvent(event: string): boolean {\n return event.startsWith('smore:');\n}\n"],"names":["SmoreSDKError"],"mappings":";;;;AAqBO,MAAM,YAAA,GAAe;AAAA;AAAA,EAE1B,SAAA,EAAW,iBAAA;AAAA,EACX,eAAA,EAAiB,uBAAA;AAAA;AAAA;AAAA,EAGjB,aAAA,EAAe,qBAAA;AAAA,EACf,WAAA,EAAa,mBAAA;AAAA,EACb,mBAAA,EAAqB,2BAAA;AAAA,EACrB,kBAAA,EAAoB,0BAAA;AAAA;AAAA,EAGpB,wBAAA,EAA0B,gCAAA;AAAA;AAAA,EAG1B,YAAA,EAAc,oBAAA;AAAA;AAAA,EAGd,cAAA,EAAgB;AAAA;AAClB;AAIkD,IAAI,GAAA;AAAA,EACpD,MAAA,CAAO,OAAO,YAAY;AAC5B;AAGO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,qBAAA,GAAwB;AAuB9B,SAAS,kBAAkB,KAAA,EAAqB;AACrD,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,IAAA,MAAM,IAAIA,oBAAA,CAAc,eAAA,EAAiB,uCAAuC,CAAA;AAAA,EAClF;AACA,EAAA,IAAI,KAAA,CAAM,SAAS,qBAAA,EAAuB;AACxC,IAAA,MAAM,IAAIA,oBAAA;AAAA,MACR,eAAA;AAAA,MACA,CAAA,qCAAA,EAAwC,qBAAqB,CAAA,iBAAA,EAAoB,KAAA,CAAM,MAAM,CAAA,EAAA,CAAA;AAAA,MAC7F,EAAE,OAAA,EAAS,EAAE,KAAA,EAAO,KAAA,CAAM,MAAM,CAAA,EAAG,EAAE,CAAA,GAAI,KAAA,EAAM;AAAE,KACnD;AAAA,EACF;AACA,EAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,CAAK,KAAK,CAAA,EAAG;AACjC,IAAA,MAAM,IAAIA,oBAAA;AAAA,MACR,eAAA;AAAA,MACA,uBAAuB,KAAK,CAAA,iIAAA,CAAA;AAAA,MAE5B,EAAE,OAAA,EAAS,EAAE,KAAA,EAAM;AAAE,KACvB;AAAA,EACF;AACF;;;;;;;"}
1
+ {"version":3,"file":"events.cjs","sources":["../../src/events.ts"],"sourcesContent":["/**\n * SDK system event constants (socket level)\n *\n * smore:* prefix = platform service events\n * User events are validated to prevent ':' usage via validateEventName()\n *\n * Note: iframe ↔ parent internal communication uses _bridge:* prefix (transport/protocol.ts)\n */\n\nimport { SmoreSDKError } from './errors';\n\n/**\n * Platform system event names (internal use only).\n *\n * These events are reserved by the S'MORE platform and cannot be used by game code.\n * All platform events use the `smore:` prefix to avoid conflicts with user events.\n *\n * User-defined events are validated to prevent `:` usage via validateEventName().\n *\n * @internal Not part of the public SDK API. Do not import directly.\n */\nexport const SMORE_EVENTS = {\n // Game lifecycle\n GAME_OVER: 'smore:game-over',\n RETURN_TO_LOBBY: 'smore:return-to-lobby', // Used internally by platform, not handled by SDK\n\n // Player management\n PLAYER_JOINED: 'smore:player-joined',\n PLAYER_LEFT: 'smore:player-left',\n PLAYER_DISCONNECTED: 'smore:player-disconnected',\n PLAYER_RECONNECTED: 'smore:player-reconnected',\n\n // Character change\n PLAYER_CHARACTER_UPDATED: 'smore:player-character-updated',\n\n // Rate limiting\n RATE_LIMITED: 'smore:rate-limited',\n\n // Game ready sync\n GAME_READY: 'smore:game-ready',\n ALL_READY: 'smore:all-ready',\n\n // Send to specific player (internal use)\n SEND_TO_PLAYER: 'smore:send-to-player', // Used internally by platform, not handled by SDK\n} as const;\n\nexport type SmoreEvent = typeof SMORE_EVENTS[keyof typeof SMORE_EVENTS];\n\nexport const SYSTEM_EVENTS: ReadonlySet<string> = new Set(\n Object.values(SMORE_EVENTS)\n);\n\n\nexport const EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;\n\nexport const EVENT_NAME_MAX_LENGTH = 128;\n\n/**\n * Validate a user-defined event name.\n *\n * Enforces naming rules to prevent conflicts with platform system events:\n * - Must start with a letter\n * - Can contain letters, numbers, hyphens, underscores\n * - Must end with a letter or number\n * - Cannot contain `:` (reserved for platform events like `smore:*`)\n * - Maximum length: 128 characters\n *\n * @throws {SmoreSDKError} INVALID_EVENT if validation fails\n *\n * @example\n * ```ts\n * validateEventName('player-ready'); // OK\n * validateEventName('score_update'); // OK\n * validateEventName('tap123'); // OK\n * validateEventName('smore:internal'); // Throws: colon not allowed\n * validateEventName('123start'); // Throws: must start with letter\n * ```\n */\nexport function validateEventName(event: string): void {\n if (!event || typeof event !== 'string') {\n throw new SmoreSDKError('INVALID_EVENT', 'Event name must be a non-empty string');\n }\n if (event.length > EVENT_NAME_MAX_LENGTH) {\n throw new SmoreSDKError(\n 'INVALID_EVENT',\n `Event name exceeds maximum length of ${EVENT_NAME_MAX_LENGTH} characters (got ${event.length}).`,\n { details: { event: event.slice(0, 50) + '...' } }\n );\n }\n if (!EVENT_NAME_REGEX.test(event)) {\n throw new SmoreSDKError(\n 'INVALID_EVENT',\n `Invalid event name \"${event}\". Event names must start with a letter, ` +\n `contain only letters, numbers, hyphens, or underscores, and end with a letter or number.`,\n { details: { event } }\n );\n }\n}\n\n/**\n * Check if an event name is a system event.\n *\n * System events use the `smore:` prefix and are reserved for platform use.\n * Prefix-based check is intentional for forward-compatibility with new system events.\n *\n * @param event - Event name to check\n * @returns true if the event is a system event\n */\nexport function isSystemEvent(event: string): boolean {\n return event.startsWith('smore:');\n}\n"],"names":["SmoreSDKError"],"mappings":";;;;AAqBO,MAAM,YAAA,GAAe;AAAA;AAAA,EAE1B,SAAA,EAAW,iBAAA;AAAA,EACX,eAAA,EAAiB,uBAAA;AAAA;AAAA;AAAA,EAGjB,aAAA,EAAe,qBAAA;AAAA,EACf,WAAA,EAAa,mBAAA;AAAA,EACb,mBAAA,EAAqB,2BAAA;AAAA,EACrB,kBAAA,EAAoB,0BAAA;AAAA;AAAA,EAGpB,wBAAA,EAA0B,gCAAA;AAAA;AAAA,EAG1B,YAAA,EAAc,oBAAA;AAAA;AAAA,EAGd,UAAA,EAAY,kBAAA;AAAA,EACZ,SAAA,EAAW,iBAAA;AAAA;AAAA,EAGX,cAAA,EAAgB;AAAA;AAClB;AAIkD,IAAI,GAAA;AAAA,EACpD,MAAA,CAAO,OAAO,YAAY;AAC5B;AAGO,MAAM,gBAAA,GAAmB;AAEzB,MAAM,qBAAA,GAAwB;AAuB9B,SAAS,kBAAkB,KAAA,EAAqB;AACrD,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,IAAA,MAAM,IAAIA,oBAAA,CAAc,eAAA,EAAiB,uCAAuC,CAAA;AAAA,EAClF;AACA,EAAA,IAAI,KAAA,CAAM,SAAS,qBAAA,EAAuB;AACxC,IAAA,MAAM,IAAIA,oBAAA;AAAA,MACR,eAAA;AAAA,MACA,CAAA,qCAAA,EAAwC,qBAAqB,CAAA,iBAAA,EAAoB,KAAA,CAAM,MAAM,CAAA,EAAA,CAAA;AAAA,MAC7F,EAAE,OAAA,EAAS,EAAE,KAAA,EAAO,KAAA,CAAM,MAAM,CAAA,EAAG,EAAE,CAAA,GAAI,KAAA,EAAM;AAAE,KACnD;AAAA,EACF;AACA,EAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,CAAK,KAAK,CAAA,EAAG;AACjC,IAAA,MAAM,IAAIA,oBAAA;AAAA,MACR,eAAA;AAAA,MACA,uBAAuB,KAAK,CAAA,iIAAA,CAAA;AAAA,MAE5B,EAAE,OAAA,EAAS,EAAE,KAAA,EAAM;AAAE,KACvB;AAAA,EACF;AACF;;;;;;;"}
@@ -4,6 +4,7 @@ var screen = require('./screen.cjs');
4
4
  var controller = require('./controller.cjs');
5
5
  var errors = require('./errors.cjs');
6
6
  var events = require('./events.cjs');
7
+ var config = require('./config.cjs');
7
8
  var testing = require('./testing.cjs');
8
9
 
9
10
 
@@ -12,6 +13,7 @@ exports.createScreen = screen.createScreen;
12
13
  exports.createController = controller.createController;
13
14
  exports.SmoreSDKError = errors.SmoreSDKError;
14
15
  exports.validateEventName = events.validateEventName;
16
+ exports.configure = config.configure;
15
17
  exports.createMockController = testing.createMockController;
16
18
  exports.createMockScreen = testing.createMockScreen;
17
19
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
@@ -5,6 +5,7 @@ var protocol = require('./transport/protocol.cjs');
5
5
  var errors = require('./errors.cjs');
6
6
  var events = require('./events.cjs');
7
7
  var logger = require('./logger.cjs');
8
+ var config = require('./config.cjs');
8
9
 
9
10
  const DEFAULT_TIMEOUT = 1e4;
10
11
  function validatePlayerIndex(playerIndex, controllers) {
@@ -105,6 +106,11 @@ class ScreenImpl {
105
106
  controllers: this._controllers.length
106
107
  });
107
108
  this.config.onReady?.();
109
+ const autoReady = this.config.autoReady ?? config.getGlobalConfig().autoReady ?? true;
110
+ if (autoReady) {
111
+ this.logger.lifecycle("Auto-signaling ready (autoReady enabled)");
112
+ this.signalReady();
113
+ }
108
114
  resolve();
109
115
  } else if (msg.type === "_bridge:update") {
110
116
  if (!this._isReady) {
@@ -223,6 +229,10 @@ class ScreenImpl {
223
229
  this.logger.warn(`Rate limited: ${event}`);
224
230
  this.config.onRateLimited?.(event);
225
231
  });
232
+ this.registerTransportHandler(events.SMORE_EVENTS.ALL_READY, () => {
233
+ this.logger.lifecycle("All participants ready");
234
+ this.config.onAllReady?.();
235
+ });
226
236
  if (this.config.listeners) {
227
237
  for (const [event, handler] of Object.entries(this.config.listeners)) {
228
238
  if (!handler) continue;
@@ -392,6 +402,11 @@ class ScreenImpl {
392
402
  this.logger.lifecycle("Game over", results);
393
403
  this.transport.emit(events.SMORE_EVENTS.GAME_OVER, { results });
394
404
  }
405
+ signalReady() {
406
+ this.ensureReady("signalReady");
407
+ this.logger.lifecycle("Signaling ready");
408
+ this.transport.emit(events.SMORE_EVENTS.GAME_READY, {});
409
+ }
395
410
  // ---------------------------------------------------------------------------
396
411
  // Event Subscription
397
412
  // ---------------------------------------------------------------------------