@smoregg/sdk 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/controller.cjs +69 -0
- package/dist/cjs/controller.cjs.map +1 -1
- package/dist/cjs/events.cjs +8 -2
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/screen.cjs +61 -0
- package/dist/cjs/screen.cjs.map +1 -1
- package/dist/cjs/testing.cjs +57 -0
- package/dist/cjs/testing.cjs.map +1 -1
- package/dist/cjs/transport/protocol.cjs.map +1 -1
- package/dist/cjs/types.cjs.map +1 -1
- package/dist/esm/controller.js +69 -0
- package/dist/esm/controller.js.map +1 -1
- package/dist/esm/events.js +8 -2
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/screen.js +61 -0
- package/dist/esm/screen.js.map +1 -1
- package/dist/esm/testing.js +57 -0
- package/dist/esm/testing.js.map +1 -1
- package/dist/esm/transport/protocol.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/types/controller.d.ts.map +1 -1
- package/dist/types/events.d.ts +4 -0
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/screen.d.ts.map +1 -1
- package/dist/types/testing.d.ts +2 -0
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/transport/protocol.d.ts +1 -0
- package/dist/types/transport/protocol.d.ts.map +1 -1
- package/dist/types/types.d.ts +41 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +138 -2
- package/dist/umd/smore-sdk.umd.js.map +1 -1
- package/dist/umd/smore-sdk.umd.min.js +1 -1
- package/dist/umd/smore-sdk.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/cjs/controller.cjs
CHANGED
|
@@ -23,6 +23,8 @@ class ControllerImpl {
|
|
|
23
23
|
// Maps user-facing handler -> transport wrappedHandler for proper cleanup in on()/off()
|
|
24
24
|
handlerToTransport = /* @__PURE__ */ new Map();
|
|
25
25
|
_controllers = [];
|
|
26
|
+
_customStates = /* @__PURE__ */ new Map();
|
|
27
|
+
_stateChangeListeners = /* @__PURE__ */ new Set();
|
|
26
28
|
// Pending handlers registered via on() before transport is ready
|
|
27
29
|
_pendingHandlers = [];
|
|
28
30
|
// Unified lifecycle listener map (supports both onXxx() and on('$xxx') patterns)
|
|
@@ -79,6 +81,9 @@ class ControllerImpl {
|
|
|
79
81
|
get controllers() {
|
|
80
82
|
return [...this._controllers];
|
|
81
83
|
}
|
|
84
|
+
get me() {
|
|
85
|
+
return this._controllers.find((c) => c.playerIndex === this._myPlayerIndex);
|
|
86
|
+
}
|
|
82
87
|
/**
|
|
83
88
|
* Returns the number of currently connected players.
|
|
84
89
|
*/
|
|
@@ -178,6 +183,10 @@ class ControllerImpl {
|
|
|
178
183
|
this._pendingHandlers = [];
|
|
179
184
|
this._isConnected = true;
|
|
180
185
|
this._isReady = true;
|
|
186
|
+
if (initData.gameInProgress && this.transport) {
|
|
187
|
+
this.logger.lifecycle("Game in progress detected, requesting state recovery");
|
|
188
|
+
this.transport.emit(events.SMORE_EVENTS.STATE_GET_ALL, {});
|
|
189
|
+
}
|
|
181
190
|
for (const buffered of this._outboundBuffer) {
|
|
182
191
|
try {
|
|
183
192
|
switch (buffered.method) {
|
|
@@ -328,6 +337,44 @@ class ControllerImpl {
|
|
|
328
337
|
this.logger.lifecycle("Connection restored");
|
|
329
338
|
this._emitLifecycle("$connection-change", true);
|
|
330
339
|
});
|
|
340
|
+
this.registerHandler(events.SMORE_EVENTS.STATE_CHANGED, (raw) => {
|
|
341
|
+
const data = raw;
|
|
342
|
+
if (typeof data?.playerIndex === "number" && data.state) {
|
|
343
|
+
this._customStates.set(data.playerIndex, data.state);
|
|
344
|
+
this._stateChangeListeners.forEach((cb) => {
|
|
345
|
+
try {
|
|
346
|
+
cb(data.playerIndex, data.state);
|
|
347
|
+
} catch (err) {
|
|
348
|
+
this.handleError(
|
|
349
|
+
new errors.SmoreSDKError("UNKNOWN", "Error in custom state change listener", {
|
|
350
|
+
cause: err instanceof Error ? err : void 0
|
|
351
|
+
})
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
this.registerHandler(events.SMORE_EVENTS.STATE_ALL, (raw) => {
|
|
358
|
+
const data = raw;
|
|
359
|
+
if (data?.states) {
|
|
360
|
+
for (const [key, value] of Object.entries(data.states)) {
|
|
361
|
+
const pi = Number(key);
|
|
362
|
+
this._customStates.set(pi, value);
|
|
363
|
+
this._stateChangeListeners.forEach((cb) => {
|
|
364
|
+
try {
|
|
365
|
+
cb(pi, value);
|
|
366
|
+
} catch (err) {
|
|
367
|
+
this.handleError(
|
|
368
|
+
new errors.SmoreSDKError("UNKNOWN", "Error in custom state change listener", {
|
|
369
|
+
cause: err instanceof Error ? err : void 0
|
|
370
|
+
})
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
this._emitLifecycle("$state-recovery", data.states);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
331
378
|
}
|
|
332
379
|
/**
|
|
333
380
|
* Sets up a user event handler for controller events.
|
|
@@ -431,6 +478,26 @@ class ControllerImpl {
|
|
|
431
478
|
return this._addLifecycleListener("$game-over", callback);
|
|
432
479
|
}
|
|
433
480
|
// ---------------------------------------------------------------------------
|
|
481
|
+
// Custom State Methods
|
|
482
|
+
// ---------------------------------------------------------------------------
|
|
483
|
+
setState(state) {
|
|
484
|
+
const current = this._customStates.get(this._myPlayerIndex) ?? {};
|
|
485
|
+
const merged = { ...current, ...state };
|
|
486
|
+
this._customStates.set(this._myPlayerIndex, merged);
|
|
487
|
+
if (this.transport) {
|
|
488
|
+
this.transport.emit(events.SMORE_EVENTS.STATE_SET, { state });
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
getMyState() {
|
|
492
|
+
return this._customStates.get(this._myPlayerIndex);
|
|
493
|
+
}
|
|
494
|
+
onCustomStateChange(listener) {
|
|
495
|
+
this._stateChangeListeners.add(listener);
|
|
496
|
+
return () => {
|
|
497
|
+
this._stateChangeListeners.delete(listener);
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
// ---------------------------------------------------------------------------
|
|
434
501
|
// Communication Methods
|
|
435
502
|
// ---------------------------------------------------------------------------
|
|
436
503
|
/**
|
|
@@ -643,6 +710,8 @@ class ControllerImpl {
|
|
|
643
710
|
this.handlerToTransport.clear();
|
|
644
711
|
this._pendingHandlers = [];
|
|
645
712
|
this._lifecycleListeners.clear();
|
|
713
|
+
this._customStates.clear();
|
|
714
|
+
this._stateChangeListeners.clear();
|
|
646
715
|
this._isConnected = false;
|
|
647
716
|
this._outboundBuffer = [];
|
|
648
717
|
if (this.transport) {
|
|
@@ -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 Controller instance synchronously. The controller begins listening\n * for the bridge init message immediately. Use `.ready` to await full\n * initialization, and `.on()` / lifecycle methods to register handlers\n * (can be called before ready).\n *\n * @example\n * ```ts\n * const controller = createController<MyEvents>({ debug: true });\n *\n * controller.on('phase-update', (data) => setPhase(data.phase));\n * controller.onAllReady(() => console.log('Ready!'));\n *\n * await controller.ready;\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n */\n\nimport type {\n Controller,\n ControllerConfig,\n ControllerEventHandler,\n ControllerInfo,\n EventData,\n EventMap,\n EventNames,\n PlayerIndex,\n RoomCode,\n SmoreError,\n GameResults,\n CharacterAppearance,\n} from './types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport {\n isBridgeMessage,\n validateInitPayload,\n PROTOCOL_VERSION,\n type BridgeInitMessage,\n type BridgeUpdateMessage,\n} from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName, CONTROLLER_LIFECYCLE_EVENTS } from './events';\nimport { DebugLogger } from './logger';\nimport { mapPlayerDTO, validatePayloadSize } from './shared';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// CONTROLLER IMPLEMENTATION\n// =============================================================================\n\nclass ControllerImpl<TEvents extends EventMap> implements Controller<TEvents> {\n private transport: Transport | null = null;\n private config: ControllerConfig;\n private logger: DebugLogger;\n private _roomCode: RoomCode = '';\n private _myPlayerIndex: PlayerIndex = -1;\n private _isReady: boolean = false;\n private _isDestroyed: boolean = false;\n private _initTimeoutId: ReturnType<typeof setTimeout> | null = null;\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\n // Pending handlers registered via on() before transport is ready\n private _pendingHandlers: Array<{ event: string; handler: ControllerEventHandler<unknown> }> = [];\n\n // Unified lifecycle listener map (supports both onXxx() and on('$xxx') patterns)\n private _lifecycleListeners = new Map<string, Set<Function>>();\n\n // Outbound message buffer (messages sent before ready)\n private _outboundBuffer: Array<{ method: string; args: unknown[] }> = [];\n\n // Whether all-ready has fired\n private _allReadyFired = false;\n\n // Self-connection awareness\n private _isConnected = false;\n\n // Protocol versioning\n private _protocolVersion: number = PROTOCOL_VERSION;\n\n // Ready promise\n private _readyResolve!: () => void;\n private _readyReject!: (err: Error) => void;\n readonly ready: Promise<void>;\n\n constructor(config: ControllerConfig = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreController]');\n\n // Create ready promise\n this.ready = new Promise<void>((resolve, reject) => {\n this._readyResolve = resolve;\n this._readyReject = reject;\n });\n\n // Start initialization immediately\n this.startInitialization();\n }\n\n // ---------------------------------------------------------------------------\n // Properties (readonly)\n // ---------------------------------------------------------------------------\n\n get myPlayerIndex(): PlayerIndex {\n return this._myPlayerIndex;\n }\n\n get roomCode(): RoomCode {\n return this._roomCode;\n }\n\n get isReady(): boolean {\n return this._isReady;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n get protocolVersion(): number {\n return this._protocolVersion;\n }\n\n /**\n * 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 getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return this._controllers.find((c) => c.playerIndex === playerIndex);\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n private startInitialization(): 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 this._initTimeoutId = 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 this._readyReject(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(this._initTimeoutId!);\n this.handleInit(msg as BridgeInitMessage, parentOrigin);\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', protocolVersion: PROTOCOL_VERSION }, parentOrigin);\n }\n\n private handleInit(\n msg: BridgeInitMessage,\n parentOrigin: string,\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 this._readyReject(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 this._readyReject(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 this._readyReject(error);\n return;\n }\n\n // Initialize transport\n this.transport = this.config.transport ?? new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n\n // Protocol version negotiation\n const serverProtocolVersion = initData.protocolVersion;\n if (serverProtocolVersion !== undefined) {\n this._protocolVersion = serverProtocolVersion;\n if (serverProtocolVersion !== PROTOCOL_VERSION) {\n this.logger.warn(\n `Protocol version mismatch: SDK v${PROTOCOL_VERSION}, server v${serverProtocolVersion}. ` +\n `Some features may not work correctly.`\n );\n }\n }\n\n this._myPlayerIndex = 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, index) => mapPlayerDTO(p, index));\n\n this.setupEventHandlers();\n\n // Register all pending user event handlers\n for (const { event, handler } of this._pendingHandlers) {\n this.setupUserEventHandler(event, handler);\n }\n this._pendingHandlers = [];\n\n this._isConnected = true;\n this._isReady = true;\n\n // Flush buffered outbound messages\n for (const buffered of this._outboundBuffer) {\n try {\n switch (buffered.method) {\n case 'send':\n this.send(buffered.args[0] as any, buffered.args[1] as any);\n break;\n }\n } catch (err) {\n this.handleError(err instanceof SmoreSDKError ? err : new SmoreSDKError('UNKNOWN', 'Failed to flush buffered message'));\n }\n }\n this._outboundBuffer = [];\n\n this.logger.lifecycle('Controller ready', {\n roomCode: this._roomCode,\n myIndex: this._myPlayerIndex,\n });\n\n // Auto-signal ready unless explicitly disabled via config\n if (this.config.autoReady !== false) {\n this.logger.lifecycle('Auto-signaling ready (autoReady enabled)');\n this.signalReady();\n }\n\n this._readyResolve();\n }\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, index) => mapPlayerDTO(p, index));\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._emitLifecycle('$controller-join', nc.playerIndex, nc);\n }\n }\n\n // Detect leaves\n for (const oc of oldControllers) {\n if (!newControllers.some(nc => nc.playerIndex === oc.playerIndex)) {\n this._emitLifecycle('$controller-leave', 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._emitLifecycle('$controller-disconnect', 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._emitLifecycle('$controller-reconnect', 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 = playerInfo\n ? mapPlayerDTO(playerInfo, playerIndex)\n : mapPlayerDTO({ playerIndex, connected: true }, playerIndex);\n this._controllers = [...this._controllers, controllerInfo];\n this.logger.debug('Player joined', { playerIndex });\n this._emitLifecycle('$controller-join', 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._emitLifecycle('$controller-leave', 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._emitLifecycle('$controller-disconnect', 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 = playerData\n ? mapPlayerDTO(playerData, playerIndex)\n : mapPlayerDTO({ playerIndex, connected: true }, playerIndex);\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._emitLifecycle('$controller-reconnect', 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 pi = playerData.playerIndex;\n const appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === pi ? { ...c, appearance } : c\n );\n this.logger.debug('Player character updated', { playerIndex: pi });\n this._emitLifecycle('$character-updated', pi, appearance ?? null);\n }\n });\n\n // Rate limited: route through error handling\n this.registerHandler(SMORE_EVENTS.RATE_LIMITED, (raw: unknown) => {\n const data = raw as { event?: string };\n const eventName = data?.event ?? 'unknown';\n this.handleError(\n new SmoreSDKError('RATE_LIMITED', `Server rate-limited event: ${eventName}`, {\n details: { event: eventName },\n })\n );\n });\n\n // Game over: game has ended\n this.registerHandler(SMORE_EVENTS.GAME_OVER, (raw: unknown) => {\n const data = raw as { results?: GameResults };\n this.logger.lifecycle('Game over', data?.results);\n this._emitLifecycle('$game-over', data?.results);\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._allReadyFired = true;\n this._emitLifecycle('$all-ready');\n });\n\n // Self connection status\n this.registerHandler(SMORE_EVENTS.SELF_DISCONNECTED, () => {\n this._isConnected = false;\n this.logger.lifecycle('Connection lost');\n this._emitLifecycle('$connection-change', false);\n });\n\n this.registerHandler(SMORE_EVENTS.SELF_RECONNECTED, () => {\n this._isConnected = true;\n this.logger.lifecycle('Connection restored');\n this._emitLifecycle('$connection-change', true);\n });\n }\n\n /**\n * Sets up a user event handler for controller events.\n * Used for registering pending handlers after transport becomes available.\n */\n private setupUserEventHandler(event: string, handler: ControllerEventHandler<unknown>): void {\n const transportHandler: TransportEventHandler = (data: unknown) => {\n this.logReceive(event, data);\n try {\n handler(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 // Also store in eventListeners for on/off management\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);\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 // Lifecycle Listener Helpers\n // ---------------------------------------------------------------------------\n\n private _addLifecycleListener(event: string, listener: Function): () => void {\n let set = this._lifecycleListeners.get(event);\n if (!set) {\n set = new Set();\n this._lifecycleListeners.set(event, set);\n }\n set.add(listener);\n return () => {\n set!.delete(listener);\n if (set!.size === 0) this._lifecycleListeners.delete(event);\n };\n }\n\n private _emitLifecycle(event: string, ...args: unknown[]): void {\n this._lifecycleListeners.get(event)?.forEach(cb => {\n try {\n (cb as Function)(...args);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in lifecycle handler for \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\n }\n });\n }\n\n private _hasLifecycleListeners(event: string): boolean {\n const set = this._lifecycleListeners.get(event);\n return set !== undefined && set.size > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle Methods\n // ---------------------------------------------------------------------------\n\n onAllReady(callback: () => void): () => void {\n if (this._allReadyFired) {\n callback();\n }\n return this._addLifecycleListener('$all-ready', callback);\n }\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-join', callback);\n }\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-leave', callback);\n }\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-disconnect', callback);\n }\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-reconnect', callback);\n }\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n return this._addLifecycleListener('$character-updated', callback);\n }\n\n onError(callback: (error: SmoreError) => void): () => void {\n return this._addLifecycleListener('$error', callback);\n }\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n return this._addLifecycleListener('$connection-change', callback);\n }\n\n onGameOver(callback: (results?: GameResults) => void): () => void {\n return this._addLifecycleListener('$game-over', callback);\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 if (this._isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call send() after destroy()');\n }\n if (!this._isReady || !this.transport) {\n this._outboundBuffer.push({ method: 'send', args: [event, data] });\n this.logger.debug(`Buffered send \"${event}\" (controller not ready yet)`);\n return;\n }\n validateEventName(event);\n validatePayloadSize(data);\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 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 * Can be called before the Controller is ready. Handlers registered before ready\n * are queued and activated when the transport becomes available.\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 on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n // Route lifecycle events ($-prefixed)\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = CONTROLLER_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\". Valid lifecycle events: ${Array.from(validEvents).join(', ')}`);\n }\n // Special handling for $all-ready: fire immediately if already happened\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n }\n return this._addLifecycleListener(event as string, handler as Function);\n }\n\n validateEventName(event);\n\n // 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 if (this.transport) {\n // Register with transport immediately\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 this.transport.on(event, transportHandler);\n this.registeredHandlers.push({ event, handler: transportHandler });\n this.handlerToTransport.set(handler as Function, { event, transportHandler });\n } else {\n // Store for later registration when transport becomes available\n this._pendingHandlers.push({ event: event as string, handler: handler as ControllerEventHandler<unknown> });\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\n // Remove from pending if not yet registered\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n\n // Remove from transport if registered\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== entry.transportHandler,\n );\n this.handlerToTransport.delete(handler as Function);\n }\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 once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = CONTROLLER_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\"`);\n }\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n return () => {};\n }\n const wrapper = (...args: unknown[]) => {\n unsub();\n (handler as Function)(...args);\n };\n const unsub = this._addLifecycleListener(event as string, wrapper);\n return unsub;\n }\n\n const 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 (typeof event === 'string' && (event as string).startsWith('$')) {\n if (!handler) {\n this._lifecycleListeners.delete(event as string);\n } else {\n this._lifecycleListeners.get(event as string)?.delete(handler as Function);\n }\n return;\n }\n\n if (!handler) {\n // Remove all handlers for this event\n this.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 // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n // Remove specific handler\n const listeners = this.eventListeners.get(event);\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n // 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 // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n }\n }\n\n removeAllListeners(event?: string): void {\n if (event) {\n // Remove all handlers for specific event\n this.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 this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n // Remove all user event handlers\n for (const evt of [...this.eventListeners.keys()]) {\n this.removeAllListeners(evt);\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._isDestroyed = true;\n this._isReady = false;\n this.cleanup();\n }\n\n private cleanup(): void {\n // Clear init timeout if still pending\n if (this._initTimeoutId) {\n clearTimeout(this._initTimeoutId);\n this._initTimeoutId = null;\n }\n\n 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 this._pendingHandlers = [];\n\n // Clear lifecycle callbacks\n this._lifecycleListeners.clear();\n this._isConnected = false;\n this._outboundBuffer = [];\n\n // Destroy transport\n if (this.transport) {\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 controller.ready.`,\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 const smoreError = error.toSmoreError();\n if (this._hasLifecycleListeners('$error')) {\n this._emitLifecycle('$error', smoreError);\n } else {\n this.logger.error(error.message, error.details);\n }\n }\n\n private 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 Controller instance synchronously. The controller begins listening\n * for the bridge init message immediately. Register event handlers and\n * lifecycle callbacks on the instance, then await `.ready` if needed.\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Controller configuration (debug, parentOrigin, timeout)\n * @returns Controller instance\n *\n * @example\n * ```ts\n * const controller = createController<MyEvents>({ debug: true });\n *\n * controller.on('phase-update', (data) => setPhase(data.phase));\n * controller.onAllReady(() => console.log('Ready!'));\n *\n * await controller.ready;\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n */\nexport function createController<TEvents extends EventMap = EventMap>(\n config?: ControllerConfig,\n): Controller<TEvents> {\n return new ControllerImpl<TEvents>(config ?? {});\n}\n"],"names":["PROTOCOL_VERSION","DebugLogger","SmoreSDKError","isBridgeMessage","validateInitPayload","PostMessageTransport","mapPlayerDTO","SMORE_EVENTS","validateEventName","validatePayloadSize","CONTROLLER_LIFECYCLE_EVENTS"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"controller.cjs","sources":["../../src/controller.ts"],"sourcesContent":["/**\n * createController - Factory function for creating a Controller instance.\n *\n * Returns a Controller instance synchronously. The controller begins listening\n * for the bridge init message immediately. Use `.ready` to await full\n * initialization, and `.on()` / lifecycle methods to register handlers\n * (can be called before ready).\n *\n * @example\n * ```ts\n * const controller = createController<MyEvents>({ debug: true });\n *\n * controller.on('phase-update', (data) => setPhase(data.phase));\n * controller.onAllReady(() => console.log('Ready!'));\n *\n * await controller.ready;\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n */\n\nimport type {\n Controller,\n ControllerConfig,\n ControllerEventHandler,\n ControllerInfo,\n EventData,\n EventMap,\n EventNames,\n PlayerIndex,\n RoomCode,\n SmoreError,\n GameResults,\n CharacterAppearance,\n} from './types';\nimport { PostMessageTransport } from './transport/PostMessageTransport';\nimport type { Transport, TransportEventHandler } from './transport/types';\nimport {\n isBridgeMessage,\n validateInitPayload,\n PROTOCOL_VERSION,\n type BridgeInitMessage,\n type BridgeUpdateMessage,\n} from './transport/protocol';\nimport { SmoreSDKError } from './errors';\nimport { SMORE_EVENTS, validateEventName, CONTROLLER_LIFECYCLE_EVENTS } from './events';\nimport { DebugLogger } from './logger';\nimport { mapPlayerDTO, validatePayloadSize } from './shared';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_TIMEOUT = 10000;\n\n// =============================================================================\n// CONTROLLER IMPLEMENTATION\n// =============================================================================\n\nclass ControllerImpl<TEvents extends EventMap> implements Controller<TEvents> {\n private transport: Transport | null = null;\n private config: ControllerConfig;\n private logger: DebugLogger;\n private _roomCode: RoomCode = '';\n private _myPlayerIndex: PlayerIndex = -1;\n private _isReady: boolean = false;\n private _isDestroyed: boolean = false;\n private _initTimeoutId: ReturnType<typeof setTimeout> | null = null;\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 private _customStates = new Map<number, Record<string, any>>();\n private _stateChangeListeners = new Set<(playerIndex: number, state: Record<string, any>) => void>();\n\n // Pending handlers registered via on() before transport is ready\n private _pendingHandlers: Array<{ event: string; handler: ControllerEventHandler<unknown> }> = [];\n\n // Unified lifecycle listener map (supports both onXxx() and on('$xxx') patterns)\n private _lifecycleListeners = new Map<string, Set<Function>>();\n\n // Outbound message buffer (messages sent before ready)\n private _outboundBuffer: Array<{ method: string; args: unknown[] }> = [];\n\n // Whether all-ready has fired\n private _allReadyFired = false;\n\n // Self-connection awareness\n private _isConnected = false;\n\n // Protocol versioning\n private _protocolVersion: number = PROTOCOL_VERSION;\n\n // Ready promise\n private _readyResolve!: () => void;\n private _readyReject!: (err: Error) => void;\n readonly ready: Promise<void>;\n\n constructor(config: ControllerConfig = {}) {\n this.config = config;\n this.logger = new DebugLogger(config.debug, '[SmoreController]');\n\n // Create ready promise\n this.ready = new Promise<void>((resolve, reject) => {\n this._readyResolve = resolve;\n this._readyReject = reject;\n });\n\n // Start initialization immediately\n this.startInitialization();\n }\n\n // ---------------------------------------------------------------------------\n // Properties (readonly)\n // ---------------------------------------------------------------------------\n\n get myPlayerIndex(): PlayerIndex {\n return this._myPlayerIndex;\n }\n\n get roomCode(): RoomCode {\n return this._roomCode;\n }\n\n get isReady(): boolean {\n return this._isReady;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n get protocolVersion(): number {\n return this._protocolVersion;\n }\n\n /**\n * 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 get me(): ControllerInfo | undefined {\n return this._controllers.find(c => c.playerIndex === this._myPlayerIndex);\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 getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return this._controllers.find((c) => c.playerIndex === playerIndex);\n }\n\n // ---------------------------------------------------------------------------\n // Initialization\n // ---------------------------------------------------------------------------\n\n private startInitialization(): 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 this._initTimeoutId = 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 this._readyReject(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(this._initTimeoutId!);\n this.handleInit(msg as BridgeInitMessage, parentOrigin);\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', protocolVersion: PROTOCOL_VERSION }, parentOrigin);\n }\n\n private handleInit(\n msg: BridgeInitMessage,\n parentOrigin: string,\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 this._readyReject(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 this._readyReject(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 this._readyReject(error);\n return;\n }\n\n // Initialize transport\n this.transport = this.config.transport ?? new PostMessageTransport(parentOrigin);\n this._roomCode = initData.roomCode;\n\n // Protocol version negotiation\n const serverProtocolVersion = initData.protocolVersion;\n if (serverProtocolVersion !== undefined) {\n this._protocolVersion = serverProtocolVersion;\n if (serverProtocolVersion !== PROTOCOL_VERSION) {\n this.logger.warn(\n `Protocol version mismatch: SDK v${PROTOCOL_VERSION}, server v${serverProtocolVersion}. ` +\n `Some features may not work correctly.`\n );\n }\n }\n\n this._myPlayerIndex = 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, index) => mapPlayerDTO(p, index));\n\n this.setupEventHandlers();\n\n // Register all pending user event handlers\n for (const { event, handler } of this._pendingHandlers) {\n this.setupUserEventHandler(event, handler);\n }\n this._pendingHandlers = [];\n\n this._isConnected = true;\n this._isReady = true;\n\n // Request state recovery if reconnecting to a game in progress\n if (initData.gameInProgress && this.transport) {\n this.logger.lifecycle('Game in progress detected, requesting state recovery');\n this.transport.emit(SMORE_EVENTS.STATE_GET_ALL, {});\n }\n\n // Flush buffered outbound messages\n for (const buffered of this._outboundBuffer) {\n try {\n switch (buffered.method) {\n case 'send':\n this.send(buffered.args[0] as any, buffered.args[1] as any);\n break;\n }\n } catch (err) {\n this.handleError(err instanceof SmoreSDKError ? err : new SmoreSDKError('UNKNOWN', 'Failed to flush buffered message'));\n }\n }\n this._outboundBuffer = [];\n\n this.logger.lifecycle('Controller ready', {\n roomCode: this._roomCode,\n myIndex: this._myPlayerIndex,\n });\n\n // Auto-signal ready unless explicitly disabled via config\n if (this.config.autoReady !== false) {\n this.logger.lifecycle('Auto-signaling ready (autoReady enabled)');\n this.signalReady();\n }\n\n this._readyResolve();\n }\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, index) => mapPlayerDTO(p, index));\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._emitLifecycle('$controller-join', nc.playerIndex, nc);\n }\n }\n\n // Detect leaves\n for (const oc of oldControllers) {\n if (!newControllers.some(nc => nc.playerIndex === oc.playerIndex)) {\n this._emitLifecycle('$controller-leave', 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._emitLifecycle('$controller-disconnect', 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._emitLifecycle('$controller-reconnect', 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 = playerInfo\n ? mapPlayerDTO(playerInfo, playerIndex)\n : mapPlayerDTO({ playerIndex, connected: true }, playerIndex);\n this._controllers = [...this._controllers, controllerInfo];\n this.logger.debug('Player joined', { playerIndex });\n this._emitLifecycle('$controller-join', 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._emitLifecycle('$controller-leave', 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._emitLifecycle('$controller-disconnect', 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 = playerData\n ? mapPlayerDTO(playerData, playerIndex)\n : mapPlayerDTO({ playerIndex, connected: true }, playerIndex);\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._emitLifecycle('$controller-reconnect', 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 pi = playerData.playerIndex;\n const appearance = (playerData.character ?? null) as ControllerInfo['appearance'];\n this._controllers = this._controllers.map(c =>\n c.playerIndex === pi ? { ...c, appearance } : c\n );\n this.logger.debug('Player character updated', { playerIndex: pi });\n this._emitLifecycle('$character-updated', pi, appearance ?? null);\n }\n });\n\n // Rate limited: route through error handling\n this.registerHandler(SMORE_EVENTS.RATE_LIMITED, (raw: unknown) => {\n const data = raw as { event?: string };\n const eventName = data?.event ?? 'unknown';\n this.handleError(\n new SmoreSDKError('RATE_LIMITED', `Server rate-limited event: ${eventName}`, {\n details: { event: eventName },\n })\n );\n });\n\n // Game over: game has ended\n this.registerHandler(SMORE_EVENTS.GAME_OVER, (raw: unknown) => {\n const data = raw as { results?: GameResults };\n this.logger.lifecycle('Game over', data?.results);\n this._emitLifecycle('$game-over', data?.results);\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._allReadyFired = true;\n this._emitLifecycle('$all-ready');\n });\n\n // Self connection status\n this.registerHandler(SMORE_EVENTS.SELF_DISCONNECTED, () => {\n this._isConnected = false;\n this.logger.lifecycle('Connection lost');\n this._emitLifecycle('$connection-change', false);\n });\n\n this.registerHandler(SMORE_EVENTS.SELF_RECONNECTED, () => {\n this._isConnected = true;\n this.logger.lifecycle('Connection restored');\n this._emitLifecycle('$connection-change', true);\n });\n\n // Custom state changed: a controller's custom state was updated\n this.registerHandler(SMORE_EVENTS.STATE_CHANGED, (raw: unknown) => {\n const data = raw as { playerIndex?: number; state?: Record<string, any> };\n if (typeof data?.playerIndex === 'number' && data.state) {\n this._customStates.set(data.playerIndex, data.state);\n this._stateChangeListeners.forEach(cb => {\n try {\n cb(data.playerIndex!, data.state!);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', 'Error in custom state change listener', {\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n });\n }\n });\n\n // All custom states: bulk state update (used for reconnection recovery)\n this.registerHandler(SMORE_EVENTS.STATE_ALL, (raw: unknown) => {\n const data = raw as { states?: Record<number, Record<string, any>> };\n if (data?.states) {\n for (const [key, value] of Object.entries(data.states)) {\n const pi = Number(key);\n this._customStates.set(pi, value);\n this._stateChangeListeners.forEach(cb => {\n try {\n cb(pi, value);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', 'Error in custom state change listener', {\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n });\n }\n this._emitLifecycle('$state-recovery', data.states);\n }\n });\n }\n\n /**\n * Sets up a user event handler for controller events.\n * Used for registering pending handlers after transport becomes available.\n */\n private setupUserEventHandler(event: string, handler: ControllerEventHandler<unknown>): void {\n const transportHandler: TransportEventHandler = (data: unknown) => {\n this.logReceive(event, data);\n try {\n handler(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 // Also store in eventListeners for on/off management\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);\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 // Lifecycle Listener Helpers\n // ---------------------------------------------------------------------------\n\n private _addLifecycleListener(event: string, listener: Function): () => void {\n let set = this._lifecycleListeners.get(event);\n if (!set) {\n set = new Set();\n this._lifecycleListeners.set(event, set);\n }\n set.add(listener);\n return () => {\n set!.delete(listener);\n if (set!.size === 0) this._lifecycleListeners.delete(event);\n };\n }\n\n private _emitLifecycle(event: string, ...args: unknown[]): void {\n this._lifecycleListeners.get(event)?.forEach(cb => {\n try {\n (cb as Function)(...args);\n } catch (err) {\n this.handleError(\n new SmoreSDKError('UNKNOWN', `Error in lifecycle handler for \"${event}\"`, {\n cause: err instanceof Error ? err : undefined,\n details: { event },\n })\n );\n }\n });\n }\n\n private _hasLifecycleListeners(event: string): boolean {\n const set = this._lifecycleListeners.get(event);\n return set !== undefined && set.size > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle Methods\n // ---------------------------------------------------------------------------\n\n onAllReady(callback: () => void): () => void {\n if (this._allReadyFired) {\n callback();\n }\n return this._addLifecycleListener('$all-ready', callback);\n }\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-join', callback);\n }\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-leave', callback);\n }\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n return this._addLifecycleListener('$controller-disconnect', callback);\n }\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n return this._addLifecycleListener('$controller-reconnect', callback);\n }\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n return this._addLifecycleListener('$character-updated', callback);\n }\n\n onError(callback: (error: SmoreError) => void): () => void {\n return this._addLifecycleListener('$error', callback);\n }\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n return this._addLifecycleListener('$connection-change', callback);\n }\n\n onGameOver(callback: (results?: GameResults) => void): () => void {\n return this._addLifecycleListener('$game-over', callback);\n }\n\n // ---------------------------------------------------------------------------\n // Custom State Methods\n // ---------------------------------------------------------------------------\n\n setState(state: Record<string, any>): void {\n const current = this._customStates.get(this._myPlayerIndex) ?? {};\n const merged = { ...current, ...state };\n this._customStates.set(this._myPlayerIndex, merged);\n if (this.transport) {\n this.transport.emit(SMORE_EVENTS.STATE_SET, { state });\n }\n }\n\n getMyState(): Record<string, any> | undefined {\n return this._customStates.get(this._myPlayerIndex);\n }\n\n onCustomStateChange(listener: (playerIndex: number, state: Record<string, any>) => void): () => void {\n this._stateChangeListeners.add(listener);\n return () => {\n this._stateChangeListeners.delete(listener);\n };\n }\n\n // ---------------------------------------------------------------------------\n // Communication Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Send 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 if (this._isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call send() after destroy()');\n }\n if (!this._isReady || !this.transport) {\n this._outboundBuffer.push({ method: 'send', args: [event, data] });\n this.logger.debug(`Buffered send \"${event}\" (controller not ready yet)`);\n return;\n }\n validateEventName(event);\n validatePayloadSize(data);\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 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 * Can be called before the Controller is ready. Handlers registered before ready\n * are queued and activated when the transport becomes available.\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 on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n // Route lifecycle events ($-prefixed)\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = CONTROLLER_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\". Valid lifecycle events: ${Array.from(validEvents).join(', ')}`);\n }\n // Special handling for $all-ready: fire immediately if already happened\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n }\n return this._addLifecycleListener(event as string, handler as Function);\n }\n\n validateEventName(event);\n\n // 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 if (this.transport) {\n // Register with transport immediately\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 this.transport.on(event, transportHandler);\n this.registeredHandlers.push({ event, handler: transportHandler });\n this.handlerToTransport.set(handler as Function, { event, transportHandler });\n } else {\n // Store for later registration when transport becomes available\n this._pendingHandlers.push({ event: event as string, handler: handler as ControllerEventHandler<unknown> });\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\n // Remove from pending if not yet registered\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n\n // Remove from transport if registered\n const entry = this.handlerToTransport.get(handler as Function);\n if (entry) {\n this.transport?.off(event, entry.transportHandler);\n this.registeredHandlers = this.registeredHandlers.filter(\n (h) => h.handler !== entry.transportHandler,\n );\n this.handlerToTransport.delete(handler as Function);\n }\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 once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n if (typeof event === 'string' && (event as string).startsWith('$')) {\n const validEvents = CONTROLLER_LIFECYCLE_EVENTS;\n if (!validEvents.has(event as string)) {\n throw new SmoreSDKError('INVALID_EVENT', `Unknown lifecycle event: \"${event}\"`);\n }\n if (event === '$all-ready' && this._allReadyFired) {\n (handler as unknown as () => void)();\n return () => {};\n }\n const wrapper = (...args: unknown[]) => {\n unsub();\n (handler as Function)(...args);\n };\n const unsub = this._addLifecycleListener(event as string, wrapper);\n return unsub;\n }\n\n const 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 (typeof event === 'string' && (event as string).startsWith('$')) {\n if (!handler) {\n this._lifecycleListeners.delete(event as string);\n } else {\n this._lifecycleListeners.get(event as string)?.delete(handler as Function);\n }\n return;\n }\n\n if (!handler) {\n // Remove all handlers for this event\n this.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 // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n // Remove specific handler\n const listeners = this.eventListeners.get(event);\n listeners?.delete(handler as ControllerEventHandler<unknown>);\n if (listeners?.size === 0) {\n this.eventListeners.delete(event);\n }\n // 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 // Remove from pending\n this._pendingHandlers = this._pendingHandlers.filter(\n p => !(p.event === event && p.handler === handler)\n );\n }\n }\n\n removeAllListeners(event?: string): void {\n if (event) {\n // Remove all handlers for specific event\n this.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 this._pendingHandlers = this._pendingHandlers.filter(p => p.event !== event);\n } else {\n // Remove all user event handlers\n for (const evt of [...this.eventListeners.keys()]) {\n this.removeAllListeners(evt);\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._isDestroyed = true;\n this._isReady = false;\n this.cleanup();\n }\n\n private cleanup(): void {\n // Clear init timeout if still pending\n if (this._initTimeoutId) {\n clearTimeout(this._initTimeoutId);\n this._initTimeoutId = null;\n }\n\n 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 this._pendingHandlers = [];\n\n // Clear lifecycle callbacks\n this._lifecycleListeners.clear();\n this._customStates.clear();\n this._stateChangeListeners.clear();\n this._isConnected = false;\n this._outboundBuffer = [];\n\n // Destroy transport\n if (this.transport) {\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 controller.ready.`,\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 const smoreError = error.toSmoreError();\n if (this._hasLifecycleListeners('$error')) {\n this._emitLifecycle('$error', smoreError);\n } else {\n this.logger.error(error.message, error.details);\n }\n }\n\n private 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 Controller instance synchronously. The controller begins listening\n * for the bridge init message immediately. Register event handlers and\n * lifecycle callbacks on the instance, then await `.ready` if needed.\n *\n * @template TEvents - Event map type for type-safe events\n * @param config - Controller configuration (debug, parentOrigin, timeout)\n * @returns Controller instance\n *\n * @example\n * ```ts\n * const controller = createController<MyEvents>({ debug: true });\n *\n * controller.on('phase-update', (data) => setPhase(data.phase));\n * controller.onAllReady(() => console.log('Ready!'));\n *\n * await controller.ready;\n * controller.send('tap', { x: 100, y: 200 });\n * ```\n */\nexport function createController<TEvents extends EventMap = EventMap>(\n config?: ControllerConfig,\n): Controller<TEvents> {\n return new ControllerImpl<TEvents>(config ?? {});\n}\n"],"names":["PROTOCOL_VERSION","DebugLogger","SmoreSDKError","isBridgeMessage","validateInitPayload","PostMessageTransport","mapPlayerDTO","SMORE_EVENTS","validateEventName","validatePayloadSize","CONTROLLER_LIFECYCLE_EVENTS"],"mappings":"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"}
|
package/dist/cjs/events.cjs
CHANGED
|
@@ -21,8 +21,13 @@ const SMORE_EVENTS = {
|
|
|
21
21
|
SELF_DISCONNECTED: "smore:self-disconnected",
|
|
22
22
|
SELF_RECONNECTED: "smore:self-reconnected",
|
|
23
23
|
// Send to specific player (internal use)
|
|
24
|
-
SEND_TO_PLAYER: "smore:send-to-player"
|
|
24
|
+
SEND_TO_PLAYER: "smore:send-to-player",
|
|
25
25
|
// Used internally by platform, not handled by SDK
|
|
26
|
+
// Custom state management
|
|
27
|
+
STATE_SET: "smore:set-custom-state",
|
|
28
|
+
STATE_CHANGED: "smore:custom-state-changed",
|
|
29
|
+
STATE_GET_ALL: "smore:get-custom-states",
|
|
30
|
+
STATE_ALL: "smore:custom-states"
|
|
26
31
|
};
|
|
27
32
|
new Set(
|
|
28
33
|
Object.values(SMORE_EVENTS)
|
|
@@ -60,7 +65,8 @@ const SCREEN_LIFECYCLE_EVENTS = /* @__PURE__ */ new Set([
|
|
|
60
65
|
]);
|
|
61
66
|
const CONTROLLER_LIFECYCLE_EVENTS = /* @__PURE__ */ new Set([
|
|
62
67
|
...SCREEN_LIFECYCLE_EVENTS,
|
|
63
|
-
"$game-over"
|
|
68
|
+
"$game-over",
|
|
69
|
+
"$state-recovery"
|
|
64
70
|
]);
|
|
65
71
|
|
|
66
72
|
exports.CONTROLLER_LIFECYCLE_EVENTS = CONTROLLER_LIFECYCLE_EVENTS;
|
package/dist/cjs/events.cjs.map
CHANGED
|
@@ -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\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 // Connection status (self)\n SELF_DISCONNECTED: 'smore:self-disconnected',\n SELF_RECONNECTED: 'smore:self-reconnected',\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\n/**\n * Set of valid Screen lifecycle event names (used for $-prefix routing).\n */\nexport const SCREEN_LIFECYCLE_EVENTS: ReadonlySet<string> = new Set([\n '$all-ready',\n '$controller-join',\n '$controller-leave',\n '$controller-disconnect',\n '$controller-reconnect',\n '$character-updated',\n '$error',\n '$connection-change',\n]);\n\n/**\n * Set of valid Controller lifecycle event names (used for $-prefix routing).\n */\nexport const CONTROLLER_LIFECYCLE_EVENTS: ReadonlySet<string> = new Set([\n ...SCREEN_LIFECYCLE_EVENTS,\n '$game-over',\n]);\n"],"names":["SmoreSDKError"],"mappings":";;;;AAqBO,MAAM,YAAA,GAAe;AAAA;AAAA,EAE1B,SAAA,EAAW,iBAAA;AAAA;AAAA,EAGX,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,iBAAA,EAAmB,yBAAA;AAAA,EACnB,gBAAA,EAAkB,wBAAA;AAAA;AAAA,EAGlB,cAAA,EAAgB;AAAA;
|
|
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\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 // Connection status (self)\n SELF_DISCONNECTED: 'smore:self-disconnected',\n SELF_RECONNECTED: 'smore:self-reconnected',\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\n // Custom state management\n STATE_SET: 'smore:set-custom-state',\n STATE_CHANGED: 'smore:custom-state-changed',\n STATE_GET_ALL: 'smore:get-custom-states',\n STATE_ALL: 'smore:custom-states',\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\n/**\n * Set of valid Screen lifecycle event names (used for $-prefix routing).\n */\nexport const SCREEN_LIFECYCLE_EVENTS: ReadonlySet<string> = new Set([\n '$all-ready',\n '$controller-join',\n '$controller-leave',\n '$controller-disconnect',\n '$controller-reconnect',\n '$character-updated',\n '$error',\n '$connection-change',\n]);\n\n/**\n * Set of valid Controller lifecycle event names (used for $-prefix routing).\n */\nexport const CONTROLLER_LIFECYCLE_EVENTS: ReadonlySet<string> = new Set([\n ...SCREEN_LIFECYCLE_EVENTS,\n '$game-over',\n '$state-recovery',\n]);\n"],"names":["SmoreSDKError"],"mappings":";;;;AAqBO,MAAM,YAAA,GAAe;AAAA;AAAA,EAE1B,SAAA,EAAW,iBAAA;AAAA;AAAA,EAGX,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,iBAAA,EAAmB,yBAAA;AAAA,EACnB,gBAAA,EAAkB,wBAAA;AAAA;AAAA,EAGlB,cAAA,EAAgB,sBAAA;AAAA;AAAA;AAAA,EAGhB,SAAA,EAAW,wBAAA;AAAA,EACX,aAAA,EAAe,4BAAA;AAAA,EACf,aAAA,EAAe,yBAAA;AAAA,EACf,SAAA,EAAW;AACb;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;AAkBO,MAAM,uBAAA,uBAAmD,GAAA,CAAI;AAAA,EAClE,YAAA;AAAA,EACA,kBAAA;AAAA,EACA,mBAAA;AAAA,EACA,wBAAA;AAAA,EACA,uBAAA;AAAA,EACA,oBAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC;AAKM,MAAM,2BAAA,uBAAuD,GAAA,CAAI;AAAA,EACtE,GAAG,uBAAA;AAAA,EACH,YAAA;AAAA,EACA;AACF,CAAC;;;;;;;;;"}
|
package/dist/cjs/screen.cjs
CHANGED
|
@@ -44,6 +44,9 @@ class ScreenImpl {
|
|
|
44
44
|
_allReadyFired = false;
|
|
45
45
|
// Self-connection awareness
|
|
46
46
|
_isConnected = false;
|
|
47
|
+
// Custom state management
|
|
48
|
+
_customStates = /* @__PURE__ */ new Map();
|
|
49
|
+
_stateChangeListeners = /* @__PURE__ */ new Set();
|
|
47
50
|
// Protocol versioning
|
|
48
51
|
_protocolVersion = protocol.PROTOCOL_VERSION;
|
|
49
52
|
// Ready promise
|
|
@@ -271,6 +274,43 @@ class ScreenImpl {
|
|
|
271
274
|
this.logger.lifecycle("Connection restored");
|
|
272
275
|
this._emitLifecycle("$connection-change", true);
|
|
273
276
|
});
|
|
277
|
+
this.registerTransportHandler(events.SMORE_EVENTS.STATE_CHANGED, (raw) => {
|
|
278
|
+
const data = raw;
|
|
279
|
+
if (typeof data?.playerIndex === "number" && data.state) {
|
|
280
|
+
this._customStates.set(data.playerIndex, data.state);
|
|
281
|
+
this._stateChangeListeners.forEach((cb) => {
|
|
282
|
+
try {
|
|
283
|
+
cb(data.playerIndex, data.state);
|
|
284
|
+
} catch (err) {
|
|
285
|
+
this.handleError(
|
|
286
|
+
new errors.SmoreSDKError("UNKNOWN", "Error in custom state change listener", {
|
|
287
|
+
cause: err instanceof Error ? err : void 0
|
|
288
|
+
})
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
this.registerTransportHandler(events.SMORE_EVENTS.STATE_ALL, (raw) => {
|
|
295
|
+
const data = raw;
|
|
296
|
+
if (data?.states) {
|
|
297
|
+
for (const [key, value] of Object.entries(data.states)) {
|
|
298
|
+
const pi = Number(key);
|
|
299
|
+
this._customStates.set(pi, value);
|
|
300
|
+
this._stateChangeListeners.forEach((cb) => {
|
|
301
|
+
try {
|
|
302
|
+
cb(pi, value);
|
|
303
|
+
} catch (err) {
|
|
304
|
+
this.handleError(
|
|
305
|
+
new errors.SmoreSDKError("UNKNOWN", "Error in custom state change listener", {
|
|
306
|
+
cause: err instanceof Error ? err : void 0
|
|
307
|
+
})
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
274
314
|
}
|
|
275
315
|
/**
|
|
276
316
|
* Sets up a user event handler with playerIndex extraction.
|
|
@@ -408,6 +448,25 @@ class ScreenImpl {
|
|
|
408
448
|
return this._addLifecycleListener("$connection-change", callback);
|
|
409
449
|
}
|
|
410
450
|
// ---------------------------------------------------------------------------
|
|
451
|
+
// Custom State Methods
|
|
452
|
+
// ---------------------------------------------------------------------------
|
|
453
|
+
getControllerState(playerIndex) {
|
|
454
|
+
return this._customStates.get(playerIndex);
|
|
455
|
+
}
|
|
456
|
+
getAllControllerStates() {
|
|
457
|
+
const result = {};
|
|
458
|
+
for (const [key, value] of this._customStates) {
|
|
459
|
+
result[key] = value;
|
|
460
|
+
}
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
onCustomStateChange(listener) {
|
|
464
|
+
this._stateChangeListeners.add(listener);
|
|
465
|
+
return () => {
|
|
466
|
+
this._stateChangeListeners.delete(listener);
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
411
470
|
// Communication Methods
|
|
412
471
|
// ---------------------------------------------------------------------------
|
|
413
472
|
/**
|
|
@@ -677,6 +736,8 @@ class ScreenImpl {
|
|
|
677
736
|
this.handlerToTransport.clear();
|
|
678
737
|
this._pendingHandlers = [];
|
|
679
738
|
this._lifecycleListeners.clear();
|
|
739
|
+
this._customStates.clear();
|
|
740
|
+
this._stateChangeListeners.clear();
|
|
680
741
|
this._isConnected = false;
|
|
681
742
|
this._outboundBuffer = [];
|
|
682
743
|
if (this.transport instanceof PostMessageTransport.PostMessageTransport) {
|