@smoregg/sdk 1.2.0 → 2.0.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 (179) hide show
  1. package/dist/cjs/config.cjs.map +1 -1
  2. package/dist/cjs/controller.cjs +215 -145
  3. package/dist/cjs/controller.cjs.map +1 -1
  4. package/dist/cjs/screen.cjs +220 -178
  5. package/dist/cjs/screen.cjs.map +1 -1
  6. package/dist/cjs/testing.cjs +160 -151
  7. package/dist/cjs/testing.cjs.map +1 -1
  8. package/dist/esm/config.js.map +1 -1
  9. package/dist/esm/controller.js +216 -146
  10. package/dist/esm/controller.js.map +1 -1
  11. package/dist/esm/screen.js +221 -179
  12. package/dist/esm/screen.js.map +1 -1
  13. package/dist/esm/testing.js +160 -151
  14. package/dist/esm/testing.js.map +1 -1
  15. package/dist/types/config.d.ts +1 -2
  16. package/dist/types/config.d.ts.map +1 -1
  17. package/dist/types/controller.d.ts +22 -43
  18. package/dist/types/controller.d.ts.map +1 -1
  19. package/dist/types/index.d.ts +14 -14
  20. package/dist/types/index.d.ts.map +1 -1
  21. package/dist/types/screen.d.ts +26 -37
  22. package/dist/types/screen.d.ts.map +1 -1
  23. package/dist/types/testing.d.ts +16 -0
  24. package/dist/types/testing.d.ts.map +1 -1
  25. package/dist/types/types.d.ts +244 -338
  26. package/dist/types/types.d.ts.map +1 -1
  27. package/dist/umd/smore-sdk.umd.js +595 -474
  28. package/dist/umd/smore-sdk.umd.js.map +1 -1
  29. package/dist/umd/smore-sdk.umd.min.js +1 -1
  30. package/dist/umd/smore-sdk.umd.min.js.map +1 -1
  31. package/package.json +1 -1
  32. package/dist/cjs/SmoreHost.cjs +0 -306
  33. package/dist/cjs/SmoreHost.cjs.map +0 -1
  34. package/dist/cjs/SmorePlayer.cjs +0 -229
  35. package/dist/cjs/SmorePlayer.cjs.map +0 -1
  36. package/dist/cjs/components/DirectionPad.cjs +0 -68
  37. package/dist/cjs/components/DirectionPad.cjs.map +0 -1
  38. package/dist/cjs/components/DirectionPad.module.css.cjs +0 -12
  39. package/dist/cjs/components/DirectionPad.module.css.cjs.map +0 -1
  40. package/dist/cjs/components/HoldButton.cjs +0 -57
  41. package/dist/cjs/components/HoldButton.cjs.map +0 -1
  42. package/dist/cjs/components/HoldButton.module.css.cjs +0 -12
  43. package/dist/cjs/components/HoldButton.module.css.cjs.map +0 -1
  44. package/dist/cjs/components/IframeGameBridge.cjs +0 -115
  45. package/dist/cjs/components/IframeGameBridge.cjs.map +0 -1
  46. package/dist/cjs/components/SwipeArea.cjs +0 -58
  47. package/dist/cjs/components/SwipeArea.cjs.map +0 -1
  48. package/dist/cjs/components/SwipeArea.module.css.cjs +0 -12
  49. package/dist/cjs/components/SwipeArea.module.css.cjs.map +0 -1
  50. package/dist/cjs/components/TapButton.cjs +0 -58
  51. package/dist/cjs/components/TapButton.cjs.map +0 -1
  52. package/dist/cjs/components/TapButton.module.css.cjs +0 -12
  53. package/dist/cjs/components/TapButton.module.css.cjs.map +0 -1
  54. package/dist/cjs/context/RoomProvider.cjs +0 -118
  55. package/dist/cjs/context/RoomProvider.cjs.map +0 -1
  56. package/dist/cjs/hooks/useExternalGames.cjs +0 -49
  57. package/dist/cjs/hooks/useExternalGames.cjs.map +0 -1
  58. package/dist/cjs/hooks/useGameHost.cjs +0 -206
  59. package/dist/cjs/hooks/useGameHost.cjs.map +0 -1
  60. package/dist/cjs/hooks/useGamePlayer.cjs +0 -134
  61. package/dist/cjs/hooks/useGamePlayer.cjs.map +0 -1
  62. package/dist/cjs/iframe/index.cjs +0 -260
  63. package/dist/cjs/iframe/index.cjs.map +0 -1
  64. package/dist/cjs/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.cjs +0 -33
  65. package/dist/cjs/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.cjs.map +0 -1
  66. package/dist/cjs/server/index.cjs +0 -45
  67. package/dist/cjs/server/index.cjs.map +0 -1
  68. package/dist/cjs/transport/DirectTransport.cjs +0 -23
  69. package/dist/cjs/transport/DirectTransport.cjs.map +0 -1
  70. package/dist/cjs/utils/connectionMonitor.cjs +0 -77
  71. package/dist/cjs/utils/connectionMonitor.cjs.map +0 -1
  72. package/dist/cjs/utils/preloadAssets.cjs +0 -66
  73. package/dist/cjs/utils/preloadAssets.cjs.map +0 -1
  74. package/dist/cjs/utils/serverTime.cjs +0 -43
  75. package/dist/cjs/utils/serverTime.cjs.map +0 -1
  76. package/dist/esm/SmoreHost.js +0 -304
  77. package/dist/esm/SmoreHost.js.map +0 -1
  78. package/dist/esm/SmorePlayer.js +0 -227
  79. package/dist/esm/SmorePlayer.js.map +0 -1
  80. package/dist/esm/components/DirectionPad.js +0 -66
  81. package/dist/esm/components/DirectionPad.js.map +0 -1
  82. package/dist/esm/components/DirectionPad.module.css.js +0 -8
  83. package/dist/esm/components/DirectionPad.module.css.js.map +0 -1
  84. package/dist/esm/components/HoldButton.js +0 -55
  85. package/dist/esm/components/HoldButton.js.map +0 -1
  86. package/dist/esm/components/HoldButton.module.css.js +0 -8
  87. package/dist/esm/components/HoldButton.module.css.js.map +0 -1
  88. package/dist/esm/components/IframeGameBridge.js +0 -113
  89. package/dist/esm/components/IframeGameBridge.js.map +0 -1
  90. package/dist/esm/components/SwipeArea.js +0 -56
  91. package/dist/esm/components/SwipeArea.js.map +0 -1
  92. package/dist/esm/components/SwipeArea.module.css.js +0 -8
  93. package/dist/esm/components/SwipeArea.module.css.js.map +0 -1
  94. package/dist/esm/components/TapButton.js +0 -56
  95. package/dist/esm/components/TapButton.js.map +0 -1
  96. package/dist/esm/components/TapButton.module.css.js +0 -8
  97. package/dist/esm/components/TapButton.module.css.js.map +0 -1
  98. package/dist/esm/context/RoomProvider.js +0 -109
  99. package/dist/esm/context/RoomProvider.js.map +0 -1
  100. package/dist/esm/hooks/useExternalGames.js +0 -47
  101. package/dist/esm/hooks/useExternalGames.js.map +0 -1
  102. package/dist/esm/hooks/useGameHost.js +0 -204
  103. package/dist/esm/hooks/useGameHost.js.map +0 -1
  104. package/dist/esm/hooks/useGamePlayer.js +0 -132
  105. package/dist/esm/hooks/useGamePlayer.js.map +0 -1
  106. package/dist/esm/iframe/index.js +0 -257
  107. package/dist/esm/iframe/index.js.map +0 -1
  108. package/dist/esm/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.js +0 -29
  109. package/dist/esm/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.js.map +0 -1
  110. package/dist/esm/server/index.js +0 -43
  111. package/dist/esm/server/index.js.map +0 -1
  112. package/dist/esm/transport/DirectTransport.js +0 -21
  113. package/dist/esm/transport/DirectTransport.js.map +0 -1
  114. package/dist/esm/utils/connectionMonitor.js +0 -75
  115. package/dist/esm/utils/connectionMonitor.js.map +0 -1
  116. package/dist/esm/utils/preloadAssets.js +0 -63
  117. package/dist/esm/utils/preloadAssets.js.map +0 -1
  118. package/dist/esm/utils/serverTime.js +0 -41
  119. package/dist/esm/utils/serverTime.js.map +0 -1
  120. package/dist/types/SmoreHost.d.ts +0 -187
  121. package/dist/types/SmoreHost.d.ts.map +0 -1
  122. package/dist/types/SmorePlayer.d.ts +0 -146
  123. package/dist/types/SmorePlayer.d.ts.map +0 -1
  124. package/dist/types/components/DirectionPad.d.ts +0 -21
  125. package/dist/types/components/DirectionPad.d.ts.map +0 -1
  126. package/dist/types/components/HoldButton.d.ts +0 -22
  127. package/dist/types/components/HoldButton.d.ts.map +0 -1
  128. package/dist/types/components/IframeGameBridge.d.ts +0 -38
  129. package/dist/types/components/IframeGameBridge.d.ts.map +0 -1
  130. package/dist/types/components/SwipeArea.d.ts +0 -19
  131. package/dist/types/components/SwipeArea.d.ts.map +0 -1
  132. package/dist/types/components/TapButton.d.ts +0 -19
  133. package/dist/types/components/TapButton.d.ts.map +0 -1
  134. package/dist/types/components/index.d.ts +0 -6
  135. package/dist/types/components/index.d.ts.map +0 -1
  136. package/dist/types/context/RoomProvider.d.ts +0 -69
  137. package/dist/types/context/RoomProvider.d.ts.map +0 -1
  138. package/dist/types/context/index.d.ts +0 -3
  139. package/dist/types/context/index.d.ts.map +0 -1
  140. package/dist/types/dev/DevSimulator.d.ts +0 -31
  141. package/dist/types/dev/DevSimulator.d.ts.map +0 -1
  142. package/dist/types/dev/index.d.ts +0 -2
  143. package/dist/types/dev/index.d.ts.map +0 -1
  144. package/dist/types/hooks/index.d.ts +0 -7
  145. package/dist/types/hooks/index.d.ts.map +0 -1
  146. package/dist/types/hooks/useExternalGames.d.ts +0 -32
  147. package/dist/types/hooks/useExternalGames.d.ts.map +0 -1
  148. package/dist/types/hooks/useGameHost.d.ts +0 -67
  149. package/dist/types/hooks/useGameHost.d.ts.map +0 -1
  150. package/dist/types/hooks/useGamePlayer.d.ts +0 -55
  151. package/dist/types/hooks/useGamePlayer.d.ts.map +0 -1
  152. package/dist/types/iframe/IframeRoomProvider.d.ts +0 -31
  153. package/dist/types/iframe/IframeRoomProvider.d.ts.map +0 -1
  154. package/dist/types/iframe/index.d.ts +0 -18
  155. package/dist/types/iframe/index.d.ts.map +0 -1
  156. package/dist/types/iframe/vanilla-entry.d.ts +0 -7
  157. package/dist/types/iframe/vanilla-entry.d.ts.map +0 -1
  158. package/dist/types/iframe/vanilla.d.ts +0 -49
  159. package/dist/types/iframe/vanilla.d.ts.map +0 -1
  160. package/dist/types/server/createGameRelay.d.ts +0 -26
  161. package/dist/types/server/createGameRelay.d.ts.map +0 -1
  162. package/dist/types/server/index.d.ts +0 -3
  163. package/dist/types/server/index.d.ts.map +0 -1
  164. package/dist/types/utils/connectionMonitor.d.ts +0 -57
  165. package/dist/types/utils/connectionMonitor.d.ts.map +0 -1
  166. package/dist/types/utils/index.d.ts +0 -7
  167. package/dist/types/utils/index.d.ts.map +0 -1
  168. package/dist/types/utils/preloadAssets.d.ts +0 -29
  169. package/dist/types/utils/preloadAssets.d.ts.map +0 -1
  170. package/dist/types/utils/serverTime.d.ts +0 -28
  171. package/dist/types/utils/serverTime.d.ts.map +0 -1
  172. package/dist/umd/smore-sdk-iframe.umd.js +0 -266
  173. package/dist/umd/smore-sdk-iframe.umd.js.map +0 -1
  174. package/dist/umd/smore-sdk-iframe.umd.min.js +0 -2
  175. package/dist/umd/smore-sdk-iframe.umd.min.js.map +0 -1
  176. package/dist/umd/smore-sdk-vanilla.umd.js +0 -1275
  177. package/dist/umd/smore-sdk-vanilla.umd.js.map +0 -1
  178. package/dist/umd/smore-sdk-vanilla.umd.min.js +0 -2
  179. package/dist/umd/smore-sdk-vanilla.umd.min.js.map +0 -1
