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
@@ -20,9 +20,9 @@ const Storage = require('./Storage');
20
20
  class AsyncStorageAdapter extends Storage {
21
21
  /**
22
22
  * Creates a new AsyncStorageAdapter instance
23
- * @param {Object} [options={}] - Storage options
24
- * @param {string} [options.prefix='mesh'] - Key prefix for namespacing
25
- * @param {Object} [options.AsyncStorage] - AsyncStorage instance (optional)
23
+ * @param {any} [options={}] - Storage options
24
+ *
25
+ *
26
26
  */
27
27
  constructor(options = {}) {
28
28
  super({
@@ -32,7 +32,7 @@ class AsyncStorageAdapter extends Storage {
32
32
 
33
33
  /**
34
34
  * AsyncStorage instance
35
- * @type {Object|null}
35
+ * @type {any}
36
36
  * @private
37
37
  */
38
38
  this._storage = options.AsyncStorage || null;
@@ -58,6 +58,7 @@ class AsyncStorageAdapter extends Storage {
58
58
  // Try to load AsyncStorage if not provided
59
59
  if (!this._storage) {
60
60
  try {
61
+ // @ts-ignore
61
62
  const AsyncStorageModule = require('@react-native-async-storage/async-storage');
62
63
  this._storage = AsyncStorageModule.default || AsyncStorageModule;
63
64
  } catch (error) {
@@ -117,8 +118,8 @@ class AsyncStorageAdapter extends Storage {
117
118
  * Sets a value by key
118
119
  * @param {string} key - Key to set
119
120
  * @param {any} value - Value to store
120
- * @param {Object} [options={}] - Set options
121
- * @param {number} [options.ttl] - Time to live in milliseconds
121
+ * @param {any} [options={}] - Set options
122
+ *
122
123
  * @returns {Promise<void>}
123
124
  */
124
125
  async set(key, value, options = {}) {
@@ -126,6 +127,7 @@ class AsyncStorageAdapter extends Storage {
126
127
 
127
128
  const prefixedKey = this._getKey(key);
128
129
 
130
+ /** @type {any} */
129
131
  const item = {
130
132
  value,
131
133
  createdAt: Date.now()
@@ -170,7 +172,7 @@ class AsyncStorageAdapter extends Storage {
170
172
 
171
173
  const allKeys = await this._storage.getAllKeys();
172
174
  const prefix = `${this._options.prefix}:`;
173
- const keysToRemove = allKeys.filter(key => key.startsWith(prefix));
175
+ const keysToRemove = allKeys.filter((/** @type {any} */ key) => key.startsWith(prefix));
174
176
 
175
177
  if (keysToRemove.length > 0) {
176
178
  await this._storage.multiRemove(keysToRemove);
@@ -188,8 +190,8 @@ class AsyncStorageAdapter extends Storage {
188
190
  const prefix = `${this._options.prefix}:`;
189
191
 
190
192
  return allKeys
191
- .filter(key => key.startsWith(prefix))
192
- .map(key => key.slice(prefix.length));
193
+ .filter((/** @type {any} */ key) => key.startsWith(prefix))
194
+ .map((/** @type {any} */ key) => key.slice(prefix.length));
193
195
  }
194
196
 
195
197
  /**
@@ -205,7 +207,7 @@ class AsyncStorageAdapter extends Storage {
205
207
  const result = new Map();
206
208
  const now = Date.now();
207
209
 
208
- for (const [prefixedKey, jsonValue] of pairs) {
210
+ for (const [prefixedKey, jsonValue] of /** @type {any[]} */ (pairs)) {
209
211
  if (jsonValue !== null) {
210
212
  const key = prefixedKey.slice(this._options.prefix.length + 1);
211
213
  try {
@@ -224,9 +226,9 @@ class AsyncStorageAdapter extends Storage {
224
226
 
225
227
  /**
226
228
  * Sets multiple key-value pairs
227
- * @param {Map<string, any>|Object} entries - Entries to set
228
- * @param {Object} [options={}] - Set options
229
- * @param {number} [options.ttl] - Time to live in milliseconds
229
+ * @param {Map<string, any>|any} entries - Entries to set
230
+ * @param {any} [options={}] - Set options
231
+ *
230
232
  * @returns {Promise<void>}
231
233
  */
232
234
  async setMany(entries, options = {}) {
@@ -237,7 +239,8 @@ class AsyncStorageAdapter extends Storage {
237
239
  : Object.entries(entries);
238
240
 
239
241
  const now = Date.now();
240
- const keyValuePairs = pairs.map(([key, value]) => {
242
+ const keyValuePairs = pairs.map((/** @type {any} */ [key, value]) => {
243
+ /** @type {any} */
241
244
  const item = {
242
245
  value,
243
246
  createdAt: now
@@ -18,9 +18,9 @@ const Storage = require('./Storage');
18
18
  class MemoryStorage extends Storage {
19
19
  /**
20
20
  * Creates a new MemoryStorage instance
21
- * @param {Object} [options={}] - Storage options
22
- * @param {string} [options.prefix=''] - Key prefix for namespacing
23
- * @param {number} [options.maxSize=0] - Maximum number of items (0 = unlimited)
21
+ * @param {any} [options={}] - Storage options
22
+ *
23
+ *
24
24
  */
25
25
  constructor(options = {}) {
26
26
  super(options);
@@ -66,8 +66,8 @@ class MemoryStorage extends Storage {
66
66
  * Sets a value by key
67
67
  * @param {string} key - Key to set
68
68
  * @param {any} value - Value to store
69
- * @param {Object} [options={}] - Set options
70
- * @param {number} [options.ttl] - Time to live in milliseconds
69
+ * @param {any} [options={}] - Set options
70
+ *
71
71
  * @returns {Promise<void>}
72
72
  */
73
73
  async set(key, value, options = {}) {
@@ -79,9 +79,12 @@ class MemoryStorage extends Storage {
79
79
  this._store.size >= this._maxSize) {
80
80
  // Remove oldest entry (first entry in Map)
81
81
  const firstKey = this._store.keys().next().value;
82
- this._store.delete(firstKey);
82
+ if (firstKey !== undefined) {
83
+ this._store.delete(firstKey);
84
+ }
83
85
  }
84
86
 
87
+ /** @type {any} */
85
88
  const item = {
86
89
  value,
87
90
  createdAt: Date.now()
@@ -156,7 +159,7 @@ class MemoryStorage extends Storage {
156
159
  const unprefixedKey = prefix ? key.slice(prefix.length) : key;
157
160
  // Check expiration before including
158
161
  const item = this._store.get(key);
159
- if (!item.expiresAt || Date.now() <= item.expiresAt) {
162
+ if (item && (!item.expiresAt || Date.now() <= item.expiresAt)) {
160
163
  result.push(unprefixedKey);
161
164
  }
162
165
  }
@@ -196,7 +199,7 @@ class MemoryStorage extends Storage {
196
199
 
197
200
  /**
198
201
  * Gets all entries as an array of [key, value] pairs
199
- * @returns {Promise<Array>} Array of [key, value] entries
202
+ * @returns {Promise<any[]>} Array of [key, value] entries
200
203
  */
201
204
  async entries() {
202
205
  const allKeys = await this.keys();
@@ -8,6 +8,60 @@
8
8
  const MemoryStorage = require('./MemoryStorage');
9
9
  const { MESH_CONFIG } = require('../constants');
10
10
 
11
+ /**
12
+ * Base64 encoding/decoding for compact Uint8Array serialization
13
+ * @private
14
+ */
15
+ const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
16
+
17
+ /**
18
+ * @param {Uint8Array} bytes
19
+ * @returns {string}
20
+ */
21
+ function uint8ArrayToBase64(bytes) {
22
+ let result = '';
23
+ const len = bytes.length;
24
+ for (let i = 0; i < len; i += 3) {
25
+ const b0 = bytes[i];
26
+ const b1 = i + 1 < len ? bytes[i + 1] : 0;
27
+ const b2 = i + 2 < len ? bytes[i + 2] : 0;
28
+ result += BASE64_CHARS[b0 >> 2];
29
+ result += BASE64_CHARS[((b0 & 3) << 4) | (b1 >> 4)];
30
+ result += (i + 1 < len) ? BASE64_CHARS[((b1 & 15) << 2) | (b2 >> 6)] : '=';
31
+ result += (i + 2 < len) ? BASE64_CHARS[b2 & 63] : '=';
32
+ }
33
+ return result;
34
+ }
35
+
36
+ const BASE64_LOOKUP = new Uint8Array(128);
37
+ for (let i = 0; i < BASE64_CHARS.length; i++) {
38
+ BASE64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;
39
+ }
40
+
41
+ /**
42
+ * @param {string} str
43
+ * @returns {Uint8Array}
44
+ */
45
+ function base64ToUint8Array(str) {
46
+ const len = str.length;
47
+ let padding = 0;
48
+ if (str[len - 1] === '=') { padding++; }
49
+ if (str[len - 2] === '=') { padding++; }
50
+ const byteLen = (len * 3 / 4) - padding;
51
+ const bytes = new Uint8Array(byteLen);
52
+ let j = 0;
53
+ for (let i = 0; i < len; i += 4) {
54
+ const a = BASE64_LOOKUP[str.charCodeAt(i)];
55
+ const b = BASE64_LOOKUP[str.charCodeAt(i + 1)];
56
+ const c = BASE64_LOOKUP[str.charCodeAt(i + 2)];
57
+ const d = BASE64_LOOKUP[str.charCodeAt(i + 3)];
58
+ bytes[j++] = (a << 2) | (b >> 4);
59
+ if (j < byteLen) { bytes[j++] = ((b & 15) << 4) | (c >> 2); }
60
+ if (j < byteLen) { bytes[j++] = ((c & 3) << 6) | d; }
61
+ }
62
+ return bytes;
63
+ }
64
+
11
65
  /**
12
66
  * Message store for persisting and retrieving mesh network messages.
13
67
  * Provides message caching, deduplication support, and cleanup functionality.
@@ -17,15 +71,12 @@ const { MESH_CONFIG } = require('../constants');
17
71
  class MessageStore {
18
72
  /**
19
73
  * Creates a new MessageStore instance
20
- * @param {Object} [options={}] - Store options
21
- * @param {Object} [options.storage] - Storage backend (defaults to MemoryStorage)
22
- * @param {number} [options.maxMessages=1000] - Maximum messages to store
23
- * @param {number} [options.messageTtlMs] - Message TTL in milliseconds
74
+ * @param {any} [options={}] - Store options *
24
75
  */
25
76
  constructor(options = {}) {
26
77
  /**
27
78
  * Storage backend
28
- * @type {Object}
79
+ * @type {any}
29
80
  * @private
30
81
  */
31
82
  this._storage = options.storage || new MemoryStorage({
@@ -42,7 +93,7 @@ class MessageStore {
42
93
 
43
94
  /**
44
95
  * Index storage for queries
45
- * @type {Object}
96
+ * @type {any}
46
97
  * @private
47
98
  */
48
99
  this._indexStorage = options.indexStorage || new MemoryStorage({
@@ -52,13 +103,7 @@ class MessageStore {
52
103
 
53
104
  /**
54
105
  * Saves a message to storage
55
- * @param {Object} message - Message to save
56
- * @param {string} message.id - Message ID
57
- * @param {number} message.type - Message type
58
- * @param {Uint8Array} [message.payload] - Message payload
59
- * @param {string} [message.senderId] - Sender peer ID
60
- * @param {string} [message.recipientId] - Recipient peer ID
61
- * @param {number} [message.timestamp] - Message timestamp
106
+ * @param {any} message - Message to save *
62
107
  * @returns {Promise<void>}
63
108
  */
64
109
  async saveMessage(message) {
@@ -69,8 +114,9 @@ class MessageStore {
69
114
  const storedMessage = {
70
115
  ...message,
71
116
  payload: message.payload
72
- ? Array.from(message.payload)
117
+ ? uint8ArrayToBase64(message.payload)
73
118
  : undefined,
119
+ payloadEncoding: message.payload ? 'base64' : undefined,
74
120
  storedAt: Date.now()
75
121
  };
76
122
 
@@ -85,7 +131,7 @@ class MessageStore {
85
131
  /**
86
132
  * Gets a message by ID
87
133
  * @param {string} id - Message ID
88
- * @returns {Promise<Object|null>} Message or null if not found
134
+ * @returns {Promise<any>} Message or null if not found
89
135
  */
90
136
  async getMessage(id) {
91
137
  const message = await this._storage.get(id);
@@ -96,7 +142,13 @@ class MessageStore {
96
142
 
97
143
  // Convert payload back to Uint8Array
98
144
  if (message.payload) {
99
- message.payload = new Uint8Array(message.payload);
145
+ if (message.payloadEncoding === 'base64') {
146
+ message.payload = base64ToUint8Array(message.payload);
147
+ } else if (Array.isArray(message.payload)) {
148
+ // Backwards compatibility with old Array.from() format
149
+ message.payload = new Uint8Array(message.payload);
150
+ }
151
+ delete message.payloadEncoding;
100
152
  }
101
153
 
102
154
  return message;
@@ -104,15 +156,8 @@ class MessageStore {
104
156
 
105
157
  /**
106
158
  * Gets messages matching query options
107
- * @param {Object} [options={}] - Query options
108
- * @param {string} [options.senderId] - Filter by sender ID
109
- * @param {string} [options.recipientId] - Filter by recipient ID
110
- * @param {number} [options.type] - Filter by message type
111
- * @param {number} [options.since] - Filter messages since timestamp
112
- * @param {number} [options.until] - Filter messages until timestamp
113
- * @param {number} [options.limit=50] - Maximum messages to return
114
- * @param {number} [options.offset=0] - Offset for pagination
115
- * @returns {Promise<Object[]>} Array of messages
159
+ * @param {any} [options={}] - Query options *
160
+ * @returns {Promise<any[]>} Array of messages
116
161
  */
117
162
  async getMessages(options = {}) {
118
163
  const {
@@ -217,7 +262,7 @@ class MessageStore {
217
262
  * Gets messages by sender
218
263
  * @param {string} senderId - Sender peer ID
219
264
  * @param {number} [limit=50] - Maximum messages
220
- * @returns {Promise<Object[]>} Array of messages
265
+ * @returns {Promise<any[]>} Array of messages
221
266
  */
222
267
  async getMessagesBySender(senderId, limit = 50) {
223
268
  return this.getMessages({ senderId, limit });
@@ -227,7 +272,7 @@ class MessageStore {
227
272
  * Gets messages by recipient
228
273
  * @param {string} recipientId - Recipient peer ID
229
274
  * @param {number} [limit=50] - Maximum messages
230
- * @returns {Promise<Object[]>} Array of messages
275
+ * @returns {Promise<any[]>} Array of messages
231
276
  */
232
277
  async getMessagesByRecipient(recipientId, limit = 50) {
233
278
  return this.getMessages({ recipientId, limit });
@@ -238,7 +283,7 @@ class MessageStore {
238
283
  * @param {string} peerId1 - First peer ID
239
284
  * @param {string} peerId2 - Second peer ID
240
285
  * @param {number} [limit=50] - Maximum messages
241
- * @returns {Promise<Object[]>} Array of messages
286
+ * @returns {Promise<any[]>} Array of messages
242
287
  */
243
288
  async getConversation(peerId1, peerId2, limit = 50) {
244
289
  const allKeys = await this._storage.keys();
@@ -263,7 +308,7 @@ class MessageStore {
263
308
 
264
309
  /**
265
310
  * Updates indexes for a message
266
- * @param {Object} message - Message to index
311
+ * @param {any} message - Message to index
267
312
  * @returns {Promise<void>}
268
313
  * @private
269
314
  */
@@ -291,7 +336,7 @@ class MessageStore {
291
336
 
292
337
  /**
293
338
  * Removes a message from indexes
294
- * @param {Object} message - Message to remove from indexes
339
+ * @param {any} message - Message to remove from indexes
295
340
  * @returns {Promise<void>}
296
341
  * @private
297
342
  */
@@ -299,14 +344,14 @@ class MessageStore {
299
344
  if (message.senderId) {
300
345
  const key = `sender:${message.senderId}`;
301
346
  const ids = (await this._indexStorage.get(key)) || [];
302
- const filtered = ids.filter(id => id !== message.id);
347
+ const filtered = ids.filter((/** @type {any} */ id) => id !== message.id);
303
348
  await this._indexStorage.set(key, filtered);
304
349
  }
305
350
 
306
351
  if (message.recipientId) {
307
352
  const key = `recipient:${message.recipientId}`;
308
353
  const ids = (await this._indexStorage.get(key)) || [];
309
- const filtered = ids.filter(id => id !== message.id);
354
+ const filtered = ids.filter((/** @type {any} */ id) => id !== message.id);
310
355
  await this._indexStorage.set(key, filtered);
311
356
  }
312
357
  }
@@ -15,13 +15,13 @@
15
15
  class Storage {
16
16
  /**
17
17
  * Creates a new Storage instance
18
- * @param {Object} [options={}] - Storage options
19
- * @param {string} [options.prefix=''] - Key prefix for namespacing
18
+ * @param {any} [options={}] - Storage options
19
+ *
20
20
  */
21
21
  constructor(options = {}) {
22
22
  /**
23
23
  * Storage options
24
- * @type {Object}
24
+ * @type {any}
25
25
  * @protected
26
26
  */
27
27
  this._options = {
@@ -43,7 +43,7 @@ class Storage {
43
43
  /**
44
44
  * Gets a value by key
45
45
  * @abstract
46
- * @param {string} key - Key to retrieve
46
+ * @param {string} _key - Key to retrieve
47
47
  * @returns {Promise<any>} Stored value or undefined
48
48
  * @throws {Error} If not implemented by subclass
49
49
  */
@@ -54,8 +54,8 @@ class Storage {
54
54
  /**
55
55
  * Sets a value by key
56
56
  * @abstract
57
- * @param {string} key - Key to set
58
- * @param {any} value - Value to store
57
+ * @param {string} _key - Key to set
58
+ * @param {any} _value - Value to store
59
59
  * @returns {Promise<void>}
60
60
  * @throws {Error} If not implemented by subclass
61
61
  */
@@ -66,7 +66,7 @@ class Storage {
66
66
  /**
67
67
  * Deletes a value by key
68
68
  * @abstract
69
- * @param {string} key - Key to delete
69
+ * @param {string} _key - Key to delete
70
70
  * @returns {Promise<void>}
71
71
  * @throws {Error} If not implemented by subclass
72
72
  */
@@ -77,7 +77,7 @@ class Storage {
77
77
  /**
78
78
  * Checks if a key exists
79
79
  * @abstract
80
- * @param {string} key - Key to check
80
+ * @param {string} _key - Key to check
81
81
  * @returns {Promise<boolean>} True if key exists
82
82
  * @throws {Error} If not implemented by subclass
83
83
  */
@@ -134,7 +134,7 @@ class Storage {
134
134
 
135
135
  /**
136
136
  * Sets multiple key-value pairs
137
- * @param {Map<string, any>|Object} entries - Entries to set
137
+ * @param {Map<string, any>|any} entries - Entries to set
138
138
  * @returns {Promise<void>}
139
139
  */
140
140
  async setMany(entries) {
@@ -25,7 +25,7 @@ const { ConnectionError } = require('../errors');
25
25
  class BLETransport extends Transport {
26
26
  /**
27
27
  * Creates a new BLETransport instance
28
- * @param {Object} adapter - BLE adapter instance (RNBLEAdapter or NodeBLEAdapter)
28
+ * @param {any} adapter - BLE adapter instance (RNBLEAdapter or NodeBLEAdapter)
29
29
  * @param {Object} [options={}] - Transport options
30
30
  * @param {string} [options.powerMode='BALANCED'] - Power mode
31
31
  * @param {number} [options.maxPeers=8] - Maximum peers
@@ -41,16 +41,17 @@ class BLETransport extends Transport {
41
41
 
42
42
  /**
43
43
  * BLE adapter instance
44
- * @type {Object}
44
+ * @type {any}
45
45
  * @private
46
46
  */
47
47
  this._adapter = adapter;
48
48
 
49
49
  /**
50
50
  * Current power mode
51
- * @type {Object}
51
+ * @type {any}
52
52
  * @private
53
53
  */
54
+ // @ts-ignore
54
55
  this._powerMode = POWER_MODE[options.powerMode] || POWER_MODE.BALANCED;
55
56
 
56
57
  /**
@@ -76,7 +77,7 @@ class BLETransport extends Transport {
76
77
 
77
78
  /**
78
79
  * Per-peer write queues for serializing BLE writes
79
- * @type {Map<string, Array>}
80
+ * @type {Map<string, any[]>}
80
81
  * @private
81
82
  */
82
83
  this._writeQueue = new Map();
@@ -90,7 +91,7 @@ class BLETransport extends Transport {
90
91
 
91
92
  /**
92
93
  * Bound event handlers for cleanup
93
- * @type {Object}
94
+ * @type {any}
94
95
  * @private
95
96
  */
96
97
  this._handlers = {
@@ -140,7 +141,7 @@ class BLETransport extends Transport {
140
141
 
141
142
  // Register disconnect callback if adapter supports it
142
143
  if (typeof this._adapter.onDeviceDisconnected === 'function') {
143
- this._adapter.onDeviceDisconnected((peerId) => {
144
+ this._adapter.onDeviceDisconnected((/** @type {any} */ peerId) => {
144
145
  this._handleDeviceDisconnected(peerId);
145
146
  });
146
147
  }
@@ -149,7 +150,7 @@ class BLETransport extends Transport {
149
150
 
150
151
  // Auto-start scanning for peers
151
152
  await this.startScanning();
152
- } catch (error) {
153
+ } catch (/** @type {any} */ error) {
153
154
  this._setState(Transport.STATE.ERROR);
154
155
  throw error;
155
156
  }
@@ -232,12 +233,12 @@ class BLETransport extends Transport {
232
233
  }
233
234
 
234
235
  try {
235
- let timeoutId;
236
+ /** @type {any} */ let timeoutId;
236
237
  const timeoutPromise = new Promise((_, reject) => {
237
238
  timeoutId = setTimeout(() => reject(new Error('Connection timeout')), this._connectTimeoutMs);
238
239
  });
239
240
  const device = await Promise.race([
240
- this._adapter.connect(peerId).then(d => { clearTimeout(timeoutId); return d; }),
241
+ this._adapter.connect(peerId).then((/** @type {any} */ d) => { clearTimeout(timeoutId); return d; }),
241
242
  timeoutPromise
242
243
  ]);
243
244
 
@@ -259,7 +260,7 @@ class BLETransport extends Transport {
259
260
  peerId,
260
261
  BLE_SERVICE_UUID,
261
262
  BLE_CHARACTERISTIC_RX,
262
- (data) => this._handleData(peerId, data)
263
+ (/** @type {any} */ data) => this._handleData(peerId, data)
263
264
  );
264
265
 
265
266
  const connectionInfo = {
@@ -271,7 +272,7 @@ class BLETransport extends Transport {
271
272
 
272
273
  this._peers.set(peerId, connectionInfo);
273
274
  this.emit('peerConnected', { peerId, rssi: device.rssi || -50 });
274
- } catch (error) {
275
+ } catch (/** @type {any} */ error) {
275
276
  if (error.message === 'Connection timeout') {
276
277
  throw ConnectionError.connectionTimeout(peerId);
277
278
  }
@@ -293,6 +294,15 @@ class BLETransport extends Transport {
293
294
  await this._adapter.disconnect(peerId);
294
295
  } finally {
295
296
  this._peers.delete(peerId);
297
+
298
+ // Clean up write queue and writing state
299
+ const queue = this._writeQueue.get(peerId);
300
+ if (queue) {
301
+ queue.forEach(({ reject }) => reject(new Error('Peer disconnected')));
302
+ this._writeQueue.delete(peerId);
303
+ }
304
+ this._writing.delete(peerId);
305
+
296
306
  this.emit('peerDisconnected', { peerId, reason: 'user_request' });
297
307
  }
298
308
  }
@@ -325,7 +335,7 @@ class BLETransport extends Transport {
325
335
 
326
336
  // Chunk data for BLE MTU compliance
327
337
  for (let offset = 0; offset < data.length; offset += chunkSize) {
328
- const chunk = data.slice(offset, Math.min(offset + chunkSize, data.length));
338
+ const chunk = data.subarray(offset, Math.min(offset + chunkSize, data.length));
329
339
  await this._queuedWrite(peerId, chunk);
330
340
  }
331
341
  }
@@ -353,7 +363,7 @@ class BLETransport extends Transport {
353
363
  * @param {string} modeName - Power mode name (PERFORMANCE, BALANCED, POWER_SAVER)
354
364
  */
355
365
  setPowerMode(modeName) {
356
- const mode = POWER_MODE[modeName];
366
+ const mode = /** @type {any} */ (POWER_MODE)[modeName];
357
367
  if (mode) {
358
368
  this._powerMode = mode;
359
369
  }
@@ -375,7 +385,7 @@ class BLETransport extends Transport {
375
385
 
376
386
  /**
377
387
  * Handles discovered BLE devices
378
- * @param {Object} device - Discovered device info
388
+ * @param {any} device - Discovered device info
379
389
  * @private
380
390
  */
381
391
  _handleDeviceDiscovered(device) {
@@ -414,7 +424,7 @@ class BLETransport extends Transport {
414
424
  * @private
415
425
  */
416
426
  _handleData(peerId, data) {
417
- this.emit('message', { peerId, data: new Uint8Array(data) });
427
+ this.emit('message', { peerId, data: data instanceof Uint8Array ? data : new Uint8Array(data) });
418
428
  }
419
429
 
420
430
  /**
@@ -443,7 +453,8 @@ class BLETransport extends Transport {
443
453
  }
444
454
 
445
455
  return new Promise((resolve, reject) => {
446
- this._writeQueue.get(peerId).push({ data, resolve, reject });
456
+ // @ts-ignore
457
+ this?._writeQueue.get(peerId).push({ data, resolve, reject });
447
458
  this._processWriteQueue(peerId);
448
459
  });
449
460
  }
@@ -50,6 +50,7 @@ class MockTransport extends Transport {
50
50
  * @type {string|null}
51
51
  * @private
52
52
  */
53
+ // @ts-ignore
53
54
  this._localPeerId = options.localPeerId || `mock-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
54
55
  }
55
56
 
@@ -122,7 +123,8 @@ class MockTransport extends Transport {
122
123
 
123
124
  // Deliver to linked transport if available
124
125
  const linkedTransport = this._linkedTransports.get(peerId);
125
- if (linkedTransport && linkedTransport.isRunning) {
126
+ if (linkedTransport && /** @type {any} */ (linkedTransport).isRunning) {
127
+ // @ts-ignore
126
128
  linkedTransport._receiveMessage(this._localPeerId, data);
127
129
  }
128
130
  }
@@ -157,7 +159,7 @@ class MockTransport extends Transport {
157
159
  /**
158
160
  * Simulates a peer connection
159
161
  * @param {string} peerId - Connecting peer ID
160
- * @param {Object} [info={}] - Connection info (rssi, etc.)
162
+ * @param {any} [info={}] - Connection info (rssi, etc.)
161
163
  */
162
164
  simulatePeerConnect(peerId, info = {}) {
163
165
  if (!this.isRunning || this._peers.has(peerId)) {
@@ -203,6 +205,7 @@ class MockTransport extends Transport {
203
205
  }
204
206
 
205
207
  this._linkedTransports.set(otherPeerId, otherTransport);
208
+ // @ts-ignore
206
209
  otherTransport._linkedTransports.set(this._localPeerId, this);
207
210
  }
208
211
 
@@ -212,7 +215,9 @@ class MockTransport extends Transport {
212
215
  */
213
216
  unlinkFrom(otherTransport) {
214
217
  const otherPeerId = otherTransport.localPeerId;
218
+ // @ts-ignore
215
219
  this._linkedTransports.delete(otherPeerId);
220
+ // @ts-ignore
216
221
  otherTransport._linkedTransports.delete(this._localPeerId);
217
222
  }
218
223