@wvdsh/sdk-js 1.3.7 → 1.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +48 -34
  2. package/dist/index.js +304 -177
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -93,6 +93,44 @@ var WavedashManager = class {
93
93
  }
94
94
  };
95
95
 
96
+ // src/utils/logger.ts
97
+ var LOG_LEVEL = {
98
+ DEBUG: 0,
99
+ // Most verbose
100
+ INFO: 1,
101
+ WARN: 2,
102
+ ERROR: 3
103
+ };
104
+ var WavedashLogger = class {
105
+ constructor(logLevel = LOG_LEVEL.WARN) {
106
+ this.logLevel = logLevel;
107
+ }
108
+ setLogLevel(level) {
109
+ this.logLevel = level;
110
+ }
111
+ debug(message, ...args) {
112
+ if (this.logLevel <= LOG_LEVEL.DEBUG) {
113
+ console.log(`[WavedashJS] ${message}`, ...args);
114
+ }
115
+ }
116
+ info(message, ...args) {
117
+ if (this.logLevel <= LOG_LEVEL.INFO) {
118
+ console.log(`[WavedashJS] ${message}`, ...args);
119
+ }
120
+ }
121
+ warn(message, ...args) {
122
+ if (this.logLevel <= LOG_LEVEL.WARN) {
123
+ console.warn(`[WavedashJS] ${message}`, ...args);
124
+ }
125
+ }
126
+ error(message, ...args) {
127
+ if (this.logLevel <= LOG_LEVEL.ERROR) {
128
+ console.error(`[WavedashJS] ${message}`, ...args);
129
+ }
130
+ }
131
+ };
132
+ var logger = new WavedashLogger();
133
+
96
134
  // src/services/lobby.ts
97
135
  var _LobbyManager = class _LobbyManager extends WavedashManager {
98
136
  constructor(sdk) {
@@ -153,7 +191,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
153
191
  for (const user of previousUsers) {
154
192
  if (!newUserIds.has(user.userId)) {
155
193
  if (user.userId === this.sdk.getUserId()) {
156
- this.sdk.logger.warn(
194
+ logger.warn(
157
195
  "USER WAS KICKED FROM LOBBY! Received notification for myself leaving."
158
196
  );
159
197
  }
@@ -169,7 +207,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
169
207
  }
170
208
  if (this.lobbyId) {
171
209
  this.p2pUpdateQueue = this.p2pUpdateQueue.then(() => this.updateP2PConnections(newUsers)).catch((error) => {
172
- this.sdk.logger.error("Error in queued P2P update:", error);
210
+ logger.error("Error in queued P2P update:", error);
173
211
  });
174
212
  }
175
213
  };
@@ -204,7 +242,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
204
242
  {},
205
243
  this.processInviteUpdates,
206
244
  (error) => {
207
- this.sdk.logger.error(`Lobby invites subscription error: ${error}`);
245
+ logger.error(`Lobby invites subscription error: ${error}`);
208
246
  }
209
247
  );
210
248
  }
@@ -232,7 +270,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
232
270
  }
