@smoregg/sdk 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/controller.cjs +193 -115
- package/dist/cjs/controller.cjs.map +1 -1
- package/dist/cjs/errors.cjs +1 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/events.cjs +19 -2
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -7
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/screen.cjs +185 -130
- package/dist/cjs/screen.cjs.map +1 -1
- package/dist/cjs/shared.cjs +34 -0
- package/dist/cjs/shared.cjs.map +1 -0
- package/dist/cjs/testing.cjs +125 -74
- package/dist/cjs/testing.cjs.map +1 -1
- package/dist/cjs/transport/PostMessageTransport.cjs +12 -0
- package/dist/cjs/transport/PostMessageTransport.cjs.map +1 -1
- package/dist/cjs/transport/protocol.cjs +2 -0
- package/dist/cjs/transport/protocol.cjs.map +1 -1
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.cjs.map +1 -0
- package/dist/esm/controller.js +195 -117
- package/dist/esm/controller.js.map +1 -1
- package/dist/esm/errors.js +1 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/events.js +18 -3
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/index.js +1 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/screen.js +187 -132
- package/dist/esm/screen.js.map +1 -1
- package/dist/esm/shared.js +30 -0
- package/dist/esm/shared.js.map +1 -0
- package/dist/esm/testing.js +125 -74
- package/dist/esm/testing.js.map +1 -1
- package/dist/esm/transport/PostMessageTransport.js +12 -0
- package/dist/esm/transport/PostMessageTransport.js.map +1 -1
- package/dist/esm/transport/protocol.js +2 -1
- package/dist/esm/transport/protocol.js.map +1 -1
- package/dist/esm/types.js +14 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/controller.d.ts +1 -1
- package/dist/types/controller.d.ts.map +1 -1
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/events.d.ts +10 -1
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/screen.d.ts +3 -3
- package/dist/types/screen.d.ts.map +1 -1
- package/dist/types/shared.d.ts +21 -0
- package/dist/types/shared.d.ts.map +1 -0
- package/dist/types/testing.d.ts +63 -4
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/transport/PostMessageTransport.d.ts +1 -0
- package/dist/types/transport/PostMessageTransport.d.ts.map +1 -1
- package/dist/types/transport/protocol.d.ts +4 -0
- package/dist/types/transport/protocol.d.ts.map +1 -1
- package/dist/types/types.d.ts +215 -347
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +442 -787
- package/dist/umd/smore-sdk.umd.js.map +1 -1
- package/dist/umd/smore-sdk.umd.min.js +1 -1
- package/dist/umd/smore-sdk.umd.min.js.map +1 -1
- package/package.json +7 -1
- package/dist/cjs/config.cjs +0 -13
- package/dist/cjs/config.cjs.map +0 -1
- package/dist/esm/config.js +0 -10
- package/dist/esm/config.js.map +0 -1
- package/dist/types/config.d.ts +0 -35
- package/dist/types/config.d.ts.map +0 -1
package/dist/cjs/testing.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var events = require('./events.cjs');
|
|
4
|
+
var errors = require('./errors.cjs');
|
|
4
5
|
|
|
5
6
|
function createMockScreen(options = {}) {
|
|
6
7
|
const {
|
|
@@ -10,6 +11,7 @@ function createMockScreen(options = {}) {
|
|
|
10
11
|
} = options;
|
|
11
12
|
let _controllers = [...initialControllers];
|
|
12
13
|
let _isReady = false;
|
|
14
|
+
let _isConnected = false;
|
|
13
15
|
let _isDestroyed = false;
|
|
14
16
|
const listeners = /* @__PURE__ */ new Map();
|
|
15
17
|
const _onAllReadyCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -18,8 +20,8 @@ function createMockScreen(options = {}) {
|
|
|
18
20
|
const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
|
|
19
21
|
const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
|
|
20
22
|
const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
21
|
-
const _onRateLimitedCallbacks = /* @__PURE__ */ new Set();
|
|
22
23
|
const _onErrorCallbacks = /* @__PURE__ */ new Set();
|
|
24
|
+
const _onConnectionChangeCallbacks = /* @__PURE__ */ new Set();
|
|
23
25
|
let _allReadyFired = false;
|
|
24
26
|
let _readyResolve;
|
|
25
27
|
const _readyPromise = new Promise((resolve) => {
|
|
@@ -41,6 +43,12 @@ function createMockScreen(options = {}) {
|
|
|
41
43
|
get isDestroyed() {
|
|
42
44
|
return _isDestroyed;
|
|
43
45
|
},
|
|
46
|
+
get isConnected() {
|
|
47
|
+
return _isConnected;
|
|
48
|
+
},
|
|
49
|
+
get protocolVersion() {
|
|
50
|
+
return 1;
|
|
51
|
+
},
|
|
44
52
|
get ready() {
|
|
45
53
|
return _readyPromise;
|
|
46
54
|
},
|
|
@@ -84,87 +92,82 @@ function createMockScreen(options = {}) {
|
|
|
84
92
|
_onCharacterUpdatedCallbacks.delete(callback);
|
|
85
93
|
};
|
|
86
94
|
},
|
|
87
|
-
onRateLimited(callback) {
|
|
88
|
-
_onRateLimitedCallbacks.add(callback);
|
|
89
|
-
return () => {
|
|
90
|
-
_onRateLimitedCallbacks.delete(callback);
|
|
91
|
-
};
|
|
92
|
-
},
|
|
93
95
|
onError(callback) {
|
|
94
96
|
_onErrorCallbacks.add(callback);
|
|
95
97
|
return () => {
|
|
96
98
|
_onErrorCallbacks.delete(callback);
|
|
97
99
|
};
|
|
98
100
|
},
|
|
101
|
+
onConnectionChange(callback) {
|
|
102
|
+
_onConnectionChangeCallbacks.add(callback);
|
|
103
|
+
return () => {
|
|
104
|
+
_onConnectionChangeCallbacks.delete(callback);
|
|
105
|
+
};
|
|
106
|
+
},
|
|
99
107
|
// === Communication Methods ===
|
|
100
108
|
broadcast(event, data) {
|
|
101
109
|
if (_isDestroyed) {
|
|
102
|
-
throw new
|
|
110
|
+
throw new errors.SmoreSDKError("DESTROYED", "Cannot call broadcast() after destroy()");
|
|
103
111
|
}
|
|
104
112
|
if (!_isReady) {
|
|
105
|
-
throw new
|
|
106
|
-
}
|
|
107
|
-
events.validateEventName(event);
|
|
108
|
-
broadcasts.push({ event, data });
|
|
109
|
-
},
|
|
110
|
-
broadcastRaw(event, data) {
|
|
111
|
-
if (_isDestroyed) {
|
|
112
|
-
throw new Error("Cannot broadcast: screen is destroyed");
|
|
113
|
-
}
|
|
114
|
-
if (!_isReady) {
|
|
115
|
-
throw new Error("Cannot broadcast: screen is not ready");
|
|
113
|
+
throw new errors.SmoreSDKError("NOT_READY", "Cannot call broadcast() before screen is ready");
|
|
116
114
|
}
|
|
117
115
|
events.validateEventName(event);
|
|
118
116
|
broadcasts.push({ event, data });
|
|
119
117
|
},
|
|
120
118
|
sendToController(playerIndex, event, data) {
|
|
121
119
|
if (_isDestroyed) {
|
|
122
|
-
throw new
|
|
120
|
+
throw new errors.SmoreSDKError("DESTROYED", "Cannot call sendToController() after destroy()");
|
|
123
121
|
}
|
|
124
122
|
if (!_isReady) {
|
|
125
|
-
throw new
|
|
123
|
+
throw new errors.SmoreSDKError("NOT_READY", "Cannot call sendToController() before screen is ready");
|
|
126
124
|
}
|
|
127
125
|
events.validateEventName(event);
|
|
128
126
|
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
129
|
-
throw new
|
|
130
|
-
}
|
|
131
|
-
sends.push({ playerIndex, event, data });
|
|
132
|
-
},
|
|
133
|
-
sendToControllerRaw(playerIndex, event, data) {
|
|
134
|
-
if (_isDestroyed) {
|
|
135
|
-
throw new Error("Cannot send: screen is destroyed");
|
|
136
|
-
}
|
|
137
|
-
if (!_isReady) {
|
|
138
|
-
throw new Error("Cannot send: screen is not ready");
|
|
139
|
-
}
|
|
140
|
-
events.validateEventName(event);
|
|
141
|
-
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
142
|
-
throw new Error(`Invalid player index: ${playerIndex}`);
|
|
127
|
+
throw new errors.SmoreSDKError("INVALID_PLAYER", `No controller found with player index ${playerIndex}`);
|
|
143
128
|
}
|
|
144
129
|
sends.push({ playerIndex, event, data });
|
|
145
130
|
},
|
|
146
131
|
// === Game Lifecycle ===
|
|
147
132
|
gameOver(results) {
|
|
148
133
|
if (_isDestroyed) {
|
|
149
|
-
throw new
|
|
134
|
+
throw new errors.SmoreSDKError("DESTROYED", "Cannot call gameOver() after destroy()");
|
|
150
135
|
}
|
|
151
136
|
if (!_isReady) {
|
|
152
|
-
throw new
|
|
137
|
+
throw new errors.SmoreSDKError("NOT_READY", "Cannot call gameOver() before screen is ready");
|
|
153
138
|
}
|
|
154
139
|
broadcasts.push({ event: "smore:game-over", data: { results } });
|
|
155
140
|
},
|
|
156
141
|
signalReady() {
|
|
157
142
|
if (_isDestroyed) {
|
|
158
|
-
throw new
|
|
143
|
+
throw new errors.SmoreSDKError("DESTROYED", "Cannot call signalReady() after destroy()");
|
|
159
144
|
}
|
|
160
145
|
if (!_isReady) {
|
|
161
|
-
throw new
|
|
146
|
+
throw new errors.SmoreSDKError("NOT_READY", "Cannot call signalReady() before screen is ready");
|
|
162
147
|
}
|
|
163
148
|
},
|
|
164
149
|
// === Event Subscription ===
|
|
165
150
|
on(event, handler) {
|
|
166
|
-
events.validateEventName(event);
|
|
167
151
|
const eventStr = event;
|
|
152
|
+
if (eventStr.startsWith("$")) {
|
|
153
|
+
switch (eventStr) {
|
|
154
|
+
case "$controller-join":
|
|
155
|
+
return screen.onControllerJoin(handler);
|
|
156
|
+
case "$controller-leave":
|
|
157
|
+
return screen.onControllerLeave(handler);
|
|
158
|
+
case "$controller-disconnect":
|
|
159
|
+
return screen.onControllerDisconnect(handler);
|
|
160
|
+
case "$controller-reconnect":
|
|
161
|
+
return screen.onControllerReconnect(handler);
|
|
162
|
+
case "$character-updated":
|
|
163
|
+
return screen.onCharacterUpdated(handler);
|
|
164
|
+
case "$all-ready":
|
|
165
|
+
return screen.onAllReady(handler);
|
|
166
|
+
case "$error":
|
|
167
|
+
return screen.onError(handler);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
events.validateEventName(event);
|
|
168
171
|
if (!listeners.has(eventStr)) {
|
|
169
172
|
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
170
173
|
}
|
|
@@ -190,6 +193,13 @@ function createMockScreen(options = {}) {
|
|
|
190
193
|
listeners.get(eventStr)?.delete(handler);
|
|
191
194
|
}
|
|
192
195
|
},
|
|
196
|
+
removeAllListeners(event) {
|
|
197
|
+
if (event) {
|
|
198
|
+
listeners.delete(event);
|
|
199
|
+
} else {
|
|
200
|
+
listeners.clear();
|
|
201
|
+
}
|
|
202
|
+
},
|
|
193
203
|
// === Utilities ===
|
|
194
204
|
getController(playerIndex) {
|
|
195
205
|
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
@@ -197,9 +207,6 @@ function createMockScreen(options = {}) {
|
|
|
197
207
|
getControllerCount() {
|
|
198
208
|
return _controllers.filter((c) => c.connected).length;
|
|
199
209
|
},
|
|
200
|
-
hasAnyConnectedControllers() {
|
|
201
|
-
return _controllers.some((c) => c.connected);
|
|
202
|
-
},
|
|
203
210
|
// === Cleanup ===
|
|
204
211
|
/**
|
|
205
212
|
* Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.
|
|
@@ -235,7 +242,7 @@ function createMockScreen(options = {}) {
|
|
|
235
242
|
simulateControllerDisconnect(playerIndex) {
|
|
236
243
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
237
244
|
if (!controller) {
|
|
238
|
-
throw new
|
|
245
|
+
throw new errors.SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
239
246
|
}
|
|
240
247
|
_controllers = _controllers.map(
|
|
241
248
|
(c) => c.playerIndex === playerIndex ? { ...c, connected: false } : c
|
|
@@ -248,7 +255,7 @@ function createMockScreen(options = {}) {
|
|
|
248
255
|
simulateControllerReconnect(playerIndex) {
|
|
249
256
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
250
257
|
if (!controller) {
|
|
251
|
-
throw new
|
|
258
|
+
throw new errors.SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
252
259
|
}
|
|
253
260
|
const reconnectedController = { ...controller, connected: true };
|
|
254
261
|
_controllers = _controllers.map(
|
|
@@ -259,16 +266,13 @@ function createMockScreen(options = {}) {
|
|
|
259
266
|
simulateCharacterUpdate(playerIndex, appearance) {
|
|
260
267
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
261
268
|
if (!controller) {
|
|
262
|
-
throw new
|
|
269
|
+
throw new errors.SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
263
270
|
}
|
|
264
271
|
_controllers = _controllers.map(
|
|
265
272
|
(c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
|
|
266
273
|
);
|
|
267
274
|
_onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
|
|
268
275
|
},
|
|
269
|
-
simulateRateLimited(event) {
|
|
270
|
-
_onRateLimitedCallbacks.forEach((cb) => cb(event));
|
|
271
|
-
},
|
|
272
276
|
simulateAllReady() {
|
|
273
277
|
_allReadyFired = true;
|
|
274
278
|
_onAllReadyCallbacks.forEach((cb) => cb());
|
|
@@ -276,6 +280,10 @@ function createMockScreen(options = {}) {
|
|
|
276
280
|
simulateError(error) {
|
|
277
281
|
_onErrorCallbacks.forEach((cb) => cb(error));
|
|
278
282
|
},
|
|
283
|
+
simulateConnectionChange(connected) {
|
|
284
|
+
_isConnected = connected;
|
|
285
|
+
_onConnectionChangeCallbacks.forEach((cb) => cb(connected));
|
|
286
|
+
},
|
|
279
287
|
getBroadcasts() {
|
|
280
288
|
return [...broadcasts];
|
|
281
289
|
},
|
|
@@ -288,6 +296,7 @@ function createMockScreen(options = {}) {
|
|
|
288
296
|
},
|
|
289
297
|
triggerReady() {
|
|
290
298
|
_isReady = true;
|
|
299
|
+
_isConnected = true;
|
|
291
300
|
_readyResolve();
|
|
292
301
|
}
|
|
293
302
|
};
|
|
@@ -299,10 +308,11 @@ function createMockScreen(options = {}) {
|
|
|
299
308
|
function createMockController(options = {}) {
|
|
300
309
|
const {
|
|
301
310
|
roomCode = "TEST",
|
|
302
|
-
|
|
311
|
+
myPlayerIndex = 0,
|
|
303
312
|
autoReady = true
|
|
304
313
|
} = options;
|
|
305
314
|
let _isReady = false;
|
|
315
|
+
let _isConnected = false;
|
|
306
316
|
let _isDestroyed = false;
|
|
307
317
|
let _controllers = options.controllers ?? [];
|
|
308
318
|
const listeners = /* @__PURE__ */ new Map();
|
|
@@ -312,8 +322,9 @@ function createMockController(options = {}) {
|
|
|
312
322
|
const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
|
|
313
323
|
const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
|
|
314
324
|
const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
315
|
-
const _onRateLimitedCallbacks = /* @__PURE__ */ new Set();
|
|
316
325
|
const _onErrorCallbacks = /* @__PURE__ */ new Set();
|
|
326
|
+
const _onGameOverCallbacks = /* @__PURE__ */ new Set();
|
|
327
|
+
const _onConnectionChangeCallbacks = /* @__PURE__ */ new Set();
|
|
317
328
|
let _allReadyFired = false;
|
|
318
329
|
let _readyResolve;
|
|
319
330
|
const _readyPromise = new Promise((resolve) => {
|
|
@@ -322,8 +333,8 @@ function createMockController(options = {}) {
|
|
|
322
333
|
const sentEvents = [];
|
|
323
334
|
const controller = {
|
|
324
335
|
// === Properties ===
|
|
325
|
-
get
|
|
326
|
-
return
|
|
336
|
+
get myPlayerIndex() {
|
|
337
|
+
return myPlayerIndex;
|
|
327
338
|
},
|
|
328
339
|
get roomCode() {
|
|
329
340
|
return roomCode;
|
|
@@ -334,6 +345,12 @@ function createMockController(options = {}) {
|
|
|
334
345
|
get isDestroyed() {
|
|
335
346
|
return _isDestroyed;
|
|
336
347
|
},
|
|
348
|
+
get isConnected() {
|
|
349
|
+
return _isConnected;
|
|
350
|
+
},
|
|
351
|
+
get protocolVersion() {
|
|
352
|
+
return 1;
|
|
353
|
+
},
|
|
337
354
|
get controllers() {
|
|
338
355
|
return [..._controllers];
|
|
339
356
|
},
|
|
@@ -380,48 +397,70 @@ function createMockController(options = {}) {
|
|
|
380
397
|
_onCharacterUpdatedCallbacks.delete(callback);
|
|
381
398
|
};
|
|
382
399
|
},
|
|
383
|
-
onRateLimited(callback) {
|
|
384
|
-
_onRateLimitedCallbacks.add(callback);
|
|
385
|
-
return () => {
|
|
386
|
-
_onRateLimitedCallbacks.delete(callback);
|
|
387
|
-
};
|
|
388
|
-
},
|
|
389
400
|
onError(callback) {
|
|
390
401
|
_onErrorCallbacks.add(callback);
|
|
391
402
|
return () => {
|
|
392
403
|
_onErrorCallbacks.delete(callback);
|
|
393
404
|
};
|
|
394
405
|
},
|
|
406
|
+
onConnectionChange(callback) {
|
|
407
|
+
_onConnectionChangeCallbacks.add(callback);
|
|
408
|
+
return () => {
|
|
409
|
+
_onConnectionChangeCallbacks.delete(callback);
|
|
410
|
+
};
|
|
411
|
+
},
|
|
412
|
+
onGameOver(callback) {
|
|
413
|
+
_onGameOverCallbacks.add(callback);
|
|
414
|
+
return () => {
|
|
415
|
+
_onGameOverCallbacks.delete(callback);
|
|
416
|
+
};
|
|
417
|
+
},
|
|
395
418
|
getControllerCount() {
|
|
396
419
|
return _controllers.filter((c) => c.connected).length;
|
|
397
420
|
},
|
|
421
|
+
getController(playerIndex) {
|
|
422
|
+
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
423
|
+
},
|
|
398
424
|
// === Communication Methods ===
|
|
399
425
|
send(event, data) {
|
|
400
426
|
if (_isDestroyed) {
|
|
401
|
-
throw new
|
|
402
|
-
}
|
|
403
|
-
events.validateEventName(event);
|
|
404
|
-
sentEvents.push({ event, data });
|
|
405
|
-
},
|
|
406
|
-
sendRaw(event, data) {
|
|
407
|
-
if (_isDestroyed) {
|
|
408
|
-
throw new Error("Cannot send: controller is destroyed");
|
|
427
|
+
throw new errors.SmoreSDKError("DESTROYED", "Cannot call send() after destroy()");
|
|
409
428
|
}
|
|
410
429
|
events.validateEventName(event);
|
|
411
430
|
sentEvents.push({ event, data });
|
|
412
431
|
},
|
|
413
432
|
signalReady() {
|
|
414
433
|
if (_isDestroyed) {
|
|
415
|
-
throw new
|
|
434
|
+
throw new errors.SmoreSDKError("DESTROYED", "Cannot call signalReady() after destroy()");
|
|
416
435
|
}
|
|
417
436
|
if (!_isReady) {
|
|
418
|
-
throw new
|
|
437
|
+
throw new errors.SmoreSDKError("NOT_READY", "Cannot call signalReady() before controller is ready");
|
|
419
438
|
}
|
|
420
439
|
},
|
|
421
440
|
// === Event Subscription ===
|
|
422
441
|
on(event, handler) {
|
|
423
|
-
events.validateEventName(event);
|
|
424
442
|
const eventStr = event;
|
|
443
|
+
if (eventStr.startsWith("$")) {
|
|
444
|
+
switch (eventStr) {
|
|
445
|
+
case "$controller-join":
|
|
446
|
+
return controller.onControllerJoin(handler);
|
|
447
|
+
case "$controller-leave":
|
|
448
|
+
return controller.onControllerLeave(handler);
|
|
449
|
+
case "$controller-disconnect":
|
|
450
|
+
return controller.onControllerDisconnect(handler);
|
|
451
|
+
case "$controller-reconnect":
|
|
452
|
+
return controller.onControllerReconnect(handler);
|
|
453
|
+
case "$character-updated":
|
|
454
|
+
return controller.onCharacterUpdated(handler);
|
|
455
|
+
case "$all-ready":
|
|
456
|
+
return controller.onAllReady(handler);
|
|
457
|
+
case "$error":
|
|
458
|
+
return controller.onError(handler);
|
|
459
|
+
case "$game-over":
|
|
460
|
+
return controller.onGameOver(handler);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
events.validateEventName(event);
|
|
425
464
|
if (!listeners.has(eventStr)) {
|
|
426
465
|
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
427
466
|
}
|
|
@@ -447,6 +486,13 @@ function createMockController(options = {}) {
|
|
|
447
486
|
listeners.get(eventStr)?.delete(handler);
|
|
448
487
|
}
|
|
449
488
|
},
|
|
489
|
+
removeAllListeners(event) {
|
|
490
|
+
if (event) {
|
|
491
|
+
listeners.delete(event);
|
|
492
|
+
} else {
|
|
493
|
+
listeners.clear();
|
|
494
|
+
}
|
|
495
|
+
},
|
|
450
496
|
// === Cleanup ===
|
|
451
497
|
destroy() {
|
|
452
498
|
_isDestroyed = true;
|
|
@@ -472,6 +518,7 @@ function createMockController(options = {}) {
|
|
|
472
518
|
},
|
|
473
519
|
triggerReady() {
|
|
474
520
|
_isReady = true;
|
|
521
|
+
_isConnected = true;
|
|
475
522
|
_readyResolve();
|
|
476
523
|
},
|
|
477
524
|
/**
|
|
@@ -512,22 +559,26 @@ function createMockController(options = {}) {
|
|
|
512
559
|
simulateCharacterUpdate(playerIndex, appearance) {
|
|
513
560
|
const ctrl = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
514
561
|
if (!ctrl) {
|
|
515
|
-
throw new
|
|
562
|
+
throw new errors.SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
516
563
|
}
|
|
517
564
|
_controllers = _controllers.map(
|
|
518
565
|
(c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
|
|
519
566
|
);
|
|
520
567
|
_onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
|
|
521
568
|
},
|
|
522
|
-
simulateRateLimited(event) {
|
|
523
|
-
_onRateLimitedCallbacks.forEach((cb) => cb(event));
|
|
524
|
-
},
|
|
525
569
|
simulateAllReady() {
|
|
526
570
|
_allReadyFired = true;
|
|
527
571
|
_onAllReadyCallbacks.forEach((cb) => cb());
|
|
528
572
|
},
|
|
529
573
|
simulateError(error) {
|
|
530
574
|
_onErrorCallbacks.forEach((cb) => cb(error));
|
|
575
|
+
},
|
|
576
|
+
simulateGameOver(results) {
|
|
577
|
+
_onGameOverCallbacks.forEach((cb) => cb(results));
|
|
578
|
+
},
|
|
579
|
+
simulateConnectionChange(connected) {
|
|
580
|
+
_isConnected = connected;
|
|
581
|
+
_onConnectionChangeCallbacks.forEach((cb) => cb(connected));
|
|
531
582
|
}
|
|
532
583
|
};
|
|
533
584
|
if (autoReady) {
|
package/dist/cjs/testing.cjs.map
CHANGED
|
@@ -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 * 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
|
+
{"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 SmoreError,\n Screen,\n Controller,\n RoomCode,\n} from './types';\nimport { validateEventName } from './events';\nimport { SmoreSDKError } from './errors';\n\n// =============================================================================\n// MOCK TYPES (internal — not part of public API)\n// =============================================================================\n\n/**\n * Options for creating mock instances.\n */\nexport interface MockOptions {\n /** Initial room code */\n roomCode?: RoomCode;\n /** Initial controllers for Screen mock */\n controllers?: ControllerInfo[];\n /** My index for Controller mock */\n myPlayerIndex?: PlayerIndex;\n /** Auto-trigger ready state */\n autoReady?: boolean;\n}\n\n/**\n * Mock Screen for testing.\n * Extends Screen with additional test utilities.\n */\nexport interface MockScreen<TEvents extends EventMap = EventMap>\n extends Screen<TEvents> {\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void;\n simulateControllerJoin(info: ControllerInfo): void;\n simulateControllerLeave(playerIndex: PlayerIndex): void;\n simulateControllerDisconnect(playerIndex: PlayerIndex): void;\n simulateControllerReconnect(playerIndex: PlayerIndex): void;\n getBroadcasts(): Array<{ event: string; data: unknown }>;\n getSentToController(playerIndex: PlayerIndex): Array<{ event: string; data: unknown }>;\n clearRecordedEvents(): void;\n triggerReady(): void;\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;\n simulateAllReady(): void;\n simulateError(error: any): void;\n simulateConnectionChange(connected: boolean): void;\n}\n\n/**\n * Mock Controller for testing.\n * Extends Controller with additional test utilities.\n */\nexport interface MockController<TEvents extends EventMap = EventMap>\n extends Controller<TEvents> {\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void;\n getSentEvents(): Array<{ event: string; data: unknown }>;\n clearRecordedEvents(): void;\n triggerReady(): void;\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void;\n simulatePlayerLeave(playerIndex: PlayerIndex): void;\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void;\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void;\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;\n simulateAllReady(): void;\n simulateError(error: any): void;\n simulateGameOver(results?: GameResults): void;\n simulateConnectionChange(connected: boolean): void;\n}\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', (playerIndex, data) => handleTap(playerIndex, 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 _isConnected = 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 _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n const _onConnectionChangeCallbacks = new Set<(connected: boolean) => 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 isConnected() {\n return _isConnected;\n },\n get protocolVersion() {\n return 1;\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 onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n _onConnectionChangeCallbacks.add(callback);\n return () => { _onConnectionChangeCallbacks.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 SmoreSDKError('DESTROYED', 'Cannot call broadcast() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call broadcast() before screen is ready');\n }\n validateEventName(event as string);\n broadcasts.push({ event: event as string, 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 SmoreSDKError('DESTROYED', 'Cannot call sendToController() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call sendToController() before screen is ready');\n }\n validateEventName(event as string);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new SmoreSDKError('INVALID_PLAYER', `No controller found with player index ${playerIndex}`);\n }\n sends.push({ playerIndex, event: event as string, data });\n },\n\n // === Game Lifecycle ===\n gameOver(results?: GameResults): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call gameOver() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call gameOver() before screen is ready');\n }\n broadcasts.push({ event: 'smore:game-over', data: { results } });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call signalReady() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call signalReady() before screen is 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 const eventStr = event as string;\n\n // Handle $-prefixed lifecycle events by delegating to appropriate lifecycle method\n if (eventStr.startsWith('$')) {\n switch (eventStr) {\n case '$controller-join':\n return screen.onControllerJoin(handler as any);\n case '$controller-leave':\n return screen.onControllerLeave(handler as any);\n case '$controller-disconnect':\n return screen.onControllerDisconnect(handler as any);\n case '$controller-reconnect':\n return screen.onControllerReconnect(handler as any);\n case '$character-updated':\n return screen.onCharacterUpdated(handler as any);\n case '$all-ready':\n return screen.onAllReady(handler as any);\n case '$error':\n return screen.onError(handler as any);\n default:\n // Unknown lifecycle event, just treat as regular event\n break;\n }\n }\n\n validateEventName(event as string);\n\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 removeAllListeners(event?: string): void {\n if (event) {\n listeners.delete(event);\n } else {\n listeners.clear();\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 // === 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 SmoreSDKError('INVALID_PLAYER', `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 SmoreSDKError('INVALID_PLAYER', `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 SmoreSDKError('INVALID_PLAYER', `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 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 simulateConnectionChange(connected: boolean): void {\n _isConnected = connected;\n _onConnectionChangeCallbacks.forEach(cb => cb(connected));\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 _isConnected = 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 * myPlayerIndex: 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 myPlayerIndex = 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 _isConnected = 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 _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n const _onGameOverCallbacks = new Set<(results?: GameResults) => void>();\n const _onConnectionChangeCallbacks = new Set<(connected: boolean) => 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 myPlayerIndex() {\n return myPlayerIndex;\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 isConnected() {\n return _isConnected;\n },\n\n get protocolVersion() {\n return 1;\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 onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n _onConnectionChangeCallbacks.add(callback);\n return () => { _onConnectionChangeCallbacks.delete(callback); };\n },\n\n onGameOver(callback: (results?: GameResults) => void): () => void {\n _onGameOverCallbacks.add(callback);\n return () => { _onGameOverCallbacks.delete(callback); };\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\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 SmoreSDKError('DESTROYED', 'Cannot call send() after destroy()');\n }\n validateEventName(event as string);\n sentEvents.push({ event: event as string, data });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call signalReady() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call signalReady() before controller is 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 const eventStr = event as string;\n\n // Handle $-prefixed lifecycle events by delegating to appropriate lifecycle method\n if (eventStr.startsWith('$')) {\n switch (eventStr) {\n case '$controller-join':\n return controller.onControllerJoin(handler as any);\n case '$controller-leave':\n return controller.onControllerLeave(handler as any);\n case '$controller-disconnect':\n return controller.onControllerDisconnect(handler as any);\n case '$controller-reconnect':\n return controller.onControllerReconnect(handler as any);\n case '$character-updated':\n return controller.onCharacterUpdated(handler as any);\n case '$all-ready':\n return controller.onAllReady(handler as any);\n case '$error':\n return controller.onError(handler as any);\n case '$game-over':\n return controller.onGameOver(handler as any);\n default:\n // Unknown lifecycle event, just treat as regular event\n break;\n }\n }\n\n validateEventName(event as string);\n\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 removeAllListeners(event?: string): void {\n if (event) {\n listeners.delete(event);\n } else {\n listeners.clear();\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 _isConnected = 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 SmoreSDKError('INVALID_PLAYER', `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 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 simulateGameOver(results?: GameResults): void {\n _onGameOverCallbacks.forEach(cb => cb(results));\n },\n\n simulateConnectionChange(connected: boolean): void {\n _isConnected = connected;\n _onConnectionChangeCallbacks.forEach(cb => cb(connected));\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"],"names":["SmoreSDKError","validateEventName"],"mappings":"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"}
|