@onekeyfe/hd-transport-electron 1.2.3-alpha.8 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-474dbbae.js → index-e2e3d2ba.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-23c86603.js → noble-ble-handler-d0991fa0.js} +29 -9
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/noble-ble-handler.test.ts +83 -2
- package/src/noble-ble-handler.ts +32 -11
|
@@ -55,13 +55,13 @@ function invokeNobleBleIpc(request) {
|
|
|
55
55
|
|
|
56
56
|
function initNobleBleSupport(webContents) {
|
|
57
57
|
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
-
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-d0991fa0.js'); });
|
|
59
59
|
setupNobleBleHandlers(webContents);
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
function disposeNobleBleSupport(releaseNoble) {
|
|
63
63
|
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
-
const handler = yield Promise.resolve().then(function () { return require('./noble-ble-handler-
|
|
64
|
+
const handler = yield Promise.resolve().then(function () { return require('./noble-ble-handler-d0991fa0.js'); });
|
|
65
65
|
yield handler.disposeNobleBleSupport(releaseNoble);
|
|
66
66
|
});
|
|
67
67
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-e2e3d2ba.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
5
|
var pRetry = require('p-retry');
|
|
6
6
|
|
|
@@ -123,8 +123,7 @@ function assertBleActive() {
|
|
|
123
123
|
}
|
|
124
124
|
function createNobleBleConnectionError(error, messagePrefix = '') {
|
|
125
125
|
const errorMessage = error.message;
|
|
126
|
-
const isInvalidMacOsBond =
|
|
127
|
-
(error.nativeErrorCode === 15 && error.nativeErrorDomain === 'CBATTErrorDomain');
|
|
126
|
+
const isInvalidMacOsBond = error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain';
|
|
128
127
|
if (isInvalidMacOsBond) {
|
|
129
128
|
const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
|
|
130
129
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
|
|
@@ -1255,11 +1254,15 @@ function tryDirectConnectById(deviceId) {
|
|
|
1255
1254
|
return peripheral;
|
|
1256
1255
|
}
|
|
1257
1256
|
catch (error) {
|
|
1258
|
-
directConnectCooldownUntil.set(deviceId, Date.now() + DIRECT_CONNECT_COOLDOWN_MS);
|
|
1259
1257
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Direct connect-by-id failed, falling back to scan', {
|
|
1260
1258
|
deviceId,
|
|
1261
1259
|
error: String(error),
|
|
1262
1260
|
});
|
|
1261
|
+
const nativeError = error;
|
|
1262
|
+
if (nativeError.nativeErrorCode === 14 && nativeError.nativeErrorDomain === 'CBErrorDomain') {
|
|
1263
|
+
throw createNobleBleConnectionError(nativeError);
|
|
1264
|
+
}
|
|
1265
|
+
directConnectCooldownUntil.set(deviceId, Date.now() + DIRECT_CONNECT_COOLDOWN_MS);
|
|
1263
1266
|
return undefined;
|
|
1264
1267
|
}
|
|
1265
1268
|
finally {
|
|
@@ -1310,17 +1313,29 @@ function connectDevice(deviceId, webContents) {
|
|
|
1310
1313
|
return undefined;
|
|
1311
1314
|
}
|
|
1312
1315
|
});
|
|
1316
|
+
let staleBondError;
|
|
1313
1317
|
const connectById = () => index.__awaiter(this, void 0, void 0, function* () {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1318
|
+
try {
|
|
1319
|
+
const found = yield tryDirectConnectById(deviceId);
|
|
1320
|
+
if (found) {
|
|
1321
|
+
discoveredDevices.set(deviceId, found);
|
|
1322
|
+
}
|
|
1323
|
+
return found;
|
|
1324
|
+
}
|
|
1325
|
+
catch (error) {
|
|
1326
|
+
if (error.errorCode !== hdShared.HardwareErrorCode.BleBondInvalid) {
|
|
1327
|
+
throw error;
|
|
1328
|
+
}
|
|
1329
|
+
staleBondError = error;
|
|
1330
|
+
return undefined;
|
|
1317
1331
|
}
|
|
1318
|
-
return found;
|
|
1319
1332
|
});
|
|
1320
1333
|
peripheral = byIdFirst ? yield connectById() : yield scanForPeripheral();
|
|
1321
1334
|
if (!peripheral) {
|
|
1322
1335
|
peripheral = byIdFirst ? yield scanForPeripheral() : yield connectById();
|
|
1323
1336
|
}
|
|
1337
|
+
if (!peripheral && staleBondError)
|
|
1338
|
+
throw staleBondError;
|
|
1324
1339
|
}
|
|
1325
1340
|
assertBleActive();
|
|
1326
1341
|
if (!peripheral) {
|
|
@@ -1517,7 +1532,12 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1517
1532
|
});
|
|
1518
1533
|
}
|
|
1519
1534
|
catch (error) {
|
|
1520
|
-
|
|
1535
|
+
const nativeError = error;
|
|
1536
|
+
if (nativeError.nativeErrorCode === 15 &&
|
|
1537
|
+
nativeError.nativeErrorDomain === 'CBATTErrorDomain') {
|
|
1538
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleDeviceNotBonded], { nativeErrorMessage: `Notification subscription failed: ${nativeError.message}` });
|
|
1539
|
+
}
|
|
1540
|
+
throw createNobleBleConnectionError(nativeError, 'Notification subscription failed: ');
|
|
1521
1541
|
}
|
|
1522
1542
|
finally {
|
|
1523
1543
|
if (!disposing)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAiC1F,KAAK,mBAAmB,GAAG,KAAK,GAAG;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,mBAAmB,EAAE,aAAa,SAAK,+
|
|
1
|
+
{"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAiC1F,KAAK,mBAAmB,GAAG,KAAK,GAAG;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,mBAAmB,EAAE,aAAa,SAAK,+CAgB3F;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAgCvF;AA+KD,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AA2zDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkOpE;AAOD,wBAAgB,sBAAsB,CACpC,YAAY,GAAE,CAAC,QAAQ,EAAE;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,KAAK,IAAkC,GAC/E,OAAO,CAAC,IAAI,CAAC,CAiEf"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-electron",
|
|
3
|
-
"version": "1.2.3
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"electron-log": ">=4.0.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-core": "1.2.3
|
|
29
|
-
"@onekeyfe/hd-shared": "1.2.3
|
|
30
|
-
"@onekeyfe/hd-transport": "1.2.3
|
|
28
|
+
"@onekeyfe/hd-core": "1.2.3",
|
|
29
|
+
"@onekeyfe/hd-shared": "1.2.3",
|
|
30
|
+
"@onekeyfe/hd-transport": "1.2.3",
|
|
31
31
|
"@stoprocent/noble": "2.3.16",
|
|
32
32
|
"p-retry": "^4.6.2"
|
|
33
33
|
},
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"electron": "^25.0.0",
|
|
37
37
|
"typescript": "^5.3.3"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "de8e0beb9d993464fbcac6231d7efe501d789c7d"
|
|
40
40
|
}
|
|
@@ -68,7 +68,7 @@ describe('Electron Noble BLE device discovery', () => {
|
|
|
68
68
|
})
|
|
69
69
|
)
|
|
70
70
|
).toMatchObject({
|
|
71
|
-
errorCode: HardwareErrorCode.
|
|
71
|
+
errorCode: HardwareErrorCode.BleConnectedError,
|
|
72
72
|
});
|
|
73
73
|
expect(createNobleBleConnectionError(new Error('connection failed'))).toMatchObject({
|
|
74
74
|
errorCode: HardwareErrorCode.BleConnectedError,
|
|
@@ -352,6 +352,87 @@ describe('Electron Noble BLE device discovery', () => {
|
|
|
352
352
|
}
|
|
353
353
|
);
|
|
354
354
|
|
|
355
|
+
test.each([
|
|
356
|
+
{ rediscovered: false, expectedCode: HardwareErrorCode.BleBondInvalid },
|
|
357
|
+
{ rediscovered: true, expectedCode: HardwareErrorCode.BleConnectedError },
|
|
358
|
+
])(
|
|
359
|
+
'reports a macOS stale bond only when fallback scan misses (rediscovered=$rediscovered)',
|
|
360
|
+
async ({ rediscovered, expectedCode }) => {
|
|
361
|
+
jest.useFakeTimers({ doNotFake: ['performance'] });
|
|
362
|
+
const handlers = new Map<string, IpcHandler>();
|
|
363
|
+
const stalePeripheral = createPeripheral('device', 'Pro2 A1B2');
|
|
364
|
+
const freshPeripheral = Object.assign(
|
|
365
|
+
new EventEmitter(),
|
|
366
|
+
createPeripheral('device', 'Pro2 A1B2'),
|
|
367
|
+
{
|
|
368
|
+
connect: jest.fn((callback: (error?: Error) => void) => {
|
|
369
|
+
callback(new Error('fresh connection failed'));
|
|
370
|
+
}),
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
const staleBondError = Object.assign(new Error('Peer removed pairing information'), {
|
|
374
|
+
nativeErrorCode: 14,
|
|
375
|
+
nativeErrorDomain: 'CBErrorDomain',
|
|
376
|
+
});
|
|
377
|
+
let scanStarted: (() => void) | undefined;
|
|
378
|
+
const scanning = new Promise<void>(resolve => {
|
|
379
|
+
scanStarted = resolve;
|
|
380
|
+
});
|
|
381
|
+
const noble = Object.assign(new EventEmitter(), {
|
|
382
|
+
state: 'poweredOn',
|
|
383
|
+
startScanning: jest.fn((_services, _duplicates, callback) => {
|
|
384
|
+
callback?.();
|
|
385
|
+
if (rediscovered) noble.emit('discover', freshPeripheral);
|
|
386
|
+
scanStarted?.();
|
|
387
|
+
}),
|
|
388
|
+
stopScanning: jest.fn(callback => callback?.()),
|
|
389
|
+
connectAsync: jest.fn(() => Promise.reject(staleBondError)),
|
|
390
|
+
});
|
|
391
|
+
jest.doMock('@stoprocent/noble', () => noble);
|
|
392
|
+
jest.doMock('electron', () => ({
|
|
393
|
+
ipcMain: {
|
|
394
|
+
handle: (channel: string, handler: IpcHandler) => handlers.set(channel, handler),
|
|
395
|
+
removeHandler: (channel: string) => handlers.delete(channel),
|
|
396
|
+
},
|
|
397
|
+
}));
|
|
398
|
+
jest.doMock('electron-log', () => ({
|
|
399
|
+
info: jest.fn(),
|
|
400
|
+
debug: jest.fn(),
|
|
401
|
+
error: jest.fn(),
|
|
402
|
+
}));
|
|
403
|
+
const { setupNobleBleHandlers } = await import('../noble-ble-handler');
|
|
404
|
+
setupNobleBleHandlers({ on: jest.fn(), send: jest.fn() } as unknown as WebContents);
|
|
405
|
+
const availability = handlers.get(EOneKeyBleMessageKeys.BLE_AVAILABILITY_CHECK);
|
|
406
|
+
const connect = handlers.get(EOneKeyBleMessageKeys.NOBLE_BLE_CONNECT);
|
|
407
|
+
if (!availability || !connect) throw new Error('Noble handlers were not registered');
|
|
408
|
+
await availability();
|
|
409
|
+
noble.emit('discover', stalePeripheral);
|
|
410
|
+
|
|
411
|
+
const connecting = Promise.resolve(connect(undefined, stalePeripheral.id));
|
|
412
|
+
await scanning;
|
|
413
|
+
jest.advanceTimersByTime(1500);
|
|
414
|
+
await expect(connecting).resolves.toMatchObject({
|
|
415
|
+
success: false,
|
|
416
|
+
error: { errorCode: expectedCode },
|
|
417
|
+
});
|
|
418
|
+
if (!rediscovered) {
|
|
419
|
+
const secondScan = new Promise<void>(resolve => {
|
|
420
|
+
scanStarted = resolve;
|
|
421
|
+
});
|
|
422
|
+
const retry = Promise.resolve(connect(undefined, stalePeripheral.id));
|
|
423
|
+
await secondScan;
|
|
424
|
+
jest.advanceTimersByTime(1500);
|
|
425
|
+
await expect(retry).resolves.toMatchObject({
|
|
426
|
+
success: false,
|
|
427
|
+
error: { errorCode: HardwareErrorCode.BleBondInvalid },
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
expect(noble.connectAsync).toHaveBeenCalledTimes(rediscovered ? 1 : 2);
|
|
431
|
+
expect(noble.startScanning).toHaveBeenCalledTimes(rediscovered ? 1 : 2);
|
|
432
|
+
expect(freshPeripheral.connect).toHaveBeenCalledTimes(rediscovered ? 1 : 0);
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
|
|
355
436
|
test('waits for a targeted scan to stop before connecting', async () => {
|
|
356
437
|
jest.useFakeTimers({ doNotFake: ['performance'] });
|
|
357
438
|
|
|
@@ -705,7 +786,7 @@ describe('Electron Noble BLE device discovery', () => {
|
|
|
705
786
|
type: 'NobleBleIpcError',
|
|
706
787
|
success: false,
|
|
707
788
|
error: {
|
|
708
|
-
errorCode: HardwareErrorCode.
|
|
789
|
+
errorCode: HardwareErrorCode.BleDeviceNotBonded,
|
|
709
790
|
params: {
|
|
710
791
|
nativeErrorMessage: 'Notification subscription failed: Encryption is insufficient',
|
|
711
792
|
},
|
package/src/noble-ble-handler.ts
CHANGED
|
@@ -75,8 +75,7 @@ type NobleBleNativeError = Error & {
|
|
|
75
75
|
export function createNobleBleConnectionError(error: NobleBleNativeError, messagePrefix = '') {
|
|
76
76
|
const errorMessage = error.message;
|
|
77
77
|
const isInvalidMacOsBond =
|
|
78
|
-
|
|
79
|
-
(error.nativeErrorCode === 15 && error.nativeErrorDomain === 'CBATTErrorDomain');
|
|
78
|
+
error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain';
|
|
80
79
|
if (isInvalidMacOsBond) {
|
|
81
80
|
const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
|
|
82
81
|
return ERRORS.TypedError(
|
|
@@ -1742,11 +1741,15 @@ async function tryDirectConnectById(deviceId: string): Promise<Peripheral | unde
|
|
|
1742
1741
|
logger?.info('[NobleBLE] Direct connect-by-id succeeded', { deviceId });
|
|
1743
1742
|
return peripheral;
|
|
1744
1743
|
} catch (error) {
|
|
1745
|
-
directConnectCooldownUntil.set(deviceId, Date.now() + DIRECT_CONNECT_COOLDOWN_MS);
|
|
1746
1744
|
logger?.info('[NobleBLE] Direct connect-by-id failed, falling back to scan', {
|
|
1747
1745
|
deviceId,
|
|
1748
1746
|
error: String(error),
|
|
1749
1747
|
});
|
|
1748
|
+
const nativeError = error as NobleBleNativeError;
|
|
1749
|
+
if (nativeError.nativeErrorCode === 14 && nativeError.nativeErrorDomain === 'CBErrorDomain') {
|
|
1750
|
+
throw createNobleBleConnectionError(nativeError);
|
|
1751
|
+
}
|
|
1752
|
+
directConnectCooldownUntil.set(deviceId, Date.now() + DIRECT_CONNECT_COOLDOWN_MS);
|
|
1750
1753
|
return undefined;
|
|
1751
1754
|
} finally {
|
|
1752
1755
|
clearTimeout(timer);
|
|
@@ -1809,12 +1812,21 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
|
|
|
1809
1812
|
}
|
|
1810
1813
|
};
|
|
1811
1814
|
|
|
1815
|
+
let staleBondError: Error | undefined;
|
|
1812
1816
|
const connectById = async () => {
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1817
|
+
try {
|
|
1818
|
+
const found = await tryDirectConnectById(deviceId);
|
|
1819
|
+
if (found) {
|
|
1820
|
+
discoveredDevices.set(deviceId, found);
|
|
1821
|
+
}
|
|
1822
|
+
return found;
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
if ((error as { errorCode?: number }).errorCode !== HardwareErrorCode.BleBondInvalid) {
|
|
1825
|
+
throw error;
|
|
1826
|
+
}
|
|
1827
|
+
staleBondError = error as Error;
|
|
1828
|
+
return undefined;
|
|
1816
1829
|
}
|
|
1817
|
-
return found;
|
|
1818
1830
|
};
|
|
1819
1831
|
|
|
1820
1832
|
peripheral = byIdFirst ? await connectById() : await scanForPeripheral();
|
|
@@ -1823,6 +1835,7 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
|
|
|
1823
1835
|
// silent), or not reachable by id. Try the other one before giving up.
|
|
1824
1836
|
peripheral = byIdFirst ? await scanForPeripheral() : await connectById();
|
|
1825
1837
|
}
|
|
1838
|
+
if (!peripheral && staleBondError) throw staleBondError;
|
|
1826
1839
|
}
|
|
1827
1840
|
|
|
1828
1841
|
assertBleActive();
|
|
@@ -2120,10 +2133,18 @@ async function subscribeNotifications(
|
|
|
2120
2133
|
ms: Date.now() - subscribeStartedAt,
|
|
2121
2134
|
});
|
|
2122
2135
|
} catch (error) {
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2136
|
+
const nativeError = error as NobleBleNativeError;
|
|
2137
|
+
if (
|
|
2138
|
+
nativeError.nativeErrorCode === 15 &&
|
|
2139
|
+
nativeError.nativeErrorDomain === 'CBATTErrorDomain'
|
|
2140
|
+
) {
|
|
2141
|
+
throw ERRORS.TypedError(
|
|
2142
|
+
HardwareErrorCode.BleDeviceNotBonded,
|
|
2143
|
+
HardwareErrorCodeMessage[HardwareErrorCode.BleDeviceNotBonded],
|
|
2144
|
+
{ nativeErrorMessage: `Notification subscription failed: ${nativeError.message}` }
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
throw createNobleBleConnectionError(nativeError, 'Notification subscription failed: ');
|
|
2127
2148
|
} finally {
|
|
2128
2149
|
// 🔒 CRITICAL: Always clear operation state (even on error)
|
|
2129
2150
|
if (!disposing) subscriptionOperations.set(deviceId, 'idle');
|