@onekeyfe/hd-transport-electron 1.2.0-alpha.157 → 1.2.0-alpha.158

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-c88e98df.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-7c14645d.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,6 +25,7 @@ interface NobleModule {
24
25
  interface Logger {
25
26
  info(message: string, ...args: any[]): void;
26
27
  debug(message: string, ...args: any[]): void;
28
+ warn(message: string, ...args: any[]): void;
27
29
  error(message: string, ...args: any[]): void;
28
30
  }
29
31
 
@@ -31,6 +33,7 @@ interface Logger {
31
33
  * Desktop API types for Electron preload script
32
34
  * These types define the core interface for Noble BLE communication
33
35
  */
36
+
34
37
  interface NobleBleWriteOptions {
35
38
  pacingDelayMs?: number;
36
39
  }
@@ -55,9 +58,16 @@ interface NobleBleAPI {
55
58
  id: string;
56
59
  mtu: number;
57
60
  }) => void) => () => void;
61
+ /**
62
+ * Fires whenever a BLE link drops. `reason` distinguishes a real peripheral
63
+ * drop from the main process freeing an idle link on its keep-alive timer;
64
+ * it is optional so an older host bridge that omits it still type-checks
65
+ * (absent is treated as a real drop by consumers).
66
+ */
58
67
  onDeviceDisconnected: (callback: (device: {
59
68
  id: string;
60
69
  name: string;
70
+ reason?: EBleDisconnectReason;
61
71
  }) => void) => () => void;
