react-native-ble-mesh 2.1.0 → 2.1.3

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 +9 -2
  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 +50 -15
  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 +10 -4
  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 +19 -26
  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 +22 -20
  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 +25 -24
  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 +19 -24
  81. package/src/utils/EventEmitter.js +17 -12
  82. package/src/utils/LRUCache.js +10 -4
  83. package/src/utils/RateLimiter.js +1 -1
  84. package/src/utils/compression.js +6 -8
  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
@@ -13,7 +13,7 @@ const EventEmitter = require('../utils/EventEmitter');
13
13
 
14
14
  /**
15
15
  * Panic trigger types
16
- * @constant {Object}
16
+ * @constant {any}
17
17
  */
18
18
  const PANIC_TRIGGER = Object.freeze({
19
19
  TRIPLE_TAP: 'triple_tap',
@@ -24,7 +24,7 @@ const PANIC_TRIGGER = Object.freeze({
24
24
 
25
25
  /**
26
26
  * Default configuration
27
- * @constant {Object}
27
+ * @constant {any}
28
28
  */
29
29
  const DEFAULT_CONFIG = Object.freeze({
30
30
  /** Trigger type for panic mode */
@@ -63,18 +63,14 @@ const DEFAULT_CONFIG = Object.freeze({
63
63
  class EmergencyManager extends EventEmitter {
64
64
  /**
65
65
  * Creates a new EmergencyManager instance.
66
- * @param {Object} [options={}] - Configuration options
67
- * @param {string} [options.trigger='triple_tap'] - Panic trigger type
68
- * @param {number} [options.tapWindowMs=500] - Time window for taps
69
- * @param {number} [options.tapCount=3] - Required tap count
70
- * @param {boolean} [options.requireConfirmation=false] - Require confirmation
66
+ * @param {any} [options] - Configuration options
71
67
  */
72
68
  constructor(options = {}) {
73
69
  super();
74
70
 
75
71
  /**
76
72
  * Configuration
77
- * @type {Object}
73
+ * @type {any}
78
74
  * @private
79
75
  */
80
76
  this._config = { ...DEFAULT_CONFIG, ...options };
@@ -88,7 +84,7 @@ class EmergencyManager extends EventEmitter {
88
84
 
89
85
  /**
90
86
  * Tap tracking
91
- * @type {Object}
87
+ * @type {any}
92
88
  * @private
93
89
  */
94
90
  this._tapState = {
@@ -98,7 +94,7 @@ class EmergencyManager extends EventEmitter {
98
94
 
99
95
  /**
100
96
  * Shake tracking
101
- * @type {Object}
97
+ * @type {any}
102
98
  * @private
103
99
  */
104
100
  this._shakeState = {
@@ -122,7 +118,7 @@ class EmergencyManager extends EventEmitter {
122
118
 
123
119
  /**
124
120
  * Statistics
125
- * @type {Object}
121
+ * @type {any}
126
122
  * @private
127
123
  */
128
124
  this._stats = {
@@ -134,9 +130,7 @@ class EmergencyManager extends EventEmitter {
134
130
 
135
131
  /**
136
132
  * Enables panic mode.
137
- * @param {Object} [options={}] - Enable options
138
- * @param {Function} [options.onWipe] - Callback after wipe
139
- * @param {string} [options.trigger] - Override trigger type
133
+ * @param {any} [options] - Enable options
140
134
  */
141
135
  enablePanicMode(options = {}) {
142
136
  this._enabled = true;
@@ -208,10 +202,7 @@ class EmergencyManager extends EventEmitter {
208
202
 
209
203
  /**
210
204
  * Registers accelerometer data for shake detection.
211
- * @param {Object} data - Accelerometer data
212
- * @param {number} data.x - X acceleration
213
- * @param {number} data.y - Y acceleration
214
- * @param {number} data.z - Z acceleration
205
+ * @param {any} data - Accelerometer data
215
206
  */
216
207
  registerAccelerometer(data) {
217
208
  if (!this._enabled || this._config.trigger !== PANIC_TRIGGER.SHAKE) {
@@ -236,7 +227,7 @@ class EmergencyManager extends EventEmitter {
236
227
 
237
228
  /**
238
229
  * Manually triggers panic wipe.
239
- * @returns {Promise<Object>} Wipe result
230
+ * @returns {Promise<any>} Wipe result
240
231
  */
241
232
  async triggerManualWipe() {
242
233
  return this._executeWipe(PANIC_TRIGGER.MANUAL);
@@ -244,7 +235,7 @@ class EmergencyManager extends EventEmitter {
244
235
 
245
236
  /**
246
237
  * Wipes all registered data.
247
- * @returns {Promise<Object>} Wipe result with timing
238
+ * @returns {Promise<any>} Wipe result with timing
248
239
  */
249
240
  async wipeAllData() {
250
241
  return this._executeWipe(PANIC_TRIGGER.MANUAL);
@@ -252,7 +243,7 @@ class EmergencyManager extends EventEmitter {
252
243
 
253
244
  /**
254
245
  * Gets emergency statistics.
255
- * @returns {Object} Statistics
246
+ * @returns {any} Statistics
256
247
  */
257
248
  getStats() {
258
249
  return { ...this._stats };
@@ -280,7 +271,7 @@ class EmergencyManager extends EventEmitter {
280
271
 
281
272
  try {
282
273
  await this._executeWipe(trigger);
283
- } catch (error) {
274
+ } catch (/** @type {any} */ error) {
284
275
  this.emit('error', {
285
276
  message: 'Panic wipe failed',
286
277
  error: error.message,
@@ -292,7 +283,7 @@ class EmergencyManager extends EventEmitter {
292
283
  /**
293
284
  * Executes the data wipe.
294
285
  * @param {string} trigger - Trigger type
295
- * @returns {Promise<Object>} Wipe result
286
+ * @returns {Promise<any>} Wipe result
296
287
  * @private
297
288
  */
298
289
  async _executeWipe(trigger) {
@@ -300,6 +291,7 @@ class EmergencyManager extends EventEmitter {
300
291
 
301
292
  this.emit('panic-wipe-started', { trigger, timestamp: startTime });
302
293
 
294
+ /** @type {any} */
303
295
  const results = {
304
296
  trigger,
305
297
  startTime,
@@ -308,12 +300,13 @@ class EmergencyManager extends EventEmitter {
308
300
  };
309
301
 
310
302
  // Execute all clearers in parallel for speed
303
+ /** @type {any[]} */
311
304
  const clearerResults = new Array(this._clearers.length);
312
305
  const promises = this._clearers.map(async (clearer, index) => {
313
306
  try {
314
307
  await clearer();
315
308
  clearerResults[index] = { index, success: true };
316
- } catch (error) {
309
+ } catch (/** @type {any} */ error) {
317
310
  results.errors.push({ index, error: error.message });
318
311
  clearerResults[index] = { index, success: false, error: error.message };
319
312
  }
@@ -344,12 +337,12 @@ class EmergencyManager extends EventEmitter {
344
337
  if (this._onWipe) {
345
338
  try {
346
339
  this._onWipe(results);
347
- } catch (error) {
340
+ } catch (/** @type {any} */ error) {
348
341
  // Ignore callback errors
349
342
  }
350
343
  }
351
344
 
352
- console.log(`Panic wipe completed in ${elapsedMs}ms`);
345
+ // Panic wipe completed — avoid logging sensitive operations in production
353
346
 
354
347
  return results;
355
348
  }
@@ -21,15 +21,27 @@ const STATE = Object.freeze({
21
21
  * @extends EventEmitter
22
22
  */
23
23
  class HandshakeManager extends EventEmitter {
24
+ /**
25
+ * @param {any} keyManager
26
+ * @param {any} sessionManager
27
+ */
24
28
  constructor(keyManager, sessionManager) {
25
29
  super();
26
30
  if (!keyManager || !sessionManager) { throw new Error('keyManager and sessionManager required'); }
31
+ /** @type {any} */
27
32
  this._keyManager = keyManager;
33
+ /** @type {any} */
28
34
  this._sessionManager = sessionManager;
35
+ /** @type {Map<string, any>} */
29
36
  this._pending = new Map();
37
+ /** @type {number} */
30
38
  this._timeout = MESH_CONFIG.HANDSHAKE_TIMEOUT_MS;
31
39
  }
32
40
 
41
+ /**
42
+ * @param {string} peerId
43
+ * @param {any} transport
44
+ */
33
45
  async initiateHandshake(peerId, transport) {
34
46
  if (this._pending.has(peerId)) { throw HandshakeError.alreadyInProgress(peerId); }
35
47
 
@@ -44,12 +56,18 @@ class HandshakeManager extends EventEmitter {
44
56
  hs.step = 1;
45
57
  this.emit(EVENTS.HANDSHAKE_PROGRESS, { peerId, step: 1, role: 'initiator' });
46
58
  return await this._waitForCompletion(peerId);
47
- } catch (err) {
59
+ } catch (/** @type {any} */ err) {
48
60
  this._fail(peerId, err);
49
61
  throw err;
50
62
  }
51
63
  }
52
64
 
65
+ /**
66
+ * @param {string} peerId
67
+ * @param {number} type
68
+ * @param {Uint8Array} payload
69
+ * @param {any} transport
70
+ */
53
71
  async handleIncomingHandshake(peerId, type, payload, transport) {
54
72
  try {
55
73
  if (type === MESSAGE_TYPE.HANDSHAKE_INIT) {
@@ -62,12 +80,15 @@ class HandshakeManager extends EventEmitter {
62
80
  return await this._onFinal(peerId, payload);
63
81
  }
64
82
  throw HandshakeError.handshakeFailed(peerId, null, { reason: 'Unknown type' });
65
- } catch (err) {
83
+ } catch (/** @type {any} */ err) {
66
84
  this._fail(peerId, err);
67
85
  throw err;
68
86
  }
69
87
  }
70
88
 
89
+ /**
90
+ * @param {string} peerId
91
+ */
71
92
  cancelHandshake(peerId) {
72
93
  const hs = this._pending.get(peerId);
73
94
  if (!hs) { return; }
@@ -77,9 +98,20 @@ class HandshakeManager extends EventEmitter {
77
98
  this.emit(EVENTS.HANDSHAKE_FAILED, { peerId, reason: 'cancelled' });
78
99
  }
79
100
 
101
+ /**
102
+ * @param {string} peerId
103
+ * @returns {boolean}
104
+ */
80
105
  isHandshakePending(peerId) { return this._pending.has(peerId); }
106
+ /** @returns {number} */
81
107
  getPendingCount() { return this._pending.size; }
82
108
 
109
+ /**
110
+ * @param {string} peerId
111
+ * @param {boolean} isInitiator
112
+ * @returns {any}
113
+ * @private
114
+ */
83
115
  _createState(peerId, isInitiator) {
84
116
  const kp = this._keyManager.getStaticKeyPair();
85
117
  return {
@@ -88,11 +120,18 @@ class HandshakeManager extends EventEmitter {
88
120
  };
89
121
  }
90
122
 
123
+ /**
124
+ * @param {any} keyPair
125
+ * @param {boolean} isInitiator
126
+ * @returns {any}
127
+ * @private
128
+ */
91
129
  _createNoise(keyPair, isInitiator) {
92
130
  // Get crypto provider from keyManager if available
93
131
  const provider = this._keyManager.provider;
94
132
 
95
133
  // Generate ephemeral key pair for this handshake
134
+ /** @type {any} */
96
135
  let ephemeralKeyPair;
97
136
  if (provider && typeof provider.generateKeyPair === 'function') {
98
137
  ephemeralKeyPair = provider.generateKeyPair();
@@ -101,11 +140,18 @@ class HandshakeManager extends EventEmitter {
101
140
  ephemeralKeyPair = { publicKey: randomBytes(32), secretKey: randomBytes(32) };
102
141
  }
103
142
 
143
+ /** @type {Uint8Array | null} */
104
144
  let remoteEphemeralPublic = null;
145
+ /** @type {Uint8Array | null} */
105
146
  let sharedSecret = null;
147
+ /** @type {any} */
106
148
  let sessionKeys = null;
107
149
  let complete = false;
108
150
 
151
+ /**
152
+ * @param {Uint8Array} secret
153
+ * @returns {any}
154
+ */
109
155
  const deriveSessionKeys = (secret) => {
110
156
  // Derive send/receive keys from shared secret
111
157
  // Use hash to derive two different keys from the secret
@@ -125,6 +171,7 @@ class HandshakeManager extends EventEmitter {
125
171
  return ephemeralKeyPair.publicKey;
126
172
  },
127
173
 
174
+ /** @param {Uint8Array} msg */
128
175
  readMessage1: (msg) => {
129
176
  // Responder receives initiator's ephemeral public key
130
177
  remoteEphemeralPublic = new Uint8Array(msg);
@@ -137,10 +184,12 @@ class HandshakeManager extends EventEmitter {
137
184
  } else {
138
185
  throw new Error('Crypto provider required for secure handshake. Install tweetnacl: npm install tweetnacl');
139
186
  }
187
+ // @ts-ignore
140
188
  sessionKeys = deriveSessionKeys(sharedSecret);
141
189
  return ephemeralKeyPair.publicKey;
142
190
  },
143
191
 
192
+ /** @param {Uint8Array} msg */
144
193
  readMessage2: (msg) => {
145
194
  // Initiator receives responder's ephemeral public key and derives shared secret
146
195
  remoteEphemeralPublic = new Uint8Array(msg);
@@ -149,6 +198,7 @@ class HandshakeManager extends EventEmitter {
149
198
  } else {
150
199
  throw new Error('Crypto provider required for secure handshake. Install tweetnacl: npm install tweetnacl');
151
200
  }
201
+ // @ts-ignore
152
202
  sessionKeys = deriveSessionKeys(sharedSecret);
153
203
  },
154
204
 
@@ -158,6 +208,7 @@ class HandshakeManager extends EventEmitter {
158
208
  return ephemeralKeyPair.publicKey;
159
209
  },
160
210
 
211
+ /** @param {any} _msg */
161
212
  readMessage3: (_msg) => {
162
213
  // Responder confirms handshake completion
163
214
  complete = true;
@@ -184,6 +235,7 @@ class HandshakeManager extends EventEmitter {
184
235
  const recvNonceView = new DataView(recvNonceBuf.buffer);
185
236
 
186
237
  return {
238
+ /** @param {Uint8Array} plaintext */
187
239
  encrypt: (plaintext) => {
188
240
  if (provider && typeof provider.encrypt === 'function') {
189
241
  // Store counter in last 8 bytes of nonce
@@ -194,6 +246,7 @@ class HandshakeManager extends EventEmitter {
194
246
  throw new Error('Crypto provider required for encryption');
195
247
  },
196
248
 
249
+ /** @param {Uint8Array} ciphertext */
197
250
  decrypt: (ciphertext) => {
198
251
  if (provider && typeof provider.decrypt === 'function') {
199
252
  recvNonceView.setUint32(16, 0, true);
@@ -215,6 +268,12 @@ class HandshakeManager extends EventEmitter {
215
268
  };
216
269
  }
217
270
 
271
+ /**
272
+ * @param {string} peerId
273
+ * @param {Uint8Array} payload
274
+ * @param {any} transport
275
+ * @private
276
+ */
218
277
  async _onInit(peerId, payload, transport) {
219
278
  const existing = this._pending.get(peerId);
220
279
 
@@ -245,6 +304,12 @@ class HandshakeManager extends EventEmitter {
245
304
  return null;
246
305
  }
247
306
 
307
+ /**
308
+ * @param {string} peerId
309
+ * @param {Uint8Array} payload
310
+ * @param {any} transport
311
+ * @private
312
+ */
248
313
  async _onResponse(peerId, payload, transport) {
249
314
  const hs = this._pending.get(peerId);
250
315
  if (!hs || !hs.isInitiator) {
@@ -258,6 +323,11 @@ class HandshakeManager extends EventEmitter {
258
323
  return this._complete(peerId, hs);
259
324
  }
260
325
 
326
+ /**
327
+ * @param {string} peerId
328
+ * @param {Uint8Array} payload
329
+ * @private
330
+ */
261
331
  async _onFinal(peerId, payload) {
262
332
  const hs = this._pending.get(peerId);
263
333
  if (!hs || hs.isInitiator) { throw HandshakeError.invalidState(peerId, 3); }
@@ -266,6 +336,11 @@ class HandshakeManager extends EventEmitter {
266
336
  return this._complete(peerId, hs);
267
337
  }
268
338
 
339
+ /**
340
+ * @param {string} peerId
341
+ * @param {any} hs
342
+ * @private
343
+ */
269
344
  _complete(peerId, hs) {
270
345
  if (hs.timer) { clearTimeout(hs.timer); }
271
346
  const session = hs.noise.getSession();
@@ -279,6 +354,11 @@ class HandshakeManager extends EventEmitter {
279
354
  return session;
280
355
  }
281
356
 
357
+ /**
358
+ * @param {string} peerId
359
+ * @param {any} error
360
+ * @private
361
+ */
282
362
  _fail(peerId, error) {
283
363
  const hs = this._pending.get(peerId);
284
364
  if (hs) {
@@ -289,6 +369,10 @@ class HandshakeManager extends EventEmitter {
289
369
  this.emit(EVENTS.HANDSHAKE_FAILED, { peerId, error: error.message, step: hs?.step });
290
370
  }
291
371
 
372
+ /**
373
+ * @param {string} peerId
374
+ * @private
375
+ */
292
376
  _setTimeout(peerId) {
293
377
  const hs = this._pending.get(peerId);
294
378
  if (!hs) { return; }
@@ -299,6 +383,10 @@ class HandshakeManager extends EventEmitter {
299
383
  }, this._timeout);
300
384
  }
301
385
 
386
+ /**
387
+ * @param {string} peerId
388
+ * @private
389
+ */
302
390
  _waitForCompletion(peerId) {
303
391
  return new Promise((resolve, reject) => {
304
392
  const hs = this._pending.get(peerId);
@@ -309,6 +397,11 @@ class HandshakeManager extends EventEmitter {
309
397
  });
310
398
  }
311
399
 
400
+ /**
401
+ * @param {any} localKey
402
+ * @param {any} remoteId
403
+ * @private
404
+ */
312
405
  _compareKeys(localKey, remoteId) {
313
406
  // Simple string/byte comparison for deterministic tie-breaking
314
407
  const localStr = typeof localKey === 'string' ? localKey : Array.from(localKey).join(',');
@@ -316,6 +409,11 @@ class HandshakeManager extends EventEmitter {
316
409
  return localStr < remoteStr ? -1 : localStr > remoteStr ? 1 : 0;
317
410
  }
318
411
 
412
+ /**
413
+ * @param {number} type
414
+ * @param {Uint8Array} payload
415
+ * @private
416
+ */
319
417
  _wrap(type, payload) {
320
418
  const w = new Uint8Array(1 + payload.length);
321
419
  w[0] = type;