react-native-ble-mesh 2.1.2 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -2
- package/src/crypto/providers/QuickCryptoProvider.js +38 -8
- package/src/hooks/AppStateManager.js +8 -3
- package/src/service/EmergencyManager.js +1 -1
- package/src/service/audio/codec/LC3Codec.js +4 -1
- package/src/transport/WiFiDirectTransport.js +5 -9
- package/src/transport/adapters/RNBLEAdapter.js +5 -13
- package/src/utils/EventEmitter.js +8 -5
- package/src/utils/compression.js +1 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-ble-mesh",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "React Native Bluetooth Low Energy (BLE) mesh networking library with end-to-end encryption, offline messaging, peer-to-peer communication, and Noise Protocol security for iOS and Android",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -78,7 +78,8 @@
|
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"react-native": ">=0.60.0",
|
|
80
80
|
"react-native-get-random-values": ">=1.8.0",
|
|
81
|
-
"react-native-ble-plx": ">=2.0.0"
|
|
81
|
+
"react-native-ble-plx": ">=2.0.0",
|
|
82
|
+
"tweetnacl": ">=1.0.0"
|
|
82
83
|
},
|
|
83
84
|
"peerDependenciesMeta": {
|
|
84
85
|
"react-native": {
|
|
@@ -86,6 +87,12 @@
|
|
|
86
87
|
},
|
|
87
88
|
"react-native-ble-plx": {
|
|
88
89
|
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"react-native-get-random-values": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"tweetnacl": {
|
|
95
|
+
"optional": true
|
|
89
96
|
}
|
|
90
97
|
},
|
|
91
98
|
"files": [
|
|
@@ -10,10 +10,40 @@
|
|
|
10
10
|
|
|
11
11
|
const CryptoProvider = require('../CryptoProvider');
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Converts a hex string to Uint8Array (Buffer-free for React Native compatibility)
|
|
15
|
+
* @param {string} hex - Hex string
|
|
16
|
+
* @returns {Uint8Array}
|
|
17
|
+
*/
|
|
18
|
+
function hexToBytes(hex) {
|
|
19
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
20
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
21
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
22
|
+
}
|
|
23
|
+
return bytes;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Concatenates multiple Uint8Arrays into a single Uint8Array
|
|
28
|
+
* @param {...Uint8Array} arrays
|
|
29
|
+
* @returns {Uint8Array}
|
|
30
|
+
*/
|
|
31
|
+
function concatBytes(...arrays) {
|
|
32
|
+
let totalLength = 0;
|
|
33
|
+
for (const arr of arrays) { totalLength += arr.length; }
|
|
34
|
+
const result = new Uint8Array(totalLength);
|
|
35
|
+
let offset = 0;
|
|
36
|
+
for (const arr of arrays) {
|
|
37
|
+
result.set(arr, offset);
|
|
38
|
+
offset += arr.length;
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
13
43
|
/** DER header for PKCS8 private key wrapping (X25519) */
|
|
14
|
-
const PKCS8_HEADER =
|
|
44
|
+
const PKCS8_HEADER = hexToBytes('302e020100300506032b656e04220420');
|
|
15
45
|
/** DER header for SPKI public key wrapping (X25519) */
|
|
16
|
-
const SPKI_HEADER =
|
|
46
|
+
const SPKI_HEADER = hexToBytes('302a300506032b656e032100');
|
|
17
47
|
|
|
18
48
|
/**
|
|
19
49
|
* Crypto provider using react-native-quick-crypto.
|
|
@@ -63,18 +93,18 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
63
93
|
sharedSecret(/** @type {any} */ secretKey, /** @type {any} */ publicKey) {
|
|
64
94
|
const crypto = this._getCrypto();
|
|
65
95
|
const privKey = crypto.createPrivateKey({
|
|
66
|
-
key:
|
|
96
|
+
key: concatBytes(
|
|
67
97
|
PKCS8_HEADER,
|
|
68
|
-
|
|
69
|
-
|
|
98
|
+
new Uint8Array(secretKey)
|
|
99
|
+
),
|
|
70
100
|
format: 'der',
|
|
71
101
|
type: 'pkcs8'
|
|
72
102
|
});
|
|
73
103
|
const pubKey = crypto.createPublicKey({
|
|
74
|
-
key:
|
|
104
|
+
key: concatBytes(
|
|
75
105
|
SPKI_HEADER,
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
new Uint8Array(publicKey)
|
|
107
|
+
),
|
|
78
108
|
format: 'der',
|
|
79
109
|
type: 'spki'
|
|
80
110
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
/* global __DEV__ */
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @fileoverview React Native app state management for mesh service
|
|
@@ -63,7 +64,7 @@ class AppStateManager {
|
|
|
63
64
|
this._AppState = AppState;
|
|
64
65
|
} catch (e) {
|
|
65
66
|
// React Native not available (Node.js environment)
|
|
66
|
-
|
|
67
|
+
// React Native not available (Node.js or test environment) — silently skip
|
|
67
68
|
return false;
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -122,7 +123,9 @@ class AppStateManager {
|
|
|
122
123
|
this._mesh._saveState();
|
|
123
124
|
}
|
|
124
125
|
} catch (e) {
|
|
125
|
-
|
|
126
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
127
|
+
console.warn('AppStateManager: Error handling background transition', e);
|
|
128
|
+
}
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
131
|
|
|
@@ -144,7 +147,9 @@ class AppStateManager {
|
|
|
144
147
|
this._mesh._restoreState();
|
|
145
148
|
}
|
|
146
149
|
} catch (e) {
|
|
147
|
-
|
|
150
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
151
|
+
console.warn('AppStateManager: Error handling foreground transition', e);
|
|
152
|
+
}
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
/* global __DEV__ */
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @fileoverview LC3 codec wrapper for React Native
|
|
@@ -79,7 +80,9 @@ class LC3Codec extends EventEmitter {
|
|
|
79
80
|
} else {
|
|
80
81
|
// Use mock implementation for testing
|
|
81
82
|
this._useMock = true;
|
|
82
|
-
|
|
83
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
84
|
+
console.warn('LC3Codec: Native module not available, using mock implementation');
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
this._initialized = true;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const Transport = require('./Transport');
|
|
12
12
|
const { ConnectionError } = require('../errors');
|
|
13
|
+
const base64 = require('../utils/base64');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Wi-Fi Direct transport states
|
|
@@ -228,14 +229,14 @@ class WiFiDirectTransport extends Transport {
|
|
|
228
229
|
const peerInfo = this._peers.get(peerId);
|
|
229
230
|
|
|
230
231
|
// Convert Uint8Array to base64 for transfer
|
|
231
|
-
const
|
|
232
|
+
const encoded = this._uint8ArrayToBase64(data);
|
|
232
233
|
|
|
233
234
|
if (this._isGroupOwner) {
|
|
234
235
|
// Group owner sends via server socket
|
|
235
|
-
await p2p?.sendMessage(
|
|
236
|
+
await p2p?.sendMessage(encoded);
|
|
236
237
|
} else {
|
|
237
238
|
// Client sends to group owner address
|
|
238
|
-
await p2p?.sendMessageTo(peerInfo.groupOwnerAddress, this._port,
|
|
239
|
+
await p2p?.sendMessageTo(peerInfo.groupOwnerAddress, this._port, encoded);
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
|
|
@@ -289,12 +290,7 @@ class WiFiDirectTransport extends Transport {
|
|
|
289
290
|
|
|
290
291
|
/** @private */
|
|
291
292
|
_uint8ArrayToBase64(/** @type {any} */ bytes) {
|
|
292
|
-
|
|
293
|
-
for (let i = 0; i < bytes.length; i += 8192) {
|
|
294
|
-
chunks.push(String.fromCharCode.apply(null, bytes.subarray(i, Math.min(i + 8192, bytes.length))));
|
|
295
|
-
}
|
|
296
|
-
const binary = chunks.join('');
|
|
297
|
-
return typeof btoa !== 'undefined' ? btoa(binary) : Buffer.from(bytes).toString('base64');
|
|
293
|
+
return base64.encode(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes));
|
|
298
294
|
}
|
|
299
295
|
}
|
|
300
296
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const BLEAdapter = require('./BLEAdapter');
|
|
9
|
+
const base64 = require('../../utils/base64');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* React Native BLE adapter implementation.
|
|
@@ -360,26 +361,17 @@ class RNBLEAdapter extends BLEAdapter {
|
|
|
360
361
|
* @private
|
|
361
362
|
*/
|
|
362
363
|
_uint8ArrayToBase64(bytes) {
|
|
363
|
-
|
|
364
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
365
|
-
binary += String.fromCharCode(bytes[i]);
|
|
366
|
-
}
|
|
367
|
-
return btoa(binary);
|
|
364
|
+
return base64.encode(bytes);
|
|
368
365
|
}
|
|
369
366
|
|
|
370
367
|
/**
|
|
371
368
|
* Converts Base64 string to Uint8Array
|
|
372
|
-
* @param {string}
|
|
369
|
+
* @param {string} base64Str - Base64 string
|
|
373
370
|
* @returns {Uint8Array} Byte array
|
|
374
371
|
* @private
|
|
375
372
|
*/
|
|
376
|
-
_base64ToUint8Array(
|
|
377
|
-
|
|
378
|
-
const bytes = new Uint8Array(binary.length);
|
|
379
|
-
for (let i = 0; i < binary.length; i++) {
|
|
380
|
-
bytes[i] = binary.charCodeAt(i);
|
|
381
|
-
}
|
|
382
|
-
return bytes;
|
|
373
|
+
_base64ToUint8Array(base64Str) {
|
|
374
|
+
return base64.decode(base64Str);
|
|
383
375
|
}
|
|
384
376
|
}
|
|
385
377
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
/* global __DEV__ */
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @fileoverview Enhanced EventEmitter class
|
|
@@ -51,11 +52,13 @@ class EventEmitter {
|
|
|
51
52
|
|
|
52
53
|
// Warn if max listeners exceeded
|
|
53
54
|
if (listeners.length >= this._maxListeners) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
56
|
+
console.warn(
|
|
57
|
+
'MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ' +
|
|
58
|
+
`${listeners.length + 1} ${event} listeners added. ` +
|
|
59
|
+
'Use setMaxListeners() to increase limit'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
listeners.push({ listener, once: false });
|
package/src/utils/compression.js
CHANGED
|
@@ -91,9 +91,7 @@ class MessageCompressor {
|
|
|
91
91
|
}
|
|
92
92
|
} catch (/** @type {any} */ error) {
|
|
93
93
|
// Log compression error at debug level for troubleshooting
|
|
94
|
-
|
|
95
|
-
console.debug('Compression failed, using uncompressed:', error.message);
|
|
96
|
-
}
|
|
94
|
+
// Compression failure is non-fatal — silently fall back to uncompressed
|
|
97
95
|
}
|
|
98
96
|
|
|
99
97
|
this._stats.bytesOut += payload.length;
|