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