@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
@@ -6,39 +6,25 @@ function createMockScreen(options = {}) {
6
6
  const {
7
7
  roomCode = "TEST",
8
8
  controllers: initialControllers = [],
9
- autoReady = true,
10
- onReady: onReadyCb,
11
- onControllerJoin: onJoinCb,
12
- onControllerLeave: onLeaveCb,
13
- onControllerDisconnect: onDisconnectCb,
14
- onControllerReconnect: onReconnectCb,
15
- onCharacterUpdated: onCharacterUpdatedCb,
16
- onRateLimited: onRateLimitedCb,
17
- onAllReady: onAllReadyCb,
18
- onError: onErrorCb
9
+ autoReady = true
19
10
  } = options;
20
11
  let _controllers = [...initialControllers];
21
12
  let _isReady = false;
22
13
  let _isDestroyed = false;
23
14
  const listeners = /* @__PURE__ */ new Map();
24
- let onReadyCallback;
25
- let onControllerJoinCallback;
26
- let onControllerLeaveCallback;
27
- let onControllerDisconnectCallback;
28
- let onControllerReconnectCallback;
29
- let onCharacterUpdatedCallback;
30
- let onRateLimitedCallback;
31
- let onAllReadyCallback;
32
- let onErrorCallback;
33
- onReadyCallback = onReadyCb;
34
- onControllerJoinCallback = onJoinCb;
35
- onControllerLeaveCallback = onLeaveCb;
36
- onControllerDisconnectCallback = onDisconnectCb;
37
- onControllerReconnectCallback = onReconnectCb;
38
- onCharacterUpdatedCallback = onCharacterUpdatedCb;
39
- onRateLimitedCallback = onRateLimitedCb;
40
- onAllReadyCallback = onAllReadyCb;
41
- onErrorCallback = onErrorCb;
15
+ const _onAllReadyCallbacks = /* @__PURE__ */ new Set();
16
+ const _onControllerJoinCallbacks = /* @__PURE__ */ new Set();
17
+ const _onControllerLeaveCallbacks = /* @__PURE__ */ new Set();
18
+ const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
19
+ const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
20
+ const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
21
+ const _onRateLimitedCallbacks = /* @__PURE__ */ new Set();
22
+ const _onErrorCallbacks = /* @__PURE__ */ new Set();
23
+ let _allReadyFired = false;
24
+ let _readyResolve;
25
+ const _readyPromise = new Promise((resolve) => {
26
+ _readyResolve = resolve;
27
+ });
42
28
  const broadcasts = [];
43
29
  const sends = [];
44
30
  const screen = {
@@ -55,6 +41,61 @@ function createMockScreen(options = {}) {
55
41
  get isDestroyed() {
56
42
  return _isDestroyed;
57
43
  },
44
+ get ready() {
45
+ return _readyPromise;
46
+ },
47
+ // === Lifecycle Methods ===
48
+ onAllReady(callback) {
49
+ if (_allReadyFired) {
50
+ callback();
51
+ }
52
+ _onAllReadyCallbacks.add(callback);
53
+ return () => {
54
+ _onAllReadyCallbacks.delete(callback);
55
+ };
56
+ },
57
+ onControllerJoin(callback) {
58
+ _onControllerJoinCallbacks.add(callback);
59
+ return () => {
60
+ _onControllerJoinCallbacks.delete(callback);
61
+ };
62
+ },
63
+ onControllerLeave(callback) {
64
+ _onControllerLeaveCallbacks.add(callback);
65
+ return () => {
66
+ _onControllerLeaveCallbacks.delete(callback);
67
+ };
68
+ },
69
+ onControllerDisconnect(callback) {
70
+ _onControllerDisconnectCallbacks.add(callback);
71
+ return () => {
72
+ _onControllerDisconnectCallbacks.delete(callback);
73
+ };
74
+ },
75
+ onControllerReconnect(callback) {
76
+ _onControllerReconnectCallbacks.add(callback);
77
+ return () => {
78
+ _onControllerReconnectCallbacks.delete(callback);
79
+ };
80
+ },
81
+ onCharacterUpdated(callback) {
82
+ _onCharacterUpdatedCallbacks.add(callback);
83
+ return () => {
84
+ _onCharacterUpdatedCallbacks.delete(callback);
85
+ };
86
+ },
87
+ onRateLimited(callback) {
88
+ _onRateLimitedCallbacks.add(callback);
89
+ return () => {
90
+ _onRateLimitedCallbacks.delete(callback);
91
+ };
92
+ },
93
+ onError(callback) {
94
+ _onErrorCallbacks.add(callback);
95
+ return () => {
96
+ _onErrorCallbacks.delete(callback);
97
+ };
98
+ },
58
99
  // === Communication Methods ===
59
100
  broadcast(event, data) {
60
101
  if (_isDestroyed) {
@@ -182,24 +223,14 @@ function createMockScreen(options = {}) {
182
223
  },
183
224
  simulateControllerJoin(info) {
184
225
  _controllers.push(info);
185
- if (onControllerJoinCallback) {
186
- onControllerJoinCallback(info.playerIndex, info);
187
- }
226
+ _onControllerJoinCallbacks.forEach((cb) => cb(info.playerIndex, info));
188
227
  },
189
228
  simulateControllerLeave(playerIndex) {
190
229
  _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);
191
- if (onControllerLeaveCallback) {
192
- onControllerLeaveCallback(playerIndex);
193
- }
230
+ _onControllerLeaveCallbacks.forEach((cb) => cb(playerIndex));
194
231
  },
195
232
  /**
196
233
  * Simulate a controller network disconnect (player still in room but unreachable).
197
- *
198
- * @example
199
- * ```ts
200
- * screen.simulateControllerDisconnect(0);
201
- * expect(screen.getController(0)?.connected).toBe(false);
202
- * ```
203
234
  */
204
235
  simulateControllerDisconnect(playerIndex) {
205
236
  const controller = _controllers.find((c) => c.playerIndex === playerIndex);
@@ -209,19 +240,10 @@ function createMockScreen(options = {}) {
209
240
  _controllers = _controllers.map(
210
241
  (c) => c.playerIndex === playerIndex ? { ...c, connected: false } : c
211
242
  );
212
- if (onControllerDisconnectCallback) {
213
- onControllerDisconnectCallback(playerIndex);
214
- }
243
+ _onControllerDisconnectCallbacks.forEach((cb) => cb(playerIndex));
215
244
  },
216
245
  /**
217
246
  * Simulate a controller network reconnect after disconnect.
218
- *
219
- * @example
220
- * ```ts
221
- * screen.simulateControllerDisconnect(0);
222
- * screen.simulateControllerReconnect(0);
223
- * expect(screen.getController(0)?.connected).toBe(true);
224
- * ```
225
247
  */
226
248
  simulateControllerReconnect(playerIndex) {
227
249
  const controller = _controllers.find((c) => c.playerIndex === playerIndex);
@@ -232,9 +254,7 @@ function createMockScreen(options = {}) {
232
254
  _controllers = _controllers.map(
233
255
  (c) => c.playerIndex === playerIndex ? reconnectedController : c
234
256
  );
235
- if (onControllerReconnectCallback) {
236
- onControllerReconnectCallback(playerIndex, reconnectedController);
237
- }
257
+ _onControllerReconnectCallbacks.forEach((cb) => cb(playerIndex, reconnectedController));
238
258
  },
239
259
  simulateCharacterUpdate(playerIndex, appearance) {
240
260
  const controller = _controllers.find((c) => c.playerIndex === playerIndex);
@@ -244,24 +264,17 @@ function createMockScreen(options = {}) {
244
264
  _controllers = _controllers.map(
245
265
  (c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
246
266
  );
247
- if (onCharacterUpdatedCallback) {
248
- onCharacterUpdatedCallback(playerIndex, appearance);
249
- }
267
+ _onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
250
268
  },
251
269
  simulateRateLimited(event) {
252
- if (onRateLimitedCallback) {
253
- onRateLimitedCallback(event);
254
- }
270
+ _onRateLimitedCallbacks.forEach((cb) => cb(event));
255
271
  },
256
272
  simulateAllReady() {
257
- if (onAllReadyCallback) {
258
- onAllReadyCallback();
259
- }
273
+ _allReadyFired = true;
274
+ _onAllReadyCallbacks.forEach((cb) => cb());
260
275
  },
261
276
  simulateError(error) {
262
- if (onErrorCallback) {
263
- onErrorCallback(error);
264
- }
277
+ _onErrorCallbacks.forEach((cb) => cb(error));
265
278
  },
266
279
  getBroadcasts() {
267
280
  return [...broadcasts];
@@ -275,9 +288,7 @@ function createMockScreen(options = {}) {
275
288
  },
276
289
  triggerReady() {
277
290
  _isReady = true;
278
- if (onReadyCallback) {
279
- onReadyCallback();
280
- }
291
+ _readyResolve();
281
292
  }
282
293
  };
283
294
  if (autoReady) {
@@ -289,39 +300,25 @@ function createMockController(options = {}) {
289
300
  const {
290
301
  roomCode = "TEST",
291
302
  myIndex = 0,
292
- autoReady = true,
293
- onReady: onReadyCb,
294
- onControllerJoin: onJoinCb,
295
- onControllerLeave: onLeaveCb,
296
- onControllerDisconnect: onDisconnectCb,
297
- onControllerReconnect: onReconnectCb,
298
- onCharacterUpdated: onCharacterUpdatedCb,
299
- onRateLimited: onRateLimitedCb,
300
- onAllReady: onAllReadyCb,
301
- onError: onErrorCb
303
+ autoReady = true
302
304
  } = options;
303
305
  let _isReady = false;
304
306
  let _isDestroyed = false;
305
307
  let _controllers = options.controllers ?? [];
306
308
  const listeners = /* @__PURE__ */ new Map();
307
- let onReadyCallback;
308
- let onControllerJoinCallback;
309
- let onControllerLeaveCallback;
310
- let onControllerDisconnectCallback;
311
- let onControllerReconnectCallback;
312
- let onCharacterUpdatedCallback;
313
- let onRateLimitedCallback;
314
- let onAllReadyCallback;
315
- let onErrorCallback;
316
- onReadyCallback = onReadyCb;
317
- onControllerJoinCallback = onJoinCb;
318
- onControllerLeaveCallback = onLeaveCb;
319
- onControllerDisconnectCallback = onDisconnectCb;
320
- onControllerReconnectCallback = onReconnectCb;
321
- onCharacterUpdatedCallback = onCharacterUpdatedCb;
322
- onRateLimitedCallback = onRateLimitedCb;
323
- onAllReadyCallback = onAllReadyCb;
324
- onErrorCallback = onErrorCb;
309
+ const _onAllReadyCallbacks = /* @__PURE__ */ new Set();
310
+ const _onControllerJoinCallbacks = /* @__PURE__ */ new Set();
311
+ const _onControllerLeaveCallbacks = /* @__PURE__ */ new Set();
312
+ const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
313
+ const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
314
+ const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
315
+ const _onRateLimitedCallbacks = /* @__PURE__ */ new Set();
316
+ const _onErrorCallbacks = /* @__PURE__ */ new Set();
317
+ let _allReadyFired = false;
318
+ let _readyResolve;
319
+ const _readyPromise = new Promise((resolve) => {
320
+ _readyResolve = resolve;
321
+ });
325
322
  const sentEvents = [];
326
323
  const controller = {
327
324
  // === Properties ===
@@ -340,6 +337,61 @@ function createMockController(options = {}) {
340
337
  get controllers() {
341
338
  return [..._controllers];
342
339
  },
340
+ get ready() {
341
+ return _readyPromise;
342
+ },
343
+ // === Lifecycle Methods ===
344
+ onAllReady(callback) {
345
+ if (_allReadyFired) {
346
+ callback();
347
+ }
348
+ _onAllReadyCallbacks.add(callback);
349
+ return () => {
350
+ _onAllReadyCallbacks.delete(callback);
351
+ };
352
+ },
353
+ onControllerJoin(callback) {
354
+ _onControllerJoinCallbacks.add(callback);
355
+ return () => {
356
+ _onControllerJoinCallbacks.delete(callback);
357
+ };
358
+ },
359
+ onControllerLeave(callback) {
360
+ _onControllerLeaveCallbacks.add(callback);
361
+ return () => {
362
+ _onControllerLeaveCallbacks.delete(callback);
363
+ };
364
+ },
365
+ onControllerDisconnect(callback) {
366
+ _onControllerDisconnectCallbacks.add(callback);
367
+ return () => {
368
+ _onControllerDisconnectCallbacks.delete(callback);
369
+ };
370
+ },
371
+ onControllerReconnect(callback) {
372
+ _onControllerReconnectCallbacks.add(callback);
373
+ return () => {
374
+ _onControllerReconnectCallbacks.delete(callback);
375
+ };
376
+ },
377
+ onCharacterUpdated(callback) {
378
+ _onCharacterUpdatedCallbacks.add(callback);
379
+ return () => {
380
+ _onCharacterUpdatedCallbacks.delete(callback);
381
+ };
382
+ },
383
+ onRateLimited(callback) {
384
+ _onRateLimitedCallbacks.add(callback);
385
+ return () => {
386
+ _onRateLimitedCallbacks.delete(callback);
387
+ };
388
+ },
389
+ onError(callback) {
390
+ _onErrorCallbacks.add(callback);
391
+ return () => {
392
+ _onErrorCallbacks.delete(callback);
393
+ };
394
+ },
343
395
  getControllerCount() {
344
396
  return _controllers.filter((c) => c.connected).length;
345
397
  },
@@ -420,105 +472,62 @@ function createMockController(options = {}) {
420
472
  },
421
473
  triggerReady() {
422
474
  _isReady = true;
423
- if (onReadyCallback) {
424
- onReadyCallback();
425
- }
475
+ _readyResolve();
426
476
  },
427
477
  /**
428
478
  * Simulate a new player joining the room.
429
479
  * Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.
430
- *
431
- * @example
432
- * ```ts
433
- * controller.simulatePlayerJoin(2, { playerIndex: 2, nickname: 'Alice', connected: true });
434
- * expect(controller.getControllerCount()).toBe(3);
435
- * expect(controller.controllers.find(c => c.playerIndex === 2)?.nickname).toBe('Alice');
436
- * ```
437
480
  */
438
481
  simulatePlayerJoin(playerIndex, info) {
439
482
  if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
440
483
  _controllers = [..._controllers, { ...info, connected: info.connected ?? true }];
441
484
  }
442
- if (onControllerJoinCallback) {
443
- onControllerJoinCallback(playerIndex, info);
444
- }
485
+ _onControllerJoinCallbacks.forEach((cb) => cb(playerIndex, info));
445
486
  },
446
487
  /**
447
488
  * Simulate a player leaving the room (fully removed).
448
- *
449
- * @example
450
- * ```ts
451
- * controller.simulatePlayerLeave(1);
452
- * expect(controller.controllers.some(c => c.playerIndex === 1)).toBe(false);
453
- * ```
454
489
  */
455
490
  simulatePlayerLeave(playerIndex) {
456
491
  _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);
457
- if (onControllerLeaveCallback) {
458
- onControllerLeaveCallback(playerIndex);
459
- }
492
+ _onControllerLeaveCallbacks.forEach((cb) => cb(playerIndex));
460
493
  },
461
494
  /**
462
495
  * Simulate a player network disconnect (player still in room but unreachable).
463
- *
464
- * @example
465
- * ```ts
466
- * controller.simulatePlayerDisconnect(1);
467
- * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(false);
468
- * ```
469
496
  */
470
497
  simulatePlayerDisconnect(playerIndex) {
471
498
  _controllers = _controllers.map(
472
499
  (c) => c.playerIndex === playerIndex ? { ...c, connected: false } : c
473
500
  );
474
- if (onControllerDisconnectCallback) {
475
- onControllerDisconnectCallback(playerIndex);
476
- }
501
+ _onControllerDisconnectCallbacks.forEach((cb) => cb(playerIndex));
477
502
  },
478
503
  /**
479
504
  * Simulate a player network reconnect after disconnect.
480
- *
481
- * @example
482
- * ```ts
483
- * controller.simulatePlayerDisconnect(1);
484
- * controller.simulatePlayerReconnect(1, { playerIndex: 1, nickname: 'Bob', connected: true });
485
- * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(true);
486
- * ```
487
505
  */
488
506
  simulatePlayerReconnect(playerIndex, info) {
489
507
  _controllers = _controllers.map(
490
508
  (c) => c.playerIndex === playerIndex ? { ...info, connected: true } : c
491
509
  );
492
- if (onControllerReconnectCallback) {
493
- onControllerReconnectCallback(playerIndex, info);
494
- }
510
+ _onControllerReconnectCallbacks.forEach((cb) => cb(playerIndex, info));
495
511
  },
496
512
  simulateCharacterUpdate(playerIndex, appearance) {
497
- const controller2 = _controllers.find((c) => c.playerIndex === playerIndex);
498
- if (!controller2) {
513
+ const ctrl = _controllers.find((c) => c.playerIndex === playerIndex);
514
+ if (!ctrl) {
499
515
  throw new Error(`Controller ${playerIndex} not found`);
500
516
  }
501
517
  _controllers = _controllers.map(
502
518
  (c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
503
519
  );
504
- if (onCharacterUpdatedCallback) {
505
- onCharacterUpdatedCallback(playerIndex, appearance);
506
- }
520
+ _onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
507
521
  },
508
522
  simulateRateLimited(event) {
509
- if (onRateLimitedCallback) {
510
- onRateLimitedCallback(event);
511
- }
523
+ _onRateLimitedCallbacks.forEach((cb) => cb(event));
512
524
  },
513
525
  simulateAllReady() {
514
- if (onAllReadyCallback) {
515
- onAllReadyCallback();
516
- }
526
+ _allReadyFired = true;
527
+ _onAllReadyCallbacks.forEach((cb) => cb());
517
528
  },
518
529
  simulateError(error) {
519
- if (onErrorCallback) {
520
- onErrorCallback(error);
521
- }
530
+ _onErrorCallbacks.forEach((cb) => cb(error));
522
531
  }
523
532
  };
524
533
  if (autoReady) {
@@ -1 +1 @@
1
- {"version":3,"file":"testing.cjs","sources":["../../src/testing.ts"],"sourcesContent":["/**\n * @smoregg/sdk - Testing Utilities\n *\n * Mock implementations of Screen and Controller for unit testing.\n * All methods work synchronously for predictable test execution.\n *\n * @packageDocumentation\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n CharacterAppearance,\n ControllerInfo,\n PlayerIndex,\n GameResults,\n ScreenEventHandler,\n ControllerEventHandler,\n MockScreen,\n MockController,\n MockOptions,\n} from './types';\nimport { validateEventName } from './events';\n\n// =============================================================================\n// MOCK SCREEN IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedBroadcast {\n event: string;\n data: unknown;\n}\n\ninterface RecordedSend {\n playerIndex: PlayerIndex;\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Screen for testing game logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * @example\n * ```ts\n * const screen = createMockScreen<MyEvents>({\n * controllers: [\n * { playerIndex: 0, nickname: 'Player 1', connected: true },\n * { playerIndex: 1, nickname: 'Player 2', connected: true },\n * ],\n * });\n *\n * // Simulate player input\n * screen.simulateEvent(0, 'tap', { x: 100, y: 200 });\n *\n * // Check what was broadcast\n * expect(screen.getBroadcasts()).toContainEqual({\n * event: 'score-update',\n * data: { scores: { 0: 10 } },\n * });\n * ```\n */\nexport function createMockScreen<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockScreen<TEvents> {\n const {\n roomCode = 'TEST',\n controllers: initialControllers = [],\n autoReady = true,\n onReady: onReadyCb,\n onControllerJoin: onJoinCb,\n onControllerLeave: onLeaveCb,\n onControllerDisconnect: onDisconnectCb,\n onControllerReconnect: onReconnectCb,\n onCharacterUpdated: onCharacterUpdatedCb,\n onRateLimited: onRateLimitedCb,\n onAllReady: onAllReadyCb,\n onError: onErrorCb,\n } = options;\n\n // Internal state\n let _controllers: ControllerInfo[] = [...initialControllers];\n let _isReady = false;\n let _isDestroyed = false;\n\n // Event listeners\n const listeners = new Map<string, Set<ScreenEventHandler>>();\n\n // Lifecycle callbacks\n let onReadyCallback: (() => void) | undefined;\n let onControllerJoinCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onControllerLeaveCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerDisconnectCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerReconnectCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onCharacterUpdatedCallback:\n | ((index: PlayerIndex, appearance: CharacterAppearance | null) => void)\n | undefined;\n let onRateLimitedCallback: ((event: string) => void) | undefined;\n let onAllReadyCallback: (() => void) | undefined;\n let onErrorCallback: ((error: any) => void) | undefined;\n\n // Assign callbacks from options\n onReadyCallback = onReadyCb;\n onControllerJoinCallback = onJoinCb;\n onControllerLeaveCallback = onLeaveCb;\n onControllerDisconnectCallback = onDisconnectCb;\n onControllerReconnectCallback = onReconnectCb;\n onCharacterUpdatedCallback = onCharacterUpdatedCb;\n onRateLimitedCallback = onRateLimitedCb;\n onAllReadyCallback = onAllReadyCb;\n onErrorCallback = onErrorCb;\n\n // Recorded events for testing\n const broadcasts: RecordedBroadcast[] = [];\n const sends: RecordedSend[] = [];\n\n // Screen implementation\n const screen: MockScreen<TEvents> = {\n // === Properties ===\n get controllers() {\n return [..._controllers];\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n // === Communication Methods ===\n broadcast<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event as string);\n broadcasts.push({ event: event as string, data });\n },\n\n broadcastRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event);\n broadcasts.push({ event, data });\n },\n\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event as string);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event: event as string, data });\n },\n\n sendToControllerRaw(\n playerIndex: PlayerIndex,\n event: string,\n data?: unknown,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event, data });\n },\n\n // === Game Lifecycle ===\n gameOver(results?: GameResults): void {\n if (_isDestroyed) {\n throw new Error('Cannot call gameOver: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call gameOver: screen is not ready');\n }\n broadcasts.push({ event: 'smore:game-over', data: { results } });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: screen is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ScreenEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n handler(playerIndex, data);\n screen.off(event, wrapper);\n };\n return screen.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n }\n },\n\n // === Utilities ===\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n hasAnyConnectedControllers(): boolean {\n return _controllers.some(c => c.connected);\n },\n\n // === Cleanup ===\n /**\n * Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.\n */\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(playerIndex, data);\n });\n }\n },\n\n simulateControllerJoin(info: ControllerInfo): void {\n _controllers.push(info);\n if (onControllerJoinCallback) {\n onControllerJoinCallback(info.playerIndex, info);\n }\n },\n\n simulateControllerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);\n if (onControllerLeaveCallback) {\n onControllerLeaveCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a controller network disconnect (player still in room but unreachable).\n *\n * @example\n * ```ts\n * screen.simulateControllerDisconnect(0);\n * expect(screen.getController(0)?.connected).toBe(false);\n * ```\n */\n simulateControllerDisconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as disconnected (need to create new object since ControllerInfo is readonly)\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex\n ? { ...c, connected: false }\n : c\n );\n if (onControllerDisconnectCallback) {\n onControllerDisconnectCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a controller network reconnect after disconnect.\n *\n * @example\n * ```ts\n * screen.simulateControllerDisconnect(0);\n * screen.simulateControllerReconnect(0);\n * expect(screen.getController(0)?.connected).toBe(true);\n * ```\n */\n simulateControllerReconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as connected (need to create new object since ControllerInfo is readonly)\n const reconnectedController = { ...controller, connected: true };\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? reconnectedController : c\n );\n if (onControllerReconnectCallback) {\n onControllerReconnectCallback(playerIndex, reconnectedController);\n }\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n if (onCharacterUpdatedCallback) {\n onCharacterUpdatedCallback(playerIndex, appearance);\n }\n },\n\n simulateRateLimited(event: string): void {\n if (onRateLimitedCallback) {\n onRateLimitedCallback(event);\n }\n },\n\n simulateAllReady(): void {\n if (onAllReadyCallback) {\n onAllReadyCallback();\n }\n },\n\n simulateError(error: any): void {\n if (onErrorCallback) {\n onErrorCallback(error);\n }\n },\n\n getBroadcasts(): Array<{ event: string; data: unknown }> {\n return [...broadcasts];\n },\n\n getSentToController(\n playerIndex: PlayerIndex,\n ): Array<{ event: string; data: unknown }> {\n return sends\n .filter((s) => s.playerIndex === playerIndex)\n .map((s) => ({ event: s.event, data: s.data }));\n },\n\n clearRecordedEvents(): void {\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n if (onReadyCallback) {\n onReadyCallback();\n }\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => screen.triggerReady(), 0);\n }\n\n return screen;\n}\n\n// =============================================================================\n// MOCK CONTROLLER IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedEvent {\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Controller for testing player input logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * @example\n * ```ts\n * const controller = createMockController<MyEvents>({\n * myIndex: 0,\n * });\n *\n * // Simulate receiving from screen\n * controller.simulateEvent('your-turn', { timeLimit: 30 });\n *\n * // Check what was sent\n * expect(controller.getSentEvents()).toContainEqual({\n * event: 'answer',\n * data: { choice: 2 },\n * });\n * ```\n */\nexport function createMockController<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockController<TEvents> {\n const {\n roomCode = 'TEST',\n myIndex = 0,\n autoReady = true,\n onReady: onReadyCb,\n onControllerJoin: onJoinCb,\n onControllerLeave: onLeaveCb,\n onControllerDisconnect: onDisconnectCb,\n onControllerReconnect: onReconnectCb,\n onCharacterUpdated: onCharacterUpdatedCb,\n onRateLimited: onRateLimitedCb,\n onAllReady: onAllReadyCb,\n onError: onErrorCb,\n } = options;\n\n // Internal state -- uses a full ControllerInfo[] array (matching MockScreen pattern)\n // to preserve nickname/appearance data from simulatePlayerJoin()\n let _isReady = false;\n let _isDestroyed = false;\n let _controllers: ControllerInfo[] = options.controllers ?? [];\n\n // Event listeners\n const listeners = new Map<string, Set<ControllerEventHandler>>();\n\n // Lifecycle callbacks\n let onReadyCallback: (() => void) | undefined;\n let onControllerJoinCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onControllerLeaveCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerDisconnectCallback: ((index: PlayerIndex) => void) | undefined;\n let onControllerReconnectCallback:\n | ((index: PlayerIndex, info: ControllerInfo) => void)\n | undefined;\n let onCharacterUpdatedCallback:\n | ((index: PlayerIndex, appearance: CharacterAppearance | null) => void)\n | undefined;\n let onRateLimitedCallback: ((event: string) => void) | undefined;\n let onAllReadyCallback: (() => void) | undefined;\n let onErrorCallback: ((error: any) => void) | undefined;\n\n // Assign callbacks from options\n onReadyCallback = onReadyCb;\n onControllerJoinCallback = onJoinCb;\n onControllerLeaveCallback = onLeaveCb;\n onControllerDisconnectCallback = onDisconnectCb;\n onControllerReconnectCallback = onReconnectCb;\n onCharacterUpdatedCallback = onCharacterUpdatedCb;\n onRateLimitedCallback = onRateLimitedCb;\n onAllReadyCallback = onAllReadyCb;\n onErrorCallback = onErrorCb;\n\n // Recorded events for testing\n const sentEvents: RecordedEvent[] = [];\n\n // Controller implementation\n const controller: MockController<TEvents> = {\n // === Properties ===\n get myIndex() {\n return myIndex;\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n get controllers(): readonly ControllerInfo[] {\n return [..._controllers];\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n // === Communication Methods ===\n send<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event as string);\n sentEvents.push({ event: event as string, data });\n },\n\n sendRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event);\n sentEvents.push({ event, data });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: controller is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: controller is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ControllerEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ControllerEventHandler<EventData<TEvents, K>> = (data) => {\n handler(data);\n controller.off(event, wrapper);\n };\n return controller.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n }\n },\n\n // === Cleanup ===\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n sentEvents.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(data);\n });\n }\n },\n\n getSentEvents(): Array<{ event: string; data: unknown }> {\n return [...sentEvents];\n },\n\n clearRecordedEvents(): void {\n sentEvents.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n if (onReadyCallback) {\n onReadyCallback();\n }\n },\n\n /**\n * Simulate a new player joining the room.\n * Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.\n *\n * @example\n * ```ts\n * controller.simulatePlayerJoin(2, { playerIndex: 2, nickname: 'Alice', connected: true });\n * expect(controller.getControllerCount()).toBe(3);\n * expect(controller.controllers.find(c => c.playerIndex === 2)?.nickname).toBe('Alice');\n * ```\n */\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void {\n if (!_controllers.some(c => c.playerIndex === playerIndex)) {\n _controllers = [..._controllers, { ...info, connected: info.connected ?? true }];\n }\n if (onControllerJoinCallback) {\n onControllerJoinCallback(playerIndex, info);\n }\n },\n\n /**\n * Simulate a player leaving the room (fully removed).\n *\n * @example\n * ```ts\n * controller.simulatePlayerLeave(1);\n * expect(controller.controllers.some(c => c.playerIndex === 1)).toBe(false);\n * ```\n */\n simulatePlayerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter(c => c.playerIndex !== playerIndex);\n if (onControllerLeaveCallback) {\n onControllerLeaveCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a player network disconnect (player still in room but unreachable).\n *\n * @example\n * ```ts\n * controller.simulatePlayerDisconnect(1);\n * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(false);\n * ```\n */\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n if (onControllerDisconnectCallback) {\n onControllerDisconnectCallback(playerIndex);\n }\n },\n\n /**\n * Simulate a player network reconnect after disconnect.\n *\n * @example\n * ```ts\n * controller.simulatePlayerDisconnect(1);\n * controller.simulatePlayerReconnect(1, { playerIndex: 1, nickname: 'Bob', connected: true });\n * expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(true);\n * ```\n */\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...info, connected: true } : c\n );\n if (onControllerReconnectCallback) {\n onControllerReconnectCallback(playerIndex, info);\n }\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n if (onCharacterUpdatedCallback) {\n onCharacterUpdatedCallback(playerIndex, appearance);\n }\n },\n\n simulateRateLimited(event: string): void {\n if (onRateLimitedCallback) {\n onRateLimitedCallback(event);\n }\n },\n\n simulateAllReady(): void {\n if (onAllReadyCallback) {\n onAllReadyCallback();\n }\n },\n\n simulateError(error: any): void {\n if (onErrorCallback) {\n onErrorCallback(error);\n }\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => controller.triggerReady(), 0);\n }\n\n return controller;\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport type { MockScreen, MockController, MockOptions };\n"],"names":["validateEventName","controller"],"mappings":"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"}
1
+ {"version":3,"file":"testing.cjs","sources":["../../src/testing.ts"],"sourcesContent":["/**\n * @smoregg/sdk - Testing Utilities\n *\n * Mock implementations of Screen and Controller for unit testing.\n * All methods work synchronously for predictable test execution.\n *\n * Mocks use the same event emitter pattern as the real implementations:\n * lifecycle callbacks are registered via methods (onAllReady, onControllerJoin, etc.)\n * on the returned instance rather than via config options.\n *\n * @packageDocumentation\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n CharacterAppearance,\n ControllerInfo,\n PlayerIndex,\n GameResults,\n ScreenEventHandler,\n ControllerEventHandler,\n MockScreen,\n MockController,\n MockOptions,\n SmoreError,\n} from './types';\nimport { validateEventName } from './events';\n\n// =============================================================================\n// MOCK SCREEN IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedBroadcast {\n event: string;\n data: unknown;\n}\n\ninterface RecordedSend {\n playerIndex: PlayerIndex;\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Screen for testing game logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * Uses the same event emitter pattern as real Screen: register lifecycle\n * callbacks via methods on the returned instance.\n *\n * @example\n * ```ts\n * const screen = createMockScreen<MyEvents>({\n * controllers: [\n * { playerIndex: 0, nickname: 'Player 1', connected: true },\n * { playerIndex: 1, nickname: 'Player 2', connected: true },\n * ],\n * });\n *\n * screen.on('tap', (pi, data) => handleTap(pi, data));\n * screen.onAllReady(() => startGame());\n *\n * // Simulate player input\n * screen.simulateEvent(0, 'tap', { x: 100, y: 200 });\n *\n * // Check what was broadcast\n * expect(screen.getBroadcasts()).toContainEqual({\n * event: 'score-update',\n * data: { scores: { 0: 10 } },\n * });\n * ```\n */\nexport function createMockScreen<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockScreen<TEvents> {\n const {\n roomCode = 'TEST',\n controllers: initialControllers = [],\n autoReady = true,\n } = options;\n\n // Internal state\n let _controllers: ControllerInfo[] = [...initialControllers];\n let _isReady = false;\n let _isDestroyed = false;\n\n // Event listeners\n const listeners = new Map<string, Set<ScreenEventHandler>>();\n\n // Lifecycle callback sets\n const _onAllReadyCallbacks = new Set<() => void>();\n const _onControllerJoinCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onControllerLeaveCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerDisconnectCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerReconnectCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onCharacterUpdatedCallbacks = new Set<(index: PlayerIndex, appearance: CharacterAppearance | null) => void>();\n const _onRateLimitedCallbacks = new Set<(event: string) => void>();\n const _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n\n // Whether all-ready has fired\n let _allReadyFired = false;\n\n // Ready promise\n let _readyResolve: () => void;\n const _readyPromise = new Promise<void>((resolve) => {\n _readyResolve = resolve;\n });\n\n // Recorded events for testing\n const broadcasts: RecordedBroadcast[] = [];\n const sends: RecordedSend[] = [];\n\n // Screen implementation\n const screen: MockScreen<TEvents> = {\n // === Properties ===\n get controllers() {\n return [..._controllers];\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n get ready() {\n return _readyPromise;\n },\n\n // === Lifecycle Methods ===\n onAllReady(callback: () => void): () => void {\n if (_allReadyFired) {\n callback();\n }\n _onAllReadyCallbacks.add(callback);\n return () => { _onAllReadyCallbacks.delete(callback); };\n },\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerJoinCallbacks.add(callback);\n return () => { _onControllerJoinCallbacks.delete(callback); };\n },\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerLeaveCallbacks.add(callback);\n return () => { _onControllerLeaveCallbacks.delete(callback); };\n },\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerDisconnectCallbacks.add(callback);\n return () => { _onControllerDisconnectCallbacks.delete(callback); };\n },\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerReconnectCallbacks.add(callback);\n return () => { _onControllerReconnectCallbacks.delete(callback); };\n },\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n _onCharacterUpdatedCallbacks.add(callback);\n return () => { _onCharacterUpdatedCallbacks.delete(callback); };\n },\n\n onRateLimited(callback: (event: string) => void): () => void {\n _onRateLimitedCallbacks.add(callback);\n return () => { _onRateLimitedCallbacks.delete(callback); };\n },\n\n onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n // === Communication Methods ===\n broadcast<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event as string);\n broadcasts.push({ event: event as string, data });\n },\n\n broadcastRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event);\n broadcasts.push({ event, data });\n },\n\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event as string);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event: event as string, data });\n },\n\n sendToControllerRaw(\n playerIndex: PlayerIndex,\n event: string,\n data?: unknown,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event, data });\n },\n\n // === Game Lifecycle ===\n gameOver(results?: GameResults): void {\n if (_isDestroyed) {\n throw new Error('Cannot call gameOver: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call gameOver: screen is not ready');\n }\n broadcasts.push({ event: 'smore:game-over', data: { results } });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: screen is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ScreenEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n handler(playerIndex, data);\n screen.off(event, wrapper);\n };\n return screen.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n }\n },\n\n // === Utilities ===\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n hasAnyConnectedControllers(): boolean {\n return _controllers.some(c => c.connected);\n },\n\n // === Cleanup ===\n /**\n * Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.\n */\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(playerIndex, data);\n });\n }\n },\n\n simulateControllerJoin(info: ControllerInfo): void {\n _controllers.push(info);\n _onControllerJoinCallbacks.forEach(cb => cb(info.playerIndex, info));\n },\n\n simulateControllerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);\n _onControllerLeaveCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a controller network disconnect (player still in room but unreachable).\n */\n simulateControllerDisconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as disconnected (need to create new object since ControllerInfo is readonly)\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex\n ? { ...c, connected: false }\n : c\n );\n _onControllerDisconnectCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a controller network reconnect after disconnect.\n */\n simulateControllerReconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as connected (need to create new object since ControllerInfo is readonly)\n const reconnectedController = { ...controller, connected: true };\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? reconnectedController : c\n );\n _onControllerReconnectCallbacks.forEach(cb => cb(playerIndex, reconnectedController));\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n _onCharacterUpdatedCallbacks.forEach(cb => cb(playerIndex, appearance));\n },\n\n simulateRateLimited(event: string): void {\n _onRateLimitedCallbacks.forEach(cb => cb(event));\n },\n\n simulateAllReady(): void {\n _allReadyFired = true;\n _onAllReadyCallbacks.forEach(cb => cb());\n },\n\n simulateError(error: any): void {\n _onErrorCallbacks.forEach(cb => cb(error));\n },\n\n getBroadcasts(): Array<{ event: string; data: unknown }> {\n return [...broadcasts];\n },\n\n getSentToController(\n playerIndex: PlayerIndex,\n ): Array<{ event: string; data: unknown }> {\n return sends\n .filter((s) => s.playerIndex === playerIndex)\n .map((s) => ({ event: s.event, data: s.data }));\n },\n\n clearRecordedEvents(): void {\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n _readyResolve();\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => screen.triggerReady(), 0);\n }\n\n return screen;\n}\n\n// =============================================================================\n// MOCK CONTROLLER IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedEvent {\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Controller for testing player input logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * Uses the same event emitter pattern as real Controller: register lifecycle\n * callbacks via methods on the returned instance.\n *\n * @example\n * ```ts\n * const controller = createMockController<MyEvents>({\n * myIndex: 0,\n * });\n *\n * controller.on('your-turn', (data) => handleTurn(data));\n * controller.onAllReady(() => console.log('Ready!'));\n *\n * // Simulate receiving from screen\n * controller.simulateEvent('your-turn', { timeLimit: 30 });\n *\n * // Check what was sent\n * expect(controller.getSentEvents()).toContainEqual({\n * event: 'answer',\n * data: { choice: 2 },\n * });\n * ```\n */\nexport function createMockController<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockController<TEvents> {\n const {\n roomCode = 'TEST',\n myIndex = 0,\n autoReady = true,\n } = options;\n\n // Internal state -- uses a full ControllerInfo[] array (matching MockScreen pattern)\n // to preserve nickname/appearance data from simulatePlayerJoin()\n let _isReady = false;\n let _isDestroyed = false;\n let _controllers: ControllerInfo[] = options.controllers ?? [];\n\n // Event listeners\n const listeners = new Map<string, Set<ControllerEventHandler>>();\n\n // Lifecycle callback sets\n const _onAllReadyCallbacks = new Set<() => void>();\n const _onControllerJoinCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onControllerLeaveCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerDisconnectCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerReconnectCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onCharacterUpdatedCallbacks = new Set<(index: PlayerIndex, appearance: CharacterAppearance | null) => void>();\n const _onRateLimitedCallbacks = new Set<(event: string) => void>();\n const _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n\n // Whether all-ready has fired\n let _allReadyFired = false;\n\n // Ready promise\n let _readyResolve: () => void;\n const _readyPromise = new Promise<void>((resolve) => {\n _readyResolve = resolve;\n });\n\n // Recorded events for testing\n const sentEvents: RecordedEvent[] = [];\n\n // Controller implementation\n const controller: MockController<TEvents> = {\n // === Properties ===\n get myIndex() {\n return myIndex;\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n get controllers(): readonly ControllerInfo[] {\n return [..._controllers];\n },\n\n get ready() {\n return _readyPromise;\n },\n\n // === Lifecycle Methods ===\n onAllReady(callback: () => void): () => void {\n if (_allReadyFired) {\n callback();\n }\n _onAllReadyCallbacks.add(callback);\n return () => { _onAllReadyCallbacks.delete(callback); };\n },\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerJoinCallbacks.add(callback);\n return () => { _onControllerJoinCallbacks.delete(callback); };\n },\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerLeaveCallbacks.add(callback);\n return () => { _onControllerLeaveCallbacks.delete(callback); };\n },\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerDisconnectCallbacks.add(callback);\n return () => { _onControllerDisconnectCallbacks.delete(callback); };\n },\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerReconnectCallbacks.add(callback);\n return () => { _onControllerReconnectCallbacks.delete(callback); };\n },\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n _onCharacterUpdatedCallbacks.add(callback);\n return () => { _onCharacterUpdatedCallbacks.delete(callback); };\n },\n\n onRateLimited(callback: (event: string) => void): () => void {\n _onRateLimitedCallbacks.add(callback);\n return () => { _onRateLimitedCallbacks.delete(callback); };\n },\n\n onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n // === Communication Methods ===\n send<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event as string);\n sentEvents.push({ event: event as string, data });\n },\n\n sendRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event);\n sentEvents.push({ event, data });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: controller is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: controller is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ControllerEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ControllerEventHandler<EventData<TEvents, K>> = (data) => {\n handler(data);\n controller.off(event, wrapper);\n };\n return controller.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n }\n },\n\n // === Cleanup ===\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n sentEvents.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(data);\n });\n }\n },\n\n getSentEvents(): Array<{ event: string; data: unknown }> {\n return [...sentEvents];\n },\n\n clearRecordedEvents(): void {\n sentEvents.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n _readyResolve();\n },\n\n /**\n * Simulate a new player joining the room.\n * Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.\n */\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void {\n if (!_controllers.some(c => c.playerIndex === playerIndex)) {\n _controllers = [..._controllers, { ...info, connected: info.connected ?? true }];\n }\n _onControllerJoinCallbacks.forEach(cb => cb(playerIndex, info));\n },\n\n /**\n * Simulate a player leaving the room (fully removed).\n */\n simulatePlayerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter(c => c.playerIndex !== playerIndex);\n _onControllerLeaveCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a player network disconnect (player still in room but unreachable).\n */\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n _onControllerDisconnectCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a player network reconnect after disconnect.\n */\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...info, connected: true } : c\n );\n _onControllerReconnectCallbacks.forEach(cb => cb(playerIndex, info));\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const ctrl = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!ctrl) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n _onCharacterUpdatedCallbacks.forEach(cb => cb(playerIndex, appearance));\n },\n\n simulateRateLimited(event: string): void {\n _onRateLimitedCallbacks.forEach(cb => cb(event));\n },\n\n simulateAllReady(): void {\n _allReadyFired = true;\n _onAllReadyCallbacks.forEach(cb => cb());\n },\n\n simulateError(error: any): void {\n _onErrorCallbacks.forEach(cb => cb(error));\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => controller.triggerReady(), 0);\n }\n\n return controller;\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport type { MockScreen, MockController, MockOptions };\n"],"names":["validateEventName"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sources":["../../src/config.ts"],"sourcesContent":["/**\n * Global SDK configuration.\n * Settings here apply to both Screen and Controller unless overridden per-instance.\n */\n\nexport interface SmoreGlobalConfig {\n /**\n * Automatically signal ready after initialization.\n * When true (default), the SDK calls signalReady() 3 seconds after init.\n * Set to false if your game needs to load resources before signaling ready.\n * Applies to both Screen and Controller.\n * Can be overridden per-instance via ScreenConfig.autoReady / ControllerConfig.autoReady.\n * @default true\n */\n autoReady?: boolean;\n}\n\nlet globalConfig: SmoreGlobalConfig = {};\n\n/**\n * Set global SDK configuration.\n * Call this before createScreen() / createController().\n *\n * @example\n * ```ts\n * import { configure, createScreen, createController } from '@smoregg/sdk';\n *\n * // Set once, applies to both screen and controller\n * configure({ autoReady: false });\n *\n * const screen = await createScreen({ ... });\n * const controller = await createController({ ... });\n * ```\n */\nexport function configure(config: SmoreGlobalConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get current global config. Used internally by Screen and Controller.\n */\nexport function getGlobalConfig(): Readonly<SmoreGlobalConfig> {\n return globalConfig;\n}\n"],"names":[],"mappings":"AAiBA,IAAI,eAAkC,EAAC;AAiBhC,SAAS,UAAU,MAAA,EAAiC;AACzD,EAAA,YAAA,GAAe,EAAE,GAAG,YAAA,EAAc,GAAG,MAAA,EAAO;AAC9C;AAKO,SAAS,eAAA,GAA+C;AAC7D,EAAA,OAAO,YAAA;AACT;;;;"}
1
+ {"version":3,"file":"config.js","sources":["../../src/config.ts"],"sourcesContent":["/**\n * Global SDK configuration.\n * Settings here apply to both Screen and Controller unless overridden per-instance.\n */\n\nexport interface SmoreGlobalConfig {\n /**\n * Automatically signal ready after initialization.\n * When true (default), the SDK calls signalReady() automatically after init completes.\n * Set to false if your game needs to load resources before signaling ready.\n * Applies to both Screen and Controller.\n * @default true\n */\n autoReady?: boolean;\n}\n\nlet globalConfig: SmoreGlobalConfig = {};\n\n/**\n * Set global SDK configuration.\n * Call this before createScreen() / createController().\n *\n * @example\n * ```ts\n * import { configure, createScreen, createController } from '@smoregg/sdk';\n *\n * // Set once, applies to both screen and controller\n * configure({ autoReady: false });\n *\n * const screen = await createScreen({ ... });\n * const controller = await createController({ ... });\n * ```\n */\nexport function configure(config: SmoreGlobalConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get current global config. Used internally by Screen and Controller.\n */\nexport function getGlobalConfig(): Readonly<SmoreGlobalConfig> {\n return globalConfig;\n}\n"],"names":[],"mappings":"AAgBA,IAAI,eAAkC,EAAC;AAiBhC,SAAS,UAAU,MAAA,EAAiC;AACzD,EAAA,YAAA,GAAe,EAAE,GAAG,YAAA,EAAc,GAAG,MAAA,EAAO;AAC9C;AAKO,SAAS,eAAA,GAA+C;AAC7D,EAAA,OAAO,YAAA;AACT;;;;"}