react-native-ble-mesh 2.0.0 → 2.1.2

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 (93) hide show
  1. package/README.md +2 -2
  2. package/docs/OPTIMIZATION.md +165 -52
  3. package/package.json +1 -1
  4. package/src/MeshNetwork.js +63 -53
  5. package/src/constants/audio.js +4 -4
  6. package/src/constants/ble.js +1 -1
  7. package/src/constants/crypto.js +1 -1
  8. package/src/constants/errors.js +2 -2
  9. package/src/constants/events.js +1 -1
  10. package/src/constants/protocol.js +2 -2
  11. package/src/crypto/AutoCrypto.js +16 -3
  12. package/src/crypto/CryptoProvider.js +17 -17
  13. package/src/crypto/providers/ExpoCryptoProvider.js +15 -9
  14. package/src/crypto/providers/QuickCryptoProvider.js +41 -12
  15. package/src/crypto/providers/TweetNaClProvider.js +10 -8
  16. package/src/errors/AudioError.js +2 -1
  17. package/src/errors/ConnectionError.js +2 -2
  18. package/src/errors/CryptoError.js +1 -1
  19. package/src/errors/HandshakeError.js +2 -2
  20. package/src/errors/MeshError.js +4 -4
  21. package/src/errors/MessageError.js +2 -2
  22. package/src/errors/ValidationError.js +3 -3
  23. package/src/expo/withBLEMesh.js +10 -10
  24. package/src/hooks/AppStateManager.js +11 -2
  25. package/src/hooks/useMesh.js +23 -10
  26. package/src/hooks/useMessages.js +17 -16
  27. package/src/hooks/usePeers.js +19 -14
  28. package/src/index.js +2 -2
  29. package/src/mesh/dedup/BloomFilter.js +45 -57
  30. package/src/mesh/dedup/DedupManager.js +36 -8
  31. package/src/mesh/dedup/MessageCache.js +3 -0
  32. package/src/mesh/fragment/Assembler.js +5 -4
  33. package/src/mesh/fragment/Fragmenter.js +3 -3
  34. package/src/mesh/monitor/ConnectionQuality.js +59 -25
  35. package/src/mesh/monitor/NetworkMonitor.js +80 -28
  36. package/src/mesh/peer/Peer.js +9 -11
  37. package/src/mesh/peer/PeerDiscovery.js +18 -19
  38. package/src/mesh/peer/PeerManager.js +29 -17
  39. package/src/mesh/router/MessageRouter.js +28 -20
  40. package/src/mesh/router/PathFinder.js +10 -13
  41. package/src/mesh/router/RouteTable.js +25 -14
  42. package/src/mesh/store/StoreAndForwardManager.js +32 -24
  43. package/src/protocol/deserializer.js +9 -10
  44. package/src/protocol/header.js +13 -7
  45. package/src/protocol/message.js +18 -14
  46. package/src/protocol/serializer.js +9 -12
  47. package/src/protocol/validator.js +29 -10
  48. package/src/service/BatteryOptimizer.js +22 -18
  49. package/src/service/EmergencyManager.js +18 -25
  50. package/src/service/HandshakeManager.js +112 -18
  51. package/src/service/MeshService.js +106 -22
  52. package/src/service/SessionManager.js +50 -13
  53. package/src/service/audio/AudioManager.js +80 -38
  54. package/src/service/audio/buffer/FrameBuffer.js +7 -8
  55. package/src/service/audio/buffer/JitterBuffer.js +1 -1
  56. package/src/service/audio/codec/LC3Codec.js +18 -19
  57. package/src/service/audio/codec/LC3Decoder.js +10 -10
  58. package/src/service/audio/codec/LC3Encoder.js +11 -9
  59. package/src/service/audio/session/AudioSession.js +14 -17
  60. package/src/service/audio/session/VoiceMessage.js +15 -22
  61. package/src/service/audio/transport/AudioFragmenter.js +17 -9
  62. package/src/service/audio/transport/AudioFramer.js +8 -12
  63. package/src/service/file/FileAssembler.js +4 -2
  64. package/src/service/file/FileChunker.js +1 -1
  65. package/src/service/file/FileManager.js +26 -20
  66. package/src/service/file/FileMessage.js +7 -12
  67. package/src/service/text/TextManager.js +75 -42
  68. package/src/service/text/broadcast/BroadcastManager.js +14 -17
  69. package/src/service/text/channel/Channel.js +10 -14
  70. package/src/service/text/channel/ChannelManager.js +10 -10
  71. package/src/service/text/message/TextMessage.js +12 -19
  72. package/src/service/text/message/TextSerializer.js +2 -2
  73. package/src/storage/AsyncStorageAdapter.js +17 -14
  74. package/src/storage/MemoryStorage.js +11 -8
  75. package/src/storage/MessageStore.js +77 -32
  76. package/src/storage/Storage.js +9 -9
  77. package/src/transport/BLETransport.js +27 -16
  78. package/src/transport/MockTransport.js +7 -2
  79. package/src/transport/MultiTransport.js +43 -11
  80. package/src/transport/Transport.js +9 -9
  81. package/src/transport/WiFiDirectTransport.js +26 -20
  82. package/src/transport/adapters/BLEAdapter.js +19 -19
  83. package/src/transport/adapters/NodeBLEAdapter.js +24 -23
  84. package/src/transport/adapters/RNBLEAdapter.js +14 -11
  85. package/src/utils/EventEmitter.js +15 -16
  86. package/src/utils/LRUCache.js +10 -4
  87. package/src/utils/RateLimiter.js +1 -1
  88. package/src/utils/bytes.js +12 -10
  89. package/src/utils/compression.js +10 -8
  90. package/src/utils/encoding.js +39 -8
  91. package/src/utils/retry.js +11 -13
  92. package/src/utils/time.js +9 -4
  93. package/src/utils/validation.js +1 -1
