react-native-ble-mesh 2.1.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 (88) hide show
  1. package/package.json +1 -1
  2. package/src/MeshNetwork.js +48 -46
  3. package/src/constants/audio.js +4 -4
  4. package/src/constants/ble.js +1 -1
  5. package/src/constants/crypto.js +1 -1
  6. package/src/constants/errors.js +2 -2
  7. package/src/constants/events.js +1 -1
  8. package/src/constants/protocol.js +2 -2
  9. package/src/crypto/AutoCrypto.js +2 -0
  10. package/src/crypto/CryptoProvider.js +17 -17
  11. package/src/crypto/providers/ExpoCryptoProvider.js +12 -7
  12. package/src/crypto/providers/QuickCryptoProvider.js +12 -7
  13. package/src/crypto/providers/TweetNaClProvider.js +9 -7
  14. package/src/errors/AudioError.js +2 -1
  15. package/src/errors/ConnectionError.js +2 -2
  16. package/src/errors/CryptoError.js +1 -1
  17. package/src/errors/HandshakeError.js +2 -2
  18. package/src/errors/MeshError.js +4 -4
  19. package/src/errors/MessageError.js +2 -2
  20. package/src/errors/ValidationError.js +3 -3
  21. package/src/expo/withBLEMesh.js +10 -10
  22. package/src/hooks/AppStateManager.js +2 -1
  23. package/src/hooks/useMesh.js +7 -7
  24. package/src/hooks/useMessages.js +13 -12
  25. package/src/hooks/usePeers.js +10 -9
  26. package/src/index.js +2 -2
  27. package/src/mesh/dedup/BloomFilter.js +1 -0
  28. package/src/mesh/dedup/DedupManager.js +4 -7
  29. package/src/mesh/dedup/MessageCache.js +3 -0
  30. package/src/mesh/fragment/Assembler.js +5 -4
  31. package/src/mesh/fragment/Fragmenter.js +2 -2
  32. package/src/mesh/monitor/ConnectionQuality.js +17 -8
  33. package/src/mesh/monitor/NetworkMonitor.js +22 -15
  34. package/src/mesh/peer/Peer.js +4 -9
  35. package/src/mesh/peer/PeerDiscovery.js +18 -19
  36. package/src/mesh/peer/PeerManager.js +14 -14
  37. package/src/mesh/router/MessageRouter.js +15 -15
  38. package/src/mesh/router/PathFinder.js +10 -13
  39. package/src/mesh/router/RouteTable.js +8 -7
  40. package/src/mesh/store/StoreAndForwardManager.js +20 -23
  41. package/src/protocol/message.js +5 -13
  42. package/src/protocol/serializer.js +4 -4
  43. package/src/protocol/validator.js +7 -6
  44. package/src/service/BatteryOptimizer.js +18 -17
  45. package/src/service/EmergencyManager.js +18 -25
  46. package/src/service/HandshakeManager.js +100 -2
  47. package/src/service/MeshService.js +106 -22
  48. package/src/service/SessionManager.js +38 -3
  49. package/src/service/audio/AudioManager.js +80 -38
  50. package/src/service/audio/buffer/FrameBuffer.js +7 -8
  51. package/src/service/audio/buffer/JitterBuffer.js +1 -1
  52. package/src/service/audio/codec/LC3Codec.js +18 -19
  53. package/src/service/audio/codec/LC3Decoder.js +10 -10
  54. package/src/service/audio/codec/LC3Encoder.js +11 -9
  55. package/src/service/audio/session/AudioSession.js +14 -17
  56. package/src/service/audio/session/VoiceMessage.js +15 -22
  57. package/src/service/audio/transport/AudioFragmenter.js +17 -9
  58. package/src/service/audio/transport/AudioFramer.js +8 -12
  59. package/src/service/file/FileAssembler.js +4 -2
  60. package/src/service/file/FileChunker.js +1 -1
  61. package/src/service/file/FileManager.js +26 -20
  62. package/src/service/file/FileMessage.js +7 -12
  63. package/src/service/text/TextManager.js +55 -28
  64. package/src/service/text/broadcast/BroadcastManager.js +14 -17
  65. package/src/service/text/channel/Channel.js +10 -14
  66. package/src/service/text/channel/ChannelManager.js +10 -10
  67. package/src/service/text/message/TextMessage.js +12 -19
  68. package/src/service/text/message/TextSerializer.js +2 -2
  69. package/src/storage/AsyncStorageAdapter.js +17 -14
  70. package/src/storage/MemoryStorage.js +11 -8
  71. package/src/storage/MessageStore.js +22 -30
  72. package/src/storage/Storage.js +9 -9
  73. package/src/transport/BLETransport.js +16 -14
  74. package/src/transport/MockTransport.js +7 -2
  75. package/src/transport/MultiTransport.js +13 -6
  76. package/src/transport/Transport.js +9 -9
  77. package/src/transport/WiFiDirectTransport.js +22 -17
  78. package/src/transport/adapters/BLEAdapter.js +19 -19
  79. package/src/transport/adapters/NodeBLEAdapter.js +24 -23
  80. package/src/transport/adapters/RNBLEAdapter.js +14 -11
  81. package/src/utils/EventEmitter.js +9 -7
  82. package/src/utils/LRUCache.js +10 -4
  83. package/src/utils/RateLimiter.js +1 -1
  84. package/src/utils/compression.js +5 -5
  85. package/src/utils/encoding.js +8 -2
  86. package/src/utils/retry.js +11 -13
  87. package/src/utils/time.js +9 -4
  88. package/src/utils/validation.js +1 -1
