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
@@ -18,9 +18,9 @@ const { ValidationError, ConnectionError } = require('../../errors');
18
18
  class PeerManager extends EventEmitter {
19
19
  /**
20
20
  * Creates a new PeerManager
21
- * @param {Object} [options] - Configuration options
22
- * @param {number} [options.maxPeers] - Maximum number of peers
23
- * @param {number} [options.peerTimeout] - Timeout for stale peers
21
+ * @param {any} [options] - Configuration options
22
+ *
23
+ *
24
24
  */
25
25
  constructor(options = {}) {
26
26
  super();
@@ -39,7 +39,7 @@ class PeerManager extends EventEmitter {
39
39
 
40
40
  /**
41
41
  * Peers by ID
42
- * @type {Map<string, Peer>}
42
+ * @type {Map<string, any>}
43
43
  * @private
44
44
  */
45
45
  this._peers = new Map();
@@ -54,8 +54,8 @@ class PeerManager extends EventEmitter {
54
54
 
55
55
  /**
56
56
  * Adds or updates a peer
57
- * @param {Object} info - Peer information
58
- * @returns {Peer} The added or updated peer
57
+ * @param {any} info - Peer information
58
+ * @returns {any} The added or updated peer
59
59
  */
60
60
  addPeer(info) {
61
61
  if (!info || typeof info.id !== 'string') {
@@ -90,7 +90,7 @@ class PeerManager extends EventEmitter {
90
90
  /**
91
91
  * Gets a peer by ID
92
92
  * @param {string} id - Peer ID
93
- * @returns {Peer|undefined} The peer or undefined
93
+ * @returns {any} The peer or undefined
94
94
  */
95
95
  getPeer(id) {
96
96
  return this._peers.get(id);
@@ -98,7 +98,7 @@ class PeerManager extends EventEmitter {
98
98
 
99
99
  /**
100
100
  * Gets all peers
101
- * @returns {Peer[]} Array of all peers
101
+ * @returns {any[]} Array of all peers
102
102
  */
103
103
  getAllPeers() {
104
104
  return Array.from(this._peers.values());
@@ -106,33 +106,45 @@ class PeerManager extends EventEmitter {
106
106
 
107
107
  /**
108
108
  * Gets connected peers
109
- * @returns {Peer[]} Array of connected peers
109
+ * @returns {any[]} Array of connected peers
110
110
  */
111
111
  getConnectedPeers() {
112
- return this.getAllPeers().filter(peer => peer.isConnected());
112
+ const result = [];
113
+ for (const peer of this._peers.values()) {
114
+ if (peer.isConnected()) { result.push(peer); }
115
+ }
116
+ return result;
113
117
  }
114
118
 
115
119
  /**
116
120
  * Gets peers with secure sessions
117
- * @returns {Peer[]} Array of secured peers
121
+ * @returns {any[]} Array of secured peers
118
122
  */
119
123
  getSecuredPeers() {
120
- return this.getAllPeers().filter(peer => peer.isSecured());
124
+ const result = [];
125
+ for (const peer of this._peers.values()) {
126
+ if (peer.isSecured()) { result.push(peer); }
127
+ }
128
+ return result;
121
129
  }
122
130
 
123
131
  /**
124
132
  * Gets directly connected peers
125
- * @returns {Peer[]} Array of direct peers
133
+ * @returns {any[]} Array of direct peers
126
134
  */
127
135
  getDirectPeers() {
128
- return this.getAllPeers().filter(peer => peer.isDirect());
136
+ const result = [];
137
+ for (const peer of this._peers.values()) {
138
+ if (peer.isDirect()) { result.push(peer); }
139
+ }
140
+ return result;
129
141
  }
130
142
 
131
143
  /**
132
144
  * Updates a peer's connection state
133
145
  * @param {string} id - Peer ID
134
146
  * @param {string} state - New connection state
135
- * @returns {Peer|undefined} Updated peer or undefined
147
+ * @returns {any} Updated peer or undefined
136
148
  */
137
149
  updateConnectionState(id, state) {
138
150
  const peer = this._peers.get(id);
@@ -154,7 +166,7 @@ class PeerManager extends EventEmitter {
154
166
  /**
155
167
  * Marks a peer as secured
156
168
  * @param {string} id - Peer ID
157
- * @returns {Peer|undefined} Updated peer or undefined
169
+ * @returns {any} Updated peer or undefined
158
170
  */
159
171
  markSecured(id) {
160
172
  const peer = this._peers.get(id);
@@ -225,7 +237,7 @@ class PeerManager extends EventEmitter {
225
237
  /**
226
238
  * Cleans up stale peers
227
239
  * @param {number} [maxAge] - Maximum age in ms, defaults to peerTimeout
228
- * @returns {Peer[]} Array of removed peers
240
+ * @returns {any[]} Array of removed peers
229
241
  */
230
242
  cleanup(maxAge = this.peerTimeout) {
231
243
  const removed = [];
@@ -12,18 +12,26 @@ const { DedupManager } = require('../dedup');
12
12
  const RouteTable = require('./RouteTable');
13
13
  const { randomBytes } = require('../../utils/bytes');
14
14
 
15
+ /**
16
+ * Hex lookup table for fast byte-to-hex conversion
17
+ * @constant {string[]}
18
+ * @private
19
+ */
20
+ const HEX = Array.from({ length: 256 }, (/** @type {any} */ _, /** @type {number} */ i) => (i < 16 ? '0' : '') + i.toString(16));
21
+
15
22
  /**
16
23
  * Generates a UUID v4 string
17
24
  * @returns {string} UUID string
18
25
  * @private
19
26
  */
20
27
  function generateUUID() {
21
- const bytes = randomBytes(16);
22
- bytes[6] = (bytes[6] & 0x0f) | 0x40;
23
- bytes[8] = (bytes[8] & 0x3f) | 0x80;
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)}-` +
26
- `${hex.slice(16, 20)}-${hex.slice(20)}`;
28
+ const b = randomBytes(16);
29
+ b[6] = (b[6] & 0x0f) | 0x40;
30
+ b[8] = (b[8] & 0x3f) | 0x80;
31
+ return `${HEX[b[0]]}${HEX[b[1]]}${HEX[b[2]]}${HEX[b[3]]}-${
32
+ HEX[b[4]]}${HEX[b[5]]}-${HEX[b[6]]}${HEX[b[7]]}-${
33
+ HEX[b[8]]}${HEX[b[9]]}-${HEX[b[10]]}${HEX[b[11]]}${
34
+ HEX[b[12]]}${HEX[b[13]]}${HEX[b[14]]}${HEX[b[15]]}`;
27
35
  }
28
36
 
29
37
  /**
@@ -34,8 +42,8 @@ function generateUUID() {
34
42
  class MessageRouter extends EventEmitter {
35
43
  /**
36
44
  * Creates a new MessageRouter
37
- * @param {Object} options - Configuration options
38
- * @param {string} options.localPeerId - Local peer ID
45
+ * @param {any} options - Configuration options
46
+ *
39
47
  */
40
48
  constructor(options = {}) {
41
49
  super();
@@ -69,7 +77,7 @@ class MessageRouter extends EventEmitter {
69
77
 
70
78
  /**
71
79
  * Statistics
72
- * @type {Object}
80
+ * @type {any}
73
81
  * @private
74
82
  */
75
83
  this._stats = {
@@ -126,9 +134,9 @@ class MessageRouter extends EventEmitter {
126
134
 
127
135
  /**
128
136
  * Processes an incoming message
129
- * @param {Object} message - Message object
137
+ * @param {any} message - Message object
130
138
  * @param {string} sourcePeerId - Peer ID that sent the message
131
- * @returns {Object|null} Processed message or null if dropped
139
+ * @returns {any} Processed message or null if dropped
132
140
  */
133
141
  processIncoming(message, sourcePeerId) {
134
142
  this._stats.messagesReceived++;
@@ -188,7 +196,7 @@ class MessageRouter extends EventEmitter {
188
196
 
189
197
  /**
190
198
  * Relays a message to other peers
191
- * @param {Object} message - Message to relay
199
+ * @param {any} message - Message to relay
192
200
  * @param {string} excludePeerId - Peer to exclude from relay
193
201
  * @private
194
202
  */
@@ -206,7 +214,7 @@ class MessageRouter extends EventEmitter {
206
214
  try {
207
215
  sendFn(relayedMessage);
208
216
  this._stats.messagesRelayed++;
209
- } catch (err) {
217
+ } catch (/** @type {any} */ err) {
210
218
  this.emit(EVENTS.ERROR, err);
211
219
  }
212
220
  }
@@ -222,7 +230,7 @@ class MessageRouter extends EventEmitter {
222
230
 
223
231
  /**
224
232
  * Gets relay targets for a message
225
- * @param {Object} message - Message
233
+ * @param {any} message - Message
226
234
  * @param {string} excludePeerId - Peer to exclude
227
235
  * @returns {string[]} Array of peer IDs
228
236
  * @private
@@ -230,7 +238,7 @@ class MessageRouter extends EventEmitter {
230
238
  _getRelayTargets(message, excludePeerId) {
231
239
  if (message.flags & MESSAGE_FLAGS.IS_BROADCAST) {
232
240
  // Broadcast: send to all except source
233
- return Array.from(this._peers.keys()).filter(id => id !== excludePeerId);
241
+ return Array.from(this._peers.keys()).filter((/** @type {string} */ id) => id !== excludePeerId);
234
242
  }
235
243
 
236
244
  // Unicast: find route to recipient
@@ -240,7 +248,7 @@ class MessageRouter extends EventEmitter {
240
248
  }
241
249
 
242
250
  // No known route - use limited flooding (max 3 peers, prefer recently active)
243
- const allPeers = Array.from(this._peers.keys()).filter(id => id !== excludePeerId);
251
+ const allPeers = Array.from(this._peers.keys()).filter((/** @type {string} */ id) => id !== excludePeerId);
244
252
  // Limit flood scope to prevent network storms
245
253
  const maxFloodPeers = Math.min(3, allPeers.length);
246
254
  return allPeers.slice(0, maxFloodPeers);
@@ -248,7 +256,7 @@ class MessageRouter extends EventEmitter {
248
256
 
249
257
  /**
250
258
  * Sends a message
251
- * @param {Object} options - Send options
259
+ * @param {any} options - Send options
252
260
  * @returns {string} Message ID
253
261
  */
254
262
  send(options) {
@@ -288,7 +296,7 @@ class MessageRouter extends EventEmitter {
288
296
  try {
289
297
  sendFn(message);
290
298
  anySent = true;
291
- } catch (err) {
299
+ } catch (/** @type {any} */ err) {
292
300
  this.emit(EVENTS.ERROR, err);
293
301
  }
294
302
  }
@@ -306,7 +314,7 @@ class MessageRouter extends EventEmitter {
306
314
 
307
315
  /**
308
316
  * Broadcasts a message to all peers
309
- * @param {Object} options - Broadcast options
317
+ * @param {any} options - Broadcast options
310
318
  * @returns {string} Message ID
311
319
  */
312
320
  broadcast(options) {
@@ -319,7 +327,7 @@ class MessageRouter extends EventEmitter {
319
327
 
320
328
  /**
321
329
  * Gets router statistics
322
- * @returns {Object} Statistics
330
+ * @returns {any} Statistics
323
331
  */
324
332
  getStats() {
325
333
  return {
@@ -28,10 +28,7 @@ const { ValidationError } = require('../../errors');
28
28
  class PathFinder extends EventEmitter {
29
29
  /**
30
30
  * Creates a new PathFinder
31
- * @param {Object} options - Configuration options
32
- * @param {string} options.localPeerId - Local peer ID
33
- * @param {Object} options.routeTable - RouteTable instance
34
- * @param {number} [options.discoveryTimeout] - Route discovery timeout
31
+ * @param {any} options - Configuration options *
35
32
  */
36
33
  constructor(options = {}) {
37
34
  super();
@@ -51,7 +48,7 @@ class PathFinder extends EventEmitter {
51
48
 
52
49
  /**
53
50
  * Route table reference
54
- * @type {Object}
51
+ * @type {any}
55
52
  */
56
53
  this.routeTable = options.routeTable;
57
54
 
@@ -63,7 +60,7 @@ class PathFinder extends EventEmitter {
63
60
 
64
61
  /**
65
62
  * Pending route requests
66
- * @type {Map<string, RouteRequest>}
63
+ * @type {Map<string, any>}
67
64
  * @private
68
65
  */
69
66
  this._pendingRequests = new Map();
@@ -77,7 +74,7 @@ class PathFinder extends EventEmitter {
77
74
 
78
75
  /**
79
76
  * Statistics
80
- * @type {Object}
77
+ * @type {any}
81
78
  * @private
82
79
  */
83
80
  this._stats = {
@@ -93,7 +90,7 @@ class PathFinder extends EventEmitter {
93
90
  /**
94
91
  * Finds a route to a destination
95
92
  * @param {string} destination - Target peer ID
96
- * @returns {Promise<Object|null>} Route or null if not found
93
+ * @returns {Promise<any>} Route or null if not found
97
94
  */
98
95
  async findRoute(destination) {
99
96
  if (!destination || typeof destination !== 'string') {
@@ -113,7 +110,7 @@ class PathFinder extends EventEmitter {
113
110
  /**
114
111
  * Initiates route discovery for a destination
115
112
  * @param {string} destination - Target peer ID
116
- * @returns {Promise<Object|null>} Discovered route or null
113
+ * @returns {Promise<any>} Discovered route or null
117
114
  * @private
118
115
  */
119
116
  _initiateDiscovery(destination) {
@@ -164,9 +161,9 @@ class PathFinder extends EventEmitter {
164
161
 
165
162
  /**
166
163
  * Processes an incoming route request
167
- * @param {Object} request - Route request data
164
+ * @param {any} request - Route request data
168
165
  * @param {string} sourcePeerId - Source peer ID
169
- * @returns {Object|null} Reply to send or null
166
+ * @returns {any} Reply to send or null
170
167
  */
171
168
  processRouteRequest(request, sourcePeerId) {
172
169
  this._stats.requestsReceived++;
@@ -219,7 +216,7 @@ class PathFinder extends EventEmitter {
219
216
 
220
217
  /**
221
218
  * Processes an incoming route reply
222
- * @param {Object} reply - Route reply data
219
+ * @param {any} reply - Route reply data
223
220
  * @param {string} sourcePeerId - Source peer ID
224
221
  */
225
222
  processRouteReply(reply, sourcePeerId) {
@@ -295,7 +292,7 @@ class PathFinder extends EventEmitter {
295
292
 
296
293
  /**
297
294
  * Gets path finder statistics
298
- * @returns {Object} Statistics
295
+ * @returns {any} Statistics
299
296
  */
300
297
  getStats() {
301
298
  return {
@@ -26,9 +26,9 @@ const { ValidationError } = require('../../errors');
26
26
  class RouteTable {
27
27
  /**
28
28
  * Creates a new RouteTable
29
- * @param {Object} [options] - Configuration options
30
- * @param {number} [options.routeTimeout] - Route expiration timeout
31
- * @param {number} [options.maxRoutes] - Maximum routes to store
29
+ * @param {any} [options] - Configuration options
30
+ *
31
+ *
32
32
  */
33
33
  constructor(options = {}) {
34
34
  /**
@@ -45,7 +45,7 @@ class RouteTable {
45
45
 
46
46
  /**
47
47
  * Routes by destination peer ID
48
- * @type {Map<string, RouteEntry>}
48
+ * @type {Map<string, any>}
49
49
  * @private
50
50
  */
51
51
  this._routes = new Map();
@@ -130,7 +130,7 @@ class RouteTable {
130
130
  /**
131
131
  * Gets a route to a destination
132
132
  * @param {string} destination - Destination peer ID
133
- * @returns {RouteEntry|undefined} Route or undefined
133
+ * @returns {any} Route or undefined
134
134
  */
135
135
  getRoute(destination) {
136
136
  const route = this._routes.get(destination);
@@ -197,7 +197,7 @@ class RouteTable {
197
197
 
198
198
  /**
199
199
  * Gets all valid routes
200
- * @returns {RouteEntry[]} Array of routes
200
+ * @returns {any[]} Array of routes
201
201
  */
202
202
  getAllRoutes() {
203
203
  const now = Date.now();
@@ -244,6 +244,7 @@ class RouteTable {
244
244
  * @private
245
245
  */
246
246
  _evictOldestRoute() {
247
+ /** @type {string|null} */
247
248
  let oldest = null;
248
249
  let oldestTime = Infinity;
249
250
 
@@ -278,19 +279,29 @@ class RouteTable {
278
279
 
279
280
  /**
280
281
  * Gets routing table statistics
281
- * @returns {Object} Statistics
282
+ * @returns {any} Statistics
282
283
  */
283
284
  getStats() {
284
- const routes = this.getAllRoutes();
285
- const hopCounts = routes.map(r => r.hopCount);
285
+ const now = Date.now();
286
+ let totalRoutes = 0;
287
+ let maxHops = 0;
288
+ let hopSum = 0;
289
+
290
+ for (const [, route] of this._routes) {
291
+ if (now <= route.expiresAt) {
292
+ totalRoutes++;
293
+ if (route.hopCount > maxHops) {
294
+ maxHops = route.hopCount;
295
+ }
296
+ hopSum += route.hopCount;
297
+ }
298
+ }
286
299
 
287
300
  return {
288
- totalRoutes: routes.length,
301
+ totalRoutes,
289
302
  directNeighbors: this._neighbors.size,
290
- maxHops: hopCounts.length > 0 ? Math.max(...hopCounts) : 0,
291
- avgHops: hopCounts.length > 0
292
- ? hopCounts.reduce((a, b) => a + b, 0) / hopCounts.length
293
- : 0
303
+ maxHops: totalRoutes > 0 ? maxHops : 0,
304
+ avgHops: totalRoutes > 0 ? hopSum / totalRoutes : 0
294
305
  };
295
306
  }
296
307
  }
@@ -11,10 +11,18 @@
11
11
  const EventEmitter = require('../../utils/EventEmitter');
12
12
  const { EVENTS } = require('../../constants');
13
13
  const { ValidationError } = require('../../errors');
14
+ const { randomBytes } = require('../../utils/bytes');
15
+
16
+ /**
17
+ * Hex lookup table for fast byte-to-hex conversion
18
+ * @constant {string[]}
19
+ * @private
20
+ */
21
+ const HEX = Array.from({ length: 256 }, (/** @type {any} */ _, /** @type {number} */ i) => (i < 16 ? '0' : '') + i.toString(16));
14
22
 
15
23
  /**
16
24
  * Default configuration for store and forward
17
- * @constant {Object}
25
+ * @constant {any}
18
26
  */
19
27
  const DEFAULT_CONFIG = Object.freeze({
20
28
  /** Maximum number of cached messages per recipient */
@@ -60,26 +68,21 @@ const DEFAULT_CONFIG = Object.freeze({
60
68
  class StoreAndForwardManager extends EventEmitter {
61
69
  /**
62
70
  * Creates a new StoreAndForwardManager instance.
63
- * @param {Object} [options={}] - Configuration options
64
- * @param {number} [options.maxMessagesPerRecipient=100] - Max messages per recipient
65
- * @param {number} [options.maxTotalMessages=1000] - Max total cached messages
66
- * @param {number} [options.maxCacheSizeBytes=10485760] - Max cache size (10MB)
67
- * @param {number} [options.retentionMs=86400000] - Message retention (24h)
68
- * @param {number} [options.cleanupIntervalMs=300000] - Cleanup interval (5min)
71
+ * @param {any} [options={}] - Configuration options *
69
72
  */
70
73
  constructor(options = {}) {
71
74
  super();
72
75
 
73
76
  /**
74
77
  * Configuration
75
- * @type {Object}
78
+ * @type {any}
76
79
  * @private
77
80
  */
78
81
  this._config = { ...DEFAULT_CONFIG, ...options };
79
82
 
80
83
  /**
81
84
  * Message cache by recipient ID
82
- * @type {Map<string, CachedMessage[]>}
85
+ * @type {Map<string, any[]>}
83
86
  * @private
84
87
  */
85
88
  this._cache = new Map();
@@ -100,14 +103,14 @@ class StoreAndForwardManager extends EventEmitter {
100
103
 
101
104
  /**
102
105
  * Cleanup timer
103
- * @type {number|null}
106
+ * @type {any}
104
107
  * @private
105
108
  */
106
109
  this._cleanupTimer = null;
107
110
 
108
111
  /**
109
112
  * Statistics
110
- * @type {Object}
113
+ * @type {any}
111
114
  * @private
112
115
  */
113
116
  this._stats = {
@@ -127,9 +130,9 @@ class StoreAndForwardManager extends EventEmitter {
127
130
  * Caches a message for an offline peer.
128
131
  * @param {string} recipientId - Recipient peer ID
129
132
  * @param {Uint8Array} encryptedPayload - Encrypted message payload
130
- * @param {Object} [options={}] - Cache options
131
- * @param {string} [options.messageId] - Message ID
132
- * @param {number} [options.ttlMs] - Custom TTL in ms
133
+ * @param {any} [options={}] - Cache options
134
+ *
135
+ *
133
136
  * @returns {Promise<string>} Cached message ID
134
137
  */
135
138
  async cacheForOfflinePeer(recipientId, encryptedPayload, options = {}) {
@@ -165,7 +168,7 @@ class StoreAndForwardManager extends EventEmitter {
165
168
  this._cache.set(recipientId, []);
166
169
  }
167
170
 
168
- const recipientCache = this._cache.get(recipientId);
171
+ const recipientCache = /** @type {any[]} */ (this._cache.get(recipientId));
169
172
 
170
173
  // Check per-recipient limit
171
174
  if (recipientCache.length >= this._config.maxMessagesPerRecipient) {
@@ -197,14 +200,15 @@ class StoreAndForwardManager extends EventEmitter {
197
200
  * Delivers all cached messages to a peer that came online.
198
201
  * @param {string} recipientId - Recipient peer ID
199
202
  * @param {Function} sendFn - Async function to send message: (payload) => Promise<void>
200
- * @returns {Promise<Object>} Delivery result with counts
203
+ * @returns {Promise<any>} Delivery result with counts
201
204
  */
202
205
  async deliverCachedMessages(recipientId, sendFn) {
203
206
  if (!this._cache.has(recipientId)) {
204
207
  return { delivered: 0, failed: 0 };
205
208
  }
206
209
 
207
- const messages = this._cache.get(recipientId);
210
+ const messages = /** @type {any[]} */ (this._cache.get(recipientId));
211
+ /** @type {any} */
208
212
  const results = { delivered: 0, failed: 0, remaining: [] };
209
213
 
210
214
  for (const msg of messages) {
@@ -230,7 +234,7 @@ class StoreAndForwardManager extends EventEmitter {
230
234
  messageId: msg.id,
231
235
  recipientId
232
236
  });
233
- } catch (error) {
237
+ } catch (/** @type {any} */ error) {
234
238
  results.failed++;
235
239
  this._stats.deliveryFailures++;
236
240
  results.remaining.push(msg);
@@ -297,7 +301,7 @@ class StoreAndForwardManager extends EventEmitter {
297
301
  }
298
302
 
299
303
  const count = cache.length;
300
- const size = cache.reduce((sum, m) => sum + m.size, 0);
304
+ const size = cache.reduce((/** @type {number} */ sum, /** @type {any} */ m) => sum + m.size, 0);
301
305
 
302
306
  this._cache.delete(recipientId);
303
307
  this._totalSize -= size;
@@ -315,7 +319,7 @@ class StoreAndForwardManager extends EventEmitter {
315
319
  let pruned = 0;
316
320
 
317
321
  for (const [recipientId, messages] of this._cache) {
318
- const validMessages = messages.filter(msg => {
322
+ const validMessages = messages.filter((/** @type {any} */ msg) => {
319
323
  if (msg.expiresAt <= now) {
320
324
  this._totalSize -= msg.size;
321
325
  this._totalCount--;
@@ -342,7 +346,7 @@ class StoreAndForwardManager extends EventEmitter {
342
346
 
343
347
  /**
344
348
  * Gets store and forward statistics.
345
- * @returns {Object} Statistics
349
+ * @returns {any} Statistics
346
350
  */
347
351
  getStats() {
348
352
  return {
@@ -410,6 +414,7 @@ class StoreAndForwardManager extends EventEmitter {
410
414
  */
411
415
  _removeOldestMessage() {
412
416
  let oldestTime = Infinity;
417
+ /** @type {string|null} */
413
418
  let oldestRecipient = null;
414
419
 
415
420
  for (const [recipientId, messages] of this._cache) {
@@ -420,7 +425,7 @@ class StoreAndForwardManager extends EventEmitter {
420
425
  }
421
426
 
422
427
  if (oldestRecipient) {
423
- const messages = this._cache.get(oldestRecipient);
428
+ const messages = /** @type {any[]} */ (this._cache.get(oldestRecipient));
424
429
  const oldest = messages.shift();
425
430
  if (oldest) {
426
431
  this._totalSize -= oldest.size;
@@ -439,9 +444,12 @@ class StoreAndForwardManager extends EventEmitter {
439
444
  * @private
440
445
  */
441
446
  _generateId() {
442
- const { randomBytes } = require('../../utils/bytes');
443
447
  const bytes = randomBytes(16);
444
- return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
448
+ let id = '';
449
+ for (let i = 0; i < bytes.length; i++) {
450
+ id += HEX[bytes[i]];
451
+ }
452
+ return id;
445
453
  }
446
454
 
447
455
  /**
@@ -39,15 +39,14 @@ function deserializeHeader(data) {
39
39
  });
40
40
  }
41
41
 
42
- const headerData = data.slice(0, HEADER_SIZE);
43
- const view = new DataView(headerData.buffer, headerData.byteOffset, HEADER_SIZE);
42
+ const headerData = data.subarray(0, HEADER_SIZE);
43
+ const view = new DataView(data.buffer, data.byteOffset, HEADER_SIZE);
44
44
 
45
45
  // Extract checksum from bytes 44-47 (big-endian)
46
46
  const storedChecksum = view.getUint32(44, false);
47
47
 
48
- // Calculate checksum over bytes 0-43
49
- const checksumData = headerData.slice(0, 44);
50
- const calculatedChecksum = crc32(checksumData);
48
+ // Calculate checksum over bytes 0-43 (subarray = zero-copy view)
49
+ const calculatedChecksum = crc32(headerData.subarray(0, 44));
51
50
 
52
51
  // Verify checksum
53
52
  if (storedChecksum !== calculatedChecksum) {
@@ -65,7 +64,7 @@ function deserializeHeader(data) {
65
64
  hopCount: headerData[3],
66
65
  maxHops: headerData[4],
67
66
  // bytes 5-7 are reserved
68
- messageId: headerData.slice(8, 24),
67
+ messageId: new Uint8Array(headerData.buffer, headerData.byteOffset + 8, 16),
69
68
  timestamp: readUint64BE(view, 24),
70
69
  expiresAt: readUint64BE(view, 32),
71
70
  payloadLength: view.getUint16(40, false),
@@ -120,8 +119,8 @@ function deserialize(data) {
120
119
  });
121
120
  }
122
121
 
123
- // Extract payload
124
- const payload = data.slice(HEADER_SIZE, HEADER_SIZE + header.payloadLength);
122
+ // Extract payload (subarray = zero-copy view)
123
+ const payload = data.subarray(HEADER_SIZE, HEADER_SIZE + header.payloadLength);
125
124
 
126
125
  return new Message(header, payload);
127
126
  }
@@ -175,7 +174,7 @@ function deserializeBatch(data) {
175
174
  }
176
175
 
177
176
  // Extract and deserialize this message
178
- const messageData = data.slice(offset, offset + messageLength);
177
+ const messageData = data.subarray(offset, offset + messageLength);
179
178
 
180
179
  try {
181
180
  const message = deserialize(messageData);
@@ -228,7 +227,7 @@ function peekMessageId(data) {
228
227
  if (!(data instanceof Uint8Array) || data.length < 24) {
229
228
  return null;
230
229
  }
231
- return data.slice(8, 24);
230
+ return data.subarray(8, 24);
232
231
  }
233
232
 
234
233
  module.exports = {