react-native-ble-mesh 1.1.1 → 2.0.0

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 (65) hide show
  1. package/README.md +288 -172
  2. package/docs/IOS-BACKGROUND-BLE.md +231 -0
  3. package/docs/OPTIMIZATION.md +70 -0
  4. package/docs/SPEC-v2.1.md +308 -0
  5. package/package.json +1 -1
  6. package/src/MeshNetwork.js +659 -465
  7. package/src/constants/index.js +1 -0
  8. package/src/crypto/AutoCrypto.js +79 -0
  9. package/src/crypto/CryptoProvider.js +99 -0
  10. package/src/crypto/index.js +15 -63
  11. package/src/crypto/providers/ExpoCryptoProvider.js +125 -0
  12. package/src/crypto/providers/QuickCryptoProvider.js +134 -0
  13. package/src/crypto/providers/TweetNaClProvider.js +124 -0
  14. package/src/crypto/providers/index.js +11 -0
  15. package/src/errors/MeshError.js +2 -1
  16. package/src/expo/withBLEMesh.js +102 -0
  17. package/src/hooks/useMesh.js +30 -9
  18. package/src/hooks/useMessages.js +2 -0
  19. package/src/index.js +23 -8
  20. package/src/mesh/dedup/DedupManager.js +36 -10
  21. package/src/mesh/fragment/Assembler.js +5 -0
  22. package/src/mesh/index.js +1 -1
  23. package/src/mesh/monitor/ConnectionQuality.js +408 -0
  24. package/src/mesh/monitor/NetworkMonitor.js +327 -316
  25. package/src/mesh/monitor/index.js +7 -3
  26. package/src/mesh/peer/PeerManager.js +6 -1
  27. package/src/mesh/router/MessageRouter.js +26 -15
  28. package/src/mesh/router/RouteTable.js +7 -1
  29. package/src/mesh/store/StoreAndForwardManager.js +295 -297
  30. package/src/mesh/store/index.js +1 -1
  31. package/src/service/BatteryOptimizer.js +282 -278
  32. package/src/service/EmergencyManager.js +224 -214
  33. package/src/service/HandshakeManager.js +167 -13
  34. package/src/service/MeshService.js +72 -6
  35. package/src/service/SessionManager.js +77 -2
  36. package/src/service/audio/AudioManager.js +8 -2
  37. package/src/service/file/FileAssembler.js +106 -0
  38. package/src/service/file/FileChunker.js +79 -0
  39. package/src/service/file/FileManager.js +307 -0
  40. package/src/service/file/FileMessage.js +122 -0
  41. package/src/service/file/index.js +15 -0
  42. package/src/service/text/broadcast/BroadcastManager.js +16 -0
  43. package/src/transport/BLETransport.js +131 -9
  44. package/src/transport/MockTransport.js +1 -1
  45. package/src/transport/MultiTransport.js +305 -0
  46. package/src/transport/WiFiDirectTransport.js +295 -0
  47. package/src/transport/adapters/NodeBLEAdapter.js +34 -0
  48. package/src/transport/adapters/RNBLEAdapter.js +56 -1
  49. package/src/transport/index.js +6 -0
  50. package/src/utils/compression.js +291 -291
  51. package/src/crypto/aead.js +0 -189
  52. package/src/crypto/chacha20.js +0 -181
  53. package/src/crypto/hkdf.js +0 -187
  54. package/src/crypto/hmac.js +0 -143
  55. package/src/crypto/keys/KeyManager.js +0 -271
  56. package/src/crypto/keys/KeyPair.js +0 -216
  57. package/src/crypto/keys/SecureStorage.js +0 -219
  58. package/src/crypto/keys/index.js +0 -32
  59. package/src/crypto/noise/handshake.js +0 -410
  60. package/src/crypto/noise/index.js +0 -27
  61. package/src/crypto/noise/session.js +0 -253
  62. package/src/crypto/noise/state.js +0 -268
  63. package/src/crypto/poly1305.js +0 -113
  64. package/src/crypto/sha256.js +0 -240
  65. package/src/crypto/x25519.js +0 -154
@@ -6,9 +6,13 @@
6
6
  */
7
7
 
8
8
  const { NetworkMonitor, HEALTH_STATUS, DEFAULT_CONFIG } = require('./NetworkMonitor');
9
+ const { ConnectionQuality, PeerQualityTracker, QUALITY_LEVEL } = require('./ConnectionQuality');
9
10
 
10
11
  module.exports = {
11
- NetworkMonitor,
12
- HEALTH_STATUS,
13
- DEFAULT_CONFIG,
12
+ NetworkMonitor,
13
+ HEALTH_STATUS,
14
+ DEFAULT_CONFIG,
15
+ ConnectionQuality,
16
+ PeerQualityTracker,
17
+ QUALITY_LEVEL
14
18
  };
