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
package/src/protocol/header.js
CHANGED
|
@@ -16,6 +16,12 @@ const { randomBytes } = require('../utils/bytes');
|
|
|
16
16
|
*/
|
|
17
17
|
const HEADER_SIZE = 48;
|
|
18
18
|
|
|
19
|
+
// Pre-computed hex lookup table (avoids Array.from().map().join() per call)
|
|
20
|
+
const HEX_TABLE = new Array(256);
|
|
21
|
+
for (let i = 0; i < 256; i++) {
|
|
22
|
+
HEX_TABLE[i] = (i < 16 ? '0' : '') + i.toString(16);
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
/**
|
|
20
26
|
* Message header class representing the 48-byte header structure.
|
|
21
27
|
* @class MessageHeader
|
|
@@ -43,7 +49,6 @@ class MessageHeader {
|
|
|
43
49
|
this.flags = options.flags ?? MESSAGE_FLAGS.NONE;
|
|
44
50
|
this.hopCount = options.hopCount ?? 0;
|
|
45
51
|
this.maxHops = options.maxHops ?? MESH_CONFIG.MAX_HOPS;
|
|
46
|
-
this.reserved = new Uint8Array(3);
|
|
47
52
|
this.messageId = options.messageId;
|
|
48
53
|
this.timestamp = options.timestamp;
|
|
49
54
|
this.expiresAt = options.expiresAt;
|
|
@@ -143,9 +148,8 @@ class MessageHeader {
|
|
|
143
148
|
view.setUint16(40, header.payloadLength, false);
|
|
144
149
|
buffer[42] = header.fragmentIndex;
|
|
145
150
|
buffer[43] = header.fragmentTotal;
|
|
146
|
-
// Calculate checksum over header without checksum field
|
|
147
|
-
const
|
|
148
|
-
const checksum = crc32(checksumData);
|
|
151
|
+
// Calculate checksum over header without checksum field (subarray = zero-copy view)
|
|
152
|
+
const checksum = crc32(buffer.subarray(0, 44));
|
|
149
153
|
view.setUint32(44, checksum, false);
|
|
150
154
|
header.checksum = checksum;
|
|
151
155
|
|
|
@@ -204,9 +208,11 @@ function writeUint64BE(view, offset, value) {
|
|
|
204
208
|
* @returns {string} Hex string
|
|
205
209
|
*/
|
|
206
210
|
function bytesToHex(bytes) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
211
|
+
let hex = '';
|
|
212
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
213
|
+
hex += HEX_TABLE[bytes[i]];
|
|
214
|
+
}
|
|
215
|
+
return hex;
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
module.exports = {
|
package/src/protocol/message.js
CHANGED
|
@@ -9,6 +9,18 @@ const { MessageHeader, HEADER_SIZE, generateUuid } = require('./header');
|
|
|
9
9
|
const { MESSAGE_FLAGS, MESH_CONFIG } = require('../constants');
|
|
10
10
|
const { MessageError } = require('../errors');
|
|
11
11
|
|
|
12
|
+
// Cached TextEncoder/TextDecoder singletons (avoids per-call allocation)
|
|
13
|
+
/** @type {any} */ let _encoder = null;
|
|
14
|
+
/** @type {any} */ let _decoder = null;
|
|
15
|
+
function _getEncoder() {
|
|
16
|
+
if (!_encoder) { _encoder = new TextEncoder(); }
|
|
17
|
+
return _encoder;
|
|
18
|
+
}
|
|
19
|
+
function _getDecoder() {
|
|
20
|
+
if (!_decoder) { _decoder = new TextDecoder(); }
|
|
21
|
+
return _decoder;
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
/**
|
|
13
25
|
* Message class representing a complete mesh network message.
|
|
14
26
|
* @class Message
|
|
@@ -16,11 +28,11 @@ const { MessageError } = require('../errors');
|
|
|
16
28
|
class Message {
|
|
17
29
|
/**
|
|
18
30
|
* Creates a new Message instance.
|
|
19
|
-
* @param {
|
|
31
|
+
* @param {any} header - Message header
|
|
20
32
|
* @param {Uint8Array} payload - Message payload
|
|
21
33
|
*/
|
|
22
34
|
constructor(header, payload) {
|
|
23
|
-
/** @type {
|
|
35
|
+
/** @type {any} */
|
|
24
36
|
this.header = header;
|
|
25
37
|
/** @type {Uint8Array} */
|
|
26
38
|
this.payload = payload;
|
|
@@ -28,15 +40,7 @@ class Message {
|
|
|
28
40
|
|
|
29
41
|
/**
|
|
30
42
|
* Creates a new message with the given options.
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {number} options.type - Message type from MESSAGE_TYPE
|
|
33
|
-
* @param {Uint8Array|string} options.payload - Message payload
|
|
34
|
-
* @param {number} [options.flags=0] - Message flags
|
|
35
|
-
* @param {number} [options.maxHops=7] - Maximum hops
|
|
36
|
-
* @param {number} [options.ttlMs] - Time-to-live in ms
|
|
37
|
-
* @param {number} [options.fragmentIndex=0] - Fragment index
|
|
38
|
-
* @param {number} [options.fragmentTotal=1] - Total fragments
|
|
39
|
-
* @param {Uint8Array} [options.messageId] - Optional message ID
|
|
43
|
+
* @param {any} options - Message options *
|
|
40
44
|
* @returns {Message} New message instance
|
|
41
45
|
*/
|
|
42
46
|
static create(options) {
|
|
@@ -44,7 +48,7 @@ class Message {
|
|
|
44
48
|
|
|
45
49
|
// Convert string payload to bytes
|
|
46
50
|
if (typeof payload === 'string') {
|
|
47
|
-
payload =
|
|
51
|
+
payload = _getEncoder().encode(payload);
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
if (!(payload instanceof Uint8Array)) {
|
|
@@ -99,7 +103,7 @@ class Message {
|
|
|
99
103
|
});
|
|
100
104
|
}
|
|
101
105
|
|
|
102
|
-
const payload = data.
|
|
106
|
+
const payload = data.subarray(HEADER_SIZE, HEADER_SIZE + header.payloadLength);
|
|
103
107
|
|
|
104
108
|
return new Message(header, payload);
|
|
105
109
|
}
|
|
@@ -171,7 +175,7 @@ class Message {
|
|
|
171
175
|
* @returns {string} Decoded payload content
|
|
172
176
|
*/
|
|
173
177
|
getContent() {
|
|
174
|
-
return
|
|
178
|
+
return _getDecoder().decode(this.payload);
|
|
175
179
|
}
|
|
176
180
|
|
|
177
181
|
/**
|
|
@@ -15,7 +15,7 @@ const { MessageError } = require('../errors');
|
|
|
15
15
|
* Serializes a message header to bytes.
|
|
16
16
|
* Calculates CRC32 checksum and includes it in the output.
|
|
17
17
|
*
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {any} header - Header to serialize
|
|
19
19
|
* @returns {Uint8Array} 48-byte serialized header
|
|
20
20
|
* @throws {MessageError} If header is invalid
|
|
21
21
|
*
|
|
@@ -71,9 +71,8 @@ function serializeHeader(header) {
|
|
|
71
71
|
// Byte 43: fragmentTotal
|
|
72
72
|
buffer[43] = header.fragmentTotal ?? 1;
|
|
73
73
|
|
|
74
|
-
// Bytes 44-47: checksum (calculated over bytes 0-43)
|
|
75
|
-
const
|
|
76
|
-
const checksum = crc32(checksumData);
|
|
74
|
+
// Bytes 44-47: checksum (calculated over bytes 0-43, subarray = zero-copy view)
|
|
75
|
+
const checksum = crc32(buffer.subarray(0, 44));
|
|
77
76
|
view.setUint32(44, checksum, false);
|
|
78
77
|
|
|
79
78
|
return buffer;
|
|
@@ -82,7 +81,7 @@ function serializeHeader(header) {
|
|
|
82
81
|
/**
|
|
83
82
|
* Serializes a complete message (header + payload) to bytes.
|
|
84
83
|
*
|
|
85
|
-
* @param {
|
|
84
|
+
* @param {any} message - Message to serialize
|
|
86
85
|
* @returns {Uint8Array} Serialized message bytes
|
|
87
86
|
* @throws {MessageError} If message is invalid
|
|
88
87
|
*
|
|
@@ -110,7 +109,8 @@ function serialize(message) {
|
|
|
110
109
|
|
|
111
110
|
// Convert string payload to bytes
|
|
112
111
|
if (typeof payload === 'string') {
|
|
113
|
-
|
|
112
|
+
if (!(/** @type {any} */ (serialize))._encoder) { /** @type {any} */ (serialize)._encoder = new TextEncoder(); }
|
|
113
|
+
payload = /** @type {any} */ (serialize)._encoder.encode(payload);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
// Default to empty payload
|
|
@@ -122,13 +122,10 @@ function serialize(message) {
|
|
|
122
122
|
throw MessageError.invalidFormat(null, { reason: 'Payload must be Uint8Array or string' });
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
// Update payloadLength
|
|
126
|
-
|
|
127
|
-
...header,
|
|
128
|
-
payloadLength: payload.length
|
|
129
|
-
};
|
|
125
|
+
// Update payloadLength directly (avoids object spread allocation)
|
|
126
|
+
header.payloadLength = payload.length;
|
|
130
127
|
|
|
131
|
-
const headerBytes = serializeHeader(
|
|
128
|
+
const headerBytes = serializeHeader(header);
|
|
132
129
|
const result = new Uint8Array(headerBytes.length + payload.length);
|
|
133
130
|
|
|
134
131
|
result.set(headerBytes, 0);
|
|
@@ -13,12 +13,19 @@ const { PROTOCOL_VERSION, MESSAGE_TYPE, MESH_CONFIG } = require('../constants');
|
|
|
13
13
|
* Set of valid message type values for fast lookup.
|
|
14
14
|
* @type {Set<number>}
|
|
15
15
|
*/
|
|
16
|
-
const VALID_MESSAGE_TYPES = new Set(Object.values(MESSAGE_TYPE));
|
|
16
|
+
const VALID_MESSAGE_TYPES = new Set(/** @type {number[]} */ (Object.values(MESSAGE_TYPE)));
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Cached frozen result for valid validations to avoid repeated allocations.
|
|
20
|
+
* @type {{ valid: boolean, errors: string[] }}
|
|
21
|
+
*/
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
const VALID_RESULT = /** @type {{ valid: boolean, errors: string[] }} */ (Object.freeze({ valid: true, errors: Object.freeze([]) }));
|
|
17
24
|
|
|
18
25
|
/**
|
|
19
26
|
* Validates a message header.
|
|
20
27
|
*
|
|
21
|
-
* @param {
|
|
28
|
+
* @param {any} header - Header to validate
|
|
22
29
|
* @returns {{ valid: boolean, errors: string[] }} Validation result
|
|
23
30
|
*
|
|
24
31
|
* @example
|
|
@@ -102,8 +109,12 @@ function validateHeader(header) {
|
|
|
102
109
|
errors.push(`Fragment index (${header.fragmentIndex}) >= total (${header.fragmentTotal})`);
|
|
103
110
|
}
|
|
104
111
|
|
|
112
|
+
if (errors.length === 0) {
|
|
113
|
+
return VALID_RESULT;
|
|
114
|
+
}
|
|
115
|
+
|
|
105
116
|
return {
|
|
106
|
-
valid:
|
|
117
|
+
valid: false,
|
|
107
118
|
errors
|
|
108
119
|
};
|
|
109
120
|
}
|
|
@@ -111,7 +122,7 @@ function validateHeader(header) {
|
|
|
111
122
|
/**
|
|
112
123
|
* Validates a complete message (header + payload).
|
|
113
124
|
*
|
|
114
|
-
* @param {
|
|
125
|
+
* @param {any} message - Message to validate
|
|
115
126
|
* @returns {{ valid: boolean, errors: string[] }} Validation result
|
|
116
127
|
*
|
|
117
128
|
* @example
|
|
@@ -152,8 +163,12 @@ function validateMessage(message) {
|
|
|
152
163
|
}
|
|
153
164
|
}
|
|
154
165
|
|
|
166
|
+
if (errors.length === 0) {
|
|
167
|
+
return VALID_RESULT;
|
|
168
|
+
}
|
|
169
|
+
|
|
155
170
|
return {
|
|
156
|
-
valid:
|
|
171
|
+
valid: false,
|
|
157
172
|
errors
|
|
158
173
|
};
|
|
159
174
|
}
|
|
@@ -171,7 +186,7 @@ function validateChecksum(headerBytes) {
|
|
|
171
186
|
|
|
172
187
|
const view = new DataView(headerBytes.buffer, headerBytes.byteOffset, HEADER_SIZE);
|
|
173
188
|
const storedChecksum = view.getUint32(44, false);
|
|
174
|
-
const checksumData = headerBytes.
|
|
189
|
+
const checksumData = headerBytes.subarray(0, 44);
|
|
175
190
|
const calculatedChecksum = crc32(checksumData);
|
|
176
191
|
|
|
177
192
|
return {
|
|
@@ -194,7 +209,7 @@ function isValidMessageType(type) {
|
|
|
194
209
|
/**
|
|
195
210
|
* Checks if a message is expired.
|
|
196
211
|
*
|
|
197
|
-
* @param {
|
|
212
|
+
* @param {any} messageOrHeader - Message or header
|
|
198
213
|
* @returns {boolean} True if expired
|
|
199
214
|
*/
|
|
200
215
|
function isExpired(messageOrHeader) {
|
|
@@ -205,7 +220,7 @@ function isExpired(messageOrHeader) {
|
|
|
205
220
|
/**
|
|
206
221
|
* Checks if hop count has exceeded maximum.
|
|
207
222
|
*
|
|
208
|
-
* @param {
|
|
223
|
+
* @param {any} messageOrHeader - Message or header
|
|
209
224
|
* @returns {boolean} True if exceeded
|
|
210
225
|
*/
|
|
211
226
|
function hasExceededMaxHops(messageOrHeader) {
|
|
@@ -242,7 +257,7 @@ function validateRawMessage(data) {
|
|
|
242
257
|
}
|
|
243
258
|
|
|
244
259
|
// Validate checksum
|
|
245
|
-
const checksumResult = validateChecksum(data.
|
|
260
|
+
const checksumResult = validateChecksum(data.subarray(0, HEADER_SIZE));
|
|
246
261
|
if (!checksumResult.valid) {
|
|
247
262
|
errors.push(
|
|
248
263
|
`Checksum mismatch: expected 0x${checksumResult.expected.toString(16)}, ` +
|
|
@@ -259,8 +274,12 @@ function validateRawMessage(data) {
|
|
|
259
274
|
errors.push(`Incomplete message: expected ${expectedTotal} bytes, got ${data.length}`);
|
|
260
275
|
}
|
|
261
276
|
|
|
277
|
+
if (errors.length === 0) {
|
|
278
|
+
return VALID_RESULT;
|
|
279
|
+
}
|
|
280
|
+
|
|
262
281
|
return {
|
|
263
|
-
valid:
|
|
282
|
+
valid: false,
|
|
264
283
|
errors
|
|
265
284
|
};
|
|
266
285
|
}
|
|
@@ -14,7 +14,7 @@ const EventEmitter = require('../utils/EventEmitter');
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Battery mode constants
|
|
17
|
-
* @constant {
|
|
17
|
+
* @constant {any}
|
|
18
18
|
*/
|
|
19
19
|
const BATTERY_MODE = Object.freeze({
|
|
20
20
|
HIGH_PERFORMANCE: 'high',
|
|
@@ -23,6 +23,9 @@ const BATTERY_MODE = Object.freeze({
|
|
|
23
23
|
AUTO: 'auto'
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
/** Pre-computed Set of valid battery modes for O(1) lookup */
|
|
27
|
+
const BATTERY_MODE_SET = new Set(Object.values(BATTERY_MODE));
|
|
28
|
+
|
|
26
29
|
/**
|
|
27
30
|
* Battery profile configuration
|
|
28
31
|
* @typedef {Object} BatteryProfile
|
|
@@ -36,7 +39,7 @@ const BATTERY_MODE = Object.freeze({
|
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* Default battery profiles
|
|
39
|
-
* @constant {
|
|
42
|
+
* @constant {Record<string, BatteryProfile>}
|
|
40
43
|
*/
|
|
41
44
|
const DEFAULT_PROFILES = Object.freeze({
|
|
42
45
|
[BATTERY_MODE.HIGH_PERFORMANCE]: {
|
|
@@ -76,7 +79,7 @@ const DEFAULT_PROFILES = Object.freeze({
|
|
|
76
79
|
|
|
77
80
|
/**
|
|
78
81
|
* Battery level thresholds for auto mode
|
|
79
|
-
* @constant {
|
|
82
|
+
* @constant {any}
|
|
80
83
|
*/
|
|
81
84
|
const BATTERY_THRESHOLDS = Object.freeze({
|
|
82
85
|
HIGH: 50, // Above 50%: high performance
|
|
@@ -87,7 +90,7 @@ const BATTERY_THRESHOLDS = Object.freeze({
|
|
|
87
90
|
|
|
88
91
|
/**
|
|
89
92
|
* Default configuration
|
|
90
|
-
* @constant {
|
|
93
|
+
* @constant {any}
|
|
91
94
|
*/
|
|
92
95
|
const DEFAULT_CONFIG = Object.freeze({
|
|
93
96
|
/** Initial battery mode */
|
|
@@ -125,21 +128,21 @@ const DEFAULT_CONFIG = Object.freeze({
|
|
|
125
128
|
class BatteryOptimizer extends EventEmitter {
|
|
126
129
|
/**
|
|
127
130
|
* Creates a new BatteryOptimizer instance.
|
|
128
|
-
* @param {
|
|
131
|
+
* @param {any} [options] - Configuration options
|
|
129
132
|
*/
|
|
130
133
|
constructor(options = {}) {
|
|
131
134
|
super();
|
|
132
135
|
|
|
133
136
|
/**
|
|
134
137
|
* Configuration
|
|
135
|
-
* @type {
|
|
138
|
+
* @type {any}
|
|
136
139
|
* @private
|
|
137
140
|
*/
|
|
138
141
|
this._config = { ...DEFAULT_CONFIG, ...options };
|
|
139
142
|
|
|
140
143
|
/**
|
|
141
144
|
* Battery profiles
|
|
142
|
-
* @type {
|
|
145
|
+
* @type {Record<string, any>}
|
|
143
146
|
* @private
|
|
144
147
|
*/
|
|
145
148
|
this._profiles = { ...DEFAULT_PROFILES };
|
|
@@ -181,21 +184,21 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
181
184
|
|
|
182
185
|
/**
|
|
183
186
|
* Battery check timer
|
|
184
|
-
* @type {
|
|
187
|
+
* @type {any}
|
|
185
188
|
* @private
|
|
186
189
|
*/
|
|
187
190
|
this._batteryCheckTimer = null;
|
|
188
191
|
|
|
189
192
|
/**
|
|
190
193
|
* Transport reference for applying settings
|
|
191
|
-
* @type {
|
|
194
|
+
* @type {any}
|
|
192
195
|
* @private
|
|
193
196
|
*/
|
|
194
197
|
this._transport = null;
|
|
195
198
|
|
|
196
199
|
/**
|
|
197
200
|
* Statistics
|
|
198
|
-
* @type {
|
|
201
|
+
* @type {any}
|
|
199
202
|
* @private
|
|
200
203
|
*/
|
|
201
204
|
this._stats = {
|
|
@@ -212,7 +215,7 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
212
215
|
|
|
213
216
|
/**
|
|
214
217
|
* Sets the transport to control.
|
|
215
|
-
* @param {
|
|
218
|
+
* @param {any} transport - Transport instance
|
|
216
219
|
*/
|
|
217
220
|
setTransport(transport) {
|
|
218
221
|
this._transport = transport;
|
|
@@ -224,7 +227,8 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
224
227
|
* @returns {Promise<void>}
|
|
225
228
|
*/
|
|
226
229
|
async setMode(mode) {
|
|
227
|
-
|
|
230
|
+
// @ts-ignore
|
|
231
|
+
if (!BATTERY_MODE_SET.has(mode)) {
|
|
228
232
|
throw new Error(`Invalid battery mode: ${mode}`);
|
|
229
233
|
}
|
|
230
234
|
|
|
@@ -259,7 +263,7 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
259
263
|
|
|
260
264
|
/**
|
|
261
265
|
* Gets the current active profile.
|
|
262
|
-
* @returns {
|
|
266
|
+
* @returns {any} Active profile
|
|
263
267
|
*/
|
|
264
268
|
getCurrentProfile() {
|
|
265
269
|
if (this._currentMode === BATTERY_MODE.AUTO) {
|
|
@@ -270,7 +274,7 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
270
274
|
|
|
271
275
|
/**
|
|
272
276
|
* Gets all available profiles.
|
|
273
|
-
* @returns {
|
|
277
|
+
* @returns {Record<string, any>} Profiles
|
|
274
278
|
*/
|
|
275
279
|
getProfiles() {
|
|
276
280
|
return { ...this._profiles };
|
|
@@ -347,7 +351,7 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
347
351
|
|
|
348
352
|
/**
|
|
349
353
|
* Gets optimizer statistics.
|
|
350
|
-
* @returns {
|
|
354
|
+
* @returns {any} Statistics
|
|
351
355
|
*/
|
|
352
356
|
getStats() {
|
|
353
357
|
return {
|
|
@@ -371,7 +375,7 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
371
375
|
/**
|
|
372
376
|
* Gets the appropriate profile for a battery level.
|
|
373
377
|
* @param {number} level - Battery level
|
|
374
|
-
* @returns {
|
|
378
|
+
* @returns {any} Profile
|
|
375
379
|
* @private
|
|
376
380
|
*/
|
|
377
381
|
_getProfileForBatteryLevel(level) {
|
|
@@ -411,7 +415,7 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
411
415
|
|
|
412
416
|
/**
|
|
413
417
|
* Applies a battery profile to the transport.
|
|
414
|
-
* @param {
|
|
418
|
+
* @param {any} profile - Profile to apply
|
|
415
419
|
* @returns {Promise<void>}
|
|
416
420
|
* @private
|
|
417
421
|
*/
|
|
@@ -442,7 +446,7 @@ class BatteryOptimizer extends EventEmitter {
|
|
|
442
446
|
}
|
|
443
447
|
|
|
444
448
|
this.emit('profile-applied', { profile });
|
|
445
|
-
} catch (error) {
|
|
449
|
+
} catch (/** @type {any} */ error) {
|
|
446
450
|
this.emit('error', {
|
|
447
451
|
message: 'Failed to apply battery profile',
|
|
448
452
|
error: error.message
|
|
@@ -13,7 +13,7 @@ const EventEmitter = require('../utils/EventEmitter');
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Panic trigger types
|
|
16
|
-
* @constant {
|
|
16
|
+
* @constant {any}
|
|
17
17
|
*/
|
|
18
18
|
const PANIC_TRIGGER = Object.freeze({
|
|
19
19
|
TRIPLE_TAP: 'triple_tap',
|
|
@@ -24,7 +24,7 @@ const PANIC_TRIGGER = Object.freeze({
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Default configuration
|
|
27
|
-
* @constant {
|
|
27
|
+
* @constant {any}
|
|
28
28
|
*/
|
|
29
29
|
const DEFAULT_CONFIG = Object.freeze({
|
|
30
30
|
/** Trigger type for panic mode */
|
|
@@ -63,18 +63,14 @@ const DEFAULT_CONFIG = Object.freeze({
|
|
|
63
63
|
class EmergencyManager extends EventEmitter {
|
|
64
64
|
/**
|
|
65
65
|
* Creates a new EmergencyManager instance.
|
|
66
|
-
* @param {
|
|
67
|
-
* @param {string} [options.trigger='triple_tap'] - Panic trigger type
|
|
68
|
-
* @param {number} [options.tapWindowMs=500] - Time window for taps
|
|
69
|
-
* @param {number} [options.tapCount=3] - Required tap count
|
|
70
|
-
* @param {boolean} [options.requireConfirmation=false] - Require confirmation
|
|
66
|
+
* @param {any} [options] - Configuration options
|
|
71
67
|
*/
|
|
72
68
|
constructor(options = {}) {
|
|
73
69
|
super();
|
|
74
70
|
|
|
75
71
|
/**
|
|
76
72
|
* Configuration
|
|
77
|
-
* @type {
|
|
73
|
+
* @type {any}
|
|
78
74
|
* @private
|
|
79
75
|
*/
|
|
80
76
|
this._config = { ...DEFAULT_CONFIG, ...options };
|
|
@@ -88,7 +84,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
88
84
|
|
|
89
85
|
/**
|
|
90
86
|
* Tap tracking
|
|
91
|
-
* @type {
|
|
87
|
+
* @type {any}
|
|
92
88
|
* @private
|
|
93
89
|
*/
|
|
94
90
|
this._tapState = {
|
|
@@ -98,7 +94,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
98
94
|
|
|
99
95
|
/**
|
|
100
96
|
* Shake tracking
|
|
101
|
-
* @type {
|
|
97
|
+
* @type {any}
|
|
102
98
|
* @private
|
|
103
99
|
*/
|
|
104
100
|
this._shakeState = {
|
|
@@ -122,7 +118,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
122
118
|
|
|
123
119
|
/**
|
|
124
120
|
* Statistics
|
|
125
|
-
* @type {
|
|
121
|
+
* @type {any}
|
|
126
122
|
* @private
|
|
127
123
|
*/
|
|
128
124
|
this._stats = {
|
|
@@ -134,9 +130,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
134
130
|
|
|
135
131
|
/**
|
|
136
132
|
* Enables panic mode.
|
|
137
|
-
* @param {
|
|
138
|
-
* @param {Function} [options.onWipe] - Callback after wipe
|
|
139
|
-
* @param {string} [options.trigger] - Override trigger type
|
|
133
|
+
* @param {any} [options] - Enable options
|
|
140
134
|
*/
|
|
141
135
|
enablePanicMode(options = {}) {
|
|
142
136
|
this._enabled = true;
|
|
@@ -208,10 +202,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
208
202
|
|
|
209
203
|
/**
|
|
210
204
|
* Registers accelerometer data for shake detection.
|
|
211
|
-
* @param {
|
|
212
|
-
* @param {number} data.x - X acceleration
|
|
213
|
-
* @param {number} data.y - Y acceleration
|
|
214
|
-
* @param {number} data.z - Z acceleration
|
|
205
|
+
* @param {any} data - Accelerometer data
|
|
215
206
|
*/
|
|
216
207
|
registerAccelerometer(data) {
|
|
217
208
|
if (!this._enabled || this._config.trigger !== PANIC_TRIGGER.SHAKE) {
|
|
@@ -236,7 +227,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
236
227
|
|
|
237
228
|
/**
|
|
238
229
|
* Manually triggers panic wipe.
|
|
239
|
-
* @returns {Promise<
|
|
230
|
+
* @returns {Promise<any>} Wipe result
|
|
240
231
|
*/
|
|
241
232
|
async triggerManualWipe() {
|
|
242
233
|
return this._executeWipe(PANIC_TRIGGER.MANUAL);
|
|
@@ -244,7 +235,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
244
235
|
|
|
245
236
|
/**
|
|
246
237
|
* Wipes all registered data.
|
|
247
|
-
* @returns {Promise<
|
|
238
|
+
* @returns {Promise<any>} Wipe result with timing
|
|
248
239
|
*/
|
|
249
240
|
async wipeAllData() {
|
|
250
241
|
return this._executeWipe(PANIC_TRIGGER.MANUAL);
|
|
@@ -252,7 +243,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
252
243
|
|
|
253
244
|
/**
|
|
254
245
|
* Gets emergency statistics.
|
|
255
|
-
* @returns {
|
|
246
|
+
* @returns {any} Statistics
|
|
256
247
|
*/
|
|
257
248
|
getStats() {
|
|
258
249
|
return { ...this._stats };
|
|
@@ -280,7 +271,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
280
271
|
|
|
281
272
|
try {
|
|
282
273
|
await this._executeWipe(trigger);
|
|
283
|
-
} catch (error) {
|
|
274
|
+
} catch (/** @type {any} */ error) {
|
|
284
275
|
this.emit('error', {
|
|
285
276
|
message: 'Panic wipe failed',
|
|
286
277
|
error: error.message,
|
|
@@ -292,7 +283,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
292
283
|
/**
|
|
293
284
|
* Executes the data wipe.
|
|
294
285
|
* @param {string} trigger - Trigger type
|
|
295
|
-
* @returns {Promise<
|
|
286
|
+
* @returns {Promise<any>} Wipe result
|
|
296
287
|
* @private
|
|
297
288
|
*/
|
|
298
289
|
async _executeWipe(trigger) {
|
|
@@ -300,6 +291,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
300
291
|
|
|
301
292
|
this.emit('panic-wipe-started', { trigger, timestamp: startTime });
|
|
302
293
|
|
|
294
|
+
/** @type {any} */
|
|
303
295
|
const results = {
|
|
304
296
|
trigger,
|
|
305
297
|
startTime,
|
|
@@ -308,12 +300,13 @@ class EmergencyManager extends EventEmitter {
|
|
|
308
300
|
};
|
|
309
301
|
|
|
310
302
|
// Execute all clearers in parallel for speed
|
|
303
|
+
/** @type {any[]} */
|
|
311
304
|
const clearerResults = new Array(this._clearers.length);
|
|
312
305
|
const promises = this._clearers.map(async (clearer, index) => {
|
|
313
306
|
try {
|
|
314
307
|
await clearer();
|
|
315
308
|
clearerResults[index] = { index, success: true };
|
|
316
|
-
} catch (error) {
|
|
309
|
+
} catch (/** @type {any} */ error) {
|
|
317
310
|
results.errors.push({ index, error: error.message });
|
|
318
311
|
clearerResults[index] = { index, success: false, error: error.message };
|
|
319
312
|
}
|
|
@@ -344,7 +337,7 @@ class EmergencyManager extends EventEmitter {
|
|
|
344
337
|
if (this._onWipe) {
|
|
345
338
|
try {
|
|
346
339
|
this._onWipe(results);
|
|
347
|
-
} catch (error) {
|
|
340
|
+
} catch (/** @type {any} */ error) {
|
|
348
341
|
// Ignore callback errors
|
|
349
342
|
}
|
|
350
343
|
}
|