@onekeyfe/hd-transport-electron 1.2.2-alpha.100 → 1.2.2-alpha.101

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.
@@ -1,2 +1,4 @@
1
+ /// <reference types="node" />
2
+ export declare function resolveNobleAttMtu(reportedMtu: number | null | undefined, platform?: NodeJS.Platform): number | undefined;
1
3
  export declare function resolveBlePacketCapacity(mtu: number | null | undefined, maximumPacketCapacity: number, fallbackPacketCapacity: number): number;
2
4
  //# sourceMappingURL=ble-packet-capacity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ble-packet-capacity.d.ts","sourceRoot":"","sources":["../src/ble-packet-capacity.ts"],"names":[],"mappings":"AAEA,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,qBAAqB,EAAE,MAAM,EAC7B,sBAAsB,EAAE,MAAM,GAC7B,MAAM,CAMR"}
1
+ {"version":3,"file":"ble-packet-capacity.d.ts","sourceRoot":"","sources":["../src/ble-packet-capacity.ts"],"names":[],"mappings":";AAEA,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACtC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,GAAG,SAAS,CASpB;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,qBAAqB,EAAE,MAAM,EAC7B,sBAAsB,EAAE,MAAM,GAC7B,MAAM,CAMR"}
@@ -32,7 +32,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
32
32
 
