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.
- package/README.md +2 -2
- package/docs/OPTIMIZATION.md +165 -52
- package/package.json +1 -1
- package/src/MeshNetwork.js +63 -53
- package/src/constants/audio.js +4 -4
- package/src/constants/ble.js +1 -1
- package/src/constants/crypto.js +1 -1
- package/src/constants/errors.js +2 -2
- package/src/constants/events.js +1 -1
- package/src/constants/protocol.js +2 -2
- package/src/crypto/AutoCrypto.js +16 -3
- package/src/crypto/CryptoProvider.js +17 -17
- package/src/crypto/providers/ExpoCryptoProvider.js +15 -9
- package/src/crypto/providers/QuickCryptoProvider.js +41 -12
- package/src/crypto/providers/TweetNaClProvider.js +10 -8
- package/src/errors/AudioError.js +2 -1
- package/src/errors/ConnectionError.js +2 -2
- package/src/errors/CryptoError.js +1 -1
- package/src/errors/HandshakeError.js +2 -2
- package/src/errors/MeshError.js +4 -4
- package/src/errors/MessageError.js +2 -2
- package/src/errors/ValidationError.js +3 -3
- package/src/expo/withBLEMesh.js +10 -10
- package/src/hooks/AppStateManager.js +11 -2
- package/src/hooks/useMesh.js +23 -10
- package/src/hooks/useMessages.js +17 -16
- package/src/hooks/usePeers.js +19 -14
- package/src/index.js +2 -2
- package/src/mesh/dedup/BloomFilter.js +45 -57
- package/src/mesh/dedup/DedupManager.js +36 -8
- package/src/mesh/dedup/MessageCache.js +3 -0
- package/src/mesh/fragment/Assembler.js +5 -4
- package/src/mesh/fragment/Fragmenter.js +3 -3
- package/src/mesh/monitor/ConnectionQuality.js +59 -25
- package/src/mesh/monitor/NetworkMonitor.js +80 -28
- package/src/mesh/peer/Peer.js +9 -11
- package/src/mesh/peer/PeerDiscovery.js +18 -19
- package/src/mesh/peer/PeerManager.js +29 -17
- package/src/mesh/router/MessageRouter.js +28 -20
- package/src/mesh/router/PathFinder.js +10 -13
- package/src/mesh/router/RouteTable.js +25 -14
- package/src/mesh/store/StoreAndForwardManager.js +32 -24
- package/src/protocol/deserializer.js +9 -10
- package/src/protocol/header.js +13 -7
- package/src/protocol/message.js +18 -14
- package/src/protocol/serializer.js +9 -12
- package/src/protocol/validator.js +29 -10
- package/src/service/BatteryOptimizer.js +22 -18
- package/src/service/EmergencyManager.js +18 -25
- package/src/service/HandshakeManager.js +112 -18
- package/src/service/MeshService.js +106 -22
- package/src/service/SessionManager.js +50 -13
- package/src/service/audio/AudioManager.js +80 -38
- package/src/service/audio/buffer/FrameBuffer.js +7 -8
- package/src/service/audio/buffer/JitterBuffer.js +1 -1
- package/src/service/audio/codec/LC3Codec.js +18 -19
- package/src/service/audio/codec/LC3Decoder.js +10 -10
- package/src/service/audio/codec/LC3Encoder.js +11 -9
- package/src/service/audio/session/AudioSession.js +14 -17
- package/src/service/audio/session/VoiceMessage.js +15 -22
- package/src/service/audio/transport/AudioFragmenter.js +17 -9
- package/src/service/audio/transport/AudioFramer.js +8 -12
- package/src/service/file/FileAssembler.js +4 -2
- package/src/service/file/FileChunker.js +1 -1
- package/src/service/file/FileManager.js +26 -20
- package/src/service/file/FileMessage.js +7 -12
- package/src/service/text/TextManager.js +75 -42
- package/src/service/text/broadcast/BroadcastManager.js +14 -17
- package/src/service/text/channel/Channel.js +10 -14
- package/src/service/text/channel/ChannelManager.js +10 -10
- package/src/service/text/message/TextMessage.js +12 -19
- package/src/service/text/message/TextSerializer.js +2 -2
- package/src/storage/AsyncStorageAdapter.js +17 -14
- package/src/storage/MemoryStorage.js +11 -8
- package/src/storage/MessageStore.js +77 -32
- package/src/storage/Storage.js +9 -9
- package/src/transport/BLETransport.js +27 -16
- package/src/transport/MockTransport.js +7 -2
- package/src/transport/MultiTransport.js +43 -11
- package/src/transport/Transport.js +9 -9
- package/src/transport/WiFiDirectTransport.js +26 -20
- package/src/transport/adapters/BLEAdapter.js +19 -19
- package/src/transport/adapters/NodeBLEAdapter.js +24 -23
- package/src/transport/adapters/RNBLEAdapter.js +14 -11
- package/src/utils/EventEmitter.js +15 -16
- package/src/utils/LRUCache.js +10 -4
- package/src/utils/RateLimiter.js +1 -1
- package/src/utils/bytes.js +12 -10
- package/src/utils/compression.js +10 -8
- package/src/utils/encoding.js +39 -8
- package/src/utils/retry.js +11 -13
- package/src/utils/time.js +9 -4
- 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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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 =
|
|
424
|
-
const messagePayload = payload.
|
|
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
|
-
/**
|
|
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 =
|
|
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
|
-
//
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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 (
|
|
472
|
-
payload.
|
|
473
|
-
offset +=
|
|
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
|
-
/**
|
|
511
|
+
/**
|
|
512
|
+
* @param {string} newState - New state
|
|
513
|
+
* @private
|
|
514
|
+
*/
|
|
487
515
|
_setState(newState) {
|
|
488
516
|
this._state = newState;
|
|
489
517
|
}
|
|
490
518
|
|
|
491
|
-
/**
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
|
60
|
+
this.emit(EVENTS.CHANNEL_LEFT, { channelId, memberCount: channel?.getMemberCount() });
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Gets all joined channels
|
|
65
|
-
* @returns {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
97
|
+
* @returns {any}
|
|
98
98
|
*/
|
|
99
99
|
function parseTextHeader(data) {
|
|
100
100
|
if (data.length < TEXT_HEADER_SIZE) {
|