@@ -19,21 +19,38 @@ const { TextManager, ChannelManager } = require('./text');
19
19
  * @extends EventEmitter
20
20
  */
21
21
  class MeshService extends EventEmitter {
22
+ /**
23
+ * @param {any} [config]
24
+ */
22
25
  constructor(config = {}) {
23
26
  super();
27
+ /** @type {any} */
24
28
  this._config = { displayName: 'Anonymous', ...config };
29
+ /** @type {string} */
25
30
  this._state = SERVICE_STATE.UNINITIALIZED;
31
+ /** @type {any} */
26
32
  this._transport = null;
33
+ /** @type {any} */
27
34
  this._keyManager = null;
35
+ /** @type {SessionManager | null} */
28
36
  this._sessionManager = null;
37
+ /** @type {HandshakeManager | null} */
29
38
  this._handshakeManager = null;
39
+ /** @type {any} */
30
40
  this._channelManager = null;
41
+ /** @type {any} */
31
42
  this._peerManager = null;
43
+ /** @type {any} */
32
44
  this._audioManager = null;
45
+ /** @type {any} */
33
46
  this._textManager = null;
47
+ /** @type {number} */
34
48
  this._messageCounter = 0;
35
49
  }
36
50
 
51
+ /**
52
+ * @param {any} [options]
53
+ */
37
54
  async initialize(options = {}) {
38
55
  if (this._state !== SERVICE_STATE.UNINITIALIZED) {
39
56
  throw new MeshError('Service already initialized', ERROR_CODE.E002);
@@ -47,12 +64,15 @@ class MeshService extends EventEmitter {
47
64
  this._setupEventForwarding();
48
65
  this._setState(SERVICE_STATE.READY);
49
66
  this.emit(EVENTS.INITIALIZED);
50
- } catch (err) {
67
+ } catch (/** @type {any} */ err) {
51
68
  this._setState(SERVICE_STATE.ERROR);
52
69
  throw new MeshError(`Initialization failed: ${err.message}`, ERROR_CODE.E001);
53
70
  }
54
71
  }
55
72
 
73
+ /**
74
+ * @param {any} transport
75
+ */
56
76
  async start(transport) {
57
77
  this._validateState([SERVICE_STATE.READY, SERVICE_STATE.SUSPENDED]);
58
78
  if (!transport) { throw new ValidationError('Transport is required', ERROR_CODE.E802); }
@@ -87,31 +107,52 @@ class MeshService extends EventEmitter {
87
107
  };
88
108
  }
89
109
 
110
+ /**
111
+ * @param {string} name
112
+ */
90
113
  setDisplayName(name) { this._config.displayName = name; }
91
114
  exportIdentity() { return this._keyManager?.exportIdentity() || null; }
115
+ /**
116
+ * @param {any} identity
117
+ */
92
118
  importIdentity(identity) { this._keyManager?.importIdentity(identity); }
93
119
 
94
120
  getPeers() { return this._peerManager?.getAllPeers() || []; }
121
+ /**
122
+ * @param {string} id
123
+ */
95
124
  getPeer(id) { return this._peerManager?.getPeer(id); }
96
125
  getConnectedPeers() { return this._peerManager?.getConnectedPeers() || []; }
97
126
  getSecuredPeers() { return this._sessionManager?.getAllSessionPeerIds() || []; }
98
127
 
128
+ /**
129
+ * @param {string} peerId
130
+ */
99
131
  async initiateHandshake(peerId) {
100
132
  this._validateState([SERVICE_STATE.ACTIVE]);
101
- return this._handshakeManager.initiateHandshake(peerId, this._transport);
133
+ return this._handshakeManager?.initiateHandshake(peerId, this._transport);
102
134
  }
103
135
 
136
+ /**
137
+ * @param {string} id
138
+ */
104
139
  blockPeer(id) {
105
140
  this._peerManager?.blockPeer(id);
106
141
  this.emit(EVENTS.PEER_BLOCKED, { peerId: id });
107
142
  }
108
143
 
144
+ /**
145
+ * @param {string} id
146
+ */
109
147
  unblockPeer(id) {
110
148
  this._peerManager?.unblockPeer(id);
111
149
  this.emit(EVENTS.PEER_UNBLOCKED, { peerId: id });
112
150
  }
113
151
 
114
152
  // Text messaging methods
153
+ /**
154
+ * @param {string} content
155
+ */
115
156
  sendBroadcast(content) {
116
157
  this._validateState([SERVICE_STATE.ACTIVE]);
117
158
  if (this._textManager) {
@@ -130,26 +171,34 @@ class MeshService extends EventEmitter {
130
171
  return messageId;
131
172
  }
132
173
 
174
+ /**
175
+ * @param {string} peerId
176
+ * @param {string} content
177
+ */
133
178
  async sendPrivateMessage(peerId, content) {
134
179
  this._validateState([SERVICE_STATE.ACTIVE]);
135
180
  if (this._textManager) {
136
181
  return this._textManager.sendPrivateMessage(peerId, content);
137
182
  }
138
- if (!this._sessionManager.hasSession(peerId)) { await this.initiateHandshake(peerId); }
183
+ if (!this._sessionManager?.hasSession(peerId)) { await this.initiateHandshake(peerId); }
139
184
  const messageId = this._generateMessageId();
140
185
  const plaintext = new TextEncoder().encode(content);
141
- const ciphertext = this._sessionManager.encryptFor(peerId, plaintext);
186
+ const ciphertext = this._sessionManager?.encryptFor(peerId, plaintext);
142
187
  await this._transport.send(peerId, ciphertext);
143
188
  this.emit(EVENTS.PRIVATE_MESSAGE_SENT, { messageId, peerId });
144
189
  return messageId;
145
190
  }
146
191
 
192
+ /**
193
+ * @param {string} channelId
194
+ * @param {string} content
195
+ */
147
196
  sendChannelMessage(channelId, content) {
148
197
  this._validateState([SERVICE_STATE.ACTIVE]);
149
198
  if (this._textManager) {
150
199
  return this._textManager.sendChannelMessage(channelId, content);
151
200
  }
152
- if (!this._channelManager.isInChannel(channelId)) {
201
+ if (!this._channelManager?.isInChannel(channelId)) {
153
202
  throw new MeshError('Not in channel', ERROR_CODE.E602);
154
203
  }
155
204
  const messageId = this._generateMessageId();
@@ -157,28 +206,38 @@ class MeshService extends EventEmitter {
157
206
  return messageId;
158
207
  }
159
208
 
209
+ /**
210
+ * @param {string} channelId
211
+ * @param {string} [password]
212
+ */
160
213
  joinChannel(channelId, password) {
161
214
  if (this._textManager) {
162
215
  return this._textManager.joinChannel(channelId, password);
163
216
  }
164
- this._channelManager.joinChannel(channelId, password);
217
+ this._channelManager?.joinChannel(channelId, password);
165
218
  }
166
219
 
220
+ /**
221
+ * @param {string} channelId
222
+ */
167
223
  leaveChannel(channelId) {
168
224
  if (this._textManager) {
169
225
  return this._textManager.leaveChannel(channelId);
170
226
  }
171
- this._channelManager.leaveChannel(channelId);
227
+ this._channelManager?.leaveChannel(channelId);
172
228
  }
173
229
 
174
230
  getChannels() {
175
231
  if (this._textManager) {
176
232
  return this._textManager.getChannels();
177
233
  }
178
- return this._channelManager.getChannels();
234
+ return this._channelManager?.getChannels() || [];
179
235
  }
180
236
 
181
237
  // Text manager methods
238
+ /**
239
+ * @param {any} [options]
240
+ */
182
241
  async initializeText(options = {}) {
183
242
  this._validateState([SERVICE_STATE.READY, SERVICE_STATE.ACTIVE]);
184
243
  if (this._textManager) {
@@ -192,6 +251,9 @@ class MeshService extends EventEmitter {
192
251
  getTextManager() { return this._textManager; }
193
252
 
194
253
  // Audio methods
254
+ /**
255
+ * @param {any} [options]
256
+ */
195
257
  async initializeAudio(options = {}) {
196
258
  this._validateState([SERVICE_STATE.READY, SERVICE_STATE.ACTIVE]);
197
259
  if (this._audioManager) {
@@ -204,6 +266,10 @@ class MeshService extends EventEmitter {
204
266
 
205
267
  getAudioManager() { return this._audioManager; }
206
268
 
269
+ /**
270
+ * @param {string} peerId
271
+ * @param {any} voiceMessage
272
+ */
207
273
  async sendVoiceMessage(peerId, voiceMessage) {
208
274
  this._validateState([SERVICE_STATE.ACTIVE]);
209
275
  if (!this._audioManager) {
@@ -212,6 +278,9 @@ class MeshService extends EventEmitter {
212
278
  return this._audioManager.sendVoiceMessage(peerId, voiceMessage);
213
279
  }
214
280
 
281
+ /**
282
+ * @param {string} peerId
283
+ */
215
284
  async requestAudioStream(peerId) {
216
285
  this._validateState([SERVICE_STATE.ACTIVE]);
217
286
  if (!this._audioManager) {
@@ -228,7 +297,7 @@ class MeshService extends EventEmitter {
228
297
  EVENTS.CHANNEL_JOINED, EVENTS.CHANNEL_LEFT, EVENTS.CHANNEL_MESSAGE,
229
298
  EVENTS.CHANNEL_MEMBER_JOINED, EVENTS.CHANNEL_MEMBER_LEFT
230
299
  ];
231
- textEvents.forEach(e => this._textManager.on(e, d => this.emit(e, d)));
300
+ textEvents.forEach((/** @type {string} */ e) => this._textManager.on(e, (/** @type {any} */ d) => this.emit(e, d)));
232
301
  }
233
302
 
234
303
  _setupAudioEventForwarding() {
@@ -237,9 +306,13 @@ class MeshService extends EventEmitter {
237
306
  EVENTS.AUDIO_STREAM_REQUEST, EVENTS.AUDIO_STREAM_STARTED, EVENTS.AUDIO_STREAM_ENDED,
238
307
  EVENTS.VOICE_MESSAGE_RECEIVED, EVENTS.VOICE_MESSAGE_SENT, EVENTS.VOICE_MESSAGE_PROGRESS
239
308
  ];
240
- audioEvents.forEach(e => this._audioManager.on(e, d => this.emit(e, d)));
309
+ audioEvents.forEach((/** @type {string} */ e) => this._audioManager.on(e, (/** @type {any} */ d) => this.emit(e, d)));
241
310
  }
242
311
 
312
+ /**
313
+ * @param {string} peerId
314
+ * @param {Uint8Array} data
315
+ */
243
316
  async _sendRaw(peerId, data) {
244
317
  if (this._state === SERVICE_STATE.DESTROYED || !this._transport) {
245
318
  return; // Silently ignore sends after destroy
@@ -251,8 +324,8 @@ class MeshService extends EventEmitter {
251
324
  return {
252
325
  state: this._state, identity: this.getIdentity(),
253
326
  peerCount: this.getConnectedPeers().length, securedPeerCount: this.getSecuredPeers().length,
254
- channelCount: this._channelManager?.getChannels().length || 0,
255
- sessionCount: this._sessionManager?.getAllSessionPeerIds().length || 0,
327
+ channelCount: this._channelManager?.getChannels()?.length || 0,
328
+ sessionCount: this._sessionManager?.getAllSessionPeerIds()?.length || 0,
256
329
  hasTextManager: !!this._textManager,
257
330
  hasAudioManager: !!this._audioManager
258
331
  };
@@ -260,12 +333,18 @@ class MeshService extends EventEmitter {
260
333
 
261
334
  getState() { return this._state; }
262
335
 
336
+ /**
337
+ * @param {string} newState
338
+ */
263
339
  _setState(newState) {
264
340
  const oldState = this._state;
265
341
  this._state = newState;
266
342
  this.emit(EVENTS.STATE_CHANGED, { oldState, newState });
267
343
  }
268
344
 
345
+ /**
346
+ * @param {string[]} allowed
347
+ */
269
348
  _validateState(allowed) {
270
349
  if (!allowed.includes(this._state)) {
271
350
  throw new MeshError(`Invalid state: ${this._state}`, ERROR_CODE.E003);
@@ -276,13 +355,15 @@ class MeshService extends EventEmitter {
276
355
 
277
356
  _createKeyManager() {
278
357
  const { createProvider } = require('../crypto/AutoCrypto');
358
+ /** @type {any} */
279
359
  let provider;
360
+ /** @type {any} */
280
361
  let keyPair;
281
362
 
282
363
  try {
283
364
  provider = createProvider('auto');
284
365
  keyPair = provider.generateKeyPair();
285
- } catch (e) {
366
+ } catch (/** @type {any} */ e) {
286
367
  // If no crypto provider is available, return a minimal fallback
287
368
  // that generates random keys using basic randomBytes
288
369
  const { randomBytes } = require('../utils/bytes');
@@ -291,7 +372,7 @@ class MeshService extends EventEmitter {
291
372
  getStaticKeyPair: () => keyPair,
292
373
  getPublicKey: () => keyPair.publicKey,
293
374
  exportIdentity: () => ({ publicKey: Array.from(keyPair.publicKey) }),
294
- importIdentity: (id) => {
375
+ importIdentity: (/** @type {any} */ id) => {
295
376
  if (id && id.publicKey) {
296
377
  keyPair.publicKey = new Uint8Array(id.publicKey);
297
378
  }
@@ -310,7 +391,7 @@ class MeshService extends EventEmitter {
310
391
  publicKey: Array.from(keyPair.publicKey),
311
392
  secretKey: Array.from(keyPair.secretKey)
312
393
  }),
313
- importIdentity: (id) => {
394
+ importIdentity: (/** @type {any} */ id) => {
314
395
  if (id && id.publicKey) {
315
396
  keyPair.publicKey = new Uint8Array(id.publicKey);
316
397
  }
@@ -322,7 +403,7 @@ class MeshService extends EventEmitter {
322
403
  }
323
404
 
324
405
  _setupEventForwarding() {
325
- const fwd = (em, evts) => evts.forEach(e => em.on(e, d => this.emit(e, d)));
406
+ const fwd = (/** @type {any} */ em, /** @type {string[]} */ evts) => evts.forEach((/** @type {string} */ e) => em.on(e, (/** @type {any} */ d) => this.emit(e, d)));
326
407
  fwd(this._handshakeManager, [EVENTS.HANDSHAKE_STARTED, EVENTS.HANDSHAKE_PROGRESS,
327
408
  EVENTS.HANDSHAKE_COMPLETE, EVENTS.HANDSHAKE_FAILED]);
328
409
  fwd(this._channelManager, [EVENTS.CHANNEL_JOINED, EVENTS.CHANNEL_LEFT,
@@ -330,15 +411,18 @@ class MeshService extends EventEmitter {
330
411
  }
331
412
 
332
413
  _setupTransportListeners() {
333
- this._transport.on('message', d => this._handleIncoming(d));
334
- this._transport.on('peerConnected', d => this.emit(EVENTS.PEER_CONNECTED, d));
335
- this._transport.on('peerDisconnected', d => this.emit(EVENTS.PEER_DISCONNECTED, d));
414
+ this._transport.on('message', (/** @type {any} */ d) => this._handleIncoming(d));
415
+ this._transport.on('peerConnected', (/** @type {any} */ d) => this.emit(EVENTS.PEER_CONNECTED, d));
416
+ this._transport.on('peerDisconnected', (/** @type {any} */ d) => this.emit(EVENTS.PEER_DISCONNECTED, d));
336
417
  }
337
418
 
419
+ /**
420
+ * @param {any} param0
421
+ */
338
422
  _handleIncoming({ peerId, data }) {
339
423
  const type = data[0], payload = data.subarray(1);
340
424
  if (type >= MESSAGE_TYPE.HANDSHAKE_INIT && type <= MESSAGE_TYPE.HANDSHAKE_FINAL) {
341
- this._handshakeManager.handleIncomingHandshake(peerId, type, payload, this._transport);
425
+ this._handshakeManager?.handleIncomingHandshake(peerId, type, payload, this._transport);
342
426
  } else if (type === MESSAGE_TYPE.CHANNEL_MESSAGE) {
343
427
  if (this._textManager) {
344
428
  this._textManager.handleIncomingMessage(peerId, type, payload);
@@ -351,7 +435,7 @@ class MeshService extends EventEmitter {
351
435
  const parsed = JSON.parse(decoded);
352
436
  channelId = parsed.channelId || '';
353
437
  content = parsed.content ? new TextEncoder().encode(parsed.content) : payload;
354
- } catch (e) {
438
+ } catch (/** @type {any} */ e) {
355
439
  // If not JSON, try to extract channelId as length-prefixed string
356
440
  if (payload.length > 1) {
357
441
  const channelIdLen = payload[0];
@@ -361,7 +445,7 @@ class MeshService extends EventEmitter {
361
445
  }
362
446
  }
363
447
  }
364
- this._channelManager.handleChannelMessage({ channelId, senderId: peerId, content });
448
+ this._channelManager?.handleChannelMessage({ channelId, senderId: peerId, content });
365
449
  }
366
450
  } else if (type >= MESSAGE_TYPE.VOICE_MESSAGE_START && type <= MESSAGE_TYPE.AUDIO_STREAM_END) {
367
451
  if (this._audioManager) {
@@ -16,9 +16,14 @@ const MAX_MESSAGE_COUNT = 1000000; // 1 million messages before nonce exhaustion
16
16
  */
17
17
  class SessionManager {
18
18
  constructor() {
19
+ /** @type {Map<string, any>} */
19
20
  this._sessions = new Map();
20
21
  }
21
22
 
23
+ /**
24
+ * @param {string} peerId
25
+ * @param {any} session
26
+ */
22
27
  createSession(peerId, session) {
23
28
  if (!peerId || typeof peerId !== 'string') {
24
29
  throw new Error('Invalid peerId: must be a non-empty string');
@@ -31,14 +36,29 @@ class SessionManager {
31
36
  });
32
37
  }
33
38
 
39
+ /**
40
+ * @param {string} peerId
41
+ * @returns {any}
42
+ */
34
43
  getSession(peerId) {
35
44
  const entry = this._sessions.get(peerId);
36
45
  return entry ? entry.session : undefined;
37
46
  }
38
47
 
48
+ /**
49
+ * @param {string} peerId
50
+ * @returns {boolean}
51
+ */
39
52
  hasSession(peerId) { return this._sessions.has(peerId); }
53
+ /**
54
+ * @param {string} peerId
55
+ */
40
56
  removeSession(peerId) { this._sessions.delete(peerId); }
41
57
 
58
+ /**
59
+ * @param {string} peerId
60
+ * @param {Uint8Array} plaintext
61
+ */
42
62
  encryptFor(peerId, plaintext) {
43
63
  const entry = this._sessions.get(peerId);
44
64
  if (!entry) { throw CryptoError.encryptionFailed({ reason: 'Session not found', peerId }); }
@@ -60,11 +80,15 @@ class SessionManager {
60
80
  entry.lastUsedAt = Date.now();
61
81
  entry.messageCount++;
62
82
  return ciphertext;
63
- } catch (error) {
83
+ } catch (/** @type {any} */ error) {
64
84
  throw CryptoError.encryptionFailed({ reason: error.message, peerId });
65
85
  }
66
86
  }
67
87
 
88
+ /**
89
+ * @param {string} peerId
90
+ * @param {Uint8Array} ciphertext
91
+ */
68
92
  decryptFrom(peerId, ciphertext) {
69
93
  const entry = this._sessions.get(peerId);
70
94
  if (!entry) { throw CryptoError.decryptionFailed({ reason: 'Session not found', peerId }); }
@@ -75,11 +99,14 @@ class SessionManager {
75
99
  entry.messageCount++;
76
100
  }
77
101
  return plaintext;
78
- } catch (error) {
102
+ } catch (/** @type {any} */ error) {
79
103
  throw CryptoError.decryptionFailed({ reason: error.message, peerId });
80
104
  }
81
105
  }
82
106
 
107
+ /**
108
+ * @param {string} peerId
109
+ */
83
110
  exportSession(peerId) {
84
111
  const entry = this._sessions.get(peerId);
85
112
  if (!entry || typeof entry.session.export !== 'function') { return null; }
@@ -89,6 +116,10 @@ class SessionManager {
89
116
  };
90
117
  }
91
118
 
119
+ /**
120
+ * @param {string} peerId
121
+ * @param {any} state
122
+ */
92
123
  importSession(peerId, state) {
93
124
  if (!state || !state.sessionData) { throw new Error('Invalid session state'); }
94
125
 
@@ -112,11 +143,12 @@ class SessionManager {
112
143
  let recvNonce = data.recvNonce || 0;
113
144
 
114
145
  // Try to get crypto provider for real encrypt/decrypt
146
+ /** @type {any} */
115
147
  let provider = null;
116
148
  try {
117
149
  const { createProvider } = require('../crypto/AutoCrypto');
118
150
  provider = createProvider('auto');
119
- } catch (e) {
151
+ } catch (/** @type {any} */ e) {
120
152
  // No crypto provider available
121
153
  }
122
154
 
@@ -127,6 +159,7 @@ class SessionManager {
127
159
  const recvNonceView = new DataView(recvNonceBuf.buffer);
128
160
 
129
161
  const session = {
162
+ /** @param {Uint8Array} plaintext */
130
163
  encrypt: (plaintext) => {
131
164
  if (provider && typeof provider.encrypt === 'function') {
132
165
  sendNonceView.setUint32(16, 0, true);
@@ -135,6 +168,7 @@ class SessionManager {
135
168
  }
136
169
  return plaintext;
137
170
  },
171
+ /** @param {Uint8Array} ciphertext */
138
172
  decrypt: (ciphertext) => {
139
173
  if (provider && typeof provider.decrypt === 'function') {
140
174
  recvNonceView.setUint32(16, 0, true);
@@ -160,6 +194,7 @@ class SessionManager {
160
194
  });
161
195
  }
162
196
 
197
+ /** @returns {string[]} */
163
198
  getAllSessionPeerIds() { return Array.from(this._sessions.keys()); }
164
199
  clear() { this._sessions.clear(); }
165
200