33
33
  function initNobleBleSupport(webContents) {
34
34
  return __awaiter(this, void 0, void 0, function* () {
35
- const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-e8b87173.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-0b455c4f.js'); });
36
36
  setupNobleBleHandlers(webContents);
37
37
  });
38
38
  }
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ interface NobleModule {
25
25
  interface Logger {
26
26
  info(message: string, ...args: any[]): void;
27
27
  debug(message: string, ...args: any[]): void;
28
+ warn(message: string, ...args: any[]): void;
28
29
  error(message: string, ...args: any[]): void;
29
30
  }
30
31
 
@@ -36,6 +37,17 @@ interface Logger {
36
37
  interface NobleBleWriteOptions {
37
38
  pacingDelayMs?: number;
38
39
  }
40
+ type NobleBleIpcErrorPayload = {
41
+ name: string;
42
+ message: string;
43
+ errorCode: number;
44
+ params?: unknown;
45
+ };
46
+ type NobleBleIpcErrorResponse = {
47
+ type: 'NobleBleIpcError';
48
+ success: false;
49
+ error: NobleBleIpcErrorPayload;
50
+ };
39
51
  interface NobleBleAPI {
40
52
  enumerate: () => Promise<{
41
53
  id: string;
@@ -81,4 +93,4 @@ interface DesktopAPI {
81
93
 
82
94
  declare function initNobleBleSupport(webContents: WebContents): Promise<void>;
83
95
 
84
- export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleBleWriteOptions, NobleModule, initNobleBleSupport };
96
+ export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleBleIpcErrorPayload, NobleBleIpcErrorResponse, NobleBleWriteOptions, NobleModule, initNobleBleSupport };
@@ -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,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEzF,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,WAAW,iBAGjE"}
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;AAE7B,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,WAAW,iBAGjE"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-b37d926c.js');
5
+ var index = require('./index-d51c7da6.js');
6
6
 
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-b37d926c.js');
3
+ var index = require('./index-d51c7da6.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -9,6 +9,15 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
9
9
  var pRetry__default = /*#__PURE__*/_interopDefaultLegacy(pRetry);
10
10
 
11
11
  const BLE_ATT_HEADER_BYTES = 3;
12
+ function resolveNobleAttMtu(reportedMtu, platform = process.platform) {
13
+ if (typeof reportedMtu !== 'number' || !Number.isFinite(reportedMtu) || reportedMtu <= 0) {
14
+ return undefined;
15
+ }
16
+ const normalizedMtu = Math.floor(reportedMtu);
17
+ return platform === 'darwin' || platform === 'win32'
18
+ ? normalizedMtu + BLE_ATT_HEADER_BYTES
19
+ : normalizedMtu;
20
+ }
12
21
  function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
13
22
  if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
14
23
  return fallbackPacketCapacity;
@@ -87,10 +96,44 @@ function softRefreshSubscription(params) {
87
96
  }
88
97
 
89
98
  const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
90
- const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10000;
99
+ const NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS = 10000;
91
100
 
92
101
  let noble = null;
93
102
  let logger = null;
103
+ function createNobleBleConnectionError(error, messagePrefix = '') {
104
+ const errorMessage = error.message;
105
+ const isInvalidMacOsBond = error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain';
106
+ if (isInvalidMacOsBond) {
107
+ const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
108
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
109
+ nativeErrorMessage,
110
+ });
111
+ }
112
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
113
+ }
114
+ function createNobleBleIpcErrorResponse(error) {
115
+ const candidate = error;
116
+ const errorCode = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.errorCode) === 'number' ? candidate.errorCode : hdShared.HardwareErrorCode.UnknownError;
117
+ const message = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.message) === 'string' ? candidate.message : String(error !== null && error !== void 0 ? error : 'Unknown error');
118
+ const name = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.name) === 'string' ? candidate.name : 'Error';
119
+ let params;
120
+ if ((candidate === null || candidate === void 0 ? void 0 : candidate.params) !== undefined) {
121
+ try {
122
+ const serializedParams = JSON.stringify(candidate.params);
123
+ params = serializedParams === undefined ? undefined : JSON.parse(serializedParams);
124
+ }
125
+ catch (_a) {
126
+ params = undefined;
127
+ }
128
+ }
129
+ return {
130
+ type: 'NobleBleIpcError',
131
+ success: false,
132
+ error: Object.assign({ name,
133
+ message,
134
+ errorCode }, (params !== undefined ? { params } : {})),
135
+ };
136
+ }
94
137
  const bluetoothState = {
95
138
  available: false,
96
139
  unsupported: false,
@@ -116,7 +159,8 @@ const DEVICE_SCAN_TIMEOUT = 5000;
116
159
  const DEVICE_CHECK_INTERVAL = 500;
117
160
  const SERVICE_DISCOVERY_TIMEOUT = 10000;
118
161
  const BLE_CLEANUP_TIMEOUT = 250;
119
- const BLE_IDLE_DISCONNECT_MS = 3 * 60000;
162
+ const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
163
+ const BLE_IDLE_DISCONNECT_MS = 20000;
120
164
  const BLE_BUSY_BACKSTOP_MS = 10 * 60000;
121
165
  const BLE_PACKET_SIZE_FALLBACK = 192;
122
166
  const BLE_PACKET_SIZE_MAXIMUM = 244;
@@ -242,6 +286,7 @@ function initializeNoble() {
242
286
  try {
243
287
  noble = require('@stoprocent/noble');
244
288
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble library loaded');
289
+ setupPersistentStateListener();
245
290
  yield new Promise((resolve, reject) => {
246
291
  if (!noble) {
247
292
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not initialized'));
@@ -251,7 +296,6 @@ function initializeNoble() {
251
296
  resolve();
252
297
  return;
253
298
  }
254
- setupPersistentStateListener();
255
299
  const timeout = setTimeout(() => {
256
300
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout'));
257
301
  }, BLUETOOTH_INIT_TIMEOUT);
@@ -336,7 +380,14 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
336
380
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
337
381
  const peripheral = connectedDevices.get(deviceId);
338
382
  const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
339
- const pending = disconnectDevice(deviceId)
383
+ const pending = unsubscribeNotifications(deviceId)
384
+ .catch(error => {
385
+ logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
386
+ deviceId,
387
+ error: error instanceof Error ? error.message : String(error),
388
+ });
389
+ })
390
+ .then(() => disconnectDevice(deviceId))
340
391
  .then(() => {
341
392
  broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
342
393
  id: deviceId,
@@ -409,6 +460,7 @@ function handleDeviceDisconnect(deviceId, webContents) {
409
460
  });
410
461
  cleanupDevice(deviceId, webContents, {
411
462
  cleanupConnection: true,
463
+ cleanupDiscoveredCache: true,
412
464
  sendDisconnectEvent: true,
413
465
  cancelOperations: true,
414
466
  reason: 'auto-disconnect',
@@ -432,13 +484,14 @@ function setupMtuListener(peripheral, deviceId, webContents) {
432
484
  existing.peripheral.removeListener('mtu', existing.listener);
433
485
  }
434
486
  const listener = (mtu) => {
435
- if (!Number.isFinite(mtu) || mtu <= 0)
487
+ const normalizedMtu = resolveNobleAttMtu(mtu);
488
+ if (normalizedMtu === undefined)
436
489
  return;
437
490
  if (webContents.isDestroyed())
438
491
  return;
439
492
  webContents.send(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_MTU_CHANGED, {
440
493
  id: deviceId,
441
- mtu,
494
+ mtu: normalizedMtu,
442
495
  });
443
496
  };
444
497
  deviceMtuListeners.set(deviceId, { peripheral, listener });
@@ -545,7 +598,7 @@ function transmitHexDataToDevice(deviceId, hexData, options) {
545
598
  armIdleDisconnect(deviceId, BLE_BUSY_BACKSTOP_MS, 'busy-backstop');
546
599
  const toBuffer = Buffer.from(hexData, 'hex');
547
600
  const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
548
- const packetCapacity = resolveBlePacketCapacity(peripheral.mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
601
+ const packetCapacity = resolveBlePacketCapacity(resolveNobleAttMtu(peripheral.mtu), BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
549
602
  const pacingDelayMs = resolveNobleBleWritePacingDelay(options);
550
603
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
551
604
  const writeCharacteristic = doGetWriteCharacteristic();
@@ -590,18 +643,37 @@ function transmitHexDataToDevice(deviceId, hexData, options) {
590
643
  }
591
644
  });
592
645
  }
646
+ const BLE_COLD_CONNECT_SCAN_TIMEOUT_MS = 1500;
647
+ const SERVICE_DISCOVERY_MAX_ATTEMPTS = 2;
648
+ const bleNamesById = new Map();
649
+ function rememberBleName(deviceId, name) {
650
+ const trimmed = name === null || name === void 0 ? void 0 : name.trim();
651
+ if (trimmed) {
652
+ bleNamesById.set(deviceId, trimmed);
653
+ }
654
+ }
655
+ function resolveBleName(deviceId) {
656
+ var _a, _b, _c, _d, _e, _f;
657
+ const live = ((_c = (_b = (_a = connectedDevices.get(deviceId)) === null || _a === void 0 ? void 0 : _a.advertisement) === null || _b === void 0 ? void 0 : _b.localName) === null || _c === void 0 ? void 0 : _c.trim()) ||
658
+ ((_f = (_e = (_d = discoveredDevices.get(deviceId)) === null || _d === void 0 ? void 0 : _d.advertisement) === null || _e === void 0 ? void 0 : _e.localName) === null || _f === void 0 ? void 0 : _f.trim());
659
+ return live || bleNamesById.get(deviceId);
660
+ }
661
+ function shouldConnectByIdFirst(deviceId) {
662
+ return hdShared.isPro2FamilyBleName(resolveBleName(deviceId));
663
+ }
593
664
  function handleDeviceDiscovered(peripheral) {
594
- var _a, _b;
665
+ var _a, _b, _c;
595
666
  if (!isOneKeyPeripheral(peripheral)) {
596
667
  return;
597
668
  }
598
669
  const isNewDevice = !discoveredDevices.has(peripheral.id);
599
670
  discoveredDevices.set(peripheral.id, peripheral);
671
+ rememberBleName(peripheral.id, (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName);
600
672
  if (isNewDevice) {
601
673
  logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] OneKey BLE device discovered', {
602
674
  deviceId: peripheral.id,
603
- name: ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device',
604
- serviceUUIDs: ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [],
675
+ name: ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName) || 'Unknown Device',
676
+ serviceUUIDs: ((_c = peripheral.advertisement) === null || _c === void 0 ? void 0 : _c.serviceUuids) || [],
605
677
  });
606
678
  }
607
679
  }
@@ -627,7 +699,7 @@ function waitForNobleScanStop(nobleInstance) {
627
699
  });
628
700
  });
629
701
  }
