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.
- package/README.md +2 -2
- package/docs/OPTIMIZATION.md +165 -52
- package/package.json +1 -1
- package/src/MeshNetwork.js +63 -53
- package/src/constants/audio.js +4 -4
- package/src/constants/ble.js +1 -1
- package/src/constants/crypto.js +1 -1
- package/src/constants/errors.js +2 -2
- package/src/constants/events.js +1 -1
- package/src/constants/protocol.js +2 -2
- package/src/crypto/AutoCrypto.js +16 -3
- package/src/crypto/CryptoProvider.js +17 -17
- package/src/crypto/providers/ExpoCryptoProvider.js +15 -9
- package/src/crypto/providers/QuickCryptoProvider.js +41 -12
- package/src/crypto/providers/TweetNaClProvider.js +10 -8
- package/src/errors/AudioError.js +2 -1
- package/src/errors/ConnectionError.js +2 -2
- package/src/errors/CryptoError.js +1 -1
- package/src/errors/HandshakeError.js +2 -2
- package/src/errors/MeshError.js +4 -4
- package/src/errors/MessageError.js +2 -2
- package/src/errors/ValidationError.js +3 -3
- package/src/expo/withBLEMesh.js +10 -10
- package/src/hooks/AppStateManager.js +11 -2
- package/src/hooks/useMesh.js +23 -10
- package/src/hooks/useMessages.js +17 -16
- package/src/hooks/usePeers.js +19 -14
- package/src/index.js +2 -2
- package/src/mesh/dedup/BloomFilter.js +45 -57
- package/src/mesh/dedup/DedupManager.js +36 -8
- package/src/mesh/dedup/MessageCache.js +3 -0
- package/src/mesh/fragment/Assembler.js +5 -4
- package/src/mesh/fragment/Fragmenter.js +3 -3
- package/src/mesh/monitor/ConnectionQuality.js +59 -25
- package/src/mesh/monitor/NetworkMonitor.js +80 -28
- package/src/mesh/peer/Peer.js +9 -11
- package/src/mesh/peer/PeerDiscovery.js +18 -19
- package/src/mesh/peer/PeerManager.js +29 -17
- package/src/mesh/router/MessageRouter.js +28 -20
- package/src/mesh/router/PathFinder.js +10 -13
- package/src/mesh/router/RouteTable.js +25 -14
- package/src/mesh/store/StoreAndForwardManager.js +32 -24
- package/src/protocol/deserializer.js +9 -10
- package/src/protocol/header.js +13 -7
- package/src/protocol/message.js +18 -14
- package/src/protocol/serializer.js +9 -12
- package/src/protocol/validator.js +29 -10
- package/src/service/BatteryOptimizer.js +22 -18
- package/src/service/EmergencyManager.js +18 -25
- package/src/service/HandshakeManager.js +112 -18
- package/src/service/MeshService.js +106 -22
- package/src/service/SessionManager.js +50 -13
- package/src/service/audio/AudioManager.js +80 -38
- package/src/service/audio/buffer/FrameBuffer.js +7 -8
- package/src/service/audio/buffer/JitterBuffer.js +1 -1
- package/src/service/audio/codec/LC3Codec.js +18 -19
- package/src/service/audio/codec/LC3Decoder.js +10 -10
- package/src/service/audio/codec/LC3Encoder.js +11 -9
- package/src/service/audio/session/AudioSession.js +14 -17
- package/src/service/audio/session/VoiceMessage.js +15 -22
- package/src/service/audio/transport/AudioFragmenter.js +17 -9
- package/src/service/audio/transport/AudioFramer.js +8 -12
- package/src/service/file/FileAssembler.js +4 -2
- package/src/service/file/FileChunker.js +1 -1
- package/src/service/file/FileManager.js +26 -20
- package/src/service/file/FileMessage.js +7 -12
- package/src/service/text/TextManager.js +75 -42
- package/src/service/text/broadcast/BroadcastManager.js +14 -17
- package/src/service/text/channel/Channel.js +10 -14
- package/src/service/text/channel/ChannelManager.js +10 -10
- package/src/service/text/message/TextMessage.js +12 -19
- package/src/service/text/message/TextSerializer.js +2 -2
- package/src/storage/AsyncStorageAdapter.js +17 -14
- package/src/storage/MemoryStorage.js +11 -8
- package/src/storage/MessageStore.js +77 -32
- package/src/storage/Storage.js +9 -9
- package/src/transport/BLETransport.js +27 -16
- package/src/transport/MockTransport.js +7 -2
- package/src/transport/MultiTransport.js +43 -11
- package/src/transport/Transport.js +9 -9
- package/src/transport/WiFiDirectTransport.js +26 -20
- package/src/transport/adapters/BLEAdapter.js +19 -19
- package/src/transport/adapters/NodeBLEAdapter.js +24 -23
- package/src/transport/adapters/RNBLEAdapter.js +14 -11
- package/src/utils/EventEmitter.js +15 -16
- package/src/utils/LRUCache.js +10 -4
- package/src/utils/RateLimiter.js +1 -1
- package/src/utils/bytes.js +12 -10
- package/src/utils/compression.js +10 -8
- package/src/utils/encoding.js +39 -8
- package/src/utils/retry.js +11 -13
- package/src/utils/time.js +9 -4
- 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 {
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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 {
|
|
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 {
|
|
121
|
-
*
|
|
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>|
|
|
228
|
-
* @param {
|
|
229
|
-
*
|
|
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 {
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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 {
|
|
70
|
-
*
|
|
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
|
-
|
|
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<
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
-
?
|
|
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<
|
|
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.
|
|
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 {
|
|
108
|
-
* @
|
|
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<
|
|
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<
|
|
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<
|
|
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 {
|
|
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 {
|
|
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
|
}
|
package/src/storage/Storage.js
CHANGED
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
class Storage {
|
|
16
16
|
/**
|
|
17
17
|
* Creates a new Storage instance
|
|
18
|
-
* @param {
|
|
19
|
-
*
|
|
18
|
+
* @param {any} [options={}] - Storage options
|
|
19
|
+
*
|
|
20
20
|
*/
|
|
21
21
|
constructor(options = {}) {
|
|
22
22
|
/**
|
|
23
23
|
* Storage options
|
|
24
|
-
* @type {
|
|
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}
|
|
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}
|
|
58
|
-
* @param {any}
|
|
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}
|
|
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}
|
|
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>|
|
|
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 {
|
|
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 {
|
|
44
|
+
* @type {any}
|
|
45
45
|
* @private
|
|
46
46
|
*/
|
|
47
47
|
this._adapter = adapter;
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* Current power mode
|
|
51
|
-
* @type {
|
|
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,
|
|
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 {
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
|