@onekeyfe/hd-transport-electron 1.2.2-alpha.101 → 1.2.2-alpha.102
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/dist/{index-d51c7da6.js → index-8e6acfc1.js} +33 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/{noble-ble-handler-0b455c4f.js → noble-ble-handler-3a1d131d.js} +217 -55
- package/dist/noble-ble-handler.d.ts +3 -0
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/dist/types/desktop-api.d.ts +2 -0
- package/dist/types/desktop-api.d.ts.map +1 -1
- package/dist/types/noble-extended.d.ts +1 -0
- package/dist/types/noble-extended.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/noble-ble-handler.test.ts +441 -0
- package/src/index.ts +7 -0
- package/src/noble-ble-handler.ts +259 -64
- package/src/types/desktop-api.ts +31 -0
- package/src/types/noble-extended.ts +1 -0
|
@@ -30,12 +30,44 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
30
30
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
function isNobleBleIpcErrorResponse(response) {
|
|
34
|
+
var _a;
|
|
35
|
+
if (!response || typeof response !== 'object') {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const candidate = response;
|
|
39
|
+
return (candidate.type === 'NobleBleIpcError' &&
|
|
40
|
+
candidate.success === false &&
|
|
41
|
+
Boolean(candidate.error) &&
|
|
42
|
+
typeof ((_a = candidate.error) === null || _a === void 0 ? void 0 : _a.name) === 'string' &&
|
|
43
|
+
typeof candidate.error.message === 'string' &&
|
|
44
|
+
typeof candidate.error.errorCode === 'number');
|
|
45
|
+
}
|
|
46
|
+
function invokeNobleBleIpc(request) {
|
|
47
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
const response = yield request;
|
|
49
|
+
if (isNobleBleIpcErrorResponse(response)) {
|
|
50
|
+
return Promise.reject(response.error);
|
|
51
|
+
}
|
|
52
|
+
return response;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
33
56
|
function initNobleBleSupport(webContents) {
|
|
34
57
|
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
-
const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-
|
|
58
|
+
const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-3a1d131d.js'); });
|
|
36
59
|
setupNobleBleHandlers(webContents);
|
|
37
60
|
});
|
|
38
61
|
}
|
|
62
|
+
function disposeNobleBleSupport(releaseNoble) {
|
|
63
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
const handler = yield Promise.resolve().then(function () { return require('./noble-ble-handler-3a1d131d.js'); });
|
|
65
|
+
yield handler.disposeNobleBleSupport(releaseNoble);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
39
68
|
|
|
40
69
|
exports.__awaiter = __awaiter;
|
|
70
|
+
exports.disposeNobleBleSupport = disposeNobleBleSupport;
|
|
41
71
|
exports.initNobleBleSupport = initNobleBleSupport;
|
|
72
|
+
exports.invokeNobleBleIpc = invokeNobleBleIpc;
|
|
73
|
+
exports.isNobleBleIpcErrorResponse = isNobleBleIpcErrorResponse;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ interface NobleModule {
|
|
|
17
17
|
state: string;
|
|
18
18
|
startScanning(serviceUUIDs: string[], allowDuplicates: boolean, callback?: (error?: Error) => void): void;
|
|
19
19
|
stopScanning(callback?: () => void): void;
|
|
20
|
+
stop(): void;
|
|
20
21
|
on(event: 'stateChange', listener: (state: string) => void): void;
|
|
21
22
|
on(event: 'discover', listener: (peripheral: Peripheral) => void): void;
|
|
22
23
|
removeListener(event: 'stateChange', listener: (state: string) => void): void;
|
|
@@ -48,6 +49,12 @@ type NobleBleIpcErrorResponse = {
|
|
|
48
49
|
success: false;
|
|
49
50
|
error: NobleBleIpcErrorPayload;
|
|
50
51
|
};
|
|
52
|
+
declare function isNobleBleIpcErrorResponse(response: unknown): response is NobleBleIpcErrorResponse;
|
|
53
|
+
/**
|
|
54
|
+
* Keeps structured Noble errors intact across Electron's IPC and contextBridge
|
|
55
|
+
* boundaries. Every preload implementation must use this for Noble invokes.
|
|
56
|
+
*/
|
|
57
|
+
declare function invokeNobleBleIpc<T>(request: Promise<T | NobleBleIpcErrorResponse>): Promise<T>;
|
|
51
58
|
interface NobleBleAPI {
|
|
52
59
|
enumerate: () => Promise<{
|
|
53
60
|
id: string;
|
|
@@ -92,5 +99,9 @@ interface DesktopAPI {
|
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
declare function initNobleBleSupport(webContents: WebContents): Promise<void>;
|
|
102
|
+
/** Terminal process cleanup. A host sharing Noble can defer native stop until all users are idle. */
|
|
103
|
+
declare function disposeNobleBleSupport(releaseNoble?: (noble: {
|
|
104
|
+
stop(): void;
|
|
105
|
+
}) => void): Promise<void>;
|
|
95
106
|
|
|
96
|
-
export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleBleIpcErrorPayload, NobleBleIpcErrorResponse, NobleBleWriteOptions, NobleModule, initNobleBleSupport };
|
|
107
|
+
export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleBleIpcErrorPayload, NobleBleIpcErrorResponse, NobleBleWriteOptions, NobleModule, disposeNobleBleSupport, initNobleBleSupport, invokeNobleBleIpc, isNobleBleIpcErrorResponse };
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,cAAc,SAAS,CAAC;AAGxB,YAAY,EACV,UAAU,EACV,WAAW,EACX,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,cAAc,SAAS,CAAC;AAGxB,YAAY,EACV,UAAU,EACV,WAAW,EACX,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEpF,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,WAAW,iBAGjE;AAGD,wBAAsB,sBAAsB,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,KAAK,IAAI,iBAG5F"}
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var index = require('./index-
|
|
5
|
+
var index = require('./index-8e6acfc1.js');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
exports.disposeNobleBleSupport = index.disposeNobleBleSupport;
|
|
9
10
|
exports.initNobleBleSupport = index.initNobleBleSupport;
|
|
11
|
+
exports.invokeNobleBleIpc = index.invokeNobleBleIpc;
|
|
12
|
+
exports.isNobleBleIpcErrorResponse = index.isNobleBleIpcErrorResponse;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-8e6acfc1.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
5
|
var pRetry = require('p-retry');
|
|
6
6
|
|
|
@@ -100,9 +100,19 @@ const NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS = 10000;
|
|
|
100
100
|
|
|
101
101
|
let noble = null;
|
|
102
102
|
let logger = null;
|
|
103
|
+
let disposing = false;
|
|
104
|
+
let disposePromise;
|
|
105
|
+
let removeIpcHandlers;
|
|
106
|
+
const pendingCancellations = new Set();
|
|
107
|
+
function assertBleActive() {
|
|
108
|
+
if (disposing) {
|
|
109
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, 'Noble BLE is shutting down');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
103
112
|
function createNobleBleConnectionError(error, messagePrefix = '') {
|
|
104
113
|
const errorMessage = error.message;
|
|
105
|
-
const isInvalidMacOsBond = error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain'
|
|
114
|
+
const isInvalidMacOsBond = (error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain') ||
|
|
115
|
+
(error.nativeErrorCode === 15 && error.nativeErrorDomain === 'CBATTErrorDomain');
|
|
106
116
|
if (isInvalidMacOsBond) {
|
|
107
117
|
const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
|
|
108
118
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
|
|
@@ -143,6 +153,57 @@ let persistentStateListener = null;
|
|
|
143
153
|
let persistentDiscoverListener = null;
|
|
144
154
|
const discoveredDevices = new Map();
|
|
145
155
|
const connectedDevices = new Map();
|
|
156
|
+
const connectingDevices = new Map();
|
|
157
|
+
let nextConnectionGeneration = 0;
|
|
158
|
+
function connectPeripheralWithCancellation(peripheral, deviceId, messagePrefix = '') {
|
|
159
|
+
assertBleActive();
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
var _a;
|
|
162
|
+
const generation = ++nextConnectionGeneration;
|
|
163
|
+
let settled = false;
|
|
164
|
+
const settle = (settler) => {
|
|
165
|
+
var _a;
|
|
166
|
+
if (settled)
|
|
167
|
+
return false;
|
|
168
|
+
settled = true;
|
|
169
|
+
if (((_a = connectingDevices.get(deviceId)) === null || _a === void 0 ? void 0 : _a.generation) === generation) {
|
|
170
|
+
connectingDevices.delete(deviceId);
|
|
171
|
+
}
|
|
172
|
+
settler();
|
|
173
|
+
return true;
|
|
174
|
+
};
|
|
175
|
+
const pendingConnection = {
|
|
176
|
+
peripheral,
|
|
177
|
+
generation,
|
|
178
|
+
cancel: error => {
|
|
179
|
+
settle(() => reject(error));
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
(_a = connectingDevices
|
|
183
|
+
.get(deviceId)) === null || _a === void 0 ? void 0 : _a.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, `Device ${deviceId} connect attempt was superseded`));
|
|
184
|
+
connectingDevices.set(deviceId, pendingConnection);
|
|
185
|
+
peripheral.connect(error => {
|
|
186
|
+
if (error) {
|
|
187
|
+
settle(() => reject(createNobleBleConnectionError(error, messagePrefix)));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!settle(() => {
|
|
191
|
+
connectedDevices.set(deviceId, peripheral);
|
|
192
|
+
resolve();
|
|
193
|
+
})) {
|
|
194
|
+
const activeConnection = connectingDevices.get(deviceId);
|
|
195
|
+
if (!activeConnection || activeConnection.peripheral !== peripheral) {
|
|
196
|
+
try {
|
|
197
|
+
peripheral.removeAllListeners('disconnect');
|
|
198
|
+
peripheral.disconnect(() => undefined);
|
|
199
|
+
}
|
|
200
|
+
catch (_a) {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
146
207
|
const pairedDevices = new Set();
|
|
147
208
|
const deviceCharacteristics = new Map();
|
|
148
209
|
const notificationCallbacks = new Map();
|
|
@@ -281,6 +342,7 @@ function updateBluetoothState(state) {
|
|
|
281
342
|
}
|
|
282
343
|
function initializeNoble() {
|
|
283
344
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
345
|
+
assertBleActive();
|
|
284
346
|
if (noble)
|
|
285
347
|
return;
|
|
286
348
|
try {
|
|
@@ -297,9 +359,11 @@ function initializeNoble() {
|
|
|
297
359
|
return;
|
|
298
360
|
}
|
|
299
361
|
const timeout = setTimeout(() => {
|
|
362
|
+
cleanup();
|
|
300
363
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout'));
|
|
301
364
|
}, BLUETOOTH_INIT_TIMEOUT);
|
|
302
365
|
const cleanup = () => {
|
|
366
|
+
pendingCancellations.delete(cancel);
|
|
303
367
|
clearTimeout(timeout);
|
|
304
368
|
if (noble) {
|
|
305
369
|
noble.removeListener('stateChange', onStateChange);
|
|
@@ -324,8 +388,14 @@ function initializeNoble() {
|
|
|
324
388
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
|
|
325
389
|
}
|
|
326
390
|
};
|
|
391
|
+
const cancel = () => {
|
|
392
|
+
cleanup();
|
|
393
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, 'Noble BLE is shutting down'));
|
|
394
|
+
};
|
|
395
|
+
pendingCancellations.add(cancel);
|
|
327
396
|
noble.on('stateChange', onStateChange);
|
|
328
397
|
});
|
|
398
|
+
assertBleActive();
|
|
329
399
|
if (!persistentDiscoverListener) {
|
|
330
400
|
persistentDiscoverListener = (peripheral) => {
|
|
331
401
|
handleDeviceDiscovered(peripheral);
|
|
@@ -372,6 +442,8 @@ function broadcastToAllWebContents(channel, payload) {
|
|
|
372
442
|
}
|
|
373
443
|
function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle') {
|
|
374
444
|
clearIdleDisconnect(deviceId);
|
|
445
|
+
if (disposing)
|
|
446
|
+
return;
|
|
375
447
|
idleDisconnectTimers.set(deviceId, setTimeout(() => {
|
|
376
448
|
var _a;
|
|
377
449
|
idleDisconnectTimers.delete(deviceId);
|
|
@@ -407,7 +479,7 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
|
|
|
407
479
|
}, ms));
|
|
408
480
|
}
|
|
409
481
|
function cleanupDevice(deviceId, webContents, options = {}) {
|
|
410
|
-
var _a;
|
|
482
|
+
var _a, _b;
|
|
411
483
|
const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', disconnectReason = hdShared.EBleDisconnectReason.DeviceDisconnected, } = options;
|
|
412
484
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting device cleanup', {
|
|
413
485
|
deviceId,
|
|
@@ -421,6 +493,8 @@ function cleanupDevice(deviceId, webContents, options = {}) {
|
|
|
421
493
|
const peripheral = connectedDevices.get(deviceId);
|
|
422
494
|
const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
|
|
423
495
|
if (cleanupConnection) {
|
|
496
|
+
(_b = connectingDevices
|
|
497
|
+
.get(deviceId)) === null || _b === void 0 ? void 0 : _b.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, `Device ${deviceId} disconnected while connecting`));
|
|
424
498
|
const disconnectEntry = deviceDisconnectListeners.get(deviceId);
|
|
425
499
|
if (disconnectEntry) {
|
|
426
500
|
disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
|
|
@@ -499,6 +573,7 @@ function setupMtuListener(peripheral, deviceId, webContents) {
|
|
|
499
573
|
}
|
|
500
574
|
function writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, buffer) {
|
|
501
575
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
576
|
+
assertBleActive();
|
|
502
577
|
return new Promise((resolve, reject) => {
|
|
503
578
|
writeCharacteristic.write(buffer, true, (error) => {
|
|
504
579
|
if (error) {
|
|
@@ -701,6 +776,7 @@ function waitForNobleScanStop(nobleInstance) {
|
|
|
701
776
|
}
|
|
702
777
|
function performTargetedScan(targetDeviceId, timeoutMs = NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS) {
|
|
703
778
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
779
|
+
assertBleActive();
|
|
704
780
|
if (!noble) {
|
|
705
781
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
|
|
706
782
|
}
|
|
@@ -712,6 +788,7 @@ function performTargetedScan(targetDeviceId, timeoutMs = NOBLE_BLE_TARGETED_SCAN
|
|
|
712
788
|
if (settled)
|
|
713
789
|
return;
|
|
714
790
|
settled = true;
|
|
791
|
+
pendingCancellations.delete(cancel);
|
|
715
792
|
if (timeoutId)
|
|
716
793
|
clearTimeout(timeoutId);
|
|
717
794
|
nobleInstance.removeListener('discover', onDiscover);
|
|
@@ -721,6 +798,7 @@ function performTargetedScan(targetDeviceId, timeoutMs = NOBLE_BLE_TARGETED_SCAN
|
|
|
721
798
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, error.message));
|
|
722
799
|
return;
|
|
723
800
|
}
|
|
801
|
+
assertBleActive();
|
|
724
802
|
if (peripheral) {
|
|
725
803
|
discoveredDevices.set(peripheral.id, peripheral);
|
|
726
804
|
}
|
|
@@ -740,6 +818,10 @@ function performTargetedScan(targetDeviceId, timeoutMs = NOBLE_BLE_TARGETED_SCAN
|
|
|
740
818
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId);
|
|
741
819
|
finish(null).catch(reject);
|
|
742
820
|
}, timeoutMs);
|
|
821
|
+
const cancel = () => {
|
|
822
|
+
finish(null, new Error('Noble BLE is shutting down')).catch(reject);
|
|
823
|
+
};
|
|
824
|
+
pendingCancellations.add(cancel);
|
|
743
825
|
nobleInstance.on('discover', onDiscover);
|
|
744
826
|
nobleInstance.startScanning([], true, (error) => {
|
|
745
827
|
if (error) {
|
|
@@ -759,14 +841,18 @@ function enumerateDevices() {
|
|
|
759
841
|
if (!noble) {
|
|
760
842
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
|
|
761
843
|
}
|
|
844
|
+
assertBleActive();
|
|
762
845
|
const nobleInstance = noble;
|
|
763
846
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting device enumeration');
|
|
764
847
|
discoveredDevices.clear();
|
|
765
848
|
ensureDiscoverListener();
|
|
766
849
|
return new Promise((resolve, reject) => {
|
|
767
850
|
const devices = [];
|
|
851
|
+
let settled = false;
|
|
768
852
|
let intervalId;
|
|
769
853
|
const cleanup = () => index.__awaiter(this, void 0, void 0, function* () {
|
|
854
|
+
settled = true;
|
|
855
|
+
pendingCancellations.delete(cancel);
|
|
770
856
|
clearTimeout(timeoutId);
|
|
771
857
|
if (intervalId)
|
|
772
858
|
clearInterval(intervalId);
|
|
@@ -788,13 +874,21 @@ function enumerateDevices() {
|
|
|
788
874
|
connectedDevices.forEach(pushDevice);
|
|
789
875
|
};
|
|
790
876
|
const timeoutId = setTimeout(() => index.__awaiter(this, void 0, void 0, function* () {
|
|
877
|
+
if (settled)
|
|
878
|
+
return;
|
|
791
879
|
checkDevices();
|
|
792
880
|
yield cleanup();
|
|
793
881
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
|
|
794
882
|
resolve(devices);
|
|
795
883
|
}), DEVICE_SCAN_TIMEOUT);
|
|
884
|
+
const cancel = () => {
|
|
885
|
+
cleanup().then(() => resolve([]), reject);
|
|
886
|
+
};
|
|
887
|
+
pendingCancellations.add(cancel);
|
|
796
888
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
|
|
797
889
|
nobleInstance.startScanning([], true, (error) => index.__awaiter(this, void 0, void 0, function* () {
|
|
890
|
+
if (settled)
|
|
891
|
+
return;
|
|
798
892
|
if (error) {
|
|
799
893
|
yield cleanup();
|
|
800
894
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start scanning:', error);
|
|
@@ -853,13 +947,16 @@ function getDevice(deviceId) {
|
|
|
853
947
|
}
|
|
854
948
|
function discoverServicesAndCharacteristics(peripheral) {
|
|
855
949
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
950
|
+
assertBleActive();
|
|
856
951
|
let timeoutId;
|
|
857
952
|
let onDisconnect;
|
|
858
953
|
const cleanup = () => {
|
|
859
954
|
if (timeoutId)
|
|
860
955
|
clearTimeout(timeoutId);
|
|
861
|
-
if (onDisconnect)
|
|
956
|
+
if (onDisconnect) {
|
|
862
957
|
peripheral.removeListener('disconnect', onDisconnect);
|
|
958
|
+
pendingCancellations.delete(onDisconnect);
|
|
959
|
+
}
|
|
863
960
|
};
|
|
864
961
|
const timeoutPromise = new Promise((_, reject) => {
|
|
865
962
|
timeoutId = setTimeout(() => {
|
|
@@ -872,6 +969,7 @@ function discoverServicesAndCharacteristics(peripheral) {
|
|
|
872
969
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Device disconnected during service discovery');
|
|
873
970
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'Device disconnected during service discovery'));
|
|
874
971
|
};
|
|
972
|
+
pendingCancellations.add(onDisconnect);
|
|
875
973
|
peripheral.once('disconnect', onDisconnect);
|
|
876
974
|
});
|
|
877
975
|
const discoveryPromise = (() => index.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -886,6 +984,7 @@ function discoverServicesAndCharacteristics(peripheral) {
|
|
|
886
984
|
}
|
|
887
985
|
});
|
|
888
986
|
});
|
|
987
|
+
assertBleActive();
|
|
889
988
|
if (!services || services.length === 0) {
|
|
890
989
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No OneKey services found');
|
|
891
990
|
}
|
|
@@ -958,17 +1057,8 @@ function forceReconnectPeripheral(peripheral, deviceId) {
|
|
|
958
1057
|
callback();
|
|
959
1058
|
}), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
|
|
960
1059
|
}
|
|
961
|
-
yield
|
|
962
|
-
peripheral.connect(error => {
|
|
963
|
-
if (error) {
|
|
964
|
-
reject(createNobleBleConnectionError(error));
|
|
965
|
-
return;
|
|
966
|
-
}
|
|
967
|
-
resolve();
|
|
968
|
-
});
|
|
969
|
-
});
|
|
1060
|
+
yield connectPeripheralWithCancellation(peripheral, deviceId);
|
|
970
1061
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
|
|
971
|
-
connectedDevices.set(deviceId, peripheral);
|
|
972
1062
|
yield hdShared.wait(500);
|
|
973
1063
|
});
|
|
974
1064
|
}
|
|
@@ -985,17 +1075,7 @@ function freshScanAndDiscover(deviceId, webContents) {
|
|
|
985
1075
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${deviceId} not found in fresh scan`);
|
|
986
1076
|
}
|
|
987
1077
|
discoveredDevices.set(deviceId, freshPeripheral);
|
|
988
|
-
yield
|
|
989
|
-
freshPeripheral.connect((error) => {
|
|
990
|
-
if (error) {
|
|
991
|
-
reject(createNobleBleConnectionError(error, 'Fresh peripheral connection failed: '));
|
|
992
|
-
}
|
|
993
|
-
else {
|
|
994
|
-
connectedDevices.set(deviceId, freshPeripheral);
|
|
995
|
-
resolve();
|
|
996
|
-
}
|
|
997
|
-
});
|
|
998
|
-
});
|
|
1078
|
+
yield connectPeripheralWithCancellation(freshPeripheral, deviceId, 'Fresh peripheral connection failed: ');
|
|
999
1079
|
setupDisconnectListener(freshPeripheral, deviceId, webContents);
|
|
1000
1080
|
yield hdShared.wait(500);
|
|
1001
1081
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Attempting service discovery with fresh peripheral');
|
|
@@ -1034,6 +1114,7 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
|
|
|
1034
1114
|
minTimeout: 500,
|
|
1035
1115
|
maxTimeout: 3000,
|
|
1036
1116
|
onFailedAttempt: error => {
|
|
1117
|
+
assertBleActive();
|
|
1037
1118
|
logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] Service discovery attempt ${error.attemptNumber} failed:`, {
|
|
1038
1119
|
message: error.message,
|
|
1039
1120
|
retriesLeft: error.retriesLeft,
|
|
@@ -1049,11 +1130,13 @@ function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
|
|
|
1049
1130
|
yield forceReconnectPeripheral(peripheral, deviceId);
|
|
1050
1131
|
}
|
|
1051
1132
|
catch (resetError) {
|
|
1052
|
-
if (hdShared.isBleStaleBondHardwareError(resetError)
|
|
1133
|
+
if (hdShared.isBleStaleBondHardwareError(resetError) ||
|
|
1134
|
+
(resetError === null || resetError === void 0 ? void 0 : resetError.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
|
|
1053
1135
|
throw resetError;
|
|
1054
1136
|
}
|
|
1055
1137
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
|
|
1056
1138
|
}
|
|
1139
|
+
assertBleActive();
|
|
1057
1140
|
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
1058
1141
|
try {
|
|
1059
1142
|
return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
|
|
@@ -1076,21 +1159,26 @@ const directConnectCooldownUntil = new Map();
|
|
|
1076
1159
|
function tryDirectConnectById(deviceId) {
|
|
1077
1160
|
var _a;
|
|
1078
1161
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
1162
|
+
assertBleActive();
|
|
1079
1163
|
const nobleInstance = noble;
|
|
1080
1164
|
if (!(nobleInstance === null || nobleInstance === void 0 ? void 0 : nobleInstance.connectAsync))
|
|
1081
1165
|
return undefined;
|
|
1082
1166
|
const cooldownUntil = (_a = directConnectCooldownUntil.get(deviceId)) !== null && _a !== void 0 ? _a : 0;
|
|
1083
1167
|
if (Date.now() < cooldownUntil)
|
|
1084
1168
|
return undefined;
|
|
1169
|
+
let timer;
|
|
1170
|
+
let cancel;
|
|
1085
1171
|
try {
|
|
1086
1172
|
const directPromise = nobleInstance.connectAsync(deviceId);
|
|
1087
1173
|
const raced = yield Promise.race([
|
|
1088
1174
|
directPromise,
|
|
1089
1175
|
new Promise(resolve => {
|
|
1090
|
-
|
|
1176
|
+
cancel = () => resolve('timeout');
|
|
1177
|
+
pendingCancellations.add(cancel);
|
|
1178
|
+
timer = setTimeout(cancel, DIRECT_CONNECT_TIMEOUT_MS);
|
|
1091
1179
|
}),
|
|
1092
1180
|
]);
|
|
1093
|
-
if (raced === 'timeout') {
|
|
1181
|
+
if (raced === 'timeout' || disposing) {
|
|
1094
1182
|
directConnectCooldownUntil.set(deviceId, Date.now() + DIRECT_CONNECT_COOLDOWN_MS);
|
|
1095
1183
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Direct connect-by-id timed out, falling back to scan', {
|
|
1096
1184
|
deviceId,
|
|
@@ -1122,6 +1210,11 @@ function tryDirectConnectById(deviceId) {
|
|
|
1122
1210
|
});
|
|
1123
1211
|
return undefined;
|
|
1124
1212
|
}
|
|
1213
|
+
finally {
|
|
1214
|
+
clearTimeout(timer);
|
|
1215
|
+
if (cancel)
|
|
1216
|
+
pendingCancellations.delete(cancel);
|
|
1217
|
+
}
|
|
1125
1218
|
});
|
|
1126
1219
|
}
|
|
1127
1220
|
function connectDevice(deviceId, webContents) {
|
|
@@ -1174,6 +1267,7 @@ function connectDevice(deviceId, webContents) {
|
|
|
1174
1267
|
peripheral = byIdFirst ? yield scanForPeripheral() : yield connectById();
|
|
1175
1268
|
}
|
|
1176
1269
|
}
|
|
1270
|
+
assertBleActive();
|
|
1177
1271
|
if (!peripheral) {
|
|
1178
1272
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${deviceId} not found`);
|
|
1179
1273
|
}
|
|
@@ -1184,6 +1278,7 @@ function connectDevice(deviceId, webContents) {
|
|
|
1184
1278
|
if (!connectedDevices.has(deviceId)) {
|
|
1185
1279
|
connectedDevices.set(deviceId, peripheral);
|
|
1186
1280
|
}
|
|
1281
|
+
assertBleActive();
|
|
1187
1282
|
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
1188
1283
|
if (deviceCharacteristics.has(deviceId)) {
|
|
1189
1284
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device characteristics already available');
|
|
@@ -1208,6 +1303,7 @@ function connectDevice(deviceId, webContents) {
|
|
|
1208
1303
|
}
|
|
1209
1304
|
try {
|
|
1210
1305
|
const characteristics = yield setupConnectionAndDiscoverServices(peripheral, deviceId, webContents);
|
|
1306
|
+
assertBleActive();
|
|
1211
1307
|
deviceCharacteristics.set(deviceId, characteristics);
|
|
1212
1308
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device ready for communication:', deviceId);
|
|
1213
1309
|
}
|
|
@@ -1218,38 +1314,31 @@ function connectDevice(deviceId, webContents) {
|
|
|
1218
1314
|
}
|
|
1219
1315
|
return;
|
|
1220
1316
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
resolve();
|
|
1236
|
-
}
|
|
1237
|
-
catch (setupError) {
|
|
1238
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection setup failed:', setupError);
|
|
1239
|
-
disconnectDevice(deviceId)
|
|
1240
|
-
.catch(() => undefined)
|
|
1241
|
-
.then(() => reject(setupError));
|
|
1242
|
-
}
|
|
1243
|
-
}));
|
|
1244
|
-
});
|
|
1317
|
+
const connectedPeripheral = peripheral;
|
|
1318
|
+
yield connectPeripheralWithCancellation(connectedPeripheral, deviceId);
|
|
1319
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connected to device:', deviceId);
|
|
1320
|
+
try {
|
|
1321
|
+
const characteristics = yield setupConnectionAndDiscoverServices(connectedPeripheral, deviceId, webContents);
|
|
1322
|
+
assertBleActive();
|
|
1323
|
+
deviceCharacteristics.set(deviceId, characteristics);
|
|
1324
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device ready for communication:', deviceId);
|
|
1325
|
+
}
|
|
1326
|
+
catch (setupError) {
|
|
1327
|
+
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection setup failed:', setupError);
|
|
1328
|
+
yield disconnectDevice(deviceId).catch(() => undefined);
|
|
1329
|
+
throw setupError;
|
|
1330
|
+
}
|
|
1245
1331
|
});
|
|
1246
1332
|
}
|
|
1247
1333
|
function disconnectDevice(deviceId) {
|
|
1334
|
+
var _a;
|
|
1248
1335
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
1249
|
-
const
|
|
1336
|
+
const pendingConnection = connectingDevices.get(deviceId);
|
|
1337
|
+
const peripheral = (_a = connectedDevices.get(deviceId)) !== null && _a !== void 0 ? _a : pendingConnection === null || pendingConnection === void 0 ? void 0 : pendingConnection.peripheral;
|
|
1250
1338
|
if (!peripheral) {
|
|
1251
1339
|
return;
|
|
1252
1340
|
}
|
|
1341
|
+
pendingConnection === null || pendingConnection === void 0 ? void 0 : pendingConnection.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, `Device ${deviceId} connection cancelled`));
|
|
1253
1342
|
const disconnectEntry = deviceDisconnectListeners.get(deviceId);
|
|
1254
1343
|
if (disconnectEntry) {
|
|
1255
1344
|
disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
|
|
@@ -1292,7 +1381,8 @@ function unsubscribeNotifications(deviceId) {
|
|
|
1292
1381
|
subscribedDevices.delete(deviceId);
|
|
1293
1382
|
}
|
|
1294
1383
|
finally {
|
|
1295
|
-
|
|
1384
|
+
if (!disposing)
|
|
1385
|
+
subscriptionOperations.set(deviceId, 'idle');
|
|
1296
1386
|
}
|
|
1297
1387
|
});
|
|
1298
1388
|
}
|
|
@@ -1342,10 +1432,12 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1342
1432
|
timeoutMs: BLE_CLEANUP_TIMEOUT,
|
|
1343
1433
|
timeoutBehavior: 'resolve',
|
|
1344
1434
|
});
|
|
1435
|
+
assertBleActive();
|
|
1345
1436
|
yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
|
|
1346
1437
|
timeoutMs: NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS,
|
|
1347
1438
|
timeoutBehavior: 'reject',
|
|
1348
1439
|
});
|
|
1440
|
+
assertBleActive();
|
|
1349
1441
|
notifyCharacteristic.on('data', (data) => {
|
|
1350
1442
|
if (!pairedDevices.has(deviceId)) {
|
|
1351
1443
|
pairedDevices.add(deviceId);
|
|
@@ -1358,25 +1450,36 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1358
1450
|
const subscribeStartedAt = Date.now();
|
|
1359
1451
|
try {
|
|
1360
1452
|
yield rebuildAppSubscription(deviceId, notifyCharacteristic);
|
|
1453
|
+
assertBleActive();
|
|
1361
1454
|
subscribedDevices.set(deviceId, true);
|
|
1362
1455
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification subscription active', {
|
|
1363
1456
|
deviceId,
|
|
1364
1457
|
ms: Date.now() - subscribeStartedAt,
|
|
1365
1458
|
});
|
|
1366
1459
|
}
|
|
1460
|
+
catch (error) {
|
|
1461
|
+
throw createNobleBleConnectionError(error, 'Notification subscription failed: ');
|
|
1462
|
+
}
|
|
1367
1463
|
finally {
|
|
1368
|
-
|
|
1464
|
+
if (!disposing)
|
|
1465
|
+
subscriptionOperations.set(deviceId, 'idle');
|
|
1369
1466
|
}
|
|
1370
1467
|
});
|
|
1371
1468
|
}
|
|
1372
1469
|
function setupNobleBleHandlers(webContents) {
|
|
1470
|
+
if (disposing)
|
|
1471
|
+
return;
|
|
1373
1472
|
try {
|
|
1374
1473
|
logger = require('electron-log');
|
|
1375
1474
|
const { ipcMain } = require('electron');
|
|
1475
|
+
const channels = new Set();
|
|
1476
|
+
removeIpcHandlers = () => channels.forEach(channel => ipcMain.removeHandler(channel));
|
|
1376
1477
|
const handle = (channel, listener) => {
|
|
1478
|
+
channels.add(channel);
|
|
1377
1479
|
ipcMain.removeHandler(channel);
|
|
1378
1480
|
ipcMain.handle(channel, (...args) => index.__awaiter(this, void 0, void 0, function* () {
|
|
1379
1481
|
try {
|
|
1482
|
+
assertBleActive();
|
|
1380
1483
|
return yield Promise.resolve(listener(...args));
|
|
1381
1484
|
}
|
|
1382
1485
|
catch (error) {
|
|
@@ -1479,6 +1582,8 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1479
1582
|
}
|
|
1480
1583
|
}));
|
|
1481
1584
|
webContents.on('destroyed', () => {
|
|
1585
|
+
if (disposing)
|
|
1586
|
+
return;
|
|
1482
1587
|
safeLog(logger, 'info', 'Cleaning up Noble BLE handlers');
|
|
1483
1588
|
(() => index.__awaiter(this, void 0, void 0, function* () {
|
|
1484
1589
|
const deviceIds = Array.from(connectedDevices.keys());
|
|
@@ -1501,8 +1606,65 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1501
1606
|
throw error;
|
|
1502
1607
|
}
|
|
1503
1608
|
}
|
|
1609
|
+
function disposeNobleBleSupport(releaseNoble = instance => instance.stop()) {
|
|
1610
|
+
if (disposePromise)
|
|
1611
|
+
return disposePromise;
|
|
1612
|
+
disposing = true;
|
|
1613
|
+
removeIpcHandlers === null || removeIpcHandlers === void 0 ? void 0 : removeIpcHandlers();
|
|
1614
|
+
const instance = noble;
|
|
1615
|
+
const deviceIds = new Set([...connectedDevices.keys(), ...connectingDevices.keys()]);
|
|
1616
|
+
const pendingPeripherals = Array.from(connectingDevices.values(), pending => pending.peripheral);
|
|
1617
|
+
for (const cancel of pendingCancellations)
|
|
1618
|
+
cancel();
|
|
1619
|
+
for (const id of idleDisconnectTimers.keys())
|
|
1620
|
+
clearIdleDisconnect(id);
|
|
1621
|
+
for (const pending of connectingDevices.values()) {
|
|
1622
|
+
pending.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, 'Noble BLE is shutting down'));
|
|
1623
|
+
}
|
|
1624
|
+
let timeout;
|
|
1625
|
+
disposePromise = (() => index.__awaiter(this, void 0, void 0, function* () {
|
|
1626
|
+
try {
|
|
1627
|
+
yield Promise.race([
|
|
1628
|
+
Promise.allSettled([
|
|
1629
|
+
stopScanning(),
|
|
1630
|
+
...pendingPeripherals.map(peripheral => runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
1631
|
+
timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
|
|
1632
|
+
timeoutBehavior: 'resolve',
|
|
1633
|
+
})),
|
|
1634
|
+
...Array.from(deviceIds, (id) => index.__awaiter(this, void 0, void 0, function* () {
|
|
1635
|
+
yield unsubscribeNotifications(id).catch(() => undefined);
|
|
1636
|
+
yield disconnectDevice(id).catch(() => undefined);
|
|
1637
|
+
})),
|
|
1638
|
+
]),
|
|
1639
|
+
new Promise(resolve => {
|
|
1640
|
+
timeout = setTimeout(() => {
|
|
1641
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Process dispose timed out; releasing native manager');
|
|
1642
|
+
resolve();
|
|
1643
|
+
}, 3500);
|
|
1644
|
+
}),
|
|
1645
|
+
]);
|
|
1646
|
+
}
|
|
1647
|
+
finally {
|
|
1648
|
+
clearTimeout(timeout);
|
|
1649
|
+
cleanupNobleListeners();
|
|
1650
|
+
if (instance && persistentStateListener) {
|
|
1651
|
+
instance.removeListener('stateChange', persistentStateListener);
|
|
1652
|
+
persistentStateListener = null;
|
|
1653
|
+
}
|
|
1654
|
+
for (const id of deviceIds)
|
|
1655
|
+
cleanupDevice(id);
|
|
1656
|
+
discoveredDevices.clear();
|
|
1657
|
+
directConnectCooldownUntil.clear();
|
|
1658
|
+
if (instance)
|
|
1659
|
+
releaseNoble(instance);
|
|
1660
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Process dispose completed');
|
|
1661
|
+
}
|
|
1662
|
+
}))();
|
|
1663
|
+
return disposePromise;
|
|
1664
|
+
}
|
|
1504
1665
|
|
|
1505
1666
|
exports.createNobleBleConnectionError = createNobleBleConnectionError;
|
|
1506
1667
|
exports.createNobleBleIpcErrorResponse = createNobleBleIpcErrorResponse;
|
|
1668
|
+
exports.disposeNobleBleSupport = disposeNobleBleSupport;
|
|
1507
1669
|
exports.resolveNobleBleWritePacingDelay = resolveNobleBleWritePacingDelay;
|
|
1508
1670
|
exports.setupNobleBleHandlers = setupNobleBleHandlers;
|