630
- function performTargetedScan(targetDeviceId) {
702
+ function performTargetedScan(targetDeviceId, timeoutMs = NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS) {
631
703
  return index.__awaiter(this, void 0, void 0, function* () {
632
704
  if (!noble) {
633
705
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
@@ -667,7 +739,7 @@ function performTargetedScan(targetDeviceId) {
667
739
  const timeoutId = setTimeout(() => {
668
740
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId);
669
741
  finish(null).catch(reject);
670
- }, NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS);
742
+ }, timeoutMs);
671
743
  nobleInstance.on('discover', onDiscover);
672
744
  nobleInstance.startScanning([], true, (error) => {
673
745
  if (error) {
@@ -763,12 +835,14 @@ function getDevice(deviceId) {
763
835
  const peripheral = discoveredDevices.get(deviceId);
764
836
  if (peripheral) {
765
837
  const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
766
- return Object.assign({ commType: 'electron-ble', id: peripheral.id, name: deviceName, state: peripheral.state || 'disconnected' }, (typeof peripheral.mtu === 'number' ? { mtu: peripheral.mtu } : {}));
838
+ const mtu = resolveNobleAttMtu(peripheral.mtu);
839
+ return Object.assign({ commType: 'electron-ble', id: peripheral.id, name: deviceName, state: peripheral.state || 'disconnected' }, (mtu === undefined ? {} : { mtu }));
767
840
  }
768
841
  const connectedPeripheral = connectedDevices.get(deviceId);
769
842
  if (connectedPeripheral) {
770
843
  const deviceName = ((_b = connectedPeripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName) || 'Unknown Device';
771
- return Object.assign({ commType: 'electron-ble', id: connectedPeripheral.id, name: deviceName, state: connectedPeripheral.state || 'connected' }, (typeof connectedPeripheral.mtu === 'number' ? { mtu: connectedPeripheral.mtu } : {}));
844
+ const mtu = resolveNobleAttMtu(connectedPeripheral.mtu);
845
+ return Object.assign({ commType: 'electron-ble', id: connectedPeripheral.id, name: deviceName, state: connectedPeripheral.state || 'connected' }, (mtu === undefined ? {} : { mtu }));
772
846
  }
773
847
  return {
774
848
  commType: 'electron-ble',
@@ -802,7 +876,7 @@ function discoverServicesAndCharacteristics(peripheral) {
802
876
  });
803
877
  const discoveryPromise = (() => index.__awaiter(this, void 0, void 0, function* () {
804
878
  const services = yield new Promise((resolve, reject) => {
805
- peripheral.discoverServices([], (error, svc) => {
879
+ peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error, svc) => {
806
880
  if (error) {
807
881
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed:', error);
808
882
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, error.message));
@@ -813,7 +887,7 @@ function discoverServicesAndCharacteristics(peripheral) {
813
887
  });
814
888
  });
815
889
  if (!services || services.length === 0) {
816
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
890
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No OneKey services found');
817
891
  }
818
892
  logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] services discovered', {
819
893
  deviceId: peripheral.id,
@@ -884,9 +958,14 @@ function forceReconnectPeripheral(peripheral, deviceId) {
884
958
  callback();
885
959
  }), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
886
960
  }
887
- yield runBleCallbackOperation(callback => peripheral.connect(callback), {
888
- timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
889
- timeoutBehavior: 'reject',
961
+ yield new Promise((resolve, reject) => {
962
+ peripheral.connect(error => {
963
+ if (error) {
964
+ reject(createNobleBleConnectionError(error));
965
+ return;
966
+ }
967
+ resolve();
968
+ });
890
969
  });
891
970
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
892
971
  connectedDevices.set(deviceId, peripheral);
@@ -909,7 +988,7 @@ function freshScanAndDiscover(deviceId, webContents) {
909
988
  yield new Promise((resolve, reject) => {
910
989
  freshPeripheral.connect((error) => {
911
990
  if (error) {
912
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Fresh peripheral connection failed: ${error.message}`));
991
+ reject(createNobleBleConnectionError(error, 'Fresh peripheral connection failed: '));
913
992
  }
914
993
  else {
915
994
  connectedDevices.set(deviceId, freshPeripheral);
@@ -930,11 +1009,11 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
930
1009
  deviceId,
931
1010
  peripheralState: peripheral.state,
932
1011
  attempt: attemptNumber,
933
- maxRetries: 5,
1012
+ maxRetries: SERVICE_DISCOVERY_MAX_ATTEMPTS,
934
1013
  targetUUIDs: ONEKEY_SERVICE_UUIDS,
935
1014
  });
936
1015
  if (attemptNumber > 1) {
937
- logger === null || logger === void 0 ? void 0 : logger.info(`[NobleBLE] Service discovery retry attempt ${attemptNumber}/5`);
1016
+ logger === null || logger === void 0 ? void 0 : logger.info(`[NobleBLE] Service discovery retry attempt ${attemptNumber}/${SERVICE_DISCOVERY_MAX_ATTEMPTS}`);
938
1017
  }
939
1018
  if (peripheral.state !== 'connected') {
940
1019
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Device not connected: ${peripheral.state}`);
@@ -943,16 +1022,16 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
943
1022
  return yield discoverServicesAndCharacteristics(peripheral);
944
1023
  }
945
1024
  catch (error) {
946
- logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] No services found (attempt ${attemptNumber}/5)`);
947
- if (attemptNumber < 5) {
948
- logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] Will retry service discovery (attempt ${attemptNumber + 1}/5)`);
1025
+ logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] No services found (attempt ${attemptNumber}/${SERVICE_DISCOVERY_MAX_ATTEMPTS})`);
1026
+ if (attemptNumber < SERVICE_DISCOVERY_MAX_ATTEMPTS) {
1027
+ logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] Will retry service discovery (attempt ${attemptNumber + 1}/${SERVICE_DISCOVERY_MAX_ATTEMPTS})`);
949
1028
  }
950
1029
  throw error;
951
1030
  }
952
1031
  }), {
953
- retries: 4,
1032
+ retries: SERVICE_DISCOVERY_MAX_ATTEMPTS - 1,
954
1033
  factor: 1.5,
955
- minTimeout: 1000,
1034
+ minTimeout: 500,
956
1035
  maxTimeout: 3000,
957
1036
  onFailedAttempt: error => {
958
1037
  logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] Service discovery attempt ${error.attemptNumber} failed:`, {
@@ -966,23 +1045,27 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
966
1045
  }
967
1046
  function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
968
1047
  return index.__awaiter(this, void 0, void 0, function* () {
969
- setupDisconnectListener(peripheral, deviceId, webContents);
970
1048
  try {
971
- return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
1049
+ yield forceReconnectPeripheral(peripheral, deviceId);
972
1050
  }
973
- catch (initialError) {
974
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Direct service discovery failed, retrying after reconnect', {
975
- deviceId,
976
- error: initialError,
977
- });
1051
+ catch (resetError) {
1052
+ if (hdShared.isBleStaleBondHardwareError(resetError)) {
1053
+ throw resetError;
1054
+ }
1055
+ logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
978
1056
  }
979
- yield forceReconnectPeripheral(peripheral, deviceId);
980
1057
  setupDisconnectListener(peripheral, deviceId, webContents);
981
1058
  try {
982
1059
  return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
983
1060
  }
984
- catch (reconnectError) {
985
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed after reconnect, attempting fresh scan...', reconnectError);
1061
+ catch (discoveryError) {
1062
+ if (peripheral.state === 'connected') {
1063
+ logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed on a live link, dropping it before the fresh scan', discoveryError);
1064
+ yield disconnectDevice(deviceId).catch(() => undefined);
1065
+ }
1066
+ else {
1067
+ logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed, attempting fresh scan...', discoveryError);
1068
+ }
986
1069
  return freshScanAndDiscover(deviceId, webContents);
987
1070
  }
988
1071
  });
@@ -1042,7 +1125,7 @@ function tryDirectConnectById(deviceId) {
1042
1125
  });
1043
1126
  }
1044
1127
  function connectDevice(deviceId, webContents) {
1045
- var _a;
1128
+ var _a, _b, _c;
1046
1129
  return index.__awaiter(this, void 0, void 0, function* () {
1047
1130
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connect device request:', {
1048
1131
  deviceId,
@@ -1054,34 +1137,47 @@ function connectDevice(deviceId, webContents) {
1054
1137
  });
1055
1138
  let peripheral = (_a = discoveredDevices.get(deviceId)) !== null && _a !== void 0 ? _a : connectedDevices.get(deviceId);
1056
1139
  if (!peripheral) {
1057
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device not discovered, attempting targeted scan for:', deviceId);
1058
1140
  if (!noble) {
1059
1141
  yield initializeNoble();
1060
1142
  }
1061
1143
  if (!noble) {
1062
1144
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
1063
1145
  }
1064
- peripheral = yield tryDirectConnectById(deviceId);
1065
- if (peripheral) {
1066
- discoveredDevices.set(deviceId, peripheral);
1067
- }
1068
- }
1069
- if (!peripheral) {
1070
- try {
1071
- const foundPeripheral = yield performTargetedScan(deviceId);
1072
- if (!foundPeripheral) {
1073
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${deviceId} not found even after targeted scan`);
1146
+ const byIdFirst = shouldConnectByIdFirst(deviceId);
1147
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Resolving device for cold connect', {
1148
+ deviceId,
1149
+ name: (_b = resolveBleName(deviceId)) !== null && _b !== void 0 ? _b : 'unknown',
1150
+ strategy: byIdFirst ? 'connect-by-id-first' : 'scan-first',
1151
+ });
1152
+ const scanForPeripheral = () => index.__awaiter(this, void 0, void 0, function* () {
1153
+ var _d;
1154
+ try {
1155
+ return (_d = (yield performTargetedScan(deviceId, BLE_COLD_CONNECT_SCAN_TIMEOUT_MS))) !== null && _d !== void 0 ? _d : undefined;
1074
1156
  }
1075
- peripheral = foundPeripheral;
1076
- }
1077
- catch (error) {
1078
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Targeted scan failed:', error);
1079
- throw error;
1157
+ catch (scanError) {
1158
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan failed', {
1159
+ deviceId,
1160
+ error: scanError instanceof Error ? scanError.message : String(scanError),
1161
+ });
1162
+ return undefined;
1163
+ }
1164
+ });
1165
+ const connectById = () => index.__awaiter(this, void 0, void 0, function* () {
1166
+ const found = yield tryDirectConnectById(deviceId);
1167
+ if (found) {
1168
+ discoveredDevices.set(deviceId, found);
1169
+ }
1170
+ return found;
1171
+ });
1172
+ peripheral = byIdFirst ? yield connectById() : yield scanForPeripheral();
1173
+ if (!peripheral) {
1174
+ peripheral = byIdFirst ? yield scanForPeripheral() : yield connectById();
1080
1175
  }
1081
1176
  }
1082
1177
  if (!peripheral) {
1083
1178
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${deviceId} not found`);
1084
1179
  }
1180
+ rememberBleName(deviceId, (_c = peripheral.advertisement) === null || _c === void 0 ? void 0 : _c.localName);
1085
1181
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connecting to device:', deviceId);
1086
1182
  if (peripheral.state === 'connected') {
1087
1183
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device already connected, skipping connection step');
@@ -1123,27 +1219,11 @@ function connectDevice(deviceId, webContents) {
1123
1219
  return;
1124
1220
  }
1125
1221
  return new Promise((resolve, reject) => {
1126
- let connectionTimedOut = false;
1127
- const timeout = setTimeout(() => {
1128
- connectionTimedOut = true;
1129
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'Connection timeout'));
1130
- }, NOBLE_BLE_CONNECTION_TIMEOUT_MS);
1131
1222
  const connectedPeripheral = peripheral;
1132
1223
  connectedPeripheral.connect((error) => index.__awaiter(this, void 0, void 0, function* () {
1133
- clearTimeout(timeout);
1134
- if (connectionTimedOut) {
1135
- if (!error) {
1136
- try {
1137
- connectedPeripheral.disconnect(() => undefined);
1138
- }
1139
- catch (_a) {
1140
- }
1141
- }
1142
- return;
1143
- }
1144
1224
  if (error) {
1145
1225
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection failed:', error);
1146
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, error.message));
1226
+ reject(createNobleBleConnectionError(error));
1147
1227
  return;
1148
1228
  }
1149
1229
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connected to device:', deviceId);
@@ -1156,9 +1236,9 @@ function connectDevice(deviceId, webContents) {
1156
1236
  }
1157
1237
  catch (setupError) {
1158
1238
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection setup failed:', setupError);
1159
- connectedPeripheral.disconnect(() => {
1160
- reject(setupError);
1161
- });
1239
+ disconnectDevice(deviceId)
1240
+ .catch(() => undefined)
1241
+ .then(() => reject(setupError));
1162
1242
  }
1163
1243
  }));
1164
1244
  });
@@ -1176,11 +1256,15 @@ function disconnectDevice(deviceId) {
1176
1256
  deviceDisconnectListeners.delete(deviceId);
1177
1257
  }
1178
1258
  yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
1179
- timeoutMs: BLE_CLEANUP_TIMEOUT,
1259
+ timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
1180
1260
  timeoutBehavior: 'resolve',
1181
1261
  });
1262
+ if (peripheral.state === 'connected') {
1263
+ logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
1264
+ }
1182
1265
  cleanupDevice(deviceId, undefined, {
1183
1266
  cleanupConnection: true,
1267
+ cleanupDiscoveredCache: true,
1184
1268
  sendDisconnectEvent: false,
1185
1269
  cancelOperations: true,
1186
1270
  reason: 'manual-disconnect',
@@ -1259,7 +1343,7 @@ function subscribeNotifications(deviceId, callback) {
1259
1343
  timeoutBehavior: 'resolve',
1260
1344
  });
1261
1345
  yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
1262
- timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
1346
+ timeoutMs: NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS,
1263
1347
  timeoutBehavior: 'reject',
1264
1348
  });
1265
1349
  notifyCharacteristic.on('data', (data) => {
@@ -1271,9 +1355,14 @@ function subscribeNotifications(deviceId, callback) {
1271
1355
  });
1272
1356
  });
1273
1357
  }
1358
+ const subscribeStartedAt = Date.now();
1274
1359
  try {
1275
1360
  yield rebuildAppSubscription(deviceId, notifyCharacteristic);
1276
1361
  subscribedDevices.set(deviceId, true);
1362
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification subscription active', {
1363
+ deviceId,
1364
+ ms: Date.now() - subscribeStartedAt,
1365
+ });
1277
1366
  }
1278
1367
  finally {
1279
1368
  subscriptionOperations.set(deviceId, 'idle');
@@ -1286,7 +1375,14 @@ function setupNobleBleHandlers(webContents) {
1286
1375
  const { ipcMain } = require('electron');
1287
1376
  const handle = (channel, listener) => {
1288
1377
  ipcMain.removeHandler(channel);
1289
- ipcMain.handle(channel, listener);
1378
+ ipcMain.handle(channel, (...args) => index.__awaiter(this, void 0, void 0, function* () {
1379
+ try {
1380
+ return yield Promise.resolve(listener(...args));
1381
+ }
1382
+ catch (error) {
1383
+ return createNobleBleIpcErrorResponse(error);
1384
+ }
1385
+ }));
1290
1386
  };
1291
1387
  safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
1292
1388
  handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, () => index.__awaiter(this, void 0, void 0, function* () {
@@ -1391,10 +1487,6 @@ function setupNobleBleHandlers(webContents) {
1391
1487
  yield disconnectDevice(deviceId).catch(() => undefined);
1392
1488
  }
1393
1489
  yield stopScanning().catch(() => undefined);
1394
- if (noble && persistentStateListener) {
1395
- noble.removeListener('stateChange', persistentStateListener);
1396
- persistentStateListener = null;
1397
- }
1398
1490
  cleanupNobleListeners();
1399
1491
  discoveredDevices.clear();
1400
1492
  safeLog(logger, 'info', 'Noble BLE cleanup completed');
@@ -1410,5 +1502,7 @@ function setupNobleBleHandlers(webContents) {
1410
1502
  }
1411
1503
  }
1412
1504
 
1505
+ exports.createNobleBleConnectionError = createNobleBleConnectionError;
1506
+ exports.createNobleBleIpcErrorResponse = createNobleBleIpcErrorResponse;
1413
1507
  exports.resolveNobleBleWritePacingDelay = resolveNobleBleWritePacingDelay;
1414
1508
  exports.setupNobleBleHandlers = setupNobleBleHandlers;
@@ -1,5 +1,12 @@
1
1
  import type { WebContents } from 'electron';
2
- import type { NobleBleWriteOptions } from './types/desktop-api';
2
+ import type { NobleBleIpcErrorResponse, NobleBleWriteOptions } from './types/desktop-api';
3
+ type NobleBleNativeError = Error & {
4
+ nativeErrorCode?: number;
5
+ nativeErrorDomain?: string;
6
+ };
7
+ export declare function createNobleBleConnectionError(error: NobleBleNativeError, messagePrefix?: string): import("@onekeyfe/hd-shared").HardwareError;
8
+ export declare function createNobleBleIpcErrorResponse(error: unknown): NobleBleIpcErrorResponse;
3
9
  export declare function resolveNobleBleWritePacingDelay(options?: NobleBleWriteOptions): number;
4
10
  export declare function setupNobleBleHandlers(webContents: WebContents): void;
11
+ export {};
5
12
  //# sourceMappingURL=noble-ble-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AA0EhE,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AA2mDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAgMpE"}
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;AAO1F,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+ED,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAuuDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAwMpE"}
@@ -1,3 +1,3 @@
1
1
  export declare const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
2
- export declare const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10000;
2
+ export declare const NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS = 10000;
3
3
  //# sourceMappingURL=noble-ble-timeouts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"noble-ble-timeouts.d.ts","sourceRoot":"","sources":["../src/noble-ble-timeouts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kCAAkC,OAAQ,CAAC;AACxD,eAAO,MAAM,+BAA+B,QAAS,CAAC"}
1
+ {"version":3,"file":"noble-ble-timeouts.d.ts","sourceRoot":"","sources":["../src/noble-ble-timeouts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kCAAkC,OAAQ,CAAC;AACxD,eAAO,MAAM,8BAA8B,QAAS,CAAC"}
@@ -2,6 +2,17 @@ import type { EBleDisconnectReason } from '@onekeyfe/hd-shared';
2
2
  export interface NobleBleWriteOptions {
3
3
  pacingDelayMs?: number;
4
4
  }
5
+ export type NobleBleIpcErrorPayload = {
6
+ name: string;
7
+ message: string;
8
+ errorCode: number;
9
+ params?: unknown;
10
+ };
11
+ export type NobleBleIpcErrorResponse = {
12
+ type: 'NobleBleIpcError';
13
+ success: false;
14
+ error: NobleBleIpcErrorPayload;
15
+ };
5
16
  export interface NobleBleAPI {
6
17
  enumerate: () => Promise<{
7
18
  id: string;