62
72
  checkAvailability: () => Promise<{
63
73
  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-fe6d3919.js');
5
+ var index = require('./index-2ec67577.js');
6
6
 
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-fe6d3919.js');
3
+ var index = require('./index-2ec67577.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -116,7 +116,8 @@ 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_IDLE_DISCONNECT_MS = 3 * 60000;
119
+ const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
120
+ const BLE_IDLE_DISCONNECT_MS = 20000;
120
121
  const BLE_BUSY_BACKSTOP_MS = 10 * 60000;
121
122
  const BLE_PACKET_SIZE_FALLBACK = 192;
122
123
  const BLE_PACKET_SIZE_MAXIMUM = 244;
@@ -242,6 +243,7 @@ function initializeNoble() {
242
243
  try {
243
244
  noble = require('@stoprocent/noble');
244
245
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble library loaded');
246
+ setupPersistentStateListener();
245
247
  yield new Promise((resolve, reject) => {
246
248
  if (!noble) {
247
249
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not initialized'));
@@ -251,7 +253,6 @@ function initializeNoble() {
251
253
  resolve();
252
254
  return;
253
255
  }
254
- setupPersistentStateListener();
255
256
  const timeout = setTimeout(() => {
256
257
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout'));
257
258
  }, BLUETOOTH_INIT_TIMEOUT);
@@ -336,11 +337,19 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
336
337
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
337
338
  const peripheral = connectedDevices.get(deviceId);
338
339
  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)
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))
340
348
  .then(() => {
341
349
  broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
342
350
  id: deviceId,
343
351
  name: deviceName,
352
+ reason: hdShared.EBleDisconnectReason.IdleKeepAlive,
344
353
  });
345
354
  })
346
355
  .catch(error => {
@@ -356,7 +365,7 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
356
365
  }
357
366
  function cleanupDevice(deviceId, webContents, options = {}) {
358
367
  var _a;
359
- const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', } = options;
368
+ const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', disconnectReason = hdShared.EBleDisconnectReason.DeviceDisconnected, } = options;
360
369
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting device cleanup', {
361
370
  deviceId,
362
371
  reason,
@@ -393,6 +402,7 @@ function cleanupDevice(deviceId, webContents, options = {}) {
393
402
  broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
394
403
  id: deviceId,
395
404
  name: deviceName,
405
+ reason: disconnectReason,
396
406
  });
397
407
  }
398
408
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device cleanup completed', { deviceId, reason });
@@ -407,6 +417,7 @@ function handleDeviceDisconnect(deviceId, webContents) {
407
417
  });
408
418
  cleanupDevice(deviceId, webContents, {
409
419
  cleanupConnection: true,
420
+ cleanupDiscoveredCache: true,
410
421
  sendDisconnectEvent: true,
411
422
  cancelOperations: true,
412
423
  reason: 'auto-disconnect',
@@ -1174,11 +1185,15 @@ function disconnectDevice(deviceId) {
1174
1185
  deviceDisconnectListeners.delete(deviceId);
1175
1186
  }
1176
1187
  yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
1177
- timeoutMs: BLE_CLEANUP_TIMEOUT,
1188
+ timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
1178
1189
  timeoutBehavior: 'resolve',
1179
1190
  });
1191
+ if (peripheral.state === 'connected') {
1192
+ logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
1193
+ }
1180
1194
  cleanupDevice(deviceId, undefined, {
1181
1195
  cleanupConnection: true,
1196
+ cleanupDiscoveredCache: true,
1182
1197
  sendDisconnectEvent: false,
1183
1198
  cancelOperations: true,
1184
1199
  reason: 'manual-disconnect',
@@ -1389,10 +1404,6 @@ function setupNobleBleHandlers(webContents) {
1389
1404
  yield disconnectDevice(deviceId).catch(() => undefined);
1390
1405
  }
1391
1406
  yield stopScanning().catch(() => undefined);
1392
- if (noble && persistentStateListener) {
1393
- noble.removeListener('stateChange', persistentStateListener);
1394
- persistentStateListener = null;
1395
- }
1396
1407
  cleanupNobleListeners();
1397
1408
  discoveredDevices.clear();
1398
1409
  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;AA0EhE,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AA8lDD,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":"AA+BA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAoFhE,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAqoDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
@@ -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,6 +22,7 @@ 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;
25
26
  error(message: string, ...args: any[]): void;
26
27
  }
27
28
  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,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,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-electron",
3
- "version": "1.2.0-alpha.157",
3
+ "version": "1.2.0-alpha.158",
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.157",
29
- "@onekeyfe/hd-shared": "1.2.0-alpha.157",
30
- "@onekeyfe/hd-transport": "1.2.0-alpha.157",
28
+ "@onekeyfe/hd-core": "1.2.0-alpha.158",
29
+ "@onekeyfe/hd-shared": "1.2.0-alpha.158",
30
+ "@onekeyfe/hd-transport": "1.2.0-alpha.158",
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": "1e9aaf5fbf64562504ebc22dc34fb22220ba0b0b"
39
+ "gitHead": "4eff5659b79164afa0c2d070833bec4f2a477f0b"
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,8 +90,18 @@ 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;
93
+ // A physical teardown must actually reach the OS before the device can return
94
+ // to a clean state; 250ms routinely declared success while CoreBluetooth was
95
+ // still disconnecting, leaving no trace when the teardown never completed.
96
+ const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
92
97
  // Renderer release is logical only; this timer physically frees the device.
93
- const BLE_IDLE_DISCONNECT_MS = 3 * 60_000;
98
+ // Keep it SHORT: the Classic 1S goes protocol-deaf when a link is dropped
99
+ // after sitting idle for minutes (field logs 2026-08-19: every deaf window
100
+ // followed a 3-minute-idle disconnect, while disconnects right after traffic
101
+ // have never produced one across 6.5.0's per-call teardown history). A hot
102
+ // disconnect ~20s after the last operation stays inside the proven-safe
103
+ // pattern and also shrinks the window in which phones cannot see the device.
104
+ const BLE_IDLE_DISCONNECT_MS = 20_000;
94
105
  // Ceiling while a call is in flight: no outstanding write, but not forever.
95
106
  const BLE_BUSY_BACKSTOP_MS = 10 * 60_000;
96
107
 
@@ -256,6 +267,11 @@ async function initializeNoble(): Promise<void> {
256
267
  noble = require('@stoprocent/noble') as NobleModule;
257
268
  logger?.info('[NobleBLE] Noble library loaded');
258
269
 
270
+ // Register the process-lifetime state listener before any early return:
271
+ // the poweredOn fast path below would otherwise skip it for the whole
272
+ // session, leaving poweredOff cache/state reconciliation dead.
273
+ setupPersistentStateListener();
274
+
259
275
  // Wait for Bluetooth to be ready
260
276
  await new Promise<void>((resolve, reject) => {
261
277
  if (!noble) {
@@ -268,9 +284,6 @@ async function initializeNoble(): Promise<void> {
268
284
  return;
269
285
  }
270
286
 
271
- // Setup persistent state listener before initialization
272
- setupPersistentStateListener();
273
-
274
287
  const timeout = setTimeout(() => {
275
288
  reject(
276
289
  ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout')
@@ -336,6 +349,12 @@ interface DeviceCleanupOptions {
336
349
  cleanupDiscoveredCache?: boolean;
337
350
  /** Whether to send disconnect event */
338
351
  sendDisconnectEvent?: boolean;
352
+ /**
353
+ * Why the link went down, forwarded to renderers on BLE_DEVICE_DISCONNECTED.
354
+ * Defaults to a real peripheral drop; the keep-alive path overrides it so
355
+ * consumers can tell an internal idle release from a device that left.
356
+ */
357
+ disconnectReason?: EBleDisconnectReason;
339
358
  /** Whether to cancel ongoing operations */
340
359
  cancelOperations?: boolean;
341
360
  /** Cleanup reason (for logging) */
@@ -391,12 +410,31 @@ function armIdleDisconnect(
391
410
  logger?.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
392
411
  const peripheral = connectedDevices.get(deviceId);
393
412
  const deviceName = peripheral?.advertisement?.localName || 'Unknown Device';
394
- const pending = disconnectDevice(deviceId)
413
+ // Unsubscribe CCCD before dropping the link, matching every other
414
+ // teardown path: the 1S leaves its notify session half-open when the
415
+ // link drops without an unsubscribe and then ignores application
416
+ // protocol traffic on NEW links for tens of minutes (field log
417
+ // 2026-08-19: the lone unsubscribe-skipping idle teardown caused a
418
+ // 6-attempt reconnect loop; unsubscribe-first teardowns reconnected
419
+ // instantly).
420
+ const pending = unsubscribeNotifications(deviceId)
421
+ .catch(error => {
422
+ logger?.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
423
+ deviceId,
424
+ error: error instanceof Error ? error.message : String(error),
425
+ });
426
+ })
427
+ .then(() => disconnectDevice(deviceId))
395
428
  .then(() => {
396
429
  // A call is still in flight here; it must reject, not hang.
430
+ // Both 'idle' and 'busy-backstop' report IdleKeepAlive on purpose:
431
+ // we reclaimed the link ourselves and have no evidence the device
432
+ // left, so consumers must not mark it disconnected. The in-flight
433
+ // call still fails, via the renderer dropping its device state.
397
434
  broadcastToAllWebContents(EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
398
435
  id: deviceId,
399
436
  name: deviceName,
437
+ reason: EBleDisconnectReason.IdleKeepAlive,
400
438
  });
401
439
  })
402
440
  .catch(error => {
@@ -426,6 +464,7 @@ function cleanupDevice(
426
464
  sendDisconnectEvent = false,
427
465
  cancelOperations = true,
428
466
  reason = 'unknown',
467
+ disconnectReason = EBleDisconnectReason.DeviceDisconnected,
429
468
  } = options;
430
469
 
431
470
  logger?.info('[NobleBLE] Starting device cleanup', {
@@ -475,6 +514,7 @@ function cleanupDevice(
475
514
  broadcastToAllWebContents(EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
476
515
  id: deviceId,
477
516
  name: deviceName,
517
+ reason: disconnectReason,
478
518
  });
479
519
  }
480
520
 
@@ -494,6 +534,9 @@ function handleDeviceDisconnect(deviceId: string, webContents: WebContents): voi
494
534
 
495
535
  cleanupDevice(deviceId, webContents, {
496
536
  cleanupConnection: true,
537
+ // Same stale-object hazard as manual disconnect: an externally dropped
538
+ // link invalidates the cached peripheral for the next reconnect.
539
+ cleanupDiscoveredCache: true,
497
540
  sendDisconnectEvent: true,
498
541
  cancelOperations: true,
499
542
  reason: 'auto-disconnect',
@@ -1583,11 +1626,18 @@ async function disconnectDevice(deviceId: string): Promise<void> {
1583
1626
  }
1584
1627
 
1585
1628
  await runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
1586
- timeoutMs: BLE_CLEANUP_TIMEOUT,
1629
+ timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
1587
1630
  timeoutBehavior: 'resolve',
1588
1631
  });
1632
+ if (peripheral.state === 'connected') {
1633
+ logger?.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
1634
+ }
1589
1635
  cleanupDevice(deviceId, undefined, {
1590
1636
  cleanupConnection: true,
1637
+ // macOS returns zero GATT services when a previously-disconnected
1638
+ // peripheral object is reconnected; drop the discovery cache so the next
1639
+ // connect resolves a fresh peripheral (direct connect by id, or scan).
1640
+ cleanupDiscoveredCache: true,
1591
1641
  sendDisconnectEvent: false,
1592
1642
  cancelOperations: true,
1593
1643
  reason: 'manual-disconnect',
@@ -1913,10 +1963,12 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
1913
1963
  }
1914
1964
 
1915
1965
  await stopScanning().catch(() => undefined);
1916
- if (noble && persistentStateListener) {
1917
- noble.removeListener('stateChange', persistentStateListener);
1918
- persistentStateListener = null;
1919
- }
1966
+ // persistentStateListener is process-lifetime, NOT per-window: this
1967
+ // destroy handler also fires on a renderer soft restart, and removing
1968
+ // the listener here permanently killed poweredOff/poweredOn
1969
+ // reconciliation for the rest of the process (nothing re-registers it:
1970
+ // initializeNoble early-returns once noble is loaded). The null-guard
1971
+ // in setupPersistentStateListener keeps it deduped to one instance.
1920
1972
  cleanupNobleListeners();
1921
1973
  discoveredDevices.clear();
1922
1974
  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,6 +39,7 @@ 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;
42
43
  error(message: string, ...args: any[]): void;
43
44
  }
44
45