233
271
  getLobbyUsers(lobbyId) {
234
272
  if (this.lobbyId !== lobbyId) {
235
- this.sdk.logger.error(
273
+ logger.error(
236
274
  "Must be a member of the lobby to access user list"
237
275
  );
238
276
  return [];
@@ -241,7 +279,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
241
279
  }
242
280
  getHostId(lobbyId) {
243
281
  if (this.lobbyId !== lobbyId) {
244
- this.sdk.logger.error(
282
+ logger.error(
245
283
  "Must be a member of the lobby to access the host ID"
246
284
  );
247
285
  return null;
@@ -317,11 +355,11 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
317
355
  sendLobbyMessage(lobbyId, message) {
318
356
  const args = { lobbyId, message };
319
357
  if (message.length === 0) {
320
- this.sdk.logger.error("Message cannot be empty");
358
+ logger.error("Message cannot be empty");
321
359
  return false;
322
360
  }
323
361
  if (message.length > LOBBY_MESSAGE_MAX_LENGTH) {
324
- this.sdk.logger.error(
362
+ logger.error(
325
363
  `Message cannot be longer than ${LOBBY_MESSAGE_MAX_LENGTH} characters`
326
364
  );
327
365
  return false;
@@ -329,7 +367,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
329
367
  try {
330
368
  this.sdk.convexClient.mutation(api.sdk.gameLobby.sendMessage, args);
331
369
  } catch (error) {
332
- this.sdk.logger.error(`Error sending lobby message: ${error}`);
370
+ logger.error(`Error sending lobby message: ${error}`);
333
371
  return false;
334
372
  }
335
373
  return true;
@@ -372,7 +410,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
372
410
  this.lobbyMetadata = response.metadata;
373
411
  this.sdk.friendsManager.cacheUsers(response.users);
374
412
  const onLobbySubscriptionError = (error) => {
375
- this.sdk.logger.error(`Lobby subscription error: ${error.message}`);
413
+ logger.error(`Lobby subscription error: ${error.message}`);
376
414
  if (error.message.includes("not a member")) {
377
415
  this.handleLobbyKicked(LobbyKickedReason.KICKED);
378
416
  } else {
@@ -406,7 +444,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
406
444
  if (response.users.length > 1) {
407
445
  this.p2pUpdateQueue = this.updateP2PConnections(response.users).catch(
408
446
  (error) => {
409
- this.sdk.logger.error("Error initializing P2P on join:", error);
447
+ logger.error("Error initializing P2P on join:", error);
410
448
  }
411
449
  );
412
450
  }
@@ -419,7 +457,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
419
457
  users: response.users,
420
458
  metadata: response.metadata
421
459
  });
422
- this.sdk.logger.debug("Subscribed to lobby:", response.lobbyId);
460
+ logger.debug("Subscribed to lobby:", response.lobbyId);
423
461
  }
424
462
  /**
425
463
  * Handle being kicked or removed from a lobby (subscription error)
@@ -429,7 +467,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
429
467
  handleLobbyKicked(reason = LobbyKickedReason.KICKED) {
430
468
  const lobbyId = this.lobbyId;
431
469
  if (!lobbyId) return;
432
- this.sdk.logger.warn(
470
+ logger.warn(
433
471
  `User was removed from lobby: ${lobbyId} (reason: ${reason})`
434
472
  );
435
473
  this.cleanupLobbyState();
@@ -516,7 +554,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
516
554
  lobbyId: this.lobbyId,
517
555
  updates
518
556
  }).catch((error) => {
519
- this.sdk.logger.error("Error updating lobby metadata:", error);
557
+ logger.error("Error updating lobby metadata:", error);
520
558
  }).finally(() => {
521
559
  this.inFlightMetadataUpdate = null;
522
560
  if (Object.keys(this.pendingMetadataUpdates).length > 0) {
@@ -538,7 +576,7 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
538
576
  try {
539
577
  if (newUsers.length <= 1) {
540
578
  this.sdk.p2pManager.disconnectP2P();
541
- this.sdk.logger.debug(
579
+ logger.debug(
542
580
  "Only one user in lobby, P2P connections disconnected"
543
581
  );
544
582
  return;
@@ -552,11 +590,11 @@ var _LobbyManager = class _LobbyManager extends WavedashManager {
552
590
  this.lobbyId,
553
591
  wavedashUsers
554
592
  );
555
- this.sdk.logger.debug(
593
+ logger.debug(
556
594
  `P2P connections updated for lobby ${this.lobbyId} with ${wavedashUsers.length} users`
557
595
  );
558
596
  } catch (error) {
559
- this.sdk.logger.error("Error updating P2P connections:", error);
597
+ logger.error("Error updating P2P connections:", error);
560
598
  }
561
599
  }
562
600
  };
@@ -701,7 +739,7 @@ var FileSystemManager = class extends WavedashManager {
701
739
  });
702
740
  if (!response.ok) {
703
741
  const msg = `Failed to delete remote file ${filePath}: ${response.status} (${response.statusText})`;
704
- this.sdk.logger.error(msg);
742
+ logger.error(msg);
705
743
  throw new Error(msg);
706
744
  }
707
745
  return filePath;
@@ -760,9 +798,9 @@ var FileSystemManager = class extends WavedashManager {
760
798
  return path;
761
799
  }
762
800
  async writeLocalFile(filePath, data) {
763
- this.sdk.logger.debug(`Writing local file: ${filePath}`);
801
+ logger.debug(`Writing local file: ${filePath}`);
764
802
  if (this.sdk.engineInstance?.FS) {
765
- this.sdk.logger.error(
803
+ logger.error(
766
804
  `${this.sdk.engineInstance.type} engine detected, use engine's builtin file access to save files.`
767
805
  );
768
806
  return false;
@@ -771,14 +809,14 @@ var FileSystemManager = class extends WavedashManager {
771
809
  await writeToIndexedDB(filePath, data);
772
810
  return true;
773
811
  } catch (error) {
774
- this.sdk.logger.error(`Failed to write local file: ${error}`);
812
+ logger.error(`Failed to write local file: ${error}`);
775
813
  return false;
776
814
  }
777
815
  }
778
816
  async readLocalFile(filePath) {
779
- this.sdk.logger.debug(`Reading local file: ${filePath}`);
817
+ logger.debug(`Reading local file: ${filePath}`);
780
818
  if (this.sdk.engineInstance?.FS) {
781
- this.sdk.logger.error(
819
+ logger.error(
782
820
  `${this.sdk.engineInstance.type} engine detected, use engine's builtin file access to read files.`
783
821
  );
784
822
  return null;
@@ -789,7 +827,7 @@ var FileSystemManager = class extends WavedashManager {
789
827
  const arrayBuffer = await blob.arrayBuffer();
790
828
  return new Uint8Array(arrayBuffer);
791
829
  } catch (error) {
792
- this.sdk.logger.error(`Failed to read local file: ${error}`);
830
+ logger.error(`Failed to read local file: ${error}`);
793
831
  return null;
794
832
  }
795
833
  }
@@ -798,9 +836,9 @@ var FileSystemManager = class extends WavedashManager {
798
836
  // ================
799
837
  // Helper to upload a local file to a presigned URL
800
838
  async upload(presignedUploadUrl, filePath) {
801
- this.sdk.logger.debug(`Uploading ${filePath} to: ${presignedUploadUrl}`);
839
+ logger.debug(`Uploading ${filePath} to: ${presignedUploadUrl}`);
802
840
  if (this.sdk.engineInstance && !this.sdk.engineInstance.FS) {
803
- this.sdk.logger.error("Engine instance is missing the Emscripten FS API");
841
+ logger.error("Engine instance is missing the Emscripten FS API");
804
842
  return false;
805
843
  }
806
844
  let success = false;
@@ -813,9 +851,9 @@ var FileSystemManager = class extends WavedashManager {
813
851
  }
814
852
  // Helper to download a file from a URL and save locally
815
853
  async download(url, filePath) {
816
- this.sdk.logger.debug(`Downloading ${filePath} from: ${url}`);
854
+ logger.debug(`Downloading ${filePath} from: ${url}`);
817
855
  if (this.sdk.engineInstance && !this.sdk.engineInstance.FS) {
818
- this.sdk.logger.error("Engine instance is missing the Emscripten FS API");
856
+ logger.error("Engine instance is missing the Emscripten FS API");
819
857
  return false;
820
858
  }
821
859
  const jwt = await this.sdk.ensureGameplayJwt();
@@ -826,7 +864,7 @@ var FileSystemManager = class extends WavedashManager {
826
864
  }
827
865
  });
828
866
  if (!response.ok) {
829
- this.sdk.logger.error(
867
+ logger.error(
830
868
  `Failed to download remote file: ${response.status} (${response.statusText})`
831
869
  );
832
870
  return false;
@@ -848,10 +886,10 @@ var FileSystemManager = class extends WavedashManager {
848
886
  const success = await this.writeLocalFile(filePath, dataArray);
849
887
  if (!success) return false;
850
888
  }
851
- this.sdk.logger.debug(`Successfully saved to: ${filePath}`);
889
+ logger.debug(`Successfully saved to: ${filePath}`);
852
890
  return true;
853
891
  } catch (error) {
854
- this.sdk.logger.error(
892
+ logger.error(
855
893
  `Failed to save file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
856
894
  );
857
895
  return false;
@@ -888,7 +926,7 @@ var FileSystemManager = class extends WavedashManager {
888
926
  try {
889
927
  const blob = await this.readLocalFileBlob(indexedDBKey);
890
928
  if (!blob) {
891
- this.sdk.logger.error(`File not found in IndexedDB: ${indexedDBKey}`);
929
+ logger.error(`File not found in IndexedDB: ${indexedDBKey}`);
892
930
  return false;
893
931
  }
894
932
  const response = await fetch(presignedUploadUrl, {
@@ -898,7 +936,7 @@ var FileSystemManager = class extends WavedashManager {
898
936
  });
899
937
  return response.ok;
900
938
  } catch (error) {
901
- this.sdk.logger.error(`Error uploading from IndexedDB: ${error}`);
939
+ logger.error(`Error uploading from IndexedDB: ${error}`);
902
940
  return false;
903
941
  }
904
942
  }
@@ -920,14 +958,14 @@ var FileSystemManager = class extends WavedashManager {
920
958
  return response.ok;
921
959
  } catch (error) {
922
960
  const msg = error instanceof Error ? error.message : String(error);
923
- this.sdk.logger.error(`Error uploading from FS: ${msg}`);
961
+ logger.error(`Error uploading from FS: ${msg}`);
924
962
  return false;
925
963
  }
926
964
  }
927
965
  async readLocalFileBlob(filePath) {
928
- this.sdk.logger.debug(`Reading local file (blob): ${filePath}`);
966
+ logger.debug(`Reading local file (blob): ${filePath}`);
929
967
  if (this.sdk.engineInstance?.FS) {
930
- this.sdk.logger.error(
968
+ logger.error(
931
969
  `${this.sdk.engineInstance.type} engine detected, use engine's builtin file access to read files.`
932
970
  );
933
971
  return null;
@@ -937,7 +975,7 @@ var FileSystemManager = class extends WavedashManager {
937
975
  if (!record) return null;
938
976
  return toBlobFromIndexedDBValue(record);
939
977
  } catch (error) {
940
- this.sdk.logger.error(`Failed to read local file blob: ${error}`);
978
+ logger.error(`Failed to read local file blob: ${error}`);
941
979
  return null;
942
980
  }
943
981
  }
@@ -1063,7 +1101,8 @@ var LeaderboardManager = class extends WavedashManager {
1063
1101
  const entry = result.entry ? {
1064
1102
  ...result.entry,
1065
1103
  userId: this.sdk.wavedashUser.id,
1066
- username: this.sdk.wavedashUser.username
1104
+ username: this.sdk.wavedashUser.username,
1105
+ userAvatarUrl: this.sdk.wavedashUser.avatarUrl
1067
1106
  } : null;
1068
1107
  return entry ? [entry] : [];
1069
1108
  }
@@ -1075,6 +1114,7 @@ var LeaderboardManager = class extends WavedashManager {
1075
1114
  if (result && result.totalEntries) {
1076
1115
  this.updateCachedTotalEntries(leaderboardId, result.totalEntries);
1077
1116
  }
1117
+ this.sdk.friendsManager.cacheLeaderboardPage(result.entries);
1078
1118
  return result.entries;
1079
1119
  }
1080
1120
  async listLeaderboardEntries(leaderboardId, offset, limit, friendsOnly = false) {
@@ -1085,6 +1125,7 @@ var LeaderboardManager = class extends WavedashManager {
1085
1125
  if (result && result.totalEntries) {
1086
1126
  this.updateCachedTotalEntries(leaderboardId, result.totalEntries);
1087
1127
  }
1128
+ this.sdk.friendsManager.cacheLeaderboardPage(result.entries);
1088
1129
  return result.entries;
1089
1130
  }
1090
1131
  async uploadLeaderboardScore(leaderboardId, score, keepBest, ugcId) {
@@ -1262,7 +1303,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1262
1303
  return this.updateP2PConnection(members);
1263
1304
  }
1264
1305
  if (this.initializationInProgress && this.initializationLobbyId === lobbyId) {
1265
- this.sdk.logger.debug(
1306
+ logger.debug(
1266
1307
  "P2P initialization already in progress, waiting..."
1267
1308
  );
1268
1309
  await this.initializationInProgress;
@@ -1330,7 +1371,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1330
1371
  if (!this.currentConnection) {
1331
1372
  throw new Error("No existing P2P connection to update");
1332
1373
  }
1333
- this.sdk.logger.debug("Updating P2P connection with new member list");
1374
+ logger.debug("Updating P2P connection with new member list");
1334
1375
  const currentPeerUserIds = new Set(
1335
1376
  Object.keys(this.currentConnection.peers)
1336
1377
  );
@@ -1345,7 +1386,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1345
1386
  existingPeer.username = member.username;
1346
1387
  }
1347
1388
  } else {
1348
- this.sdk.logger.debug(
1389
+ logger.debug(
1349
1390
  `Adding new peer: ${member.username} (${member.id})`
1350
1391
  );
1351
1392
  this.currentConnection.peers[member.id] = {
@@ -1359,7 +1400,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1359
1400
  const currentUserId = this.sdk.getUserId();
1360
1401
  const connectionPromises = connectionsToCreate.map((userId) => {
1361
1402
  const shouldCreateChannels = currentUserId < userId;
1362
- this.sdk.logger.debug(
1403
+ logger.debug(
1363
1404
  `Creating connection to new peer ${userId}, shouldCreateChannels: ${shouldCreateChannels}`
1364
1405
  );
1365
1406
  return this.createPeerConnection(
@@ -1374,13 +1415,13 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1374
1415
  );
1375
1416
  if (peersToInitiate.length > 0) {
1376
1417
  const offerPromises = peersToInitiate.map((userId) => {
1377
- this.sdk.logger.debug(
1418
+ logger.debug(
1378
1419
  `Initiating offer to new peer ${userId} (lower userId rule)`
1379
1420
  );
1380
1421
  return this.createOfferToPeer(userId);
1381
1422
  });
1382
1423
  await Promise.all(offerPromises);
1383
- this.sdk.logger.debug(
1424
+ logger.debug(
1384
1425
  `Initiated ${offerPromises.length} offers to new peers`
1385
1426
  );
1386
1427
  }
@@ -1390,7 +1431,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1390
1431
  )) {
1391
1432
  if (!newPeerUserIds.has(userId)) {
1392
1433
  const peer = this.currentConnection.peers[userId];
1393
- this.sdk.logger.debug(`Peer left: ${peer.username} (${userId})`);
1434
+ logger.debug(`Peer left: ${peer.username} (${userId})`);
1394
1435
  const pc = this.peerConnections.get(userId);
1395
1436
  if (pc) {
1396
1437
  pc.close();
@@ -1412,7 +1453,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1412
1453
  this.subscribeToSignalingMessages(connection);
1413
1454
  if (this.signalingSubscriptionReady) {
1414
1455
  await this.signalingSubscriptionReady;
1415
- this.sdk.logger.debug("Signaling subscription confirmed ready");
1456
+ logger.debug("Signaling subscription confirmed ready");
1416
1457
  }
1417
1458
  await this.establishPeerConnections(connection);
1418
1459
  }
@@ -1458,7 +1499,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1458
1499
  await this.handleSignalingMessage(message, connection);
1459
1500
  this.processedSignalingMessages.add(message._id);
1460
1501
  } catch (error) {
1461
- this.sdk.logger.error("Error handling signaling message:", error);
1502
+ logger.error("Error handling signaling message:", error);
1462
1503
  }
1463
1504
  }
1464
1505
  if (newMessageIds.length > 0) {
@@ -1471,7 +1512,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1471
1512
  this.pendingProcessedMessageIds.delete(messageId);
1472
1513
  }
1473
1514
  } catch (error) {
1474
- this.sdk.logger.error(
1515
+ logger.error(
1475
1516
  "Failed to mark signaling messages as processed:",
1476
1517
  error
1477
1518
  );
@@ -1488,7 +1529,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1488
1529
  const remoteUserId = message.fromUserId;
1489
1530
  if (!this.peerConnections.has(remoteUserId)) {
1490
1531
  if (message.messageType === P2P_SIGNALING_MESSAGE_TYPE.OFFER) {
1491
- this.sdk.logger.debug(
1532
+ logger.debug(
1492
1533
  `Received offer from ${remoteUserId} before peer connection exists, creating on-demand`
1493
1534
  );
1494
1535
  if (!connection.peers[remoteUserId]) {
@@ -1505,13 +1546,13 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1505
1546
  // shouldCreateChannels = false, we'll receive them via ondatachannel
1506
1547
  );
1507
1548
  if (!success) {
1508
- this.sdk.logger.error(
1549
+ logger.error(
1509
1550
  `Failed to create on-demand peer connection for ${remoteUserId}`
1510
1551
  );
1511
1552
  return;
1512
1553
  }
1513
1554
  } else {
1514
- this.sdk.logger.warn(
1555
+ logger.warn(
1515
1556
  `No peer connection for user ${remoteUserId}, dropping ${message.messageType} message`
1516
1557
  );
1517
1558
  return;
@@ -1521,14 +1562,14 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1521
1562
  switch (message.messageType) {
1522
1563
  case P2P_SIGNALING_MESSAGE_TYPE.OFFER: {
1523
1564
  this.iceRestartInProgress.delete(remoteUserId);
1524
- this.sdk.logger.debug(`Processing offer from peer ${remoteUserId}:`);
1565
+ logger.debug(`Processing offer from peer ${remoteUserId}:`);
1525
1566
  await pc.setRemoteDescription(
1526
1567
  new RTCSessionDescription(message.data)
1527
1568
  );
1528
1569
  await this.flushPendingIceCandidates(remoteUserId, pc);
1529
1570
  const answer = await pc.createAnswer();
1530
1571
  await pc.setLocalDescription(answer);
1531
- this.sdk.logger.debug(
1572
+ logger.debug(
1532
1573
  ` Answer created, waiting for ondatachannel events...`
1533
1574
  );
1534
1575
  const answerData = {
@@ -1553,7 +1594,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1553
1594
  const pending = this.pendingIceCandidates.get(remoteUserId) || [];
1554
1595
  pending.push(iceData);
1555
1596
  this.pendingIceCandidates.set(remoteUserId, pending);
1556
- this.sdk.logger.debug(
1597
+ logger.debug(
1557
1598
  `Buffered ICE candidate for ${remoteUserId} (remote description not yet set, ${pending.length} buffered)`
1558
1599
  );
1559
1600
  } else {
@@ -1562,7 +1603,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1562
1603
  break;
1563
1604
  }
1564
1605
  default:
1565
- this.sdk.logger.warn(
1606
+ logger.warn(
1566
1607
  "Unknown signaling message type:",
1567
1608
  message.messageType
1568
1609
  );
@@ -1579,7 +1620,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1579
1620
  try {
1580
1621
  await pc.addIceCandidate(new RTCIceCandidate(candidate));
1581
1622
  } catch (error) {
1582
- this.sdk.logger.warn(
1623
+ logger.warn(
1583
1624
  `Failed to add buffered ICE candidate for ${remoteUserId}:`,
1584
1625
  error
1585
1626
  );
@@ -1589,13 +1630,13 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1589
1630
  }
1590
1631
  }
1591
1632
  async establishPeerConnections(connection) {
1592
- this.sdk.logger.debug("Establishing WebRTC connections to peers...");
1633
+ logger.debug("Establishing WebRTC connections to peers...");
1593
1634
  const currentUserId = this.sdk.getUserId();
1594
1635
  const connectionPromises = [];
1595
1636
  Object.entries(connection.peers).forEach(
1596
1637
  ([userId, peer]) => {
1597
1638
  const shouldCreateChannels = currentUserId < userId;
1598
- this.sdk.logger.debug(
1639
+ logger.debug(
1599
1640
  `Creating connection to peer ${userId} (${peer.username}), shouldCreateChannels: ${shouldCreateChannels}`
1600
1641
  );
1601
1642
  connectionPromises.push(
@@ -1607,17 +1648,17 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1607
1648
  const peersToInitiate = Object.keys(connection.peers).filter((userId) => currentUserId < userId);
1608
1649
  if (peersToInitiate.length > 0) {
1609
1650
  const offerPromises = peersToInitiate.map((userId) => {
1610
- this.sdk.logger.debug(
1651
+ logger.debug(
1611
1652
  `Initiating offer to peer ${userId} (lower userId rule)`
1612
1653
  );
1613
1654
  return this.createOfferToPeer(userId);
1614
1655
  });
1615
1656
  await Promise.all(offerPromises);
1616
- this.sdk.logger.debug(
1657
+ logger.debug(
1617
1658
  `Created ${connectionPromises.length} peer connections and initiated ${offerPromises.length} offers`
1618
1659
  );
1619
1660
  } else {
1620
- this.sdk.logger.debug(
1661
+ logger.debug(
1621
1662
  `Created ${connectionPromises.length} peer connections, no offers to initiate`
1622
1663
  );
1623
1664
  }
@@ -1629,11 +1670,11 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1629
1670
  }
1630
1671
  const reliableChannel = this.reliableChannels.get(remoteUserId);
1631
1672
  const unreliableChannel = this.unreliableChannels.get(remoteUserId);
1632
- this.sdk.logger.debug(`Creating offer to peer ${remoteUserId}:`);
1633
- this.sdk.logger.debug(
1673
+ logger.debug(`Creating offer to peer ${remoteUserId}:`);
1674
+ logger.debug(
1634
1675
  ` Reliable channel state: ${reliableChannel?.readyState || "none"}`
1635
1676
  );
1636
- this.sdk.logger.debug(
1677
+ logger.debug(
1637
1678
  ` Unreliable channel state: ${unreliableChannel?.readyState || "none"}`
1638
1679
  );
1639
1680
  const offer = await pc.createOffer();
@@ -1650,7 +1691,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1650
1691
  async createPeerConnection(remoteUserId, connection, shouldCreateChannels = false) {
1651
1692
  const iceServers = await this.getIceServers();
1652
1693
  if (!iceServers) {
1653
- this.sdk.logger.error(
1694
+ logger.error(
1654
1695
  `No ICE servers available for peer ${remoteUserId}`
1655
1696
  );
1656
1697
  this.sdk.gameEventManager.notifyGame(
@@ -1674,7 +1715,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1674
1715
  rtcpMuxPolicy: "require"
1675
1716
  });
1676
1717
  if (shouldCreateChannels) {
1677
- this.sdk.logger.debug(`Creating data channels for peer ${remoteUserId}`);
1718
+ logger.debug(`Creating data channels for peer ${remoteUserId}`);
1678
1719
  if (this.config.enableReliableChannel) {
1679
1720
  const reliableChannel = pc.createDataChannel("reliable", {
1680
1721
  ordered: true,
@@ -1702,17 +1743,17 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1702
1743
  );
1703
1744
  }
1704
1745
  } else {
1705
- this.sdk.logger.debug(
1746
+ logger.debug(
1706
1747
  `Will receive data channels from peer ${remoteUserId} via ondatachannel`
1707
1748
  );
1708
1749
  }
1709
1750
  pc.onicecandidate = (event) => {
1710
1751
  if (event.candidate) {
1711
1752
  const candidateType = event.candidate.candidate.includes("typ host") ? "host" : event.candidate.candidate.includes("typ srflx") ? "srflx (STUN)" : event.candidate.candidate.includes("typ relay") ? "relay (TURN)" : "unknown";
1712
- this.sdk.logger.debug(
1753
+ logger.debug(
1713
1754
  `Peer ${remoteUserId} gathered ICE candidate: ${candidateType}`
1714
1755
  );
1715
- this.sdk.logger.debug(` Candidate: ${event.candidate.candidate}`);
1756
+ logger.debug(` Candidate: ${event.candidate.candidate}`);
1716
1757
  const candidateData = {
1717
1758
  candidate: event.candidate.candidate,
1718
1759
  sdpMid: event.candidate.sdpMid,
@@ -1727,7 +1768,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1727
1768
  };
1728
1769
  pc.ondatachannel = (event) => {
1729
1770
  const channel = event.channel;
1730
- this.sdk.logger.debug(
1771
+ logger.debug(
1731
1772
  `Received ${channel.label} data channel from peer ${remoteUserId}`
1732
1773
  );
1733
1774
  if (channel.label === "reliable") {
@@ -1742,21 +1783,21 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1742
1783
  );
1743
1784
  };
1744
1785
  pc.onconnectionstatechange = () => {
1745
- this.sdk.logger.debug(
1786
+ logger.debug(
1746
1787
  `Peer ${remoteUserId} connection state: ${pc.connectionState}`
1747
1788
  );
1748
1789
  if (pc.connectionState === "connected") {
1749
- this.sdk.logger.debug(
1790
+ logger.debug(
1750
1791
  ` Peer ${remoteUserId} fully connected, expecting ondatachannel events now...`
1751
1792
  );
1752
1793
  }
1753
1794
  };
1754
1795
  pc.oniceconnectionstatechange = () => {
1755
- this.sdk.logger.debug(
1796
+ logger.debug(
1756
1797
  `Peer ${remoteUserId} ICE connection state: ${pc.iceConnectionState}`
1757
1798
  );
1758
1799
  if (pc.iceConnectionState === "connected") {
1759
- this.sdk.logger.debug(
1800
+ logger.debug(
1760
1801
  ` ICE connected to peer ${remoteUserId}, data channels should be available...`
1761
1802
  );
1762
1803
  this.iceRestartAttempts.delete(remoteUserId);
@@ -1774,7 +1815,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1774
1815
  }
1775
1816
  }
1776
1817
  } else if (pc.iceConnectionState === "failed") {
1777
- this.sdk.logger.debug(
1818
+ logger.debug(
1778
1819
  `ICE connection to peer ${remoteUserId} failed, will retry in 500ms...`
1779
1820
  );
1780
1821
  if (!this.reconnectingPeers.has(remoteUserId)) {
@@ -1792,20 +1833,20 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1792
1833
  }
1793
1834
  setTimeout(() => {
1794
1835
  if (pc.iceConnectionState === "failed") {
1795
- this.sdk.logger.warn(
1836
+ logger.warn(
1796
1837
  `ICE connection to peer ${remoteUserId} still failed after delay, attempting ICE restart...`
1797
1838
  );
1798
1839
  this.attemptIceRestart(remoteUserId, pc);
1799
1840
  }
1800
1841
  }, 500);
1801
1842
  } else if (pc.iceConnectionState === "disconnected") {
1802
- this.sdk.logger.debug(
1843
+ logger.debug(
1803
1844
  `ICE connection to peer ${remoteUserId} disconnected, may recover...`
1804
1845
  );
1805
1846
  }
1806
1847
  };
1807
1848
  pc.onicegatheringstatechange = () => {
1808
- this.sdk.logger.debug(
1849
+ logger.debug(
1809
1850
  `Peer ${remoteUserId} ICE gathering state: ${pc.iceGatheringState}`
1810
1851
  );
1811
1852
  };
@@ -1819,20 +1860,20 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1819
1860
  async attemptIceRestart(remoteUserId, pc) {
1820
1861
  const currentUserId = this.sdk.getUserId();
1821
1862
  if (currentUserId > remoteUserId) {
1822
- this.sdk.logger.debug(
1863
+ logger.debug(
1823
1864
  `Waiting for peer ${remoteUserId} to initiate ICE restart (they have lower userId)`
1824
1865
  );
1825
1866
  return;
1826
1867
  }
1827
1868
  if (this.iceRestartInProgress.has(remoteUserId)) {
1828
- this.sdk.logger.debug(
1869
+ logger.debug(
1829
1870
  `ICE restart already in progress for peer ${remoteUserId}, skipping`
1830
1871
  );
1831
1872
  return;
1832
1873
  }
1833
1874
  const attempts = this.iceRestartAttempts.get(remoteUserId) || 0;
1834
1875
  if (attempts >= this.MAX_ICE_RESTART_ATTEMPTS) {
1835
- this.sdk.logger.error(
1876
+ logger.error(
1836
1877
  `Max ICE restart attempts (${this.MAX_ICE_RESTART_ATTEMPTS}) reached for peer ${remoteUserId}, giving up`
1837
1878
  );
1838
1879
  this.reconnectingPeers.delete(remoteUserId);
@@ -1853,7 +1894,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1853
1894
  }
1854
1895
  this.iceRestartAttempts.set(remoteUserId, attempts + 1);
1855
1896
  this.iceRestartInProgress.add(remoteUserId);
1856
- this.sdk.logger.debug(
1897
+ logger.debug(
1857
1898
  `ICE restart attempt ${attempts + 1}/${this.MAX_ICE_RESTART_ATTEMPTS} for peer ${remoteUserId}`
1858
1899
  );
1859
1900
  try {
@@ -1868,9 +1909,9 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1868
1909
  type: P2P_SIGNALING_MESSAGE_TYPE.OFFER,
1869
1910
  data: offerData
1870
1911
  });
1871
- this.sdk.logger.debug(`ICE restart offer sent to peer ${remoteUserId}`);
1912
+ logger.debug(`ICE restart offer sent to peer ${remoteUserId}`);
1872
1913
  } catch (error) {
1873
- this.sdk.logger.error(
1914
+ logger.error(
1874
1915
  `Failed to initiate ICE restart for peer ${remoteUserId}:`,
1875
1916
  error
1876
1917
  );
@@ -1878,7 +1919,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1878
1919
  }
1879
1920
  setupDataChannelHandlers(channel, remoteUserId, type) {
1880
1921
  channel.onopen = () => {
1881
- this.sdk.logger.debug(
1922
+ logger.debug(
1882
1923
  `${type} data channel opened with peer ${remoteUserId}`
1883
1924
  );
1884
1925
  if (this.isPeerReady(remoteUserId) && !this.establishedPeers.has(remoteUserId)) {
@@ -1899,7 +1940,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1899
1940
  this.enqueueMessage(event.data, remoteUserId);
1900
1941
  };
1901
1942
  channel.onerror = (error) => {
1902
- this.sdk.logger.error(
1943
+ logger.error(
1903
1944
  `Data channel error with peer ${remoteUserId}:`,
1904
1945
  error
1905
1946
  );
@@ -1916,7 +1957,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1916
1957
  }
1917
1958
  };
1918
1959
  channel.onclose = () => {
1919
- this.sdk.logger.debug(
1960
+ logger.debug(
1920
1961
  `${type} data channel closed with peer ${remoteUserId}`
1921
1962
  );
1922
1963
  this.establishedPeers.delete(remoteUserId);
@@ -1940,42 +1981,42 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1940
1981
  this.ensureInitialized();
1941
1982
  try {
1942
1983
  if (!this.currentConnection) {
1943
- this.sdk.logger.error(
1984
+ logger.error(
1944
1985
  `P2P send called before P2P is initialized, dropping message.`
1945
1986
  );
1946
1987
  this.reportPacketDrop(appChannel, "SEND", "PEER_NOT_READY");
1947
1988
  return false;
1948
1989
  }
1949
1990
  if (!payload) {
1950
- this.sdk.logger.error(
1991
+ logger.error(
1951
1992
  `P2P send called with missing payload, dropping message.`
1952
1993
  );
1953
1994
  this.reportPacketDrop(appChannel, "SEND", "INVALID_PAYLOAD_SIZE");
1954
1995
  return false;
1955
1996
  }
1956
1997
  if (!Number.isInteger(appChannel) || appChannel < 0 || appChannel >= this.MAX_CHANNELS) {
1957
- this.sdk.logger.error(
1998
+ logger.error(
1958
1999
  `P2P appChannel must be an integer in [0, ${this.MAX_CHANNELS}), received ${appChannel}, dropping message.`
1959
2000
  );
1960
2001
  this.reportPacketDrop(-1, "SEND", "INVALID_CHANNEL");
1961
2002
  return false;
1962
2003
  }
1963
2004
  if (payloadSize <= 0) {
1964
- this.sdk.logger.error(
2005
+ logger.error(
1965
2006
  `P2P payloadSize must be greater than 0, received ${payloadSize}, dropping message.`
1966
2007
  );
1967
2008
  this.reportPacketDrop(appChannel, "SEND", "INVALID_PAYLOAD_SIZE");
1968
2009
  return false;
1969
2010
  }
1970
2011
  if (payloadSize > this.MAX_PAYLOAD_SIZE) {
1971
- this.sdk.logger.error(
2012
+ logger.error(
1972
2013
  `P2P payload too large: ${payloadSize} bytes exceeds max ${this.MAX_PAYLOAD_SIZE} bytes, dropping message.`
1973
2014
  );
1974
2015
  this.reportPacketDrop(appChannel, "SEND", "PAYLOAD_TOO_LARGE");
1975
2016
  return false;
1976
2017
  }
1977
2018
  if (payloadSize > payload.length) {
1978
- this.sdk.logger.error(
2019
+ logger.error(
1979
2020
  `payloadSize is greater than payload buffer length: ${payloadSize} > ${payload.length}, dropping message.`
1980
2021
  );
1981
2022
  this.reportPacketDrop(appChannel, "SEND", "INVALID_PAYLOAD_SIZE");
@@ -1990,7 +2031,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1990
2031
  try {
1991
2032
  channel.send(messageData);
1992
2033
  } catch (error) {
1993
- this.sdk.logger.error(
2034
+ logger.error(
1994
2035
  `P2P broadcast to peer ${peerUserId} failed:`,
1995
2036
  error
1996
2037
  );
@@ -1999,7 +2040,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
1999
2040
  } else {
2000
2041
  const channel = channelMap.get(toUserId);
2001
2042
  if (!channel || channel.readyState !== "open") {
2002
- this.sdk.logger.error(
2043
+ logger.error(
2003
2044
  `P2P no open channel to peer ${toUserId}, dropping message.`
2004
2045
  );
2005
2046
  this.reportPacketDrop(appChannel, "SEND", "PEER_NOT_READY");
@@ -2008,7 +2049,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2008
2049
  try {
2009
2050
  channel.send(messageData);
2010
2051
  } catch (error) {
2011
- this.sdk.logger.error(
2052
+ logger.error(
2012
2053
  `P2P send to peer ${toUserId} failed, dropping message:`,
2013
2054
  error
2014
2055
  );
@@ -2018,7 +2059,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2018
2059
  }
2019
2060
  return true;
2020
2061
  } catch (error) {
2021
- this.sdk.logger.error(`Error sending P2P message:`, error);
2062
+ logger.error(`Error sending P2P message:`, error);
2022
2063
  return false;
2023
2064
  }
2024
2065
  }
@@ -2039,9 +2080,9 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2039
2080
  data: message.data
2040
2081
  }
2041
2082
  );
2042
- this.sdk.logger.debug("Sent signaling message:", message.type);
2083
+ logger.debug("Sent signaling message:", message.type);
2043
2084
  } catch (error) {
2044
- this.sdk.logger.error("Failed to send signaling message:", error);
2085
+ logger.error("Failed to send signaling message:", error);
2045
2086
  throw error;
2046
2087
  }
2047
2088
  }
@@ -2127,7 +2168,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2127
2168
  messageCount: 0,
2128
2169
  incomingDataView
2129
2170
  });
2130
- this.sdk.logger.debug(
2171
+ logger.debug(
2131
2172
  `Allocated P2P ring buffer for channel ${channel} (${(queueDataSize / 1024 / 1024).toFixed(1)}MB)`
2132
2173
  );
2133
2174
  }
@@ -2197,7 +2238,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2197
2238
  enqueueMessage(wireData, fromUserId) {
2198
2239
  try {
2199
2240
  if (wireData.byteLength < this.WIRE_PAYLOAD_OFFSET) {
2200
- this.sdk.logger.warn("Binary message too short to extract channel");
2241
+ logger.warn("Binary message too short to extract channel");
2201
2242
  this.reportPacketDrop(-1, "RECEIVE", "MALFORMED");
2202
2243
  return;
2203
2244
  }
@@ -2205,7 +2246,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2205
2246
  const channel = wireBytes[this.WIRE_CHANNEL_OFFSET];
2206
2247
  if (!this.channelQueues.has(channel)) {
2207
2248
  if (channel >= this.MAX_CHANNELS) {
2208
- this.sdk.logger.warn(
2249
+ logger.warn(
2209
2250
  `Channel ${channel} exceeds max channels (${this.MAX_CHANNELS}), dropping message`
2210
2251
  );
2211
2252
  this.reportPacketDrop(channel, "RECEIVE", "INVALID_CHANNEL");
@@ -2215,7 +2256,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2215
2256
  }
2216
2257
  const queue = this.channelQueues.get(channel);
2217
2258
  if (queue.messageCount >= this.QUEUE_SIZE) {
2218
- this.sdk.logger.warn(
2259
+ logger.warn(
2219
2260
  `P2P message queue full for channel ${channel}, dropping message`
2220
2261
  );
2221
2262
  this.reportPacketDrop(channel, "RECEIVE", "QUEUE_FULL");
@@ -2225,7 +2266,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2225
2266
  const storedSize = this.PAYLOAD_OFFSET + payloadLength;
2226
2267
  const maxMessageSize = this.MESSAGE_SIZE - this.MESSAGE_SLOT_HEADER_SIZE;
2227
2268
  if (storedSize > maxMessageSize) {
2228
- this.sdk.logger.warn(
2269
+ logger.warn(
2229
2270
  `Message too large for queue: ${storedSize} > ${maxMessageSize}, dropping message.`
2230
2271
  );
2231
2272
  this.reportPacketDrop(channel, "RECEIVE", "PAYLOAD_TOO_LARGE");
@@ -2265,7 +2306,7 @@ var _P2PManager = class _P2PManager extends WavedashManager {
2265
2306
  queue.writeIndex = (queue.writeIndex + 1) % this.QUEUE_SIZE;
2266
2307
  queue.messageCount++;
2267
2308
  } catch (error) {
2268
- this.sdk.logger.error(`Error enqueuing binary P2P message:`, error);
2309
+ logger.error(`Error enqueuing binary P2P message:`, error);
2269
2310
  }
2270
2311
  }
2271
2312
  // Returns the max payload size (what game engines should report as max packet size)
@@ -2483,7 +2524,7 @@ var StatsManager = class extends WavedashManager {
2483
2524
  );
2484
2525
  this.subscribe();
2485
2526
  this.requestStats().catch((error) => {
2486
- this.sdk.logger.error("Initial stats fetch failed:", error);
2527
+ logger.error("Initial stats fetch failed:", error);
2487
2528
  });
2488
2529
  }
2489
2530
  destroy() {
@@ -2506,7 +2547,7 @@ var StatsManager = class extends WavedashManager {
2506
2547
  this.knownStatIds = new Set(ids);
2507
2548
  },
2508
2549
  (error) => {
2509
- this.sdk.logger.error("Stat identifiers subscription error:", error);
2550
+ logger.error("Stat identifiers subscription error:", error);
2510
2551
  }
2511
2552
  ),
2512
2553
  this.sdk.convexClient.onUpdate(
@@ -2516,7 +2557,7 @@ var StatsManager = class extends WavedashManager {
2516
2557
  this.knownAchievementIds = new Set(ids);
2517
2558
  },
2518
2559
  (error) => {
2519
- this.sdk.logger.error(
2560
+ logger.error(
2520
2561
  "Achievement identifiers subscription error:",
2521
2562
  error
2522
2563
  );
@@ -2532,7 +2573,7 @@ var StatsManager = class extends WavedashManager {
2532
2573
  }
2533
2574
  },
2534
2575
  (error) => {
2535
- this.sdk.logger.error("Achievement subscription error:", error);
2576
+ logger.error("Achievement subscription error:", error);
2536
2577
  }
2537
2578
  )
2538
2579
  );
@@ -2583,7 +2624,7 @@ var StatsManager = class extends WavedashManager {
2583
2624
  });
2584
2625
  }).catch((error) => {
2585
2626
  const message = error instanceof Error ? error.message : `Error storing stats: ${error}`;
2586
- this.sdk.logger.error(message);
2627
+ logger.error(message);
2587
2628
  this.sdk.gameEventManager.notifyGame(WavedashEvents.STATS_STORED, {
2588
2629
  success: false,
2589
2630
  message
@@ -2747,7 +2788,7 @@ var HeartbeatManager = class extends WavedashManager {
2747
2788
  this.lastHeartbeatTime = Date.now();
2748
2789
  }
2749
2790
  }).catch((error) => {
2750
- this.sdk.logger.error(`Heartbeat failed: ${error}`);
2791
+ logger.error(`Heartbeat failed: ${error}`);
2751
2792
  }).finally(() => {
2752
2793
  this.heartbeatInFlight = false;
2753
2794
  });
@@ -2766,7 +2807,7 @@ var HeartbeatManager = class extends WavedashManager {
2766
2807
  });
2767
2808
  return true;
2768
2809
  } catch (error) {
2769
- this.sdk.logger.error(`Error updating presence: ${error}`);
2810
+ logger.error(`Error updating presence: ${error}`);
2770
2811
  return false;
2771
2812
  }
2772
2813
  }
@@ -2793,7 +2834,7 @@ var HeartbeatManager = class extends WavedashManager {
2793
2834
  );
2794
2835
  } else if (!this.isConnected && wasConnected) {
2795
2836
  this.disconnectedAt = Date.now();
2796
- this.sdk.logger.warn(
2837
+ logger.warn(
2797
2838
  "Backend disconnected - attempting to reconnect..."
2798
2839
  );
2799
2840
  this.sdk.gameEventManager.notifyGame(
@@ -2813,7 +2854,7 @@ var HeartbeatManager = class extends WavedashManager {
2813
2854
  this.sentDisconnectedEvent = false;
2814
2855
  }
2815
2856
  } catch (error) {
2816
- this.sdk.logger.error("Error testing connection:", error);
2857
+ logger.error("Error testing connection:", error);
2817
2858
  }
2818
2859
  }
2819
2860
  isCurrentlyConnected() {
@@ -2833,7 +2874,7 @@ var GameEventManager = class extends WavedashManager {
2833
2874
  notifyGame(event, payload) {
2834
2875
  if (!this.sdk.eventsReady) {
2835
2876
  this.eventQueue.push({ event, payload });
2836
- this.sdk.logger.debug(`Queued event: ${event}`);
2877
+ logger.debug(`Queued event: ${event}`);
2837
2878
  return;
2838
2879
  }
2839
2880
  if (!this.sdk.engineInstance) {
@@ -2851,7 +2892,7 @@ var GameEventManager = class extends WavedashManager {
2851
2892
  data
2852
2893
  );
2853
2894
  } else {
2854
- this.sdk.logger.error("Engine instance not set. Dropping event:", event);
2895
+ logger.error("Engine instance not set. Dropping event:", event);
2855
2896
  }
2856
2897
  }
2857
2898
  flushEventQueue() {
@@ -3009,21 +3050,10 @@ function getCdnImageUrl(r2Key, host, options) {
3009
3050
  var FriendsManager = class extends WavedashManager {
3010
3051
  constructor(sdk) {
3011
3052
  super(sdk);
3053
+ // Persistent cache for users with a durable relationship (self, friends, shared lobby).
3012
3054
  this.userCache = /* @__PURE__ */ new Map();
3013
- }
3014
- /**
3015
- * Cache users from any source (friends, lobby users)
3016
- * Accepts both Friend format (avatarUrl) and LobbyUser format (userAvatarUrl)
3017
- * @param users - Array of users with userId, username, and optional avatar r2Key
3018
- */
3019
- cacheUsers(users) {
3020
- for (const user of users) {
3021
- this.userCache.set(user.userId, {
3022
- username: user.username,
3023
- // Support both Friend (avatarUrl) and LobbyUser (userAvatarUrl) formats
3024
- avatarR2Key: user.avatarUrl ?? user.userAvatarUrl
3025
- });
3026
- }
3055
+ // Transient cache for only the most recently loaded leaderboard page. Cleared on each new page.
3056
+ this.leaderboardPageUserCache = /* @__PURE__ */ new Map();
3027
3057
  }
3028
3058
  /**
3029
3059
  * Returns CDN URL with size transformation for a cached user's avatar.
@@ -3033,7 +3063,7 @@ var FriendsManager = class extends WavedashManager {
3033
3063
  * @returns CDN URL with size transformation, or null if user not cached or has no avatar
3034
3064
  */
3035
3065
  getUserAvatarUrl(userId, size = AvatarSize.MEDIUM) {
3036
- const user = this.userCache.get(userId);
3066
+ const user = this.userCache.get(userId) ?? this.leaderboardPageUserCache.get(userId);
3037
3067
  if (!user?.avatarR2Key) {
3038
3068
  return null;
3039
3069
  }
@@ -3051,8 +3081,17 @@ var FriendsManager = class extends WavedashManager {
3051
3081
  * @returns The username, or null if user not cached
3052
3082
  */
3053
3083
  getUsername(userId) {
3054
- return this.userCache.get(userId)?.username ?? null;
3084
+ return this.userCache.get(userId)?.username ?? this.leaderboardPageUserCache.get(userId)?.username ?? null;
3055
3085
  }
3086
+ /**
3087
+ * List all friends for the logged in user
3088
+ * @returns Array<{
3089
+ * avatarUrl?: string;
3090
+ * isOnline: boolean;
3091
+ * userId: Id<"users">;
3092
+ * username: string;
3093
+ * }>
3094
+ */
3056
3095
  async listFriends() {
3057
3096
  const friends = await this.sdk.convexClient.query(
3058
3097
  api8.sdk.friends.listFriends,
@@ -3061,41 +3100,37 @@ var FriendsManager = class extends WavedashManager {
3061
3100
  this.cacheUsers(friends);
3062
3101
  return friends;
3063
3102
  }
3064
- };
3065
-
3066
- // src/utils/logger.ts
3067
- var LOG_LEVEL = {
3068
- DEBUG: 0,
3069
- // Most verbose
3070
- INFO: 1,
3071
- WARN: 2,
3072
- ERROR: 3
3073
- };
3074
- var WavedashLogger = class {
3075
- constructor(logLevel = LOG_LEVEL.WARN) {
3076
- this.logLevel = logLevel;
3077
- }
3078
- setLogLevel(level) {
3079
- this.logLevel = level;
3080
- }
3081
- debug(message, ...args) {
3082
- if (this.logLevel <= LOG_LEVEL.DEBUG) {
3083
- console.log(`[WavedashJS] ${message}`, ...args);
3084
- }
3085
- }
3086
- info(message, ...args) {
3087
- if (this.logLevel <= LOG_LEVEL.INFO) {
3088
- console.log(`[WavedashJS] ${message}`, ...args);
3089
- }
3090
- }
3091
- warn(message, ...args) {
3092
- if (this.logLevel <= LOG_LEVEL.WARN) {
3093
- console.warn(`[WavedashJS] ${message}`, ...args);
3103
+ /**
3104
+ * Cache users from any source (friends, lobby users)
3105
+ * Accepts both Friend format (avatarUrl) and LobbyUser format (userAvatarUrl)
3106
+ * @param users - Array of users with userId, username, and optional avatar r2Key
3107
+ * @internal
3108
+ */
3109
+ cacheUsers(users) {
3110
+ for (const user of users) {
3111
+ this.userCache.set(user.userId, {
3112
+ username: user.username,
3113
+ // Support both Friend (avatarUrl) and LobbyUser (userAvatarUrl) formats
3114
+ avatarR2Key: user.avatarUrl ?? user.userAvatarUrl
3115
+ });
3094
3116
  }
3095
3117
  }
3096
- error(message, ...args) {
3097
- if (this.logLevel <= LOG_LEVEL.ERROR) {
3098
- console.error(`[WavedashJS] ${message}`, ...args);
3118
+ /**
3119
+ * Replace the leaderboard-page cache with the users from a single page of
3120
+ * leaderboard entries.
3121
+ *
3122
+ * In general devs should just use the username and userAvatarUrl returned
3123
+ * from `listLeaderboardEntries` directly, but we cache the latest leaderboard
3124
+ * page here just so getUserAvatarUrl and getUsername still work for the current page.
3125
+ * @internal
3126
+ */
3127
+ cacheLeaderboardPage(users) {
3128
+ this.leaderboardPageUserCache.clear();
3129
+ for (const user of users) {
3130
+ this.leaderboardPageUserCache.set(user.userId, {
3131
+ username: user.username,
3132
+ avatarR2Key: user.userAvatarUrl
3133
+ });
3099
3134
  }
3100
3135
  }
3101
3136
  };
@@ -3194,6 +3229,69 @@ var IFrameMessenger = class {
3194
3229
  }
3195
3230
  };
3196
3231
 
3232
+ // src/utils/swMessenger.ts
3233
+ var SwMessenger = class {
3234
+ constructor() {
3235
+ this.handleMessage = (event) => {
3236
+ const data = event.data;
3237
+ const type = data?.type;
3238
+ if (!type) return;
3239
+ const set = this.listeners.get(type);
3240
+ if (!set || set.size === 0) return;
3241
+ const port = event.ports?.[0];
3242
+ const reply = (message) => {
3243
+ if (port) {
3244
+ try {
3245
+ port.postMessage(message);
3246
+ } catch (err) {
3247
+ logger.warn("Failed to reply to SW via port", err);
3248
+ }
3249
+ }
3250
+ this.postToServiceWorker(message);
3251
+ };
3252
+ for (const listener of set) listener(data?.payload, reply);
3253
+ };
3254
+ this.listeners = /* @__PURE__ */ new Map();
3255
+ if (typeof navigator !== "undefined" && navigator.serviceWorker) {
3256
+ navigator.serviceWorker.addEventListener("message", this.handleMessage);
3257
+ }
3258
+ }
3259
+ /**
3260
+ * Register a handler for an incoming message type from the SW. The handler
3261
+ * receives the message payload and a `reply` function that routes the
3262
+ * response back via the transferred MessagePort when present, falling back
3263
+ * to a controller postMessage otherwise.
3264
+ */
3265
+ addEventListener(type, listener) {
3266
+ let set = this.listeners.get(type);
3267
+ if (!set) {
3268
+ set = /* @__PURE__ */ new Set();
3269
+ this.listeners.set(type, set);
3270
+ }
3271
+ set.add(listener);
3272
+ }
3273
+ removeEventListener(type, listener) {
3274
+ this.listeners.get(type)?.delete(listener);
3275
+ }
3276
+ /**
3277
+ * Fire-and-forget message to the active service worker controller. No-op
3278
+ * when no SW is controlling the page (first load before activation, or
3279
+ * environments without SW support).
3280
+ */
3281
+ postToServiceWorker(message) {
3282
+ if (typeof navigator === "undefined" || !navigator.serviceWorker) {
3283
+ return false;
3284
+ }
3285
+ try {
3286
+ navigator.serviceWorker.controller?.postMessage(message);
3287
+ return true;
3288
+ } catch (err) {
3289
+ logger.warn("Failed to post message to service worker", err);
3290
+ return false;
3291
+ }
3292
+ }
3293
+ };
3294
+
3197
3295
  // src/index.ts
3198
3296
  import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE5, UrlParams } from "@wvdsh/api";
3199
3297
 
@@ -3344,7 +3442,7 @@ var WavedashSDK = class extends EventTarget {
3344
3442
  this.iframeMessenger = iframeMessenger;
3345
3443
  this.ugcHost = sdkConfig.ugcHost;
3346
3444
  this.uploadsHost = sdkConfig.uploadsHost;
3347
- this.logger = new WavedashLogger();
3445
+ this.swMessenger = new SwMessenger();
3348
3446
  this.p2pManager = new P2PManager(this);
3349
3447
  this.lobbyManager = new LobbyManager(this);
3350
3448
  this.statsManager = new StatsManager(this);
@@ -3377,10 +3475,11 @@ var WavedashSDK = class extends EventTarget {
3377
3475
  }
3378
3476
  ]);
3379
3477
  this.setupSessionEndListeners();
3478
+ this.setupSwCredsListener();
3380
3479
  this.launchParams = sdkConfig.launchParams ?? {};
3381
3480
  this.setupWarningTimeout = setTimeout(() => {
3382
3481
  this.setupWarningTimeout = null;
3383
- this.logger.warn(
3482
+ logger.warn(
3384
3483
  "Wavedash.init(), Wavedash.loadComplete(), or Wavedash.updateLoadProgressZeroToOne() not called yet"
3385
3484
  );
3386
3485
  }, 1e4);
@@ -3403,24 +3502,24 @@ var WavedashSDK = class extends EventTarget {
3403
3502
  init(config) {
3404
3503
  this.loadComplete();
3405
3504
  if (this._initialized) {
3406
- this.logger.warn("init called twice! Already initialized, skipping init");
3505
+ logger.warn("init called twice! Already initialized, skipping init");
3407
3506
  return false;
3408
3507
  }
3409
3508
  if (typeof config === "string") {
3410
3509
  try {
3411
3510
  config = JSON.parse(config);
3412
3511
  } catch (error) {
3413
- this.logger.error("Initialized with invalid config:", error);
3512
+ logger.error("Initialized with invalid config:", error);
3414
3513
  return false;
3415
3514
  }
3416
3515
  }
3417
3516
  this.config = config ?? {};
3418
3517
  this._initialized = true;
3419
- this.logger.setLogLevel(
3518
+ logger.setLogLevel(
3420
3519
  this.config.debug ? LOG_LEVEL.DEBUG : LOG_LEVEL.WARN
3421
3520
  );
3422
3521
  this.p2pManager.init(this.config.p2p);
3423
- this.logger.debug("Initialized with config:", this.config);
3522
+ logger.debug("Initialized with config:", this.config);
3424
3523
  if (!this.config.deferEvents) {
3425
3524
  this.readyForEvents();
3426
3525
  }
@@ -3567,13 +3666,13 @@ var WavedashSDK = class extends EventTarget {
3567
3666
  * @returns The user's JWT signed by the Wavedash backend
3568
3667
  */
3569
3668
  async getUserJwt() {
3570
- this.logger.debug("getUserJwt");
3669
+ logger.debug("getUserJwt");
3571
3670
  try {
3572
3671
  const data = await this.ensureGameplayJwt();
3573
3672
  return this.formatResponse({ success: true, data });
3574
3673
  } catch (error) {
3575
3674
  const message = error instanceof Error ? error.message : String(error);
3576
- this.logger.error("getUserJwt", message);
3675
+ logger.error("getUserJwt", message);
3577
3676
  return this.formatResponse({ success: false, data: null, message });
3578
3677
  }
3579
3678
  }
@@ -4217,29 +4316,29 @@ var WavedashSDK = class extends EventTarget {
4217
4316
  // require config or produce events (lobby join/create, P2P).
4218
4317
  ensureInit() {
4219
4318
  if (!this._initialized) {
4220
- this.logger.error("SDK not initialized. Call WavedashJS.init first.");
4319
+ logger.error("SDK not initialized. Call WavedashJS.init first.");
4221
4320
  throw new Error("SDK not initialized");
4222
4321
  }
4223
4322
  }
4224
4323
  async apiCall(manager, method, argSpecs, ...args) {
4225
- this.logger.debug(method, ...args);
4324
+ logger.debug(method, ...args);
4226
4325
  try {
4227
4326
  validateArgs(method, argSpecs, args);
4228
4327
  const data = await manager[method](...args);
4229
4328
  return this.formatResponse({ success: true, data });
4230
4329
  } catch (error) {
4231
4330
  const message = error instanceof Error ? error.message : String(error);
4232
- this.logger.error(method, message);
4331
+ logger.error(method, message);
4233
4332
  return this.formatResponse({ success: false, data: null, message });
4234
4333
  }
4235
4334
  }
4236
4335
  apiCallSync(target, method, argSpecs, ...args) {
4237
- this.logger.debug(method, ...args);
4336
+ logger.debug(method, ...args);
4238
4337
  try {
4239
4338
  validateArgs(method, argSpecs, args);
4240
4339
  } catch (error) {
4241
4340
  const message = error instanceof Error ? error.message : String(error);
4242
- this.logger.error(method, message);
4341
+ logger.error(method, message);
4243
4342
  throw error;
4244
4343
  }
4245
4344
  return this.formatResponse(target[method](...args));
@@ -4290,6 +4389,10 @@ var WavedashSDK = class extends EventTarget {
4290
4389
  iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE5.GAMEPLAY_JWT_READY, {
4291
4390
  gameplayJwt: this.gameplayJwt
4292
4391
  });
4392
+ this.swMessenger.postToServiceWorker({
4393
+ type: "embed.jwt-update",
4394
+ payload: { gameplayJwt: this.gameplayJwt }
4395
+ });
4293
4396
  return this.gameplayJwt;
4294
4397
  })().finally(() => {
4295
4398
  if (this.gameplayJwtPromise === promise) {
@@ -4323,6 +4426,30 @@ var WavedashSDK = class extends EventTarget {
4323
4426
  () => this.destroy()
4324
4427
  );
4325
4428
  }
4429
+ /**
4430
+ * Respond to the service worker's `embed.creds-request` with the SDK's
4431
+ * current gameplay JWT. The SW asks when it wakes from termination with no
4432
+ * in-memory or IDB credentials (e.g. Safari ITP storage decay) — we're the
4433
+ * fastest live source. JWT only; sessionToken is owned by the SW + cookies.
4434
+ */
4435
+ setupSwCredsListener() {
4436
+ this.swMessenger.addEventListener(
4437
+ "embed.creds-request",
4438
+ async (_payload, reply) => {
4439
+ let jwt;
4440
+ try {
4441
+ jwt = await this.ensureGameplayJwt();
4442
+ } catch (err) {
4443
+ logger.warn("Failed to resolve JWT for creds-request", err);
4444
+ return;
4445
+ }
4446
+ reply({
4447
+ type: "embed.creds-response",
4448
+ payload: { gameplayJwt: jwt }
4449
+ });
4450
+ }
4451
+ );
4452
+ }
4326
4453
  };
4327
4454
  function setupWavedashSDK() {
4328
4455
  const existing = window.Wavedash;