@@ -1,1275 +0,0 @@
1
- (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.SmoreSDK = {}));
5
- })(this, (function (exports) { 'use strict';
6
-
7
- const SMORE_MSG_PREFIX = "smore:";
8
- function isSmoreMessage(data) {
9
- return data && typeof data === "object" && typeof data.type === "string" && data.type.startsWith(SMORE_MSG_PREFIX);
10
- }
11
-
12
- class PostMessageTransport {
13
- handlers = /* @__PURE__ */ new Map();
14
- ackCallbacks = /* @__PURE__ */ new Map();
15
- ackCounter = 0;
16
- parentOrigin;
17
- boundMessageHandler;
18
- constructor(parentOrigin = "*") {
19
- this.parentOrigin = parentOrigin;
20
- this.boundMessageHandler = this.handleMessage.bind(this);
21
- window.addEventListener("message", this.boundMessageHandler);
22
- }
23
- emit(event, ...args) {
24
- let data = args[0];
25
- let ackId;
26
- if (args.length >= 2 && typeof args[args.length - 1] === "function") {
27
- data = args.length === 2 ? args[0] : args[0];
28
- const callback = args[args.length - 1];
29
- ackId = `ack_${++this.ackCounter}`;
30
- this.ackCallbacks.set(ackId, callback);
31
- }
32
- window.parent.postMessage(
33
- { type: "smore:emit", payload: { event, data, ackId } },
34
- this.parentOrigin
35
- );
36
- }
37
- on(event, handler) {
38
- let set = this.handlers.get(event);
39
- if (!set) {
40
- set = /* @__PURE__ */ new Set();
41
- this.handlers.set(event, set);
42
- }
43
- set.add(handler);
44
- }
45
- off(event, handler) {
46
- if (!handler) {
47
- this.handlers.delete(event);
48
- return;
49
- }
50
- this.handlers.get(event)?.delete(handler);
51
- }
52
- destroy() {
53
- window.removeEventListener("message", this.boundMessageHandler);
54
- this.handlers.clear();
55
- this.ackCallbacks.clear();
56
- }
57
- handleMessage(e) {
58
- if (this.parentOrigin !== "*" && e.origin !== this.parentOrigin) return;
59
- const msg = e.data;
60
- if (!isSmoreMessage(msg)) return;
61
- if (msg.type === "smore:event") {
62
- const { event, data } = msg.payload;
63
- const set = this.handlers.get(event);
64
- if (set) {
65
- set.forEach((handler) => handler(data));
66
- }
67
- } else if (msg.type === "smore:ack") {
68
- const { ackId, data } = msg.payload;
69
- const cb = this.ackCallbacks.get(ackId);
70
- if (cb) {
71
- this.ackCallbacks.delete(ackId);
72
- cb(data);
73
- }
74
- }
75
- }
76
- }
77
-
78
- const SYSTEM_PREFIX$1 = "smore:";
79
- const SYSTEM_EVENTS$1 = {
80
- PLAYER_JOIN: `${SYSTEM_PREFIX$1}player-join`,
81
- PLAYER_LEAVE: `${SYSTEM_PREFIX$1}player-leave`,
82
- PLAYER_RECONNECT: `${SYSTEM_PREFIX$1}player-reconnect`,
83
- GAME_OVER: `${SYSTEM_PREFIX$1}game-over`
84
- };
85
- const DEFAULT_TIMEOUT$1 = 1e4;
86
- let SmoreSDKError$1 = class SmoreSDKError extends Error {
87
- code;
88
- cause;
89
- details;
90
- constructor(code, message, options) {
91
- super(message);
92
- this.name = "SmoreSDKError";
93
- this.code = code;
94
- this.cause = options?.cause;
95
- this.details = options?.details;
96
- const ErrorWithCapture = Error;
97
- if (typeof ErrorWithCapture.captureStackTrace === "function") {
98
- ErrorWithCapture.captureStackTrace(this, SmoreSDKError);
99
- }
100
- }
101
- toSmoreError() {
102
- return {
103
- code: this.code,
104
- message: this.message,
105
- cause: this.cause,
106
- details: this.details
107
- };
108
- }
109
- };
110
- const EVENT_NAME_REGEX$1 = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
111
- function validateEventName$1(event) {
112
- if (!event || typeof event !== "string") {
113
- throw new SmoreSDKError$1("INVALID_EVENT", "Event name must be a non-empty string");
114
- }
115
- if (!EVENT_NAME_REGEX$1.test(event)) {
116
- throw new SmoreSDKError$1(
117
- "INVALID_EVENT",
118
- `Invalid event name "${event}". Event names must start with a letter, contain only letters, numbers, hyphens, or underscores, and end with a letter or number.`,
119
- { details: { event } }
120
- );
121
- }
122
- }
123
- function validatePlayerIndex(playerIndex, controllersCount) {
124
- if (typeof playerIndex !== "number" || !Number.isInteger(playerIndex)) {
125
- throw new SmoreSDKError$1("INVALID_PLAYER", "Player index must be an integer");
126
- }
127
- if (playerIndex < 0 || playerIndex >= controllersCount) {
128
- throw new SmoreSDKError$1(
129
- "INVALID_PLAYER",
130
- `Invalid player index ${playerIndex}. Valid range: 0-${controllersCount - 1}`,
131
- { details: { playerIndex, controllersCount } }
132
- );
133
- }
134
- }
135
- class DebugLogger {
136
- enabled;
137
- level;
138
- prefix;
139
- logSend;
140
- logReceive;
141
- logLifecycle;
142
- customLogger;
143
- static levelOrder = {
144
- debug: 0,
145
- info: 1,
146
- warn: 2,
147
- error: 3
148
- };
149
- constructor(options) {
150
- if (typeof options === "boolean") {
151
- this.enabled = options;
152
- this.level = "debug";
153
- this.prefix = "[SmoreScreen]";
154
- this.logSend = true;
155
- this.logReceive = true;
156
- this.logLifecycle = true;
157
- } else if (options) {
158
- this.enabled = options.enabled ?? false;
159
- this.level = options.level ?? "debug";
160
- this.prefix = options.prefix ?? "[SmoreScreen]";
161
- this.logSend = options.logSend ?? true;
162
- this.logReceive = options.logReceive ?? true;
163
- this.logLifecycle = options.logLifecycle ?? true;
164
- this.customLogger = options.logger;
165
- } else {
166
- this.enabled = false;
167
- this.level = "debug";
168
- this.prefix = "[SmoreScreen]";
169
- this.logSend = true;
170
- this.logReceive = true;
171
- this.logLifecycle = true;
172
- }
173
- }
174
- shouldLog(level) {
175
- return this.enabled && DebugLogger.levelOrder[level] >= DebugLogger.levelOrder[this.level];
176
- }
177
- log(level, message, data) {
178
- if (!this.shouldLog(level)) return;
179
- if (this.customLogger) {
180
- this.customLogger(level, `${this.prefix} ${message}`, data);
181
- return;
182
- }
183
- const consoleMethod = level === "error" ? "error" : level === "warn" ? "warn" : "log";
184
- if (data !== void 0) {
185
- console[consoleMethod](`${this.prefix} ${message}`, data);
186
- } else {
187
- console[consoleMethod](`${this.prefix} ${message}`);
188
- }
189
- }
190
- debug(message, data) {
191
- this.log("debug", message, data);
192
- }
193
- info(message, data) {
194
- this.log("info", message, data);
195
- }
196
- warn(message, data) {
197
- this.log("warn", message, data);
198
- }
199
- error(message, data) {
200
- this.log("error", message, data);
201
- }
202
- send(event, data) {
203
- if (this.logSend) {
204
- this.debug(`-> SEND: ${event}`, data);
205
- }
206
- }
207
- receive(event, data) {
208
- if (this.logReceive) {
209
- this.debug(`<- RECV: ${event}`, data);
210
- }
211
- }
212
- lifecycle(message, data) {
213
- if (this.logLifecycle) {
214
- this.info(`[Lifecycle] ${message}`, data);
215
- }
216
- }
217
- }
218
- class ScreenImpl {
219
- transport = null;
220
- config;
221
- logger;
222
- _controllers = [];
223
- _roomCode = "";
224
- _leaderIndex = -1;
225
- _isReady = false;
226
- _isDestroyed = false;
227
- eventHandlers = /* @__PURE__ */ new Map();
228
- registeredTransportHandlers = [];
229
- boundMessageHandler = null;
230
- constructor(config = {}) {
231
- this.config = config;
232
- this.logger = new DebugLogger(config.debug);
233
- if (config.listeners) {
234
- for (const event of Object.keys(config.listeners)) {
235
- validateEventName$1(event);
236
- }
237
- }
238
- }
239
- // ---------------------------------------------------------------------------
240
- // Initialization (called by factory)
241
- // ---------------------------------------------------------------------------
242
- async initialize() {
243
- this.logger.lifecycle("Initializing screen...");
244
- const parentOrigin = this.config.parentOrigin ?? "*";
245
- const timeout = this.config.timeout ?? DEFAULT_TIMEOUT$1;
246
- return new Promise((resolve, reject) => {
247
- const timeoutId = setTimeout(() => {
248
- this.cleanup();
249
- const error = new SmoreSDKError$1(
250
- "TIMEOUT",
251
- `Screen initialization timed out after ${timeout}ms. Make sure the parent frame sends smore:init.`,
252
- { details: { timeout } }
253
- );
254
- this.handleError(error);
255
- reject(error);
256
- }, timeout);
257
- this.boundMessageHandler = (e) => {
258
- if (parentOrigin !== "*" && e.origin !== parentOrigin) return;
259
- const msg = e.data;
260
- if (!isSmoreMessage(msg)) return;
261
- if (msg.type === "smore:init") {
262
- clearTimeout(timeoutId);
263
- const initData = msg.payload;
264
- if (initData.side !== "host") {
265
- const error = new SmoreSDKError$1(
266
- "INIT_FAILED",
267
- `Received init for wrong side: ${initData.side}. Expected "host".`,
268
- { details: { side: initData.side } }
269
- );
270
- this.handleError(error);
271
- reject(error);
272
- return;
273
- }
274
- this.transport = new PostMessageTransport(parentOrigin);
275
- this._roomCode = initData.roomCode;
276
- this._controllers = this.mapControllersFromInit(initData.players);
277
- this._leaderIndex = this.findLeaderIndex(initData.players, initData.leaderId);
278
- this.setupEventHandlers();
279
- this._isReady = true;
280
- this.logger.lifecycle("Screen ready", {
281
- roomCode: this._roomCode,
282
- controllers: this._controllers.length,
283
- leaderIndex: this._leaderIndex
284
- });
285
- this.config.onReady?.();
286
- resolve();
287
- } else if (msg.type === "smore:update") {
288
- const updateData = msg.payload;
289
- if (updateData.players) {
290
- this._controllers = this.mapControllersFromInit(updateData.players);
291
- }
292
- if (updateData.leaderId !== void 0) {
293
- this._leaderIndex = this.findLeaderIndex(
294
- updateData.players ?? [],
295
- updateData.leaderId
296
- );
297
- }
298
- this.logger.lifecycle("Room updated", {
299
- controllers: this._controllers.length,
300
- leaderIndex: this._leaderIndex
301
- });
302
- }
303
- };
304
- window.addEventListener("message", this.boundMessageHandler);
305
- window.parent.postMessage({ type: "smore:ready" }, parentOrigin);
306
- this.logger.lifecycle("Sent smore:ready to parent");
307
- });
308
- }
309
- mapControllersFromInit(players) {
310
- return players.map((p, index) => ({
311
- playerIndex: p.playerIndex ?? index,
312
- nickname: p.nickname || `Player ${index + 1}`,
313
- connected: p.connected !== false,
314
- appearance: p.appearance
315
- }));
316
- }
317
- findLeaderIndex(players, leaderId) {
318
- if (!leaderId) return -1;
319
- const idx = players.findIndex(
320
- (p) => p.sessionId === leaderId
321
- );
322
- return idx >= 0 ? idx : -1;
323
- }
324
- setupEventHandlers() {
325
- if (!this.transport) return;
326
- this.registerTransportHandler(SYSTEM_EVENTS$1.PLAYER_JOIN, (data) => {
327
- const payload = data;
328
- const player = payload?.player;
329
- if (player && typeof player.playerIndex === "number") {
330
- this.logger.lifecycle("Controller joined", { playerIndex: player.playerIndex });
331
- this.config.onControllerJoin?.(player.playerIndex, player);
332
- }
333
- });
334
- this.registerTransportHandler(SYSTEM_EVENTS$1.PLAYER_LEAVE, (data) => {
335
- const payload = data;
336
- if (typeof payload?.playerIndex === "number") {
337
- this.logger.lifecycle("Controller left", { playerIndex: payload.playerIndex });
338
- this.config.onControllerLeave?.(payload.playerIndex);
339
- }
340
- });
341
- this.registerTransportHandler(SYSTEM_EVENTS$1.PLAYER_RECONNECT, (data) => {
342
- const payload = data;
343
- const player = payload?.player;
344
- if (player && typeof player.playerIndex === "number") {
345
- this.logger.lifecycle("Controller reconnected", { playerIndex: player.playerIndex });
346
- this.config.onControllerReconnect?.(player.playerIndex, player);
347
- }
348
- });
349
- this.registerTransportHandler("room:player-joined", (data) => {
350
- const payload = data;
351
- const playerIndex = payload?.player?.playerIndex ?? payload?.playerIndex;
352
- if (typeof playerIndex === "number") {
353
- this.config.onControllerJoin?.(playerIndex, {
354
- playerIndex,
355
- nickname: `Player ${playerIndex + 1}`,
356
- connected: true
357
- });
358
- }
359
- });
360
- this.registerTransportHandler("room:player-left", (data) => {
361
- const payload = data;
362
- const playerIndex = payload?.playerIndex ?? payload?.player?.playerIndex;
363
- if (typeof playerIndex === "number") {
364
- this.config.onControllerLeave?.(playerIndex);
365
- }
366
- });
367
- if (this.config.listeners) {
368
- for (const [event, handler] of Object.entries(this.config.listeners)) {
369
- if (!handler) continue;
370
- this.setupUserEventHandler(event, handler);
371
- }
372
- }
373
- }
374
- setupUserEventHandler(event, handler) {
375
- const wrappedHandler = (data) => {
376
- this.logger.receive(event, data);
377
- const payload = data;
378
- const { playerIndex, ...rest } = payload;
379
- if (typeof playerIndex === "number") {
380
- try {
381
- handler(playerIndex, rest);
382
- } catch (err) {
383
- this.handleError(
384
- new SmoreSDKError$1("UNKNOWN", `Error in handler for event "${event}"`, {
385
- cause: err instanceof Error ? err : void 0,
386
- details: { event, playerIndex }
387
- })
388
- );
389
- }
390
- }
391
- };
392
- this.registerTransportHandler(event, wrappedHandler);
393
- let handlers = this.eventHandlers.get(event);
394
- if (!handlers) {
395
- handlers = /* @__PURE__ */ new Set();
396
- this.eventHandlers.set(event, handlers);
397
- }
398
- handlers.add(handler);
399
- }
400
- registerTransportHandler(event, handler) {
401
- if (!this.transport) return;
402
- this.transport.on(event, handler);
403
- this.registeredTransportHandlers.push({ event, handler });
404
- }
405
- // ---------------------------------------------------------------------------
406
- // Properties (readonly)
407
- // ---------------------------------------------------------------------------
408
- get controllers() {
409
- return [...this._controllers];
410
- }
411
- get roomCode() {
412
- return this._roomCode;
413
- }
414
- get leaderIndex() {
415
- return this._leaderIndex;
416
- }
417
- get isReady() {
418
- return this._isReady;
419
- }
420
- get isDestroyed() {
421
- return this._isDestroyed;
422
- }
423
- // ---------------------------------------------------------------------------
424
- // Communication Methods
425
- // ---------------------------------------------------------------------------
426
- broadcast(event, data) {
427
- this.ensureReady("broadcast");
428
- validateEventName$1(event);
429
- this.logger.send(event, data);
430
- this.transport.emit(event, data);
431
- }
432
- broadcastRaw(event, data) {
433
- this.ensureReady("broadcastRaw");
434
- validateEventName$1(event);
435
- this.logger.send(event, data);
436
- this.transport.emit(event, data);
437
- }
438
- sendToController(playerIndex, event, data) {
439
- this.ensureReady("sendToController");
440
- validateEventName$1(event);
441
- validatePlayerIndex(playerIndex, this._controllers.length);
442
- this.logger.send(`${event} -> Player ${playerIndex}`, data);
443
- this.transport.emit(event, {
444
- targetPlayerIndex: playerIndex,
445
- ...data && typeof data === "object" ? data : { data }
446
- });
447
- }
448
- sendToControllerRaw(playerIndex, event, data) {
449
- this.ensureReady("sendToControllerRaw");
450
- validateEventName$1(event);
451
- validatePlayerIndex(playerIndex, this._controllers.length);
452
- this.logger.send(`${event} -> Player ${playerIndex}`, data);
453
- this.transport.emit(event, {
454
- targetPlayerIndex: playerIndex,
455
- ...data && typeof data === "object" ? data : { data }
456
- });
457
- }
458
- // ---------------------------------------------------------------------------
459
- // Game Lifecycle
460
- // ---------------------------------------------------------------------------
461
- gameOver(results) {
462
- this.ensureReady("gameOver");
463
- this.logger.lifecycle("Game over", results);
464
- this.transport.emit(SYSTEM_EVENTS$1.GAME_OVER, { results });
465
- }
466
- // ---------------------------------------------------------------------------
467
- // Event Subscription
468
- // ---------------------------------------------------------------------------
469
- on(event, handler) {
470
- validateEventName$1(event);
471
- let handlers = this.eventHandlers.get(event);
472
- if (!handlers) {
473
- handlers = /* @__PURE__ */ new Set();
474
- this.eventHandlers.set(event, handlers);
475
- }
476
- handlers.add(handler);
477
- if (this.transport) {
478
- const wrappedHandler = (data) => {
479
- this.logger.receive(event, data);
480
- const payload = data;
481
- const { playerIndex, ...rest } = payload;
482
- if (typeof playerIndex === "number") {
483
- try {
484
- handler(playerIndex, rest);
485
- } catch (err) {
486
- this.handleError(
487
- new SmoreSDKError$1("UNKNOWN", `Error in handler for event "${event}"`, {
488
- cause: err instanceof Error ? err : void 0
489
- })
490
- );
491
- }
492
- }
493
- };
494
- this.registerTransportHandler(event, wrappedHandler);
495
- }
496
- return () => {
497
- handlers?.delete(handler);
498
- if (handlers?.size === 0) {
499
- this.eventHandlers.delete(event);
500
- }
501
- };
502
- }
503
- once(event, handler) {
504
- const wrappedHandler = (playerIndex, data) => {
505
- unsubscribe();
506
- handler(playerIndex, data);
507
- };
508
- const unsubscribe = this.on(event, wrappedHandler);
509
- return unsubscribe;
510
- }
511
- off(event, handler) {
512
- if (!handler) {
513
- this.eventHandlers.delete(event);
514
- this.transport?.off(event);
515
- } else {
516
- const handlers = this.eventHandlers.get(event);
517
- handlers?.delete(handler);
518
- if (handlers?.size === 0) {
519
- this.eventHandlers.delete(event);
520
- }
521
- }
522
- }
523
- // ---------------------------------------------------------------------------
524
- // Utilities
525
- // ---------------------------------------------------------------------------
526
- getController(playerIndex) {
527
- return this._controllers.find((c) => c.playerIndex === playerIndex);
528
- }
529
- isLeader(playerIndex) {
530
- return playerIndex === this._leaderIndex;
531
- }
532
- getControllerCount() {
533
- return this._controllers.filter((c) => c.connected).length;
534
- }
535
- // ---------------------------------------------------------------------------
536
- // Cleanup
537
- // ---------------------------------------------------------------------------
538
- destroy() {
539
- if (this._isDestroyed) return;
540
- this.logger.lifecycle("Destroying screen...");
541
- this._isDestroyed = true;
542
- this._isReady = false;
543
- this.cleanup();
544
- this.logger.lifecycle("Screen destroyed");
545
- }
546
- cleanup() {
547
- for (const { event, handler } of this.registeredTransportHandlers) {
548
- this.transport?.off(event, handler);
549
- }
550
- this.registeredTransportHandlers = [];
551
- this.eventHandlers.clear();
552
- if (this.transport instanceof PostMessageTransport) {
553
- this.transport.destroy();
554
- }
555
- this.transport = null;
556
- if (this.boundMessageHandler) {
557
- window.removeEventListener("message", this.boundMessageHandler);
558
- this.boundMessageHandler = null;
559
- }
560
- }
561
- // ---------------------------------------------------------------------------
562
- // Error Handling
563
- // ---------------------------------------------------------------------------
564
- handleError(error) {
565
- const smoreError = error.toSmoreError();
566
- if (this.config.onError) {
567
- this.config.onError(smoreError);
568
- } else {
569
- this.logger.error(error.message, error.details);
570
- }
571
- }
572
- ensureReady(method) {
573
- if (this._isDestroyed) {
574
- throw new SmoreSDKError$1(
575
- "DESTROYED",
576
- `Cannot call ${method}() after destroy()`,
577
- { details: { method } }
578
- );
579
- }
580
- if (!this._isReady || !this.transport) {
581
- throw new SmoreSDKError$1(
582
- "NOT_READY",
583
- `Cannot call ${method}() before screen is ready. Use await createScreen() or onReady callback.`,
584
- { details: { method } }
585
- );
586
- }
587
- }
588
- }
589
- function createScreen(config) {
590
- const screen = new ScreenImpl(config);
591
- const promise = screen.initialize().then(() => screen);
592
- promise.instance = screen;
593
- return promise;
594
- }
595
-
596
- const SYSTEM_PREFIX = "smore:";
597
- const SYSTEM_EVENTS = {
598
- PLAYER_JOIN: `${SYSTEM_PREFIX}player-join`,
599
- PLAYER_LEAVE: `${SYSTEM_PREFIX}player-leave`
600
- };
601
- const DEFAULT_TIMEOUT = 1e4;
602
- class SmoreSDKError extends Error {
603
- code;
604
- cause;
605
- details;
606
- constructor(code, message, options) {
607
- super(message);
608
- this.name = "SmoreSDKError";
609
- this.code = code;
610
- this.cause = options?.cause;
611
- this.details = options?.details;
612
- const ErrorWithCapture = Error;
613
- if (typeof ErrorWithCapture.captureStackTrace === "function") {
614
- ErrorWithCapture.captureStackTrace(this, SmoreSDKError);
615
- }
616
- }
617
- toSmoreError() {
618
- return {
619
- code: this.code,
620
- message: this.message,
621
- cause: this.cause,
622
- details: this.details
623
- };
624
- }
625
- }
626
- const EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
627
- function validateEventName(event) {
628
- if (!event || typeof event !== "string") {
629
- throw new SmoreSDKError("INVALID_EVENT", "Event name must be a non-empty string");
630
- }
631
- if (!EVENT_NAME_REGEX.test(event)) {
632
- throw new SmoreSDKError(
633
- "INVALID_EVENT",
634
- `Invalid event name "${event}". Event names must:
635
- - Start with a letter (a-z, A-Z)
636
- - Only contain letters, numbers, hyphens (-), and underscores (_)
637
- - End with a letter or number`,
638
- { details: { event } }
639
- );
640
- }
641
- }
642
- function createLogger(options) {
643
- const enabled = typeof options === "boolean" ? options : options?.enabled ?? false;
644
- const level = (typeof options === "object" ? options.level : void 0) ?? "debug";
645
- const prefix = (typeof options === "object" ? options.prefix : void 0) ?? "[SmoreController]";
646
- const customLogger = typeof options === "object" ? options.logger : void 0;
647
- const levelPriority = {
648
- debug: 0,
649
- info: 1,
650
- warn: 2,
651
- error: 3
652
- };
653
- const shouldLog = (msgLevel) => {
654
- if (!enabled) return false;
655
- return levelPriority[msgLevel] >= levelPriority[level];
656
- };
657
- const log = (msgLevel, message, data) => {
658
- if (!shouldLog(msgLevel)) return;
659
- if (customLogger) {
660
- customLogger(msgLevel, message, data);
661
- return;
662
- }
663
- const fullMessage = `${prefix} ${message}`;
664
- const consoleFn = console[msgLevel] ?? console.log;
665
- if (data !== void 0) {
666
- consoleFn(fullMessage, data);
667
- } else {
668
- consoleFn(fullMessage);
669
- }
670
- };
671
- return {
672
- debug: (msg, data) => log("debug", msg, data),
673
- info: (msg, data) => log("info", msg, data),
674
- warn: (msg, data) => log("warn", msg, data),
675
- error: (msg, data) => log("error", msg, data)
676
- };
677
- }
678
- class ControllerImpl {
679
- transport = null;
680
- config;
681
- logger;
682
- _roomCode = "";
683
- _myIndex = -1;
684
- _isLeader = false;
685
- _isReady = false;
686
- _isDestroyed = false;
687
- boundMessageHandler = null;
688
- registeredHandlers = [];
689
- eventListeners = /* @__PURE__ */ new Map();
690
- constructor(config = {}) {
691
- this.config = config;
692
- this.logger = createLogger(config.debug);
693
- if (config.listeners) {
694
- for (const event of Object.keys(config.listeners)) {
695
- validateEventName(event);
696
- }
697
- }
698
- }
699
- // ---------------------------------------------------------------------------
700
- // Properties (readonly)
701
- // ---------------------------------------------------------------------------
702
- get myIndex() {
703
- return this._myIndex;
704
- }
705
- get isLeader() {
706
- return this._isLeader;
707
- }
708
- get roomCode() {
709
- return this._roomCode;
710
- }
711
- get isReady() {
712
- return this._isReady;
713
- }
714
- get isDestroyed() {
715
- return this._isDestroyed;
716
- }
717
- // ---------------------------------------------------------------------------
718
- // Initialization
719
- // ---------------------------------------------------------------------------
720
- async initialize() {
721
- const parentOrigin = this.config.parentOrigin ?? "*";
722
- const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;
723
- this.logger.debug("Initializing controller...", { parentOrigin, timeout });
724
- return new Promise((resolve, reject) => {
725
- const timeoutId = setTimeout(() => {
726
- this.cleanup();
727
- const error = new SmoreSDKError(
728
- "TIMEOUT",
729
- `Controller initialization timed out after ${timeout}ms. Make sure the parent window sends smore:init message.`,
730
- { details: { timeout } }
731
- );
732
- this.handleError(error);
733
- reject(error);
734
- }, timeout);
735
- this.boundMessageHandler = (e) => {
736
- if (parentOrigin !== "*" && e.origin !== parentOrigin) return;
737
- const msg = e.data;
738
- if (!isSmoreMessage(msg)) return;
739
- if (msg.type === "smore:init") {
740
- clearTimeout(timeoutId);
741
- this.handleInit(msg, parentOrigin, resolve, reject);
742
- } else if (msg.type === "smore:update") {
743
- this.handleUpdate(msg);
744
- }
745
- };
746
- window.addEventListener("message", this.boundMessageHandler);
747
- this.logger.debug("Sending smore:ready to parent");
748
- window.parent.postMessage({ type: "smore:ready" }, parentOrigin);
749
- });
750
- }
751
- handleInit(msg, parentOrigin, resolve, reject) {
752
- const initData = msg.payload;
753
- this.logger.debug("Received smore:init", initData);
754
- if (initData.side !== "player") {
755
- const error = new SmoreSDKError(
756
- "INIT_FAILED",
757
- `Controller received init for wrong side: ${initData.side}`,
758
- { details: { side: initData.side } }
759
- );
760
- this.handleError(error);
761
- reject(error);
762
- return;
763
- }
764
- if (initData.myIndex === void 0) {
765
- const error = new SmoreSDKError(
766
- "INIT_FAILED",
767
- "Missing myIndex in init payload",
768
- { details: initData }
769
- );
770
- this.handleError(error);
771
- reject(error);
772
- return;
773
- }
774
- this.transport = new PostMessageTransport(parentOrigin);
775
- this._roomCode = initData.roomCode;
776
- this._myIndex = initData.myIndex;
777
- this._isLeader = initData.isLeader ?? false;
778
- this.setupEventHandlers();
779
- this._isReady = true;
780
- this.logger.info("Controller ready", {
781
- roomCode: this._roomCode,
782
- myIndex: this._myIndex,
783
- isLeader: this._isLeader
784
- });
785
- this.config.onReady?.();
786
- resolve();
787
- }
788
- handleUpdate(msg) {
789
- const updateData = msg.payload;
790
- this.logger.debug("Received smore:update", updateData);
791
- }
792
- setupEventHandlers() {
793
- if (!this.transport) return;
794
- this.registerHandler(
795
- SYSTEM_EVENTS.PLAYER_JOIN,
796
- (data) => {
797
- const playerIndex = data.player?.playerIndex ?? data.playerIndex;
798
- if (playerIndex !== void 0) {
799
- this.logger.debug("Player joined", { playerIndex });
800
- this.config.onControllerJoin?.(playerIndex, data.player);
801
- }
802
- }
803
- );
804
- this.registerHandler(
805
- SYSTEM_EVENTS.PLAYER_LEAVE,
806
- (data) => {
807
- const playerIndex = data.player?.playerIndex ?? data.playerIndex;
808
- if (playerIndex !== void 0) {
809
- this.logger.debug("Player left", { playerIndex });
810
- this.config.onControllerLeave?.(playerIndex);
811
- }
812
- }
813
- );
814
- if (this.config.listeners) {
815
- for (const [event, handler] of Object.entries(this.config.listeners)) {
816
- if (!handler) continue;
817
- this.registerHandler(event, (data) => {
818
- this.logReceive(event, data);
819
- handler(data);
820
- });
821
- }
822
- }
823
- }
824
- registerHandler(event, handler) {
825
- if (!this.transport) return;
826
- this.transport.on(event, handler);
827
- this.registeredHandlers.push({ event, handler });
828
- }
829
- // ---------------------------------------------------------------------------
830
- // Communication Methods
831
- // ---------------------------------------------------------------------------
832
- send(event, data) {
833
- this.ensureReady("send");
834
- validateEventName(event);
835
- this.logSend(event, data);
836
- this.transport.emit(event, data);
837
- }
838
- sendRaw(event, data) {
839
- this.ensureReady("sendRaw");
840
- validateEventName(event);
841
- this.logSend(event, data);
842
- this.transport.emit(event, data);
843
- }
844
- // ---------------------------------------------------------------------------
845
- // Event Subscription
846
- // ---------------------------------------------------------------------------
847
- on(event, handler) {
848
- validateEventName(event);
849
- let listeners = this.eventListeners.get(event);
850
- if (!listeners) {
851
- listeners = /* @__PURE__ */ new Set();
852
- this.eventListeners.set(event, listeners);
853
- }
854
- listeners.add(handler);
855
- const transportHandler = (data) => {
856
- this.logReceive(event, data);
857
- handler(data);
858
- };
859
- if (this.transport) {
860
- this.transport.on(event, transportHandler);
861
- this.registeredHandlers.push({ event, handler: transportHandler });
862
- }
863
- return () => {
864
- listeners?.delete(handler);
865
- if (listeners?.size === 0) {
866
- this.eventListeners.delete(event);
867
- }
868
- this.transport?.off(event, transportHandler);
869
- this.registeredHandlers = this.registeredHandlers.filter(
870
- (h) => h.handler !== transportHandler
871
- );
872
- };
873
- }
874
- once(event, handler) {
875
- const unsubscribe = this.on(event, ((data) => {
876
- unsubscribe();
877
- handler(data);
878
- }));
879
- return unsubscribe;
880
- }
881
- off(event, handler) {
882
- if (!handler) {
883
- this.eventListeners.delete(event);
884
- this.transport?.off(event);
885
- this.registeredHandlers = this.registeredHandlers.filter((h) => h.event !== event);
886
- } else {
887
- const listeners = this.eventListeners.get(event);
888
- listeners?.delete(handler);
889
- if (listeners?.size === 0) {
890
- this.eventListeners.delete(event);
891
- }
892
- }
893
- }
894
- // ---------------------------------------------------------------------------
895
- // Cleanup
896
- // ---------------------------------------------------------------------------
897
- destroy() {
898
- if (this._isDestroyed) return;
899
- this.logger.info("Destroying controller");
900
- this.cleanup();
901
- this._isDestroyed = true;
902
- }
903
- cleanup() {
904
- this._isReady = false;
905
- for (const { event, handler } of this.registeredHandlers) {
906
- this.transport?.off(event, handler);
907
- }
908
- this.registeredHandlers = [];
909
- this.eventListeners.clear();
910
- if (this.transport) {
911
- this.transport.destroy();
912
- this.transport = null;
913
- }
914
- if (this.boundMessageHandler) {
915
- window.removeEventListener("message", this.boundMessageHandler);
916
- this.boundMessageHandler = null;
917
- }
918
- }
919
- // ---------------------------------------------------------------------------
920
- // Private Helpers
921
- // ---------------------------------------------------------------------------
922
- ensureReady(method) {
923
- if (this._isDestroyed) {
924
- throw new SmoreSDKError(
925
- "DESTROYED",
926
- `Cannot call ${method}() after destroy()`,
927
- { details: { method } }
928
- );
929
- }
930
- if (!this._isReady || !this.transport) {
931
- throw new SmoreSDKError(
932
- "NOT_READY",
933
- `Cannot call ${method}() before controller is ready. Use await createController() or wait for onReady callback.`,
934
- { details: { method, isReady: this._isReady } }
935
- );
936
- }
937
- }
938
- handleError(error) {
939
- if (this.config.onError) {
940
- this.config.onError(error.toSmoreError());
941
- } else {
942
- this.logger.error(error.message, error.details);
943
- }
944
- }
945
- logSend(event, data) {
946
- const options = this.config.debug;
947
- const shouldLog = typeof options === "object" ? options.logSend ?? true : Boolean(options);
948
- if (shouldLog) {
949
- this.logger.debug(`\u2192 SEND [${event}]`, data);
950
- }
951
- }
952
- logReceive(event, data) {
953
- const options = this.config.debug;
954
- const shouldLog = typeof options === "object" ? options.logReceive ?? true : Boolean(options);
955
- if (shouldLog) {
956
- this.logger.debug(`\u2190 RECV [${event}]`, data);
957
- }
958
- }
959
- }
960
- function createController(config) {
961
- const controller = new ControllerImpl(config ?? {});
962
- const promise = controller.initialize().then(() => controller);
963
- promise.instance = controller;
964
- return promise;
965
- }
966
-
967
- class DirectTransport {
968
- constructor(socket) {
969
- this.socket = socket;
970
- }
971
- emit(event, ...args) {
972
- this.socket.emit(event, ...args);
973
- }
974
- on(event, handler) {
975
- this.socket.on(event, handler);
976
- }
977
- off(event, handler) {
978
- if (handler) {
979
- this.socket.off(event, handler);
980
- } else {
981
- this.socket.off(event);
982
- }
983
- }
984
- }
985
-
986
- const SMORE_EVENTS = {
987
- // 게임 lifecycle
988
- READY: "smore:ready",
989
- GAME_OVER: "smore:game-over",
990
- RETURN_TO_LOBBY: "smore:return-to-lobby",
991
- // 플레이어 관리
992
- PLAYER_JOIN: "smore:player-join",
993
- PLAYER_LEAVE: "smore:player-leave",
994
- // 특정 플레이어에게 전송 (내부용)
995
- SEND_TO_PLAYER: "smore:send-to-player",
996
- // 초기화
997
- INIT: "smore:init",
998
- UPDATE: "smore:update"
999
- };
1000
- function validateUserEvent(event) {
1001
- if (event.includes(":")) {
1002
- throw new Error(
1003
- `Invalid event name "${event}": User events cannot contain ':'. Use '_' or '-' instead. System events use 'smore:' prefix.`
1004
- );
1005
- }
1006
- }
1007
- function isSystemEvent(event) {
1008
- return event.startsWith("smore:");
1009
- }
1010
-
1011
- function createMockScreen(options = {}) {
1012
- const {
1013
- roomCode = "TEST",
1014
- controllers: initialControllers = [],
1015
- autoReady = true
1016
- } = options;
1017
- let _controllers = [...initialControllers];
1018
- let _isReady = false;
1019
- let _isDestroyed = false;
1020
- let _leaderIndex = initialControllers[0]?.playerIndex ?? -1;
1021
- const listeners = /* @__PURE__ */ new Map();
1022
- const broadcasts = [];
1023
- const sends = [];
1024
- const screen = {
1025
- // === Properties ===
1026
- get controllers() {
1027
- return [..._controllers];
1028
- },
1029
- get roomCode() {
1030
- return roomCode;
1031
- },
1032
- get leaderIndex() {
1033
- return _leaderIndex;
1034
- },
1035
- get isReady() {
1036
- return _isReady;
1037
- },
1038
- get isDestroyed() {
1039
- return _isDestroyed;
1040
- },
1041
- // === Communication Methods ===
1042
- broadcast(event, data) {
1043
- if (_isDestroyed) {
1044
- throw new Error("Cannot broadcast: screen is destroyed");
1045
- }
1046
- broadcasts.push({ event, data });
1047
- },
1048
- broadcastRaw(event, data) {
1049
- if (_isDestroyed) {
1050
- throw new Error("Cannot broadcast: screen is destroyed");
1051
- }
1052
- broadcasts.push({ event, data });
1053
- },
1054
- sendToController(playerIndex, event, data) {
1055
- if (_isDestroyed) {
1056
- throw new Error("Cannot send: screen is destroyed");
1057
- }
1058
- if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
1059
- throw new Error(`Invalid player index: ${playerIndex}`);
1060
- }
1061
- sends.push({ playerIndex, event, data });
1062
- },
1063
- sendToControllerRaw(playerIndex, event, data) {
1064
- if (_isDestroyed) {
1065
- throw new Error("Cannot send: screen is destroyed");
1066
- }
1067
- if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
1068
- throw new Error(`Invalid player index: ${playerIndex}`);
1069
- }
1070
- sends.push({ playerIndex, event, data });
1071
- },
1072
- // === Game Lifecycle ===
1073
- gameOver(results) {
1074
- screen.broadcastRaw("game-over", results);
1075
- },
1076
- // === Event Subscription ===
1077
- on(event, handler) {
1078
- const eventStr = event;
1079
- if (!listeners.has(eventStr)) {
1080
- listeners.set(eventStr, /* @__PURE__ */ new Set());
1081
- }
1082
- listeners.get(eventStr).add(handler);
1083
- return () => {
1084
- listeners.get(eventStr)?.delete(handler);
1085
- };
1086
- },
1087
- once(event, handler) {
1088
- const wrapper = (playerIndex, data) => {
1089
- handler(playerIndex, data);
1090
- screen.off(event, wrapper);
1091
- };
1092
- return screen.on(event, wrapper);
1093
- },
1094
- off(event, handler) {
1095
- const eventStr = event;
1096
- if (!handler) {
1097
- listeners.delete(eventStr);
1098
- } else {
1099
- listeners.get(eventStr)?.delete(handler);
1100
- }
1101
- },
1102
- // === Utilities ===
1103
- getController(playerIndex) {
1104
- return _controllers.find((c) => c.playerIndex === playerIndex);
1105
- },
1106
- isLeader(playerIndex) {
1107
- return playerIndex === _leaderIndex;
1108
- },
1109
- getControllerCount() {
1110
- return _controllers.length;
1111
- },
1112
- // === Cleanup ===
1113
- destroy() {
1114
- _isDestroyed = true;
1115
- listeners.clear();
1116
- broadcasts.length = 0;
1117
- sends.length = 0;
1118
- },
1119
- // === Mock-specific methods ===
1120
- simulateEvent(playerIndex, event, data) {
1121
- const eventStr = event;
1122
- const handlers = listeners.get(eventStr);
1123
- if (handlers) {
1124
- handlers.forEach((handler) => {
1125
- handler(playerIndex, data);
1126
- });
1127
- }
1128
- },
1129
- simulateControllerJoin(info) {
1130
- _controllers.push(info);
1131
- if (_leaderIndex === -1) {
1132
- _leaderIndex = info.playerIndex;
1133
- }
1134
- },
1135
- simulateControllerLeave(playerIndex) {
1136
- _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);
1137
- if (_leaderIndex === playerIndex) {
1138
- _leaderIndex = _controllers[0]?.playerIndex ?? -1;
1139
- }
1140
- },
1141
- getBroadcasts() {
1142
- return [...broadcasts];
1143
- },
1144
- getSentToController(playerIndex) {
1145
- return sends.filter((s) => s.playerIndex === playerIndex).map((s) => ({ event: s.event, data: s.data }));
1146
- },
1147
- clearRecordedEvents() {
1148
- broadcasts.length = 0;
1149
- sends.length = 0;
1150
- },
1151
- triggerReady() {
1152
- _isReady = true;
1153
- }
1154
- };
1155
- if (autoReady) {
1156
- setTimeout(() => screen.triggerReady(), 0);
1157
- }
1158
- return screen;
1159
- }
1160
- function createMockController(options = {}) {
1161
- const {
1162
- roomCode = "TEST",
1163
- myIndex = 0,
1164
- isLeader: initialIsLeader = false,
1165
- autoReady = true
1166
- } = options;
1167
- let _isReady = false;
1168
- let _isDestroyed = false;
1169
- let _isLeader = initialIsLeader;
1170
- const listeners = /* @__PURE__ */ new Map();
1171
- const sentEvents = [];
1172
- const controller = {
1173
- // === Properties ===
1174
- get myIndex() {
1175
- return myIndex;
1176
- },
1177
- get isLeader() {
1178
- return _isLeader;
1179
- },
1180
- get roomCode() {
1181
- return roomCode;
1182
- },
1183
- get isReady() {
1184
- return _isReady;
1185
- },
1186
- get isDestroyed() {
1187
- return _isDestroyed;
1188
- },
1189
- // === Communication Methods ===
1190
- send(event, data) {
1191
- if (_isDestroyed) {
1192
- throw new Error("Cannot send: controller is destroyed");
1193
- }
1194
- sentEvents.push({ event, data });
1195
- },
1196
- sendRaw(event, data) {
1197
- if (_isDestroyed) {
1198
- throw new Error("Cannot send: controller is destroyed");
1199
- }
1200
- sentEvents.push({ event, data });
1201
- },
1202
- // === Event Subscription ===
1203
- on(event, handler) {
1204
- const eventStr = event;
1205
- if (!listeners.has(eventStr)) {
1206
- listeners.set(eventStr, /* @__PURE__ */ new Set());
1207
- }
1208
- listeners.get(eventStr).add(handler);
1209
- return () => {
1210
- listeners.get(eventStr)?.delete(handler);
1211
- };
1212
- },
1213
- once(event, handler) {
1214
- const wrapper = (data) => {
1215
- handler(data);
1216
- controller.off(event, wrapper);
1217
- };
1218
- return controller.on(event, wrapper);
1219
- },
1220
- off(event, handler) {
1221
- const eventStr = event;
1222
- if (!handler) {
1223
- listeners.delete(eventStr);
1224
- } else {
1225
- listeners.get(eventStr)?.delete(handler);
1226
- }
1227
- },
1228
- // === Cleanup ===
1229
- destroy() {
1230
- _isDestroyed = true;
1231
- listeners.clear();
1232
- sentEvents.length = 0;
1233
- },
1234
- // === Mock-specific methods ===
1235
- simulateEvent(event, data) {
1236
- const eventStr = event;
1237
- const handlers = listeners.get(eventStr);
1238
- if (handlers) {
1239
- handlers.forEach((handler) => {
1240
- handler(data);
1241
- });
1242
- }
1243
- },
1244
- getSentEvents() {
1245
- return [...sentEvents];
1246
- },
1247
- clearRecordedEvents() {
1248
- sentEvents.length = 0;
1249
- },
1250
- triggerReady() {
1251
- _isReady = true;
1252
- },
1253
- setLeader(isLeader) {
1254
- _isLeader = isLeader;
1255
- }
1256
- };
1257
- if (autoReady) {
1258
- setTimeout(() => controller.triggerReady(), 0);
1259
- }
1260
- return controller;
1261
- }
1262
-
1263
- exports.DirectTransport = DirectTransport;
1264
- exports.PostMessageTransport = PostMessageTransport;
1265
- exports.SMORE_EVENTS = SMORE_EVENTS;
1266
- exports.SmoreSDKError = SmoreSDKError$1;
1267
- exports.createController = createController;
1268
- exports.createMockController = createMockController;
1269
- exports.createMockScreen = createMockScreen;
1270
- exports.createScreen = createScreen;
1271
- exports.isSystemEvent = isSystemEvent;
1272
- exports.validateUserEvent = validateUserEvent;
1273
-
1274
- }));
1275
- //# sourceMappingURL=smore-sdk-vanilla.umd.js.map