@onekeyfe/hd-transport-electron 1.2.0-alpha.153 → 1.2.0-alpha.155

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.
@@ -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-78adadea.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-e8b87173.js'); });
36
36
  setupNobleBleHandlers(webContents);
37
37
  });
38
38
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { WebContents } from 'electron';
2
2
  import { Characteristic, Peripheral } from '@stoprocent/noble';
3
3
  import { OneKeyDeviceInfoBase } from '@onekeyfe/hd-transport';
4
+ import { EBleDisconnectReason } from '@onekeyfe/hd-shared';
4
5
 
5
6
  interface DeviceInfo extends OneKeyDeviceInfoBase {
6
7
  id: string;
@@ -24,7 +25,6 @@ interface NobleModule {
24
25
  interface Logger {
25
26
  info(message: string, ...args: any[]): void;
26
27
  debug(message: string, ...args: any[]): void;
27
- warn(message: string, ...args: any[]): void;
28
28
  error(message: string, ...args: any[]): void;
29
29
  }
30
30
 
@@ -32,6 +32,7 @@ interface Logger {
32
32
  * Desktop API types for Electron preload script
33
33
  * These types define the core interface for Noble BLE communication
34
34
  */
35
+
35
36
  interface NobleBleWriteOptions {
36
37
  pacingDelayMs?: number;
37
38
  }
@@ -56,9 +57,16 @@ interface NobleBleAPI {
56
57
  id: string;
57
58
  mtu: number;
58
59
  }) => void) => () => void;
60
+ /**
61
+ * Fires whenever a BLE link drops. `reason` distinguishes a real peripheral
62
+ * drop from the main process freeing an idle link on its keep-alive timer;
63
+ * it is optional so an older host bridge that omits it still type-checks
64
+ * (absent is treated as a real drop by consumers).
65
+ */
59
66
  onDeviceDisconnected: (callback: (device: {
60
67
  id: string;
61
68
  name: string;
69
+ reason?: EBleDisconnectReason;
62
70
  }) => void) => () => void;
63
71
  checkAvailability: () => Promise<{
64
72
  available: boolean;
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-c28cc24a.js');
5
+ var index = require('./index-b37d926c.js');
6
6
 
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-c28cc24a.js');
3
+ var index = require('./index-b37d926c.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -116,7 +116,6 @@ const DEVICE_SCAN_TIMEOUT = 5000;
116
116
  const DEVICE_CHECK_INTERVAL = 500;
117
117
  const SERVICE_DISCOVERY_TIMEOUT = 10000;
118
118
  const BLE_CLEANUP_TIMEOUT = 250;
119
- const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
120
119
  const BLE_IDLE_DISCONNECT_MS = 3 * 60000;
121
120
  const BLE_BUSY_BACKSTOP_MS = 10 * 60000;
122
121
  const BLE_PACKET_SIZE_FALLBACK = 192;
@@ -243,7 +242,6 @@ function initializeNoble() {
243
242
  try {
244
243
  noble = require('@stoprocent/noble');
245
244
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble library loaded');
246
- setupPersistentStateListener();
247
245
  yield new Promise((resolve, reject) => {
248
246
  if (!noble) {
249
247
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not initialized'));
@@ -253,6 +251,7 @@ function initializeNoble() {
253
251
  resolve();
254
252
  return;
255
253
  }
254
+ setupPersistentStateListener();
256
255
  const timeout = setTimeout(() => {
257
256
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout'));
258
257
  }, BLUETOOTH_INIT_TIMEOUT);
@@ -337,18 +336,12 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
337
336
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
338
337
  const peripheral = connectedDevices.get(deviceId);
339
338
  const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
340
- const pending = unsubscribeNotifications(deviceId)
341
- .catch(error => {
342
- logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
343
- deviceId,
344
- error: error instanceof Error ? error.message : String(error),
345
- });
346
- })
347
- .then(() => disconnectDevice(deviceId))
339
+ const pending = disconnectDevice(deviceId)
348
340
  .then(() => {
349
341
  broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
350
342
  id: deviceId,
351
343
  name: deviceName,
344
+ reason: hdShared.EBleDisconnectReason.IdleKeepAlive,
352
345
  });
353
346
  })
354
347
  .catch(error => {
@@ -364,7 +357,7 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
364
357
  }
365
358
  function cleanupDevice(deviceId, webContents, options = {}) {
366
359
  var _a;
367
- const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', } = options;
360
+ const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', disconnectReason = hdShared.EBleDisconnectReason.DeviceDisconnected, } = options;
368
361
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting device cleanup', {
369
362
  deviceId,
370
363
  reason,
@@ -401,6 +394,7 @@ function cleanupDevice(deviceId, webContents, options = {}) {
401
394
  broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
402
395
  id: deviceId,
403
396
  name: deviceName,
397
+ reason: disconnectReason,
404
398
  });
405
399
  }
406
400
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device cleanup completed', { deviceId, reason });
@@ -415,7 +409,6 @@ function handleDeviceDisconnect(deviceId, webContents) {
415
409
  });
416
410
  cleanupDevice(deviceId, webContents, {
417
411
  cleanupConnection: true,
418
- cleanupDiscoveredCache: true,
419
412
  sendDisconnectEvent: true,
420
413
  cancelOperations: true,
421
414
  reason: 'auto-disconnect',
@@ -1183,15 +1176,11 @@ function disconnectDevice(deviceId) {
1183
1176
  deviceDisconnectListeners.delete(deviceId);
1184
1177
  }
1185
1178
  yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
1186
- timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
1179
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1187
1180
  timeoutBehavior: 'resolve',
1188
1181
  });
1189
- if (peripheral.state === 'connected') {
1190
- logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
1191
- }
1192
1182
  cleanupDevice(deviceId, undefined, {
1193
1183
  cleanupConnection: true,
1194
- cleanupDiscoveredCache: true,
1195
1184
  sendDisconnectEvent: false,
1196
1185
  cancelOperations: true,
1197
1186
  reason: 'manual-disconnect',
@@ -1402,6 +1391,10 @@ function setupNobleBleHandlers(webContents) {
1402
1391
  yield disconnectDevice(deviceId).catch(() => undefined);
1403
1392
  }
1404
1393
  yield stopScanning().catch(() => undefined);
1394
+ if (noble && persistentStateListener) {
1395
+ noble.removeListener('stateChange', persistentStateListener);
1396
+ persistentStateListener = null;
1397
+ }
1405
1398
  cleanupNobleListeners();
1406
1399
  discoveredDevices.clear();
1407
1400
  safeLog(logger, 'info', 'Noble BLE cleanup completed');
@@ -1 +1 @@
1
- {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AA8EhE,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAwnDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
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,3 +1,4 @@
1
+ import type { EBleDisconnectReason } from '@onekeyfe/hd-shared';
1
2
  export interface NobleBleWriteOptions {
2
3
  pacingDelayMs?: number;
3
4
  }
@@ -25,6 +26,7 @@ export interface NobleBleAPI {
25
26
  onDeviceDisconnected: (callback: (device: {
26
27
  id: string;
27
28
  name: string;
29
+ reason?: EBleDisconnectReason;
28
30
  }) => void) => () => void;
29
31
  checkAvailability: () => Promise<{
30
32
  available: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IACzD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACxF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGzC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACvF,oBAAoB,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC/F,iBAAiB,EAAE,MAAM,OAAO,CAAC;QAC/B,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC,CAAC;CACJ;AAGD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB"}
1
+ {"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IACzD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACxF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGzC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAOvF,oBAAoB,EAAE,CACpB,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,oBAAoB,CAAA;KAAE,KAAK,IAAI,KACpF,MAAM,IAAI,CAAC;IAChB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QAC/B,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC,CAAC;CACJ;AAGD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB"}
@@ -22,7 +22,6 @@ export interface NobleModule {
22
22
  export interface Logger {
23
23
  info(message: string, ...args: any[]): void;
24
24
  debug(message: string, ...args: any[]): void;
25
- warn(message: string, ...args: any[]): void;
26
25
  error(message: string, ...args: any[]): void;
27
26
  }
28
27
  export declare function safeLog(logger: Logger | null, level: 'info' | 'debug' | 'error', message: string, ...args: any[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"noble-extended.d.ts","sourceRoot":"","sources":["../../src/types/noble-extended.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAGnE,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAGD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CACX,YAAY,EAAE,MAAM,EAAE,EACtB,eAAe,EAAE,OAAO,EACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,GACjC,IAAI,CAAC;IACR,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1C,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAClE,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IACxE,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9E,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;CACrF;AAGD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CAC9C;AAGD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,EACjC,OAAO,EAAE,MAAM,EACf,GAAG,IAAI,EAAE,GAAG,EAAE,GACb,IAAI,CAMN"}
1
+ {"version":3,"file":"noble-extended.d.ts","sourceRoot":"","sources":["../../src/types/noble-extended.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAGnE,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAGD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CACX,YAAY,EAAE,MAAM,EAAE,EACtB,eAAe,EAAE,OAAO,EACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,GACjC,IAAI,CAAC;IACR,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1C,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAClE,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IACxE,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9E,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;CACrF;AAGD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CAC9C;AAGD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,EACjC,OAAO,EAAE,MAAM,EACf,GAAG,IAAI,EAAE,GAAG,EAAE,GACb,IAAI,CAMN"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-electron",
3
- "version": "1.2.0-alpha.153",
3
+ "version": "1.2.0-alpha.155",
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.0-alpha.153",
29
- "@onekeyfe/hd-shared": "1.2.0-alpha.153",
30
- "@onekeyfe/hd-transport": "1.2.0-alpha.153",
28
+ "@onekeyfe/hd-core": "1.2.0-alpha.155",
29
+ "@onekeyfe/hd-shared": "1.2.0-alpha.155",
30
+ "@onekeyfe/hd-transport": "1.2.0-alpha.155",
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": "2544b0bde0835515911c2c1b8c8b631788557300"
39
+ "gitHead": "922cb4e29b2d89511c9b67c25840294335cb8005"
40
40
  }
@@ -6,6 +6,7 @@
6
6
  /* eslint-disable @typescript-eslint/no-var-requires, import/no-extraneous-dependencies */
7
7
 
8
8
  import {
9
+ EBleDisconnectReason,
9
10
  EOneKeyBleMessageKeys,
10
11
  ERRORS,
11
12
  HardwareErrorCode,
@@ -89,10 +90,6 @@ const DEVICE_SCAN_TIMEOUT = 5000; // 5 seconds for device scanning
89
90
  const DEVICE_CHECK_INTERVAL = 500; // 500ms interval for periodic device checks
90
91
  const SERVICE_DISCOVERY_TIMEOUT = 10000; // 10 seconds for service discovery
91
92
  const BLE_CLEANUP_TIMEOUT = 250;
92
- // A physical teardown must actually reach the OS before the device can return
93
- // to a clean state; 250ms routinely declared success while CoreBluetooth was
94
- // still disconnecting, leaving no trace when the teardown never completed.
95
- const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
96
93
  // Renderer release is logical only; this timer physically frees the device.
97
94
  const BLE_IDLE_DISCONNECT_MS = 3 * 60_000;
98
95
  // Ceiling while a call is in flight: no outstanding write, but not forever.
@@ -260,11 +257,6 @@ async function initializeNoble(): Promise<void> {
260
257
  noble = require('@stoprocent/noble') as NobleModule;
261
258
  logger?.info('[NobleBLE] Noble library loaded');
262
259
 
263
- // Register the process-lifetime state listener before any early return:
264
- // the poweredOn fast path below would otherwise skip it for the whole
265
- // session, leaving poweredOff cache/state reconciliation dead.
266
- setupPersistentStateListener();
267
-
268
260
  // Wait for Bluetooth to be ready
269
261
  await new Promise<void>((resolve, reject) => {
270
262
  if (!noble) {
@@ -277,6 +269,9 @@ async function initializeNoble(): Promise<void> {
277
269
  return;
278
270
  }
279
271
 
272
+ // Setup persistent state listener before initialization
273
+ setupPersistentStateListener();
274
+
280
275
  const timeout = setTimeout(() => {
281
276
  reject(
282
277
  ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout')
@@ -342,6 +337,12 @@ interface DeviceCleanupOptions {
342
337
  cleanupDiscoveredCache?: boolean;
343
338
  /** Whether to send disconnect event */
344
339
  sendDisconnectEvent?: boolean;
340
+ /**
341
+ * Why the link went down, forwarded to renderers on BLE_DEVICE_DISCONNECTED.
342
+ * Defaults to a real peripheral drop; the keep-alive path overrides it so
343
+ * consumers can tell an internal idle release from a device that left.
344
+ */
345
+ disconnectReason?: EBleDisconnectReason;
345
346
  /** Whether to cancel ongoing operations */
346
347
  cancelOperations?: boolean;
347
348
  /** Cleanup reason (for logging) */
@@ -397,26 +398,17 @@ function armIdleDisconnect(
397
398
  logger?.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
398
399
  const peripheral = connectedDevices.get(deviceId);
399
400
  const deviceName = peripheral?.advertisement?.localName || 'Unknown Device';
400
- // Unsubscribe CCCD before dropping the link, matching every other
401
- // teardown path: the 1S leaves its notify session half-open when the
402
- // link drops without an unsubscribe and then ignores application
403
- // protocol traffic on NEW links for tens of minutes (field log
404
- // 2026-08-19: the lone unsubscribe-skipping idle teardown caused a
405
- // 6-attempt reconnect loop; unsubscribe-first teardowns reconnected
406
- // instantly).
407
- const pending = unsubscribeNotifications(deviceId)
408
- .catch(error => {
409
- logger?.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
410
- deviceId,
411
- error: error instanceof Error ? error.message : String(error),
412
- });
413
- })
414
- .then(() => disconnectDevice(deviceId))
401
+ const pending = disconnectDevice(deviceId)
415
402
  .then(() => {
416
403
  // A call is still in flight here; it must reject, not hang.
404
+ // Both 'idle' and 'busy-backstop' report IdleKeepAlive on purpose:
405
+ // we reclaimed the link ourselves and have no evidence the device
406
+ // left, so consumers must not mark it disconnected. The in-flight
407
+ // call still fails, via the renderer dropping its device state.
417
408
  broadcastToAllWebContents(EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
418
409
  id: deviceId,
419
410
  name: deviceName,
411
+ reason: EBleDisconnectReason.IdleKeepAlive,
420
412
  });
421
413
  })
422
414
  .catch(error => {
@@ -446,6 +438,7 @@ function cleanupDevice(
446
438
  sendDisconnectEvent = false,
447
439
  cancelOperations = true,
448
440
  reason = 'unknown',
441
+ disconnectReason = EBleDisconnectReason.DeviceDisconnected,
449
442
  } = options;
450
443
 
451
444
  logger?.info('[NobleBLE] Starting device cleanup', {
@@ -495,6 +488,7 @@ function cleanupDevice(
495
488
  broadcastToAllWebContents(EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
496
489
  id: deviceId,
497
490
  name: deviceName,
491
+ reason: disconnectReason,
498
492
  });
499
493
  }
500
494
 
@@ -514,9 +508,6 @@ function handleDeviceDisconnect(deviceId: string, webContents: WebContents): voi
514
508
 
515
509
  cleanupDevice(deviceId, webContents, {
516
510
  cleanupConnection: true,
517
- // Same stale-object hazard as manual disconnect: an externally dropped
518
- // link invalidates the cached peripheral for the next reconnect.
519
- cleanupDiscoveredCache: true,
520
511
  sendDisconnectEvent: true,
521
512
  cancelOperations: true,
522
513
  reason: 'auto-disconnect',
@@ -1606,18 +1597,11 @@ async function disconnectDevice(deviceId: string): Promise<void> {
1606
1597
  }
1607
1598
 
1608
1599
  await runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
1609
- timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
1600
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1610
1601
  timeoutBehavior: 'resolve',
1611
1602
  });
1612
- if (peripheral.state === 'connected') {
1613
- logger?.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
1614
- }
1615
1603
  cleanupDevice(deviceId, undefined, {
1616
1604
  cleanupConnection: true,
1617
- // macOS returns zero GATT services when a previously-disconnected
1618
- // peripheral object is reconnected; drop the discovery cache so the next
1619
- // connect resolves a fresh peripheral (direct connect by id, or scan).
1620
- cleanupDiscoveredCache: true,
1621
1605
  sendDisconnectEvent: false,
1622
1606
  cancelOperations: true,
1623
1607
  reason: 'manual-disconnect',
@@ -1943,12 +1927,10 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
1943
1927
  }
1944
1928
 
1945
1929
  await stopScanning().catch(() => undefined);
1946
- // persistentStateListener is process-lifetime, NOT per-window: this
1947
- // destroy handler also fires on a renderer soft restart, and removing
1948
- // the listener here permanently killed poweredOff/poweredOn
1949
- // reconciliation for the rest of the process (nothing re-registers it:
1950
- // initializeNoble early-returns once noble is loaded). The null-guard
1951
- // in setupPersistentStateListener keeps it deduped to one instance.
1930
+ if (noble && persistentStateListener) {
1931
+ noble.removeListener('stateChange', persistentStateListener);
1932
+ persistentStateListener = null;
1933
+ }
1952
1934
  cleanupNobleListeners();
1953
1935
  discoveredDevices.clear();
1954
1936
  safeLog(logger, 'info', 'Noble BLE cleanup completed');
@@ -3,6 +3,8 @@
3
3
  * These types define the core interface for Noble BLE communication
4
4
  */
5
5
 
6
+ import type { EBleDisconnectReason } from '@onekeyfe/hd-shared';
7
+
6
8
  // Noble BLE API interface - core BLE functionality
7
9
  export interface NobleBleWriteOptions {
8
10
  pacingDelayMs?: number;
@@ -21,7 +23,15 @@ export interface NobleBleAPI {
21
23
  write: (uuid: string, data: string, options?: NobleBleWriteOptions) => Promise<void>;
22
24
  onNotification: (callback: (deviceId: string, data: string) => void) => () => void;
23
25
  onMtuChanged?: (callback: (device: { id: string; mtu: number }) => void) => () => void;
24
- onDeviceDisconnected: (callback: (device: { id: string; name: string }) => void) => () => void;
26
+ /**
27
+ * Fires whenever a BLE link drops. `reason` distinguishes a real peripheral
28
+ * drop from the main process freeing an idle link on its keep-alive timer;
29
+ * it is optional so an older host bridge that omits it still type-checks
30
+ * (absent is treated as a real drop by consumers).
31
+ */
32
+ onDeviceDisconnected: (
33
+ callback: (device: { id: string; name: string; reason?: EBleDisconnectReason }) => void
34
+ ) => () => void;
25
35
  checkAvailability: () => Promise<{
26
36
  available: boolean;
27
37
  state: string;
@@ -39,7 +39,6 @@ export interface NobleModule {
39
39
  export interface Logger {
40
40
  info(message: string, ...args: any[]): void;
41
41
  debug(message: string, ...args: any[]): void;
42
- warn(message: string, ...args: any[]): void;
43
42
  error(message: string, ...args: any[]): void;
44
43
  }
45
44