react-native-ble-mesh 2.1.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/package.json +1 -1
- package/src/MeshNetwork.js +48 -46
- 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 +2 -0
- package/src/crypto/CryptoProvider.js +17 -17
- package/src/crypto/providers/ExpoCryptoProvider.js +12 -7
- package/src/crypto/providers/QuickCryptoProvider.js +12 -7
- package/src/crypto/providers/TweetNaClProvider.js +9 -7
- 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 +2 -1
- package/src/hooks/useMesh.js +7 -7
- package/src/hooks/useMessages.js +13 -12
- package/src/hooks/usePeers.js +10 -9
- package/src/index.js +2 -2
- package/src/mesh/dedup/BloomFilter.js +1 -0
- package/src/mesh/dedup/DedupManager.js +4 -7
- package/src/mesh/dedup/MessageCache.js +3 -0
- package/src/mesh/fragment/Assembler.js +5 -4
- package/src/mesh/fragment/Fragmenter.js +2 -2
- package/src/mesh/monitor/ConnectionQuality.js +17 -8
- package/src/mesh/monitor/NetworkMonitor.js +22 -15
- package/src/mesh/peer/Peer.js +4 -9
- package/src/mesh/peer/PeerDiscovery.js +18 -19
- package/src/mesh/peer/PeerManager.js +14 -14
- package/src/mesh/router/MessageRouter.js +15 -15
- package/src/mesh/router/PathFinder.js +10 -13
- package/src/mesh/router/RouteTable.js +8 -7
- package/src/mesh/store/StoreAndForwardManager.js +20 -23
- package/src/protocol/message.js +5 -13
- package/src/protocol/serializer.js +4 -4
- package/src/protocol/validator.js +7 -6
- package/src/service/BatteryOptimizer.js +18 -17
- package/src/service/EmergencyManager.js +18 -25
- package/src/service/HandshakeManager.js +100 -2
- package/src/service/MeshService.js +106 -22
- package/src/service/SessionManager.js +38 -3
- 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 +55 -28
- 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 +22 -30
- package/src/storage/Storage.js +9 -9
- package/src/transport/BLETransport.js +16 -14
- package/src/transport/MockTransport.js +7 -2
- package/src/transport/MultiTransport.js +13 -6
- package/src/transport/Transport.js +9 -9
- package/src/transport/WiFiDirectTransport.js +22 -17
- 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 +9 -7
- package/src/utils/LRUCache.js +10 -4
- package/src/utils/RateLimiter.js +1 -1
- package/src/utils/compression.js +5 -5
- package/src/utils/encoding.js +8 -2
- package/src/utils/retry.js +11 -13
- package/src/utils/time.js +9 -4
- package/src/utils/validation.js +1 -1
|
@@ -25,7 +25,9 @@ const SPKI_HEADER = Buffer.from('302a300506032b656e032100', 'hex');
|
|
|
25
25
|
class QuickCryptoProvider extends CryptoProvider {
|
|
26
26
|
constructor(options = {}) {
|
|
27
27
|
super();
|
|
28
|
-
|
|
28
|
+
/** @type {any} */
|
|
29
|
+
const opts = options;
|
|
30
|
+
this._crypto = opts.crypto || null;
|
|
29
31
|
this._nacl = null;
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -36,6 +38,7 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
36
38
|
_getCrypto() {
|
|
37
39
|
if (!this._crypto) {
|
|
38
40
|
try {
|
|
41
|
+
// @ts-ignore
|
|
39
42
|
this._crypto = require('react-native-quick-crypto');
|
|
40
43
|
} catch (e) {
|
|
41
44
|
throw new Error(
|
|
@@ -57,7 +60,7 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/** @inheritdoc */
|
|
60
|
-
sharedSecret(secretKey, publicKey) {
|
|
63
|
+
sharedSecret(/** @type {any} */ secretKey, /** @type {any} */ publicKey) {
|
|
61
64
|
const crypto = this._getCrypto();
|
|
62
65
|
const privKey = crypto.createPrivateKey({
|
|
63
66
|
key: Buffer.concat([
|
|
@@ -81,12 +84,13 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
81
84
|
|
|
82
85
|
/**
|
|
83
86
|
* Lazily loads tweetnacl (cached)
|
|
84
|
-
* @returns {
|
|
87
|
+
* @returns {any} nacl module
|
|
85
88
|
* @private
|
|
86
89
|
*/
|
|
87
90
|
_getNacl() {
|
|
88
91
|
if (!this._nacl) {
|
|
89
92
|
try {
|
|
93
|
+
// @ts-ignore
|
|
90
94
|
this._nacl = require('tweetnacl');
|
|
91
95
|
} catch (e) {
|
|
92
96
|
throw new Error(
|
|
@@ -98,7 +102,7 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
/** @inheritdoc */
|
|
101
|
-
encrypt(key, nonce, plaintext, _ad) {
|
|
105
|
+
encrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ plaintext, /** @type {any} */ _ad) {
|
|
102
106
|
// Use tweetnacl for encryption to ensure cross-provider compatibility
|
|
103
107
|
// QuickCrypto's advantage is in fast native key generation (X25519), not AEAD
|
|
104
108
|
const nacl = this._getNacl();
|
|
@@ -114,7 +118,7 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
/** @inheritdoc */
|
|
117
|
-
decrypt(key, nonce, ciphertext, _ad) {
|
|
121
|
+
decrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ ciphertext, /** @type {any} */ _ad) {
|
|
118
122
|
const nacl = this._getNacl();
|
|
119
123
|
|
|
120
124
|
// Ensure 24-byte nonce for XSalsa20-Poly1305
|
|
@@ -132,7 +136,7 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
/** @inheritdoc */
|
|
135
|
-
hash(data) {
|
|
139
|
+
hash(/** @type {any} */ data) {
|
|
136
140
|
// Use SHA-512 truncated to 32 bytes for cross-provider compatibility
|
|
137
141
|
const nacl = this._getNacl();
|
|
138
142
|
const full = nacl.hash(data); // SHA-512
|
|
@@ -140,13 +144,14 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
/** @inheritdoc */
|
|
143
|
-
randomBytes(length) {
|
|
147
|
+
randomBytes(/** @type {any} */ length) {
|
|
144
148
|
const crypto = this._getCrypto();
|
|
145
149
|
return new Uint8Array(crypto.randomBytes(length));
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
static isAvailable() {
|
|
149
153
|
try {
|
|
154
|
+
// @ts-ignore
|
|
150
155
|
require('react-native-quick-crypto');
|
|
151
156
|
return true;
|
|
152
157
|
} catch (e) {
|
|
@@ -20,7 +20,7 @@ const CryptoProvider = require('../CryptoProvider');
|
|
|
20
20
|
class TweetNaClProvider extends CryptoProvider {
|
|
21
21
|
/**
|
|
22
22
|
* @param {Object} [options={}]
|
|
23
|
-
* @param {
|
|
23
|
+
* @param {any} [options.nacl] - Injected tweetnacl instance (for testing)
|
|
24
24
|
*/
|
|
25
25
|
constructor(options = {}) {
|
|
26
26
|
super();
|
|
@@ -33,12 +33,13 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Lazily loads tweetnacl
|
|
36
|
-
* @returns {
|
|
36
|
+
* @returns {any} nacl module
|
|
37
37
|
* @private
|
|
38
38
|
*/
|
|
39
39
|
_getNacl() {
|
|
40
40
|
if (!this._nacl) {
|
|
41
41
|
try {
|
|
42
|
+
// @ts-ignore
|
|
42
43
|
this._nacl = require('tweetnacl');
|
|
43
44
|
} catch (e) {
|
|
44
45
|
throw new Error(
|
|
@@ -57,13 +58,13 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/** @inheritdoc */
|
|
60
|
-
sharedSecret(secretKey, publicKey) {
|
|
61
|
+
sharedSecret(/** @type {any} */ secretKey, /** @type {any} */ publicKey) {
|
|
61
62
|
const nacl = this._getNacl();
|
|
62
63
|
return nacl.box.before(publicKey, secretKey);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
/** @inheritdoc */
|
|
66
|
-
encrypt(key, nonce, plaintext, _ad) {
|
|
67
|
+
encrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ plaintext, /** @type {any} */ _ad) {
|
|
67
68
|
const nacl = this._getNacl();
|
|
68
69
|
// tweetnacl uses XSalsa20-Poly1305 with 24-byte nonce
|
|
69
70
|
// nacl.secretbox includes authentication
|
|
@@ -79,7 +80,7 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
/** @inheritdoc */
|
|
82
|
-
decrypt(key, nonce, ciphertext, _ad) {
|
|
83
|
+
decrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ ciphertext, /** @type {any} */ _ad) {
|
|
83
84
|
const nacl = this._getNacl();
|
|
84
85
|
|
|
85
86
|
// Ensure 24-byte nonce (pad short nonces with zeros)
|
|
@@ -94,7 +95,7 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
/** @inheritdoc */
|
|
97
|
-
hash(data) {
|
|
98
|
+
hash(/** @type {any} */ data) {
|
|
98
99
|
const nacl = this._getNacl();
|
|
99
100
|
// tweetnacl provides SHA-512; we return first 32 bytes for SHA-256 compatibility
|
|
100
101
|
const full = nacl.hash(data);
|
|
@@ -102,7 +103,7 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/** @inheritdoc */
|
|
105
|
-
randomBytes(length) {
|
|
106
|
+
randomBytes(/** @type {any} */ length) {
|
|
106
107
|
const nacl = this._getNacl();
|
|
107
108
|
return nacl.randomBytes(length);
|
|
108
109
|
}
|
|
@@ -113,6 +114,7 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
113
114
|
*/
|
|
114
115
|
static isAvailable() {
|
|
115
116
|
try {
|
|
117
|
+
// @ts-ignore
|
|
116
118
|
require('tweetnacl');
|
|
117
119
|
return true;
|
|
118
120
|
} catch (e) {
|
package/src/errors/AudioError.js
CHANGED
|
@@ -32,7 +32,7 @@ class AudioError extends MeshError {
|
|
|
32
32
|
* @returns {AudioError}
|
|
33
33
|
*/
|
|
34
34
|
static fromCode(code, details = null) {
|
|
35
|
-
const message = ERROR_MESSAGES[code] || 'Audio operation failed';
|
|
35
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || 'Audio operation failed';
|
|
36
36
|
return new AudioError(message, code, details);
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -73,6 +73,7 @@ class AudioError extends MeshError {
|
|
|
73
73
|
return new AudioError(
|
|
74
74
|
`Audio session failed with peer ${peerId}`,
|
|
75
75
|
'EA04',
|
|
76
|
+
// @ts-ignore
|
|
76
77
|
{ peerId, ...details }
|
|
77
78
|
);
|
|
78
79
|
}
|
|
@@ -40,7 +40,7 @@ class ConnectionError extends MeshError {
|
|
|
40
40
|
* @returns {ConnectionError} New ConnectionError instance
|
|
41
41
|
*/
|
|
42
42
|
static fromCode(code, peerId = null, details = null) {
|
|
43
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E200;
|
|
43
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E200;
|
|
44
44
|
return new ConnectionError(message, code, peerId, details);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -96,7 +96,7 @@ class ConnectionError extends MeshError {
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* Converts error to a JSON-serializable object
|
|
99
|
-
* @returns {
|
|
99
|
+
* @returns {any} JSON representation of the error
|
|
100
100
|
*/
|
|
101
101
|
toJSON() {
|
|
102
102
|
return {
|
|
@@ -32,7 +32,7 @@ class CryptoError extends MeshError {
|
|
|
32
32
|
* @returns {CryptoError} New CryptoError instance
|
|
33
33
|
*/
|
|
34
34
|
static fromCode(code, details = null) {
|
|
35
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E400;
|
|
35
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E400;
|
|
36
36
|
return new CryptoError(message, code, details);
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -48,7 +48,7 @@ class HandshakeError extends MeshError {
|
|
|
48
48
|
* @returns {HandshakeError} New HandshakeError instance
|
|
49
49
|
*/
|
|
50
50
|
static fromCode(code, peerId = null, step = null, details = null) {
|
|
51
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E300;
|
|
51
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E300;
|
|
52
52
|
return new HandshakeError(message, code, peerId, step, details);
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -108,7 +108,7 @@ class HandshakeError extends MeshError {
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* Converts error to a JSON-serializable object
|
|
111
|
-
* @returns {
|
|
111
|
+
* @returns {any} JSON representation of the error
|
|
112
112
|
*/
|
|
113
113
|
toJSON() {
|
|
114
114
|
return {
|
package/src/errors/MeshError.js
CHANGED
|
@@ -37,7 +37,7 @@ class MeshError extends Error {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Additional error details
|
|
40
|
-
* @type {
|
|
40
|
+
* @type {any}
|
|
41
41
|
*/
|
|
42
42
|
this.details = details;
|
|
43
43
|
|
|
@@ -60,7 +60,7 @@ class MeshError extends Error {
|
|
|
60
60
|
* @returns {MeshError} New MeshError instance
|
|
61
61
|
*/
|
|
62
62
|
static fromCode(code, details = null) {
|
|
63
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E900;
|
|
63
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E900;
|
|
64
64
|
return new MeshError(message, code, details);
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -69,12 +69,12 @@ class MeshError extends Error {
|
|
|
69
69
|
* @returns {string} Error type name
|
|
70
70
|
*/
|
|
71
71
|
getTypeName() {
|
|
72
|
-
return ERROR_CODE[this.code] || 'UNKNOWN_ERROR';
|
|
72
|
+
return /** @type {Record<string, string>} */ (ERROR_CODE)[this.code] || 'UNKNOWN_ERROR';
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Converts error to a JSON-serializable object
|
|
77
|
-
* @returns {
|
|
77
|
+
* @returns {any} JSON representation of the error
|
|
78
78
|
*/
|
|
79
79
|
toJSON() {
|
|
80
80
|
return {
|
|
@@ -40,7 +40,7 @@ class MessageError extends MeshError {
|
|
|
40
40
|
* @returns {MessageError} New MessageError instance
|
|
41
41
|
*/
|
|
42
42
|
static fromCode(code, messageId = null, details = null) {
|
|
43
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E500;
|
|
43
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E500;
|
|
44
44
|
return new MessageError(message, code, messageId, details);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -116,7 +116,7 @@ class MessageError extends MeshError {
|
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
118
|
* Converts error to a JSON-serializable object
|
|
119
|
-
* @returns {
|
|
119
|
+
* @returns {any} JSON representation of the error
|
|
120
120
|
*/
|
|
121
121
|
toJSON() {
|
|
122
122
|
return {
|
|
@@ -87,7 +87,7 @@ class ValidationError extends MeshError {
|
|
|
87
87
|
* @returns {ValidationError} New ValidationError instance
|
|
88
88
|
*/
|
|
89
89
|
static fromCode(code, field = null, value = undefined, details = null) {
|
|
90
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E800;
|
|
90
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E800;
|
|
91
91
|
return new ValidationError(message, code, field, value, details);
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -127,7 +127,7 @@ class ValidationError extends MeshError {
|
|
|
127
127
|
* Creates an out of range error
|
|
128
128
|
* @param {string} field - Name of the field
|
|
129
129
|
* @param {*} value - The invalid value
|
|
130
|
-
* @param {
|
|
130
|
+
* @param {any} range - Expected range { min, max }
|
|
131
131
|
* @returns {ValidationError} New ValidationError instance
|
|
132
132
|
*/
|
|
133
133
|
static outOfRange(field, value, range) {
|
|
@@ -136,7 +136,7 @@ class ValidationError extends MeshError {
|
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
138
|
* Converts error to a JSON-serializable object
|
|
139
|
-
* @returns {
|
|
139
|
+
* @returns {any} JSON representation of the error
|
|
140
140
|
*/
|
|
141
141
|
toJSON() {
|
|
142
142
|
return {
|
package/src/expo/withBLEMesh.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Default configuration
|
|
25
|
-
* @constant {
|
|
25
|
+
* @constant {any}
|
|
26
26
|
*/
|
|
27
27
|
const DEFAULT_OPTIONS = {
|
|
28
28
|
/** iOS NSBluetoothAlwaysUsageDescription */
|
|
@@ -40,9 +40,9 @@ const DEFAULT_OPTIONS = {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Modifies the iOS Info.plist for BLE permissions.
|
|
43
|
-
* @param {
|
|
44
|
-
* @param {
|
|
45
|
-
* @returns {
|
|
43
|
+
* @param {any} config - Expo config
|
|
44
|
+
* @param {any} options - Plugin options
|
|
45
|
+
* @returns {any} Modified config
|
|
46
46
|
*/
|
|
47
47
|
function withBLEMeshIOS(config, options) {
|
|
48
48
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
@@ -64,9 +64,9 @@ function withBLEMeshIOS(config, options) {
|
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Modifies the Android manifest for BLE permissions.
|
|
67
|
-
* @param {
|
|
68
|
-
* @param {
|
|
69
|
-
* @returns {
|
|
67
|
+
* @param {any} config - Expo config
|
|
68
|
+
* @param {any} options - Plugin options
|
|
69
|
+
* @returns {any} Modified config
|
|
70
70
|
*/
|
|
71
71
|
function withBLEMeshAndroid(config, options) {
|
|
72
72
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
@@ -86,9 +86,9 @@ function withBLEMeshAndroid(config, options) {
|
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
88
|
* Main Expo config plugin.
|
|
89
|
-
* @param {
|
|
90
|
-
* @param {
|
|
91
|
-
* @returns {
|
|
89
|
+
* @param {any} config - Expo config
|
|
90
|
+
* @param {any} [options={}] - Plugin options
|
|
91
|
+
* @returns {any} Modified config
|
|
92
92
|
*/
|
|
93
93
|
function withBLEMesh(config, options = {}) {
|
|
94
94
|
config = withBLEMeshIOS(config, options);
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
class AppStateManager {
|
|
23
23
|
/**
|
|
24
24
|
* Creates a new AppStateManager
|
|
25
|
-
* @param {
|
|
25
|
+
* @param {any} mesh - MeshService instance to manage
|
|
26
26
|
* @param {Object} [options] - Configuration options
|
|
27
27
|
* @param {string} [options.backgroundMode='ULTRA_POWER_SAVER'] - Power mode for background
|
|
28
28
|
* @param {string} [options.foregroundMode='BALANCED'] - Power mode for foreground
|
|
@@ -58,6 +58,7 @@ class AppStateManager {
|
|
|
58
58
|
|
|
59
59
|
// Try to get AppState from React Native
|
|
60
60
|
try {
|
|
61
|
+
// @ts-ignore
|
|
61
62
|
const { AppState } = require('react-native');
|
|
62
63
|
this._AppState = AppState;
|
|
63
64
|
} catch (e) {
|
package/src/hooks/useMesh.js
CHANGED
|
@@ -9,10 +9,8 @@
|
|
|
9
9
|
* React hook for managing MeshService lifecycle in React Native apps.
|
|
10
10
|
* Handles initialization, cleanup, and state management.
|
|
11
11
|
*
|
|
12
|
-
* @param {
|
|
13
|
-
* @
|
|
14
|
-
* @param {Object} [config.storage] - Storage adapter
|
|
15
|
-
* @returns {Object} Mesh state and controls
|
|
12
|
+
* @param {any} [config] - MeshService configuration
|
|
13
|
+
* @returns {any} Mesh state and controls
|
|
16
14
|
*
|
|
17
15
|
* @example
|
|
18
16
|
* function App() {
|
|
@@ -35,6 +33,7 @@ function useMesh(config = {}) {
|
|
|
35
33
|
// This hook requires React - check if available
|
|
36
34
|
let React;
|
|
37
35
|
try {
|
|
36
|
+
// @ts-ignore
|
|
38
37
|
React = require('react');
|
|
39
38
|
} catch (e) {
|
|
40
39
|
throw new Error('useMesh requires React. Install react as a dependency.');
|
|
@@ -65,7 +64,7 @@ function useMesh(config = {}) {
|
|
|
65
64
|
}, [config.displayName]);
|
|
66
65
|
|
|
67
66
|
// Initialize mesh
|
|
68
|
-
const initialize = useCallback(async (transport) => {
|
|
67
|
+
const initialize = useCallback(async (/** @type {any} */ transport) => {
|
|
69
68
|
try {
|
|
70
69
|
if (!mountedRef.current) { return; }
|
|
71
70
|
setState('initializing');
|
|
@@ -82,13 +81,14 @@ function useMesh(config = {}) {
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
// Setup state change listener
|
|
84
|
+
// @ts-ignore
|
|
85
85
|
stateHandlerRef.current = ({ newState }) => {
|
|
86
86
|
if (mountedRef.current) {
|
|
87
87
|
setState(newState);
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
errorHandlerRef.current = (err) => {
|
|
91
|
+
errorHandlerRef.current = (/** @type {any} */ err) => {
|
|
92
92
|
if (mountedRef.current) {
|
|
93
93
|
setError(err);
|
|
94
94
|
}
|
|
@@ -118,7 +118,7 @@ function useMesh(config = {}) {
|
|
|
118
118
|
}, [getMesh, config.storage]);
|
|
119
119
|
|
|
120
120
|
// Start with transport
|
|
121
|
-
const start = useCallback(async (transport) => {
|
|
121
|
+
const start = useCallback(async (/** @type {any} */ transport) => {
|
|
122
122
|
const mesh = getMesh();
|
|
123
123
|
try {
|
|
124
124
|
await mesh.start(transport);
|
package/src/hooks/useMessages.js
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* React hook for sending and receiving messages in the mesh network.
|
|
10
10
|
* Manages message state and provides send functions.
|
|
11
11
|
*
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {
|
|
14
|
-
|
|
15
|
-
* @returns {
|
|
12
|
+
* @param {any} mesh - MeshService instance
|
|
13
|
+
* @param {any} [options] - Options
|
|
14
|
+
*
|
|
15
|
+
* @returns {any} Messages state and send functions
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* function Chat({ mesh, peerId }) {
|
|
@@ -38,6 +38,7 @@ function useMessages(mesh, options = {}) {
|
|
|
38
38
|
// This hook requires React
|
|
39
39
|
let React;
|
|
40
40
|
try {
|
|
41
|
+
// @ts-ignore
|
|
41
42
|
React = require('react');
|
|
42
43
|
} catch (e) {
|
|
43
44
|
throw new Error('useMessages requires React. Install react as a dependency.');
|
|
@@ -52,11 +53,11 @@ function useMessages(mesh, options = {}) {
|
|
|
52
53
|
const messageIdRef = useRef(new Set());
|
|
53
54
|
|
|
54
55
|
// Add message to state (with dedup)
|
|
55
|
-
const addMessage = useCallback((msg) => {
|
|
56
|
+
const addMessage = useCallback((/** @type {any} */ msg) => {
|
|
56
57
|
if (messageIdRef.current.has(msg.id)) { return; }
|
|
57
58
|
messageIdRef.current.add(msg.id);
|
|
58
59
|
|
|
59
|
-
setMessages(prev => {
|
|
60
|
+
setMessages((/** @type {any} */ prev) => {
|
|
60
61
|
const updated = [msg, ...prev];
|
|
61
62
|
if (updated.length > maxMessages) {
|
|
62
63
|
for (let i = maxMessages; i < updated.length; i++) {
|
|
@@ -72,7 +73,7 @@ function useMessages(mesh, options = {}) {
|
|
|
72
73
|
useEffect(() => {
|
|
73
74
|
if (!mesh) { return; }
|
|
74
75
|
|
|
75
|
-
const handleBroadcast = (data) => {
|
|
76
|
+
const handleBroadcast = (/** @type {any} */ data) => {
|
|
76
77
|
addMessage({
|
|
77
78
|
id: data.messageId,
|
|
78
79
|
type: 'broadcast',
|
|
@@ -83,7 +84,7 @@ function useMessages(mesh, options = {}) {
|
|
|
83
84
|
});
|
|
84
85
|
};
|
|
85
86
|
|
|
86
|
-
const handlePrivate = (data) => {
|
|
87
|
+
const handlePrivate = (/** @type {any} */ data) => {
|
|
87
88
|
addMessage({
|
|
88
89
|
id: data.messageId,
|
|
89
90
|
type: 'private',
|
|
@@ -94,7 +95,7 @@ function useMessages(mesh, options = {}) {
|
|
|
94
95
|
});
|
|
95
96
|
};
|
|
96
97
|
|
|
97
|
-
const handleChannel = (data) => {
|
|
98
|
+
const handleChannel = (/** @type {any} */ data) => {
|
|
98
99
|
addMessage({
|
|
99
100
|
id: data.messageId,
|
|
100
101
|
type: 'channel',
|
|
@@ -120,7 +121,7 @@ function useMessages(mesh, options = {}) {
|
|
|
120
121
|
}, [mesh, addMessage]);
|
|
121
122
|
|
|
122
123
|
// Send broadcast message
|
|
123
|
-
const sendBroadcast = useCallback((content) => {
|
|
124
|
+
const sendBroadcast = useCallback((/** @type {any} */ content) => {
|
|
124
125
|
if (!mesh) { throw new Error('Mesh not initialized'); }
|
|
125
126
|
setError(null);
|
|
126
127
|
|
|
@@ -142,7 +143,7 @@ function useMessages(mesh, options = {}) {
|
|
|
142
143
|
}, [mesh, addMessage]);
|
|
143
144
|
|
|
144
145
|
// Send private message
|
|
145
|
-
const sendPrivate = useCallback(async (peerId, content) => {
|
|
146
|
+
const sendPrivate = useCallback(async (/** @type {any} */ peerId, /** @type {any} */ content) => {
|
|
146
147
|
if (!mesh) { throw new Error('Mesh not initialized'); }
|
|
147
148
|
setError(null);
|
|
148
149
|
setSending(true);
|
|
@@ -168,7 +169,7 @@ function useMessages(mesh, options = {}) {
|
|
|
168
169
|
}, [mesh, addMessage]);
|
|
169
170
|
|
|
170
171
|
// Send channel message
|
|
171
|
-
const sendToChannel = useCallback((channelId, content) => {
|
|
172
|
+
const sendToChannel = useCallback((/** @type {any} */ channelId, /** @type {any} */ content) => {
|
|
172
173
|
if (!mesh) { throw new Error('Mesh not initialized'); }
|
|
173
174
|
setError(null);
|
|
174
175
|
|
package/src/hooks/usePeers.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* React hook for managing and observing peers in the mesh network.
|
|
10
10
|
* Automatically updates when peers connect, disconnect, or change state.
|
|
11
11
|
*
|
|
12
|
-
* @param {
|
|
13
|
-
* @returns {
|
|
12
|
+
* @param {any} mesh - MeshService instance
|
|
13
|
+
* @returns {any} Peers state and utilities
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* function PeerList({ mesh }) {
|
|
@@ -30,6 +30,7 @@ function usePeers(mesh) {
|
|
|
30
30
|
// This hook requires React
|
|
31
31
|
let React;
|
|
32
32
|
try {
|
|
33
|
+
// @ts-ignore
|
|
33
34
|
React = require('react');
|
|
34
35
|
} catch (e) {
|
|
35
36
|
throw new Error('usePeers requires React. Install react as a dependency.');
|
|
@@ -45,8 +46,8 @@ function usePeers(mesh) {
|
|
|
45
46
|
if (mesh) {
|
|
46
47
|
try {
|
|
47
48
|
const allPeers = mesh.getPeers();
|
|
48
|
-
setPeers(prev => {
|
|
49
|
-
if (prev.length === allPeers.length && prev.every((p, i) => p.id === allPeers[i]?.id && p.connectionState === allPeers[i]?.connectionState)) {
|
|
49
|
+
setPeers((/** @type {any} */ prev) => {
|
|
50
|
+
if (prev.length === allPeers.length && prev.every((/** @type {any} */ p, /** @type {any} */ i) => p.id === allPeers[i]?.id && p.connectionState === allPeers[i]?.connectionState)) {
|
|
50
51
|
return prev;
|
|
51
52
|
}
|
|
52
53
|
return allPeers;
|
|
@@ -82,19 +83,19 @@ function usePeers(mesh) {
|
|
|
82
83
|
|
|
83
84
|
// Computed values
|
|
84
85
|
const connectedPeers = useMemo(() => {
|
|
85
|
-
return peers.filter(p => p.connectionState === 'connected' || p.connectionState === 'secured');
|
|
86
|
+
return peers.filter((/** @type {any} */ p) => p.connectionState === 'connected' || p.connectionState === 'secured');
|
|
86
87
|
}, [peers]);
|
|
87
88
|
|
|
88
89
|
const securedPeers = useMemo(() => {
|
|
89
|
-
return peers.filter(p => p.connectionState === 'secured');
|
|
90
|
+
return peers.filter((/** @type {any} */ p) => p.connectionState === 'secured');
|
|
90
91
|
}, [peers]);
|
|
91
92
|
|
|
92
93
|
// Get single peer by ID (O(1) lookup via peerMap)
|
|
93
|
-
const peerMap = useMemo(() => new Map(peers.map(p => [p.id, p])), [peers]);
|
|
94
|
-
const getPeer = useCallback((peerId) => peerMap.get(peerId), [peerMap]);
|
|
94
|
+
const peerMap = useMemo(() => new Map(peers.map((/** @type {any} */ p) => [p.id, p])), [peers]);
|
|
95
|
+
const getPeer = useCallback((/** @type {any} */ peerId) => peerMap.get(peerId), [peerMap]);
|
|
95
96
|
|
|
96
97
|
// Check if peer is connected
|
|
97
|
-
const isConnected = useCallback((peerId) => {
|
|
98
|
+
const isConnected = useCallback((/** @type {any} */ peerId) => {
|
|
98
99
|
const peer = getPeer(peerId);
|
|
99
100
|
return peer && (peer.connectionState === 'connected' || peer.connectionState === 'secured');
|
|
100
101
|
}, [getPeer]);
|
package/src/index.js
CHANGED
|
@@ -69,7 +69,7 @@ function createMeshNetwork(config) {
|
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Create a new MeshService instance
|
|
72
|
-
* @param {
|
|
72
|
+
* @param {any} [config] - Configuration options
|
|
73
73
|
* @returns {MeshService}
|
|
74
74
|
*/
|
|
75
75
|
function createMeshService(config) {
|
|
@@ -80,7 +80,7 @@ function createMeshService(config) {
|
|
|
80
80
|
* Create and initialize a MeshService for Node.js usage
|
|
81
81
|
* @param {Object} [options] - Configuration options
|
|
82
82
|
* @param {string} [options.displayName='MeshNode'] - Display name for this node
|
|
83
|
-
* @param {
|
|
83
|
+
* @param {any} [options.storage=null] - Storage adapter (null for MemoryStorage)
|
|
84
84
|
* @returns {Promise<MeshService>} Initialized MeshService
|
|
85
85
|
* @example
|
|
86
86
|
* const mesh = await createNodeMesh({ displayName: 'Alice' });
|
|
@@ -14,6 +14,7 @@ const FNV_PRIME = 0x01000193;
|
|
|
14
14
|
const FNV_OFFSET = 0x811c9dc5;
|
|
15
15
|
|
|
16
16
|
// Cached TextEncoder singleton (avoids per-call allocation)
|
|
17
|
+
/** @type {any} */
|
|
17
18
|
let _encoder = null;
|
|
18
19
|
function _getEncoder() {
|
|
19
20
|
if (!_encoder && typeof TextEncoder !== 'undefined') {
|
|
@@ -29,13 +29,10 @@ const DEFAULT_CONFIG = {
|
|
|
29
29
|
class DedupManager {
|
|
30
30
|
/**
|
|
31
31
|
* Creates a new DedupManager
|
|
32
|
-
* @param {
|
|
33
|
-
* @param {number} [options.bloomFilterSize] - Size of Bloom filter in bits
|
|
34
|
-
* @param {number} [options.bloomHashCount] - Number of hash functions
|
|
35
|
-
* @param {number} [options.cacheSize] - Maximum LRU cache entries
|
|
36
|
-
* @param {number} [options.autoResetThreshold] - Fill ratio for auto reset
|
|
32
|
+
* @param {any} [options] - Configuration options *
|
|
37
33
|
*/
|
|
38
34
|
constructor(options = {}) {
|
|
35
|
+
/** @type {any} */
|
|
39
36
|
const config = { ...DEFAULT_CONFIG, ...options };
|
|
40
37
|
|
|
41
38
|
/**
|
|
@@ -78,7 +75,7 @@ class DedupManager {
|
|
|
78
75
|
|
|
79
76
|
/**
|
|
80
77
|
* Statistics for monitoring
|
|
81
|
-
* @type {
|
|
78
|
+
* @type {any}
|
|
82
79
|
* @private
|
|
83
80
|
*/
|
|
84
81
|
this._stats = {
|
|
@@ -200,7 +197,7 @@ class DedupManager {
|
|
|
200
197
|
|
|
201
198
|
/**
|
|
202
199
|
* Gets deduplication statistics
|
|
203
|
-
* @returns {
|
|
200
|
+
* @returns {any} Statistics object
|
|
204
201
|
*/
|
|
205
202
|
getStats() {
|
|
206
203
|
return {
|
|
@@ -19,7 +19,9 @@ class CacheNode {
|
|
|
19
19
|
constructor(key, timestamp) {
|
|
20
20
|
this.key = key;
|
|
21
21
|
this.timestamp = timestamp;
|
|
22
|
+
/** @type {CacheNode|null} */
|
|
22
23
|
this.prev = null;
|
|
24
|
+
/** @type {CacheNode|null} */
|
|
23
25
|
this.next = null;
|
|
24
26
|
}
|
|
25
27
|
}
|
|
@@ -207,6 +209,7 @@ class MessageCache {
|
|
|
207
209
|
*/
|
|
208
210
|
getAll() {
|
|
209
211
|
const result = [];
|
|
212
|
+
/** @type {CacheNode|null} */
|
|
210
213
|
let node = this._head;
|
|
211
214
|
while (node) {
|
|
212
215
|
result.push(node.key);
|