@@ -230,7 +230,12 @@ class PeerManager extends EventEmitter {
230
230
  cleanup(maxAge = this.peerTimeout) {
231
231
  const removed = [];
232
232
  for (const [id, peer] of this._peers) {
233
- if (peer.isStale(maxAge) && !peer.isConnected()) {
233
+ // Don't remove peers that are actively connecting or connected
234
+ if (peer.isConnected() || peer.connectionState === CONNECTION_STATE.CONNECTING) {
235
+ continue;
236
+ }
237
+
238
+ if (peer.isStale(maxAge)) {
234
239
  this._peers.delete(id);
235
240
  removed.push(peer);
236
241
  this.emit(EVENTS.PEER_LOST, peer);
@@ -22,7 +22,8 @@ function generateUUID() {
22
22
  bytes[6] = (bytes[6] & 0x0f) | 0x40;
23
23
  bytes[8] = (bytes[8] & 0x3f) | 0x80;
24
24
  const hex = Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
25
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
25
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-` +
26
+ `${hex.slice(16, 20)}-${hex.slice(20)}`;
26
27
  }
27
28
 
28
29
  /**
@@ -148,8 +149,8 @@ class MessageRouter extends EventEmitter {
148
149
  return null;
149
150
  }
150
151
 
151
- // Check hop count
152
- if (hopCount >= (maxHops || MESH_CONFIG.MAX_HOPS)) {
152
+ // Check hop count (allow delivery at exactly maxHops, just don't relay further)
153
+ if (hopCount > (maxHops || MESH_CONFIG.MAX_HOPS)) {
153
154
  this._stats.maxHopsDropped++;
154
155
  this.emit(EVENTS.MESSAGE_DROPPED, { messageId, reason: 'max_hops' });
155
156
  return null;
@@ -165,17 +166,18 @@ class MessageRouter extends EventEmitter {
165
166
  this._routeTable.addRoute(sourcePeerId, sourcePeerId, 0);
166
167
 
167
168
  // Check if message is for us
168
- const isForUs = !message.recipientId ||
169
- message.recipientId === this.localPeerId ||
170
- (message.flags & MESSAGE_FLAGS.IS_BROADCAST);
169
+ const isForUs = !message.recipientId || message.recipientId === this.localPeerId;
170
+ const isBroadcast = (message.flags & MESSAGE_FLAGS.IS_BROADCAST) !== 0;
171
171
 
172
- if (isForUs) {
172
+ // Deliver locally if for us or broadcast
173
+ if (isForUs || isBroadcast) {
173
174
  this.emit(EVENTS.MESSAGE_RECEIVED, message);
174
175
  }
175
176
 
176
- // Relay if broadcast or not for us
177
- const shouldRelay = (message.flags & MESSAGE_FLAGS.IS_BROADCAST) ||
178
- (message.recipientId && message.recipientId !== this.localPeerId);
177
+ // Relay decision: only relay if TTL allows AND message isn't exclusively for us
178
+ const shouldRelay = isBroadcast
179
+ ? (message.hopCount < (maxHops || MESH_CONFIG.MAX_HOPS)) // Broadcasts relay if hops remain
180
+ : (!isForUs); // Unicast only relays if not for us
179
181
 
180
182
  if (shouldRelay) {
181
183
  this._relayMessage(message, sourcePeerId);
@@ -237,8 +239,11 @@ class MessageRouter extends EventEmitter {
237
239
  return [nextHop];
238
240
  }
239
241
 
240
- // No known route, flood to all
241
- return Array.from(this._peers.keys()).filter(id => id !== excludePeerId);
242
+ // No known route - use limited flooding (max 3 peers, prefer recently active)
243
+ const allPeers = Array.from(this._peers.keys()).filter(id => id !== excludePeerId);
244
+ // Limit flood scope to prevent network storms
245
+ const maxFloodPeers = Math.min(3, allPeers.length);
246
+ return allPeers.slice(0, maxFloodPeers);
242
247
  }
243
248
 
244
249
  /**
@@ -270,25 +275,31 @@ class MessageRouter extends EventEmitter {
270
275
  expiresAt: now + MESH_CONFIG.MESSAGE_TTL_MS
271
276
  };
272
277
 
273
- this._dedupManager.markSeen(messageId);
274
- this._stats.messagesSent++;
275
-
276
278
  // Determine targets
277
279
  const targets = recipientId
278
280
  ? [this._routeTable.getNextHop(recipientId) || recipientId]
279
281
  : Array.from(this._peers.keys());
280
282
 
283
+ // Send to targets
284
+ let anySent = false;
281
285
  for (const peerId of targets) {
282
286
  const sendFn = this._peers.get(peerId);
283
287
  if (sendFn) {
284
288
  try {
285
289
  sendFn(message);
290
+ anySent = true;
286
291
  } catch (err) {
287
292
  this.emit(EVENTS.ERROR, err);
288
293
  }
289
294
  }
290
295
  }
291
296
 
297
+ // Only mark as seen AFTER successful send
298
+ if (anySent) {
299
+ this._dedupManager.markSeen(messageId);
300
+ this._stats.messagesSent++;
301
+ }
302
+
292
303
  this.emit(EVENTS.MESSAGE_SENT, message);
293
304
  return messageId;
294
305
  }
@@ -99,7 +99,13 @@ class RouteTable {
99
99
 
100
100
  // Enforce max routes limit
101
101
  if (!existingRoute && this._routes.size >= this.maxRoutes) {
102
- this._evictOldestRoute();
102
+ // First try to clean up expired routes before evicting valid ones
103
+ this.cleanup();
104
+
105
+ // If still at capacity after cleanup, evict oldest
106
+ if (this._routes.size >= this.maxRoutes) {
107
+ this._evictOldestRoute();
108
+ }
103
109
  }
104
110
 
105
111
  const route = {