react-native-ble-mesh 1.1.1 → 2.0.0
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 +288 -172
- package/docs/IOS-BACKGROUND-BLE.md +231 -0
- package/docs/OPTIMIZATION.md +70 -0
- package/docs/SPEC-v2.1.md +308 -0
- package/package.json +1 -1
- package/src/MeshNetwork.js +659 -465
- package/src/constants/index.js +1 -0
- package/src/crypto/AutoCrypto.js +79 -0
- package/src/crypto/CryptoProvider.js +99 -0
- package/src/crypto/index.js +15 -63
- package/src/crypto/providers/ExpoCryptoProvider.js +125 -0
- package/src/crypto/providers/QuickCryptoProvider.js +134 -0
- package/src/crypto/providers/TweetNaClProvider.js +124 -0
- package/src/crypto/providers/index.js +11 -0
- package/src/errors/MeshError.js +2 -1
- package/src/expo/withBLEMesh.js +102 -0
- package/src/hooks/useMesh.js +30 -9
- package/src/hooks/useMessages.js +2 -0
- package/src/index.js +23 -8
- package/src/mesh/dedup/DedupManager.js +36 -10
- package/src/mesh/fragment/Assembler.js +5 -0
- package/src/mesh/index.js +1 -1
- package/src/mesh/monitor/ConnectionQuality.js +408 -0
- package/src/mesh/monitor/NetworkMonitor.js +327 -316
- package/src/mesh/monitor/index.js +7 -3
- package/src/mesh/peer/PeerManager.js +6 -1
- package/src/mesh/router/MessageRouter.js +26 -15
- package/src/mesh/router/RouteTable.js +7 -1
- package/src/mesh/store/StoreAndForwardManager.js +295 -297
- package/src/mesh/store/index.js +1 -1
- package/src/service/BatteryOptimizer.js +282 -278
- package/src/service/EmergencyManager.js +224 -214
- package/src/service/HandshakeManager.js +167 -13
- package/src/service/MeshService.js +72 -6
- package/src/service/SessionManager.js +77 -2
- package/src/service/audio/AudioManager.js +8 -2
- package/src/service/file/FileAssembler.js +106 -0
- package/src/service/file/FileChunker.js +79 -0
- package/src/service/file/FileManager.js +307 -0
- package/src/service/file/FileMessage.js +122 -0
- package/src/service/file/index.js +15 -0
- package/src/service/text/broadcast/BroadcastManager.js +16 -0
- package/src/transport/BLETransport.js +131 -9
- package/src/transport/MockTransport.js +1 -1
- package/src/transport/MultiTransport.js +305 -0
- package/src/transport/WiFiDirectTransport.js +295 -0
- package/src/transport/adapters/NodeBLEAdapter.js +34 -0
- package/src/transport/adapters/RNBLEAdapter.js +56 -1
- package/src/transport/index.js +6 -0
- package/src/utils/compression.js +291 -291
- package/src/crypto/aead.js +0 -189
- package/src/crypto/chacha20.js +0 -181
- package/src/crypto/hkdf.js +0 -187
- package/src/crypto/hmac.js +0 -143
- package/src/crypto/keys/KeyManager.js +0 -271
- package/src/crypto/keys/KeyPair.js +0 -216
- package/src/crypto/keys/SecureStorage.js +0 -219
- package/src/crypto/keys/index.js +0 -32
- package/src/crypto/noise/handshake.js +0 -410
- package/src/crypto/noise/index.js +0 -27
- package/src/crypto/noise/session.js +0 -253
- package/src/crypto/noise/state.js +0 -268
- package/src/crypto/poly1305.js +0 -113
- package/src/crypto/sha256.js +0 -240
- package/src/crypto/x25519.js +0 -154
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @fileoverview Emergency Manager for Panic Mode / Data Wipe
|
|
5
5
|
* @module service/EmergencyManager
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* Provides panic mode functionality for immediate data wipe.
|
|
8
8
|
* Critical security feature for high-risk users (activists, protesters).
|
|
9
9
|
* Target: <200ms wipe completion time.
|
|
@@ -16,10 +16,10 @@ const EventEmitter = require('../utils/EventEmitter');
|
|
|
16
16
|
* @constant {Object}
|
|
17
17
|
*/
|
|
18
18
|
const PANIC_TRIGGER = Object.freeze({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
TRIPLE_TAP: 'triple_tap',
|
|
20
|
+
SHAKE: 'shake',
|
|
21
|
+
MANUAL: 'manual',
|
|
22
|
+
VOLUME_COMBO: 'volume_combo'
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -27,41 +27,41 @@ const PANIC_TRIGGER = Object.freeze({
|
|
|
27
27
|
* @constant {Object}
|
|
28
28
|
*/
|
|
29
29
|
const DEFAULT_CONFIG = Object.freeze({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
/** Trigger type for panic mode */
|
|
31
|
+
trigger: PANIC_TRIGGER.TRIPLE_TAP,
|
|
32
|
+
/** Time window for multi-tap detection (ms) */
|
|
33
|
+
tapWindowMs: 500,
|
|
34
|
+
/** Number of taps required */
|
|
35
|
+
tapCount: 3,
|
|
36
|
+
/** Shake threshold for accelerometer */
|
|
37
|
+
shakeThreshold: 15,
|
|
38
|
+
/** Shake duration required (ms) */
|
|
39
|
+
shakeDurationMs: 500,
|
|
40
|
+
/** Confirmation required before wipe */
|
|
41
|
+
requireConfirmation: false,
|
|
42
|
+
/** Target wipe time (ms) */
|
|
43
|
+
targetWipeTimeMs: 200
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Emergency Manager for panic mode and data wipe.
|
|
48
|
-
*
|
|
48
|
+
*
|
|
49
49
|
* @class EmergencyManager
|
|
50
50
|
* @extends EventEmitter
|
|
51
51
|
* @example
|
|
52
52
|
* const emergency = new EmergencyManager({
|
|
53
53
|
* trigger: 'triple_tap',
|
|
54
54
|
* });
|
|
55
|
-
*
|
|
55
|
+
*
|
|
56
56
|
* emergency.enablePanicMode({
|
|
57
57
|
* onWipe: () => console.log('Data wiped'),
|
|
58
58
|
* });
|
|
59
|
-
*
|
|
59
|
+
*
|
|
60
60
|
* // Register tap events
|
|
61
61
|
* emergency.registerTap();
|
|
62
62
|
*/
|
|
63
63
|
class EmergencyManager extends EventEmitter {
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
65
|
* Creates a new EmergencyManager instance.
|
|
66
66
|
* @param {Object} [options={}] - Configuration options
|
|
67
67
|
* @param {string} [options.trigger='triple_tap'] - Panic trigger type
|
|
@@ -69,302 +69,312 @@ class EmergencyManager extends EventEmitter {
|
|
|
69
69
|
* @param {number} [options.tapCount=3] - Required tap count
|
|
70
70
|
* @param {boolean} [options.requireConfirmation=false] - Require confirmation
|
|
71
71
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
constructor(options = {}) {
|
|
73
|
+
super();
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
76
|
* Configuration
|
|
77
77
|
* @type {Object}
|
|
78
78
|
* @private
|
|
79
79
|
*/
|
|
80
|
-
|
|
80
|
+
this._config = { ...DEFAULT_CONFIG, ...options };
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
/**
|
|
83
83
|
* Whether panic mode is enabled
|
|
84
84
|
* @type {boolean}
|
|
85
85
|
* @private
|
|
86
86
|
*/
|
|
87
|
-
|
|
87
|
+
this._enabled = false;
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
/**
|
|
90
90
|
* Tap tracking
|
|
91
91
|
* @type {Object}
|
|
92
92
|
* @private
|
|
93
93
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
this._tapState = {
|
|
95
|
+
count: 0,
|
|
96
|
+
lastTapTime: 0
|
|
97
|
+
};
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
/**
|
|
100
100
|
* Shake tracking
|
|
101
101
|
* @type {Object}
|
|
102
102
|
* @private
|
|
103
103
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
this._shakeState = {
|
|
105
|
+
startTime: 0,
|
|
106
|
+
isShaking: false
|
|
107
|
+
};
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
/**
|
|
110
110
|
* Wipe callback
|
|
111
111
|
* @type {Function|null}
|
|
112
112
|
* @private
|
|
113
113
|
*/
|
|
114
|
-
|
|
114
|
+
this._onWipe = null;
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
/**
|
|
117
117
|
* Data clearers to execute during wipe
|
|
118
118
|
* @type {Function[]}
|
|
119
119
|
* @private
|
|
120
120
|
*/
|
|
121
|
-
|
|
121
|
+
this._clearers = [];
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
/**
|
|
124
124
|
* Statistics
|
|
125
125
|
* @type {Object}
|
|
126
126
|
* @private
|
|
127
127
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
128
|
+
this._stats = {
|
|
129
|
+
wipesTriggered: 0,
|
|
130
|
+
averageWipeTimeMs: 0,
|
|
131
|
+
lastWipeTime: null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
136
|
* Enables panic mode.
|
|
137
137
|
* @param {Object} [options={}] - Enable options
|
|
138
138
|
* @param {Function} [options.onWipe] - Callback after wipe
|
|
139
139
|
* @param {string} [options.trigger] - Override trigger type
|
|
140
140
|
*/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (options.trigger) {
|
|
146
|
-
this._config.trigger = options.trigger;
|
|
147
|
-
}
|
|
141
|
+
enablePanicMode(options = {}) {
|
|
142
|
+
this._enabled = true;
|
|
143
|
+
this._onWipe = options.onWipe || null;
|
|
148
144
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
145
|
+
if (options.trigger) {
|
|
146
|
+
this._config.trigger = options.trigger;
|
|
152
147
|
}
|
|
153
148
|
|
|
154
|
-
|
|
149
|
+
this.emit('panic-mode-enabled', {
|
|
150
|
+
trigger: this._config.trigger
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
155
|
* Disables panic mode.
|
|
156
156
|
*/
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
disablePanicMode() {
|
|
158
|
+
this._enabled = false;
|
|
159
|
+
this._resetTapState();
|
|
160
|
+
this._resetShakeState();
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
this.emit('panic-mode-disabled');
|
|
163
|
+
}
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
/**
|
|
166
166
|
* Checks if panic mode is enabled.
|
|
167
167
|
* @returns {boolean} True if enabled
|
|
168
168
|
*/
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
isEnabled() {
|
|
170
|
+
return this._enabled;
|
|
171
|
+
}
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
/**
|
|
174
174
|
* Registers a data clearer function.
|
|
175
175
|
* @param {Function} clearer - Async function to clear data
|
|
176
176
|
*/
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
177
|
+
registerClearer(clearer) {
|
|
178
|
+
if (typeof clearer === 'function') {
|
|
179
|
+
this._clearers.push(clearer);
|
|
181
180
|
}
|
|
181
|
+
}
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
/**
|
|
184
184
|
* Registers a tap event (for triple-tap detection).
|
|
185
185
|
*/
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const now = Date.now();
|
|
192
|
-
const windowExpired = (now - this._tapState.lastTapTime) > this._config.tapWindowMs;
|
|
193
|
-
|
|
194
|
-
if (windowExpired) {
|
|
195
|
-
this._tapState.count = 1;
|
|
196
|
-
} else {
|
|
197
|
-
this._tapState.count++;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
this._tapState.lastTapTime = now;
|
|
201
|
-
|
|
202
|
-
// Check if threshold reached
|
|
203
|
-
if (this._tapState.count >= this._config.tapCount) {
|
|
204
|
-
this._triggerPanic(PANIC_TRIGGER.TRIPLE_TAP);
|
|
205
|
-
this._resetTapState();
|
|
206
|
-
}
|
|
186
|
+
registerTap() {
|
|
187
|
+
if (!this._enabled || this._config.trigger !== PANIC_TRIGGER.TRIPLE_TAP) {
|
|
188
|
+
return;
|
|
207
189
|
}
|
|
208
190
|
|
|
209
|
-
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
const windowExpired = (now - this._tapState.lastTapTime) > this._config.tapWindowMs;
|
|
193
|
+
|
|
194
|
+
if (windowExpired) {
|
|
195
|
+
this._tapState.count = 1;
|
|
196
|
+
} else {
|
|
197
|
+
this._tapState.count++;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this._tapState.lastTapTime = now;
|
|
201
|
+
|
|
202
|
+
// Check if threshold reached
|
|
203
|
+
if (this._tapState.count >= this._config.tapCount) {
|
|
204
|
+
this._triggerPanic(PANIC_TRIGGER.TRIPLE_TAP).catch(() => {});
|
|
205
|
+
this._resetTapState();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
210
|
* Registers accelerometer data for shake detection.
|
|
211
211
|
* @param {Object} data - Accelerometer data
|
|
212
212
|
* @param {number} data.x - X acceleration
|
|
213
213
|
* @param {number} data.y - Y acceleration
|
|
214
214
|
* @param {number} data.z - Z acceleration
|
|
215
215
|
*/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const magnitude = Math.sqrt(data.x ** 2 + data.y ** 2 + data.z ** 2);
|
|
222
|
-
const now = Date.now();
|
|
223
|
-
|
|
224
|
-
if (magnitude > this._config.shakeThreshold) {
|
|
225
|
-
if (!this._shakeState.isShaking) {
|
|
226
|
-
this._shakeState.isShaking = true;
|
|
227
|
-
this._shakeState.startTime = now;
|
|
228
|
-
} else if ((now - this._shakeState.startTime) >= this._config.shakeDurationMs) {
|
|
229
|
-
this._triggerPanic(PANIC_TRIGGER.SHAKE);
|
|
230
|
-
this._resetShakeState();
|
|
231
|
-
}
|
|
232
|
-
} else {
|
|
233
|
-
this._resetShakeState();
|
|
234
|
-
}
|
|
216
|
+
registerAccelerometer(data) {
|
|
217
|
+
if (!this._enabled || this._config.trigger !== PANIC_TRIGGER.SHAKE) {
|
|
218
|
+
return;
|
|
235
219
|
}
|
|
236
220
|
|
|
237
|
-
|
|
221
|
+
const magnitude = Math.sqrt(data.x ** 2 + data.y ** 2 + data.z ** 2);
|
|
222
|
+
const now = Date.now();
|
|
223
|
+
|
|
224
|
+
if (magnitude > this._config.shakeThreshold) {
|
|
225
|
+
if (!this._shakeState.isShaking) {
|
|
226
|
+
this._shakeState.isShaking = true;
|
|
227
|
+
this._shakeState.startTime = now;
|
|
228
|
+
} else if ((now - this._shakeState.startTime) >= this._config.shakeDurationMs) {
|
|
229
|
+
this._triggerPanic(PANIC_TRIGGER.SHAKE).catch(() => {});
|
|
230
|
+
this._resetShakeState();
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
this._resetShakeState();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
238
|
* Manually triggers panic wipe.
|
|
239
239
|
* @returns {Promise<Object>} Wipe result
|
|
240
240
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
async triggerManualWipe() {
|
|
242
|
+
return this._executeWipe(PANIC_TRIGGER.MANUAL);
|
|
243
|
+
}
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
/**
|
|
246
246
|
* Wipes all registered data.
|
|
247
247
|
* @returns {Promise<Object>} Wipe result with timing
|
|
248
248
|
*/
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
249
|
+
async wipeAllData() {
|
|
250
|
+
return this._executeWipe(PANIC_TRIGGER.MANUAL);
|
|
251
|
+
}
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
/**
|
|
254
254
|
* Gets emergency statistics.
|
|
255
255
|
* @returns {Object} Statistics
|
|
256
256
|
*/
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
getStats() {
|
|
258
|
+
return { ...this._stats };
|
|
259
|
+
}
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
/**
|
|
262
262
|
* Destroys the manager.
|
|
263
263
|
*/
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
264
|
+
destroy() {
|
|
265
|
+
this.disablePanicMode();
|
|
266
|
+
this._clearers = [];
|
|
267
|
+
this.removeAllListeners();
|
|
268
|
+
}
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
/**
|
|
271
271
|
* Triggers panic mode.
|
|
272
272
|
* @param {string} trigger - Trigger type
|
|
273
273
|
* @private
|
|
274
274
|
*/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
275
|
+
async _triggerPanic(trigger) {
|
|
276
|
+
if (this._config.requireConfirmation) {
|
|
277
|
+
this.emit('panic-confirmation-required', { trigger });
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
try {
|
|
282
|
+
await this._executeWipe(trigger);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
this.emit('error', {
|
|
285
|
+
message: 'Panic wipe failed',
|
|
286
|
+
error: error.message,
|
|
287
|
+
trigger
|
|
288
|
+
});
|
|
282
289
|
}
|
|
290
|
+
}
|
|
283
291
|
|
|
284
|
-
|
|
292
|
+
/**
|
|
285
293
|
* Executes the data wipe.
|
|
286
294
|
* @param {string} trigger - Trigger type
|
|
287
295
|
* @returns {Promise<Object>} Wipe result
|
|
288
296
|
* @private
|
|
289
297
|
*/
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
console.log(`Panic wipe completed in ${elapsedMs}ms`);
|
|
343
|
-
|
|
344
|
-
return results;
|
|
298
|
+
async _executeWipe(trigger) {
|
|
299
|
+
const startTime = Date.now();
|
|
300
|
+
|
|
301
|
+
this.emit('panic-wipe-started', { trigger, timestamp: startTime });
|
|
302
|
+
|
|
303
|
+
const results = {
|
|
304
|
+
trigger,
|
|
305
|
+
startTime,
|
|
306
|
+
clearerResults: [],
|
|
307
|
+
errors: []
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// Execute all clearers in parallel for speed
|
|
311
|
+
const clearerResults = new Array(this._clearers.length);
|
|
312
|
+
const promises = this._clearers.map(async (clearer, index) => {
|
|
313
|
+
try {
|
|
314
|
+
await clearer();
|
|
315
|
+
clearerResults[index] = { index, success: true };
|
|
316
|
+
} catch (error) {
|
|
317
|
+
results.errors.push({ index, error: error.message });
|
|
318
|
+
clearerResults[index] = { index, success: false, error: error.message };
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
await Promise.all(promises);
|
|
323
|
+
results.clearerResults = clearerResults;
|
|
324
|
+
|
|
325
|
+
const endTime = Date.now();
|
|
326
|
+
const elapsedMs = endTime - startTime;
|
|
327
|
+
|
|
328
|
+
// Update stats with correct running average calculation
|
|
329
|
+
const previousCount = this._stats.wipesTriggered;
|
|
330
|
+
this._stats.wipesTriggered++;
|
|
331
|
+
this._stats.lastWipeTime = endTime;
|
|
332
|
+
// Proper running average: ((oldAvg * oldCount) + newValue) / newCount
|
|
333
|
+
this._stats.averageWipeTimeMs = previousCount > 0
|
|
334
|
+
? ((this._stats.averageWipeTimeMs * previousCount) + elapsedMs) / this._stats.wipesTriggered
|
|
335
|
+
: elapsedMs;
|
|
336
|
+
|
|
337
|
+
results.endTime = endTime;
|
|
338
|
+
results.elapsedMs = elapsedMs;
|
|
339
|
+
results.metTarget = elapsedMs <= this._config.targetWipeTimeMs;
|
|
340
|
+
|
|
341
|
+
this.emit('panic-wipe-completed', results);
|
|
342
|
+
|
|
343
|
+
// Call user callback
|
|
344
|
+
if (this._onWipe) {
|
|
345
|
+
try {
|
|
346
|
+
this._onWipe(results);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
// Ignore callback errors
|
|
349
|
+
}
|
|
345
350
|
}
|
|
346
351
|
|
|
347
|
-
|
|
352
|
+
console.log(`Panic wipe completed in ${elapsedMs}ms`);
|
|
353
|
+
|
|
354
|
+
return results;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
348
358
|
* Resets tap state.
|
|
349
359
|
* @private
|
|
350
360
|
*/
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
361
|
+
_resetTapState() {
|
|
362
|
+
this._tapState.count = 0;
|
|
363
|
+
this._tapState.lastTapTime = 0;
|
|
364
|
+
}
|
|
355
365
|
|
|
356
|
-
|
|
366
|
+
/**
|
|
357
367
|
* Resets shake state.
|
|
358
368
|
* @private
|
|
359
369
|
*/
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
370
|
+
_resetShakeState() {
|
|
371
|
+
this._shakeState.isShaking = false;
|
|
372
|
+
this._shakeState.startTime = 0;
|
|
373
|
+
}
|
|
364
374
|
}
|
|
365
375
|
|
|
366
376
|
module.exports = {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
377
|
+
EmergencyManager,
|
|
378
|
+
PANIC_TRIGGER,
|
|
379
|
+
DEFAULT_CONFIG
|
|
370
380
|
};
|