@@ -12,9 +12,16 @@ const TextMessage = require('./message/TextMessage');
12
12
  const { ChannelManager } = require('./channel');
13
13
  const { BroadcastManager } = require('./broadcast');
14
14
 
15
+ /**
16
+ * Cached TextEncoder/TextDecoder instances to avoid per-call allocation
17
+ * @private
18
+ */
19
+ const cachedEncoder = new TextEncoder();
20
+ const cachedDecoder = new TextDecoder();
21
+
15
22
  /**
16
23
  * Text manager states
17
- * @constant {Object}
24
+ * @constant {any}
18
25
  */
19
26
  const MANAGER_STATE = Object.freeze({
20
27
  UNINITIALIZED: 'uninitialized',
@@ -33,34 +40,34 @@ const MANAGER_STATE = Object.freeze({
33
40
  class TextManager extends EventEmitter {
34
41
  /**
35
42
  * Creates a new TextManager
36
- * @param {Object} [options] - Manager options
43
+ * @param {any} [options] - Manager options
37
44
  */
38
45
  constructor(options = {}) {
39
46
  super();
40
47
 
41
- /** @private */
48
+ /** @type {string} @private */
42
49
  this._state = MANAGER_STATE.UNINITIALIZED;
43
- /** @private */
50
+ /** @type {any} @private */
44
51
  this._meshService = null;
45
52
  /** @private */
46
53
  this._channelManager = new ChannelManager();
47
54
  /** @private */
48
55
  this._broadcastManager = new BroadcastManager(options.broadcast);
49
- /** @private */
56
+ /** @type {string | null} @private */
50
57
  this._senderId = null;
51
- /** @private */
58
+ /** @type {number} @private */
52
59
  this._messageCounter = 0;
53
- /** @private */
60
+ /** @type {Set<string>} @private */
54
61
  this._pendingReadReceipts = new Set();
55
- /** @private */
62
+ /** @type {ReturnType<typeof setTimeout> | null} @private */
56
63
  this._readReceiptBatchTimeout = null;
57
- /** @private */
64
+ /** @type {number} @private */
58
65
  this._readReceiptBatchDelayMs = options.readReceiptBatchDelayMs || 1000;
59
66
  }
60
67
 
61
68
  /**
62
69
  * Initializes the text manager
63
- * @param {MeshService} meshService - Mesh service instance
70
+ * @param {any} meshService - Mesh service instance
64
71
  * @returns {Promise<void>}
65
72
  */
66
73
  async initialize(meshService) {
@@ -82,7 +89,7 @@ class TextManager extends EventEmitter {
82
89
  // Initialize broadcast manager
83
90
  this._broadcastManager.initialize({
84
91
  senderId: this._senderId,
85
- sendCallback: (message) => this._sendBroadcastMessage(message)
92
+ sendCallback: (/** @type {any} */ message) => this._sendBroadcastMessage(message)
86
93
  });
87
94
 
88
95
  // Setup event forwarding
@@ -90,7 +97,7 @@ class TextManager extends EventEmitter {
90
97
 
91
98
  this._setState(MANAGER_STATE.READY);
92
99
  this.emit('initialized');
93
- } catch (error) {
100
+ } catch (/** @type {any} */ error) {
94
101
  this._setState(MANAGER_STATE.ERROR);
95
102
  throw new MeshError(`Text manager initialization failed: ${error.message}`, ERROR_CODE.E001);
96
103
  }
@@ -196,7 +203,7 @@ class TextManager extends EventEmitter {
196
203
  /**
197
204
  * Gets recent broadcasts
198
205
  * @param {number} [limit] - Maximum number to return
199
- * @returns {TextMessage[]}
206
+ * @returns {any[]}
200
207
  */
201
208
  getRecentBroadcasts(limit) {
202
209
  return this._broadcastManager.getRecentBroadcasts(limit);
@@ -208,6 +215,7 @@ class TextManager extends EventEmitter {
208
215
  * Joins a channel
209
216
  * @param {string} channelId - Channel ID
210
217
  * @param {string} [password] - Optional password
218
+ * @returns {any}
211
219
  */
212
220
  joinChannel(channelId, password) {
213
221
  this._validateReady();
@@ -280,7 +288,7 @@ class TextManager extends EventEmitter {
280
288
 
281
289
  /**
282
290
  * Gets all joined channels
283
- * @returns {Object[]}
291
+ * @returns {any[]}
284
292
  */
285
293
  getChannels() {
286
294
  return this._channelManager.getChannels();
@@ -289,7 +297,7 @@ class TextManager extends EventEmitter {
289
297
  /**
290
298
  * Gets a specific channel
291
299
  * @param {string} channelId - Channel ID
292
- * @returns {Channel|undefined}
300
+ * @returns {any}
293
301
  */
294
302
  getChannel(channelId) {
295
303
  return this._channelManager.getChannel(channelId);
@@ -351,6 +359,7 @@ class TextManager extends EventEmitter {
351
359
  // Extract channel ID from payload
352
360
  this._handleChannelMessagePayload(peerId, payload);
353
361
  break;
362
+ // @ts-ignore
354
363
  case MESSAGE_TYPE.READ_RECEIPT:
355
364
  this._handleReadReceipt(peerId, payload);
356
365
  break;
@@ -372,20 +381,25 @@ class TextManager extends EventEmitter {
372
381
  EVENTS.CHANNEL_MEMBER_JOINED,
373
382
  EVENTS.CHANNEL_MEMBER_LEFT
374
383
  ];
375
- channelEvents.forEach(event => {
376
- this._channelManager.on(event, data => this.emit(event, data));
384
+ channelEvents.forEach((/** @type {any} */ event) => {
385
+ this._channelManager.on(event, (/** @type {any} */ data) => this.emit(event, data));
377
386
  });
378
387
 
379
388
  // Forward broadcast events
380
- this._broadcastManager.on('broadcast-sent', data => {
389
+ this._broadcastManager.on('broadcast-sent', (/** @type {any} */ data) => {
381
390
  this.emit(EVENTS.BROADCAST_SENT, data);
382
391
  });
383
- this._broadcastManager.on('broadcast-received', data => {
392
+ this._broadcastManager.on('broadcast-received', (/** @type {any} */ data) => {
384
393
  this.emit(EVENTS.BROADCAST_RECEIVED, data);
385
394
  });
386
395
  }
387
396
 
388
- /** @private */
397
+ /**
398
+ * @param {string} peerId - Peer ID
399
+ * @param {number} type - Message type
400
+ * @param {Uint8Array} payload - Message payload
401
+ * @private
402
+ */
389
403
  async _sendMessage(peerId, type, payload) {
390
404
  const data = new Uint8Array(1 + payload.length);
391
405
  data[0] = type;
@@ -396,7 +410,10 @@ class TextManager extends EventEmitter {
396
410
  }
397
411
  }
398
412
 
399
- /** @private */
413
+ /**
414
+ * @param {any} message - Message to broadcast
415
+ * @private
416
+ */
400
417
  async _sendBroadcastMessage(message) {
401
418
  const serialized = message.serialize();
402
419
  const data = new Uint8Array(1 + serialized.length);
@@ -416,26 +433,35 @@ class TextManager extends EventEmitter {
416
433
  }
417
434
  }
418
435
 
419
- /** @private */
436
+ /**
437
+ * @param {string} peerId - Peer ID
438
+ * @param {Uint8Array} payload - Message payload
439
+ * @private
440
+ */
420
441
  _handleChannelMessagePayload(peerId, payload) {
421
442
  // First byte is channel ID length
422
443
  const channelIdLength = payload[0];
423
- const channelId = new TextDecoder().decode(payload.slice(1, 1 + channelIdLength));
424
- const messagePayload = payload.slice(1 + channelIdLength);
444
+ const channelId = cachedDecoder.decode(payload.subarray(1, 1 + channelIdLength));
445
+ const messagePayload = payload.subarray(1 + channelIdLength);
425
446
 
426
447
  this.handleChannelMessage(peerId, channelId, messagePayload);
427
448
  }
428
449
 
429
- /** @private */
450
+ /**
451
+ * @param {string} peerId - Peer ID
452
+ * @param {Uint8Array} payload - Message payload
453
+ * @private
454
+ */
430
455
  _handleReadReceipt(peerId, payload) {
431
456
  // Parse read receipt payload
457
+ /** @type {string[]} */
432
458
  const messageIds = [];
433
459
  let offset = 0;
434
460
 
435
461
  while (offset < payload.length) {
436
462
  const length = payload[offset];
437
463
  offset += 1;
438
- const messageId = new TextDecoder().decode(payload.slice(offset, offset + length));
464
+ const messageId = cachedDecoder.decode(payload.subarray(offset, offset + length));
439
465
  messageIds.push(messageId);
440
466
  offset += length;
441
467
  }
@@ -456,21 +482,20 @@ class TextManager extends EventEmitter {
456
482
  const messageIds = Array.from(this._pendingReadReceipts);
457
483
  this._pendingReadReceipts.clear();
458
484
 
459
- // Build read receipt payload
460
- const parts = messageIds.map(id => {
461
- const bytes = new TextEncoder().encode(id);
462
- const part = new Uint8Array(1 + bytes.length);
463
- part[0] = bytes.length;
464
- part.set(bytes, 1);
465
- return part;
466
- });
485
+ // Pre-calculate total size and allocate once
486
+ const encodedIds = messageIds.map((/** @type {string} */ id) => cachedEncoder.encode(id));
487
+ let totalLength = 0;
488
+ for (let i = 0; i < encodedIds.length; i++) {
489
+ totalLength += 1 + encodedIds[i].length;
490
+ }
467
491
 
468
- const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
469
492
  const payload = new Uint8Array(totalLength);
470
493
  let offset = 0;
471
- for (const part of parts) {
472
- payload.set(part, offset);
473
- offset += part.length;
494
+ for (let i = 0; i < encodedIds.length; i++) {
495
+ payload[offset] = encodedIds[i].length;
496
+ offset += 1;
497
+ payload.set(encodedIds[i], offset);
498
+ offset += encodedIds[i].length;
474
499
  }
475
500
 
476
501
  this.emit('read-receipts-sent', { messageIds, count: messageIds.length });
@@ -483,16 +508,23 @@ class TextManager extends EventEmitter {
483
508
  }
484
509
  }
485
510
 
486
- /** @private */
511
+ /**
512
+ * @param {string} newState - New state
513
+ * @private
514
+ */
487
515
  _setState(newState) {
488
516
  this._state = newState;
489
517
  }
490
518
 
491
- /** @private */
519
+ /**
520
+ * @param {any} publicKey - Public key
521
+ * @returns {string}
522
+ * @private
523
+ */
492
524
  _publicKeyToId(publicKey) {
493
525
  if (publicKey instanceof Uint8Array) {
494
526
  return Array.from(publicKey.slice(0, 8))
495
- .map(b => b.toString(16).padStart(2, '0'))
527
+ .map((/** @type {number} */ b) => b.toString(16).padStart(2, '0'))
496
528
  .join('');
497
529
  }
498
530
  return String(publicKey).slice(0, 16);
@@ -508,7 +540,7 @@ class TextManager extends EventEmitter {
508
540
 
509
541
  /**
510
542
  * Returns statistics
511
- * @returns {Object}
543
+ * @returns {any}
512
544
  */
513
545
  getStats() {
514
546
  return {
@@ -519,6 +551,7 @@ class TextManager extends EventEmitter {
519
551
  }
520
552
  }
521
553
 
554
+ /** @type {any} */
522
555
  TextManager.STATE = MANAGER_STATE;
523
556
 
524
557
  module.exports = TextManager;
@@ -10,7 +10,7 @@ const TextMessage = require('../message/TextMessage');
10
10
 
11
11
  /**
12
12
  * Broadcast configuration
13
- * @constant {Object}
13
+ * @constant {any}
14
14
  */
15
15
  const BROADCAST_CONFIG = Object.freeze({
16
16
  MAX_MESSAGE_SIZE: 1000,
@@ -26,26 +26,24 @@ const BROADCAST_CONFIG = Object.freeze({
26
26
  class BroadcastManager extends EventEmitter {
27
27
  /**
28
28
  * Creates a new BroadcastManager
29
- * @param {Object} [options] - Manager options
30
- * @param {number} [options.maxRecentBroadcasts] - Max broadcasts to keep
31
- * @param {number} [options.dedupWindowMs] - Deduplication window
29
+ * @param {any} [options] - Manager options
32
30
  */
33
31
  constructor(options = {}) {
34
32
  super();
35
33
 
36
- /** @private */
34
+ /** @type {number} @private */
37
35
  this._maxRecentBroadcasts = options.maxRecentBroadcasts || BROADCAST_CONFIG.MAX_RECENT_BROADCASTS;
38
- /** @private */
36
+ /** @type {number} @private */
39
37
  this._dedupWindowMs = options.dedupWindowMs || BROADCAST_CONFIG.DEDUP_WINDOW_MS;
40
- /** @private */
38
+ /** @type {any[]} @private */
41
39
  this._recentBroadcasts = [];
42
- /** @private */
40
+ /** @type {Map<string, number>} @private */
43
41
  this._seenMessageIds = new Map(); // messageId -> timestamp
44
- /** @private */
42
+ /** @type {string | null} @private */
45
43
  this._senderId = null;
46
- /** @private */
44
+ /** @type {Function | null} @private */
47
45
  this._sendCallback = null;
48
- /** @private */
46
+ /** @type {ReturnType<typeof setInterval> | null} @private */
49
47
  this._cleanupTimer = null;
50
48
 
51
49
  // Auto-cleanup every 5 minutes
@@ -60,9 +58,7 @@ class BroadcastManager extends EventEmitter {
60
58
 
61
59
  /**
62
60
  * Initializes the broadcast manager
63
- * @param {Object} options - Init options
64
- * @param {string} options.senderId - Local sender ID
65
- * @param {Function} options.sendCallback - Callback to send broadcast
61
+ * @param {any} options - Init options
66
62
  */
67
63
  initialize(options) {
68
64
  this._senderId = options.senderId;
@@ -155,7 +151,7 @@ class BroadcastManager extends EventEmitter {
155
151
  /**
156
152
  * Gets recent broadcasts
157
153
  * @param {number} [limit] - Maximum number to return
158
- * @returns {TextMessage[]}
154
+ * @returns {any[]}
159
155
  */
160
156
  getRecentBroadcasts(limit) {
161
157
  const broadcasts = [...this._recentBroadcasts];
@@ -180,7 +176,7 @@ class BroadcastManager extends EventEmitter {
180
176
 
181
177
  /**
182
178
  * Gets broadcast statistics
183
- * @returns {Object}
179
+ * @returns {any}
184
180
  */
185
181
  getStats() {
186
182
  return {
@@ -221,7 +217,7 @@ class BroadcastManager extends EventEmitter {
221
217
  /**
222
218
  * Adds a message to recent broadcasts
223
219
  * @private
224
- * @param {TextMessage} message - Message to add
220
+ * @param {any} message - Message to add
225
221
  */
226
222
  _addToRecent(message) {
227
223
  this._recentBroadcasts.push(message);
@@ -233,6 +229,7 @@ class BroadcastManager extends EventEmitter {
233
229
  }
234
230
  }
235
231
 
232
+ /** @type {any} */
236
233
  BroadcastManager.CONFIG = BROADCAST_CONFIG;
237
234
 
238
235
  module.exports = BroadcastManager;
@@ -12,11 +12,7 @@
12
12
  class Channel {
13
13
  /**
14
14
  * Creates a new Channel
15
- * @param {Object} options - Channel options
16
- * @param {string} options.id - Channel ID
17
- * @param {string} [options.name] - Channel display name
18
- * @param {string} [options.password] - Channel password (hashed)
19
- * @param {number} [options.createdAt] - Creation timestamp
15
+ * @param {any} options - Channel options
20
16
  */
21
17
  constructor(options) {
22
18
  const { id, name, password, createdAt } = options;
@@ -25,17 +21,17 @@ class Channel {
25
21
  throw new Error('Channel ID is required');
26
22
  }
27
23
 
28
- /** @private */
24
+ /** @type {string} @private */
29
25
  this._id = id;
30
- /** @private */
26
+ /** @type {string} @private */
31
27
  this._name = name || id;
32
- /** @private */
28
+ /** @type {Uint8Array | null} @private */
33
29
  this._passwordHash = password ? this._hashPassword(password) : null;
34
- /** @private */
30
+ /** @type {Set<string>} @private */
35
31
  this._members = new Set();
36
- /** @private */
32
+ /** @type {number} @private */
37
33
  this._createdAt = createdAt || Date.now();
38
- /** @private */
34
+ /** @type {number} @private */
39
35
  this._joinedAt = Date.now();
40
36
  }
41
37
 
@@ -157,7 +153,7 @@ class Channel {
157
153
 
158
154
  /**
159
155
  * Converts to JSON representation
160
- * @returns {Object}
156
+ * @returns {any}
161
157
  */
162
158
  toJSON() {
163
159
  return {
@@ -173,7 +169,7 @@ class Channel {
173
169
 
174
170
  /**
175
171
  * Creates a Channel from JSON
176
- * @param {Object} json - JSON object
172
+ * @param {any} json - JSON object
177
173
  * @returns {Channel}
178
174
  */
179
175
  static fromJSON(json) {
@@ -184,7 +180,7 @@ class Channel {
184
180
  });
185
181
 
186
182
  if (json.members) {
187
- json.members.forEach(peerId => channel.addMember(peerId));
183
+ json.members.forEach((/** @type {string} */ peerId) => channel.addMember(peerId));
188
184
  }
189
185
 
190
186
  channel._joinedAt = json.joinedAt || Date.now();
@@ -18,7 +18,7 @@ const Channel = require('./Channel');
18
18
  class ChannelManager extends EventEmitter {
19
19
  constructor() {
20
20
  super();
21
- /** @private */
21
+ /** @type {Map<string, any>} @private */
22
22
  this._channels = new Map();
23
23
  }
24
24
 
@@ -26,7 +26,7 @@ class ChannelManager extends EventEmitter {
26
26
  * Joins a channel
27
27
  * @param {string} channelId - Channel ID
28
28
  * @param {string} [password] - Optional password
29
- * @returns {Channel}
29
+ * @returns {any}
30
30
  */
31
31
  joinChannel(channelId, password) {
32
32
  if (!channelId || typeof channelId !== 'string') {
@@ -57,21 +57,21 @@ class ChannelManager extends EventEmitter {
57
57
  }
58
58
  const channel = this._channels.get(channelId);
59
59
  this._channels.delete(channelId);
60
- this.emit(EVENTS.CHANNEL_LEFT, { channelId, memberCount: channel.getMemberCount() });
60
+ this.emit(EVENTS.CHANNEL_LEFT, { channelId, memberCount: channel?.getMemberCount() });
61
61
  }
62
62
 
63
63
  /**
64
64
  * Gets all joined channels
65
- * @returns {Object[]}
65
+ * @returns {any[]}
66
66
  */
67
67
  getChannels() {
68
- return Array.from(this._channels.values()).map(c => c.toJSON());
68
+ return Array.from(this._channels.values()).map((/** @type {any} */ c) => c.toJSON());
69
69
  }
70
70
 
71
71
  /**
72
72
  * Gets a specific channel
73
73
  * @param {string} channelId - Channel ID
74
- * @returns {Channel|undefined}
74
+ * @returns {any}
75
75
  */
76
76
  getChannel(channelId) {
77
77
  return this._channels.get(channelId);
@@ -122,14 +122,14 @@ class ChannelManager extends EventEmitter {
122
122
 
123
123
  /**
124
124
  * Handles an incoming channel message
125
- * @param {Object} message - Message data
125
+ * @param {any} message - Message data
126
126
  */
127
127
  handleChannelMessage(message) {
128
128
  const { channelId, senderId, content, timestamp } = message;
129
129
  if (!this._channels.has(channelId)) { return; }
130
130
 
131
131
  const channel = this._channels.get(channelId);
132
- if (senderId && !channel.hasMember(senderId)) {
132
+ if (senderId && channel && !channel.hasMember(senderId)) {
133
133
  channel.addMember(senderId);
134
134
  this.emit(EVENTS.CHANNEL_MEMBER_JOINED, { channelId, peerId: senderId });
135
135
  }
@@ -158,12 +158,12 @@ class ChannelManager extends EventEmitter {
158
158
 
159
159
  /**
160
160
  * Gets channel statistics
161
- * @returns {Object}
161
+ * @returns {any}
162
162
  */
163
163
  getStats() {
164
164
  return {
165
165
  channelCount: this._channels.size,
166
- totalMembers: Array.from(this._channels.values()).reduce((s, c) => s + c.getMemberCount(), 0)
166
+ totalMembers: Array.from(this._channels.values()).reduce((/** @type {number} */ s, /** @type {any} */ c) => s + c.getMemberCount(), 0)
167
167
  };
168
168
  }
169
169
  }
@@ -14,38 +14,31 @@ const { generateUUID } = require('../../../utils');
14
14
  class TextMessage {
15
15
  /**
16
16
  * Creates a new TextMessage
17
- * @param {Object} options - Message options
18
- * @param {string} [options.id] - Message ID
19
- * @param {string} options.content - Message content
20
- * @param {string} [options.senderId] - Sender peer ID
21
- * @param {string} [options.recipientId] - Recipient peer ID (for private messages)
22
- * @param {string} [options.channelId] - Channel ID (for channel messages)
23
- * @param {number} [options.timestamp] - Message timestamp
24
- * @param {boolean} [options.isRead] - Whether message has been read
17
+ * @param {any} options - Message options
25
18
  */
26
19
  constructor(options) {
27
20
  const { id, content, senderId, recipientId, channelId, timestamp, isRead } = options;
28
21
 
29
- /** @private */
22
+ /** @type {string} @private */
30
23
  this._id = id || generateUUID();
31
- /** @private */
24
+ /** @type {string} @private */
32
25
  this._content = content || '';
33
- /** @private */
26
+ /** @type {string | null} @private */
34
27
  this._senderId = senderId || null;
35
- /** @private */
28
+ /** @type {string | null} @private */
36
29
  this._recipientId = recipientId || null;
37
- /** @private */
30
+ /** @type {string | null} @private */
38
31
  this._channelId = channelId || null;
39
- /** @private */
32
+ /** @type {number} @private */
40
33
  this._timestamp = timestamp || Date.now();
41
- /** @private */
34
+ /** @type {boolean} @private */
42
35
  this._isRead = isRead || false;
43
36
  }
44
37
 
45
38
  /**
46
39
  * Creates a TextMessage from a string
47
40
  * @param {string} content - Message content
48
- * @param {Object} [options] - Additional options
41
+ * @param {any} [options] - Additional options
49
42
  * @returns {TextMessage}
50
43
  */
51
44
  static fromString(content, options = {}) {
@@ -253,7 +246,7 @@ class TextMessage {
253
246
 
254
247
  /**
255
248
  * Returns message metadata
256
- * @returns {Object}
249
+ * @returns {any}
257
250
  */
258
251
  getMetadata() {
259
252
  return {
@@ -269,7 +262,7 @@ class TextMessage {
269
262
 
270
263
  /**
271
264
  * Converts to JSON representation
272
- * @returns {Object}
265
+ * @returns {any}
273
266
  */
274
267
  toJSON() {
275
268
  return {
@@ -285,7 +278,7 @@ class TextMessage {
285
278
 
286
279
  /**
287
280
  * Creates a TextMessage from JSON
288
- * @param {Object} json - JSON object
281
+ * @param {any} json - JSON object
289
282
  * @returns {TextMessage}
290
283
  */
291
284
  static fromJSON(json) {
@@ -7,7 +7,7 @@
7
7
 
8
8
  /**
9
9
  * Text header flags
10
- * @constant {Object}
10
+ * @constant {any}
11
11
  */
12
12
  const TEXT_HEADER_FLAGS = Object.freeze({
13
13
  HAS_SENDER: 0x01,
@@ -94,7 +94,7 @@ function createTextHeader(options) {
94
94
  /**
95
95
  * Parses a text message header
96
96
  * @param {Uint8Array} data - Header data
97
- * @returns {Object}
97
+ * @returns {any}
98
98
  */
99
99
  function parseTextHeader(data) {
100
100
  if (data.length < TEXT_HEADER_SIZE) {