@onekeyfe/hwk-desktop-noble-ble 1.2.8-alpha.52 → 1.2.8-alpha.54

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.
@@ -27,6 +27,7 @@ var THIRD_PARTY_BLE_SCAN_DURATION_MS = 5e3;
27
27
  var THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 1e4;
28
28
  var THIRD_PARTY_BLE_DEVICE_TTL_MS = 5e3;
29
29
  var THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 1e4;
30
+ var THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3e3;
30
31
 
31
32
  export {
32
33
  __require,
@@ -34,6 +35,7 @@ export {
34
35
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
35
36
  THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
36
37
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
37
- THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS
38
+ THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
39
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
38
40
  };
39
- //# sourceMappingURL=chunk-A5AENDC2.mjs.map
41
+ //# sourceMappingURL=chunk-ILABAQJY.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n"],"mappings":";;;;;;;;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n\n/** Time an availability check waits for a fresh noble to report its first state. */\nexport const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3_000;\n"],"mappings":";;;;;;;;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;AAG5C,IAAM,0CAA0C;","names":[]}
@@ -28,5 +28,7 @@ declare const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10000;
28
28
  declare const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5000;
29
29
  /** Time we'll wait for noble to reach `poweredOn` before failing init. */
30
30
  declare const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10000;
31
+ /** Time an availability check waits for a fresh noble to report its first state. */
32
+ declare const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3000;
31
33
 
32
- export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, type ThirdPartyBleChannel };
34
+ export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS, type ThirdPartyBleChannel };
@@ -28,5 +28,7 @@ declare const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10000;
28
28
  declare const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5000;
29
29
  /** Time we'll wait for noble to reach `poweredOn` before failing init. */
30
30
  declare const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10000;
31
+ /** Time an availability check waits for a fresh noble to report its first state. */
32
+ declare const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3000;
31
33
 
32
- export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, type ThirdPartyBleChannel };
34
+ export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS, type ThirdPartyBleChannel };
package/dist/constants.js CHANGED
@@ -24,7 +24,8 @@ __export(constants_exports, {
24
24
  THIRD_PARTY_BLE_DEVICE_TTL_MS: () => THIRD_PARTY_BLE_DEVICE_TTL_MS,
25
25
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS: () => THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
26
26
  THIRD_PARTY_BLE_SCAN_DURATION_MS: () => THIRD_PARTY_BLE_SCAN_DURATION_MS,
27
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS: () => THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
27
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS: () => THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
28
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS: () => THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
28
29
  });
29
30
  module.exports = __toCommonJS(constants_exports);
30
31
  var THIRD_PARTY_BLE_CHANNELS = {
@@ -48,12 +49,14 @@ var THIRD_PARTY_BLE_SCAN_DURATION_MS = 5e3;
48
49
  var THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 1e4;
49
50
  var THIRD_PARTY_BLE_DEVICE_TTL_MS = 5e3;
50
51
  var THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 1e4;
52
+ var THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3e3;
51
53
  // Annotate the CommonJS export names for ESM import in node:
52
54
  0 && (module.exports = {
53
55
  THIRD_PARTY_BLE_CHANNELS,
54
56
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
55
57
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
56
58
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
57
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
59
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
60
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
58
61
  });
59
62
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n\n/** Time an availability check waits for a fresh noble to report its first state. */\nexport const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3_000;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;AAG5C,IAAM,0CAA0C;","names":[]}
@@ -3,13 +3,15 @@ import {
3
3
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
4
4
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
5
5
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
6
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
7
- } from "./chunk-A5AENDC2.mjs";
6
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
7
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
8
+ } from "./chunk-ILABAQJY.mjs";
8
9
  export {
9
10
  THIRD_PARTY_BLE_CHANNELS,
10
11
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
11
12
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
12
13
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
13
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
14
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
15
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
14
16
  };
15
17
  //# sourceMappingURL=constants.mjs.map
package/dist/index.d.mts CHANGED
@@ -269,6 +269,8 @@ declare class NobleBleHandler {
269
269
  private _cleanupDevice;
270
270
  private _requireEntry;
271
271
  private _requireNoble;
272
+ /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */
273
+ private _waitForKnownState;
272
274
  private _waitForPoweredOn;
273
275
  private _log;
274
276
  }
package/dist/index.d.ts CHANGED
@@ -269,6 +269,8 @@ declare class NobleBleHandler {
269
269
  private _cleanupDevice;
270
270
  private _requireEntry;
271
271
  private _requireNoble;
272
+ /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */
273
+ private _waitForKnownState;
272
274
  private _waitForPoweredOn;
273
275
  private _log;
274
276
  }
package/dist/index.js CHANGED
@@ -114,6 +114,7 @@ var THIRD_PARTY_BLE_SCAN_DURATION_MS = 5e3;
114
114
  var THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 1e4;
115
115
  var THIRD_PARTY_BLE_DEVICE_TTL_MS = 5e3;
116
116
  var THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 1e4;
117
+ var THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3e3;
117
118
 
118
119
  // src/vendorProfiles.ts
119
120
  var normalizeBleUuid = (uuid) => uuid.replace(/-/g, "").toLowerCase();
@@ -290,6 +291,8 @@ var NobleBleHandler = class {
290
291
  return { available: false, state: "unsupported", initialized: false };
291
292
  }
292
293
  }
294
+ await this._waitForKnownState(THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS);
295
+ this._assertActive();
293
296
  const state = this._noble?.state ?? "unknown";
294
297
  return {
295
298
  available: state === "poweredOn",
@@ -901,6 +904,22 @@ var NobleBleHandler = class {
901
904
  if (!this._noble) throw new Error("Desktop BLE: noble was not initialized");
902
905
  return this._noble;
903
906
  }
907
+ /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */
908
+ async _waitForKnownState(timeoutMs) {
909
+ const noble = this._requireNoble();
910
+ if (noble.state !== "unknown") return;
911
+ await new Promise((resolve) => {
912
+ const settle = () => {
913
+ clearTimeout(timer);
914
+ noble.removeListener("stateChange", settle);
915
+ this._pendingCancellations.delete(settle);
916
+ resolve();
917
+ };
918
+ const timer = setTimeout(settle, timeoutMs);
919
+ this._pendingCancellations.add(settle);
920
+ noble.on("stateChange", settle);
921
+ });
922
+ }
904
923
  async _waitForPoweredOn(timeoutMs) {
905
924
  const noble = this._requireNoble();
906
925
  if (noble.state === "poweredOn") return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/debugLog.ts","../src/constants.ts","../src/vendorProfiles.ts","../src/NobleBleHandler.ts","../src/main.ts"],"sourcesContent":["export { NobleBleHandler } from './NobleBleHandler';\nexport type { NobleBleHandlerOptions } from './NobleBleHandler';\n\nexport { DEFAULT_THIRD_PARTY_BLE_VENDORS } from './vendorProfiles';\nexport type { ThirdPartyBleVendorProfile } from './vendorProfiles';\n\nexport { initThirdPartyBleSupport } from './main';\nexport type {\n InitThirdPartyBleSupportOptions,\n IpcMainLike,\n ThirdPartyBleSupportHandle,\n} from './main';\n\nexport {\n THIRD_PARTY_BLE_CHANNELS,\n THIRD_PARTY_BLE_SCAN_DURATION_MS,\n THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,\n THIRD_PARTY_BLE_DEVICE_TTL_MS,\n THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,\n} from './constants';\nexport type { ThirdPartyBleChannel } from './constants';\n\nexport type {\n ThirdPartyBleApi,\n ThirdPartyBleAvailability,\n ThirdPartyBleDeviceInfo,\n} from './types/desktop-api';\n\nexport type { BleDebugLogEntry, BleDebugLogLevel, BleDebugLogger } from './debugLog';\nexport { redactBleDebugLogData } from './debugLog';\n","export type BleDebugLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type BleDebugLogEntry = {\n level: BleDebugLogLevel;\n scope: string;\n event: string;\n data?: Record<string, unknown>;\n};\n\nexport type BleDebugLogger = (entry: BleDebugLogEntry) => void;\n\n/**\n * Keys whose values never belong in a log, kept narrow to real secrets and\n * key material. Raw frames (`packetHex`, `hexData`, `payload`) stay only because\n * they are Trezor THP ciphertext or Ledger APDUs; a vendor with a cleartext\n * protocol must move them into this list.\n */\nconst REDACTED_KEYS: ReadonlySet<string> = new Set([\n // Secrets the user types or the device derives.\n 'pin',\n 'passphrase',\n 'mnemonic',\n 'seed',\n 'seedPhrase',\n 'entropy',\n // Private key material. Public keys stay: device identity, address\n // derivation and fingerprint mismatches are all diagnosed from them.\n 'privateKey',\n 'privKey',\n 'secretKey',\n 'hostKey',\n 'trezorKey',\n 'host_static_key',\n // Pairing / session credentials. The THP static key pair is part of the\n // pairing credential, so it goes here rather than with the public keys.\n 'trezor_static_public_key',\n 'credential',\n 'credentials',\n 'sessionKey',\n 'token',\n 'accessToken',\n 'authToken',\n]);\n\n/** Guards against a cyclic or pathological `data` bag stalling the log call. */\nconst MAX_REDACT_DEPTH = 8;\n\n/**\n * Keep the shape of what was dropped so two logs still line up: a 32-byte\n * value that became 33 bytes is visible without the value itself.\n */\nfunction describeRedacted(value: unknown): string {\n if (typeof value === 'string') return `<redacted ${value.length} chars>`;\n if (ArrayBuffer.isView(value)) return `<redacted ${value.byteLength} bytes>`;\n if (Array.isArray(value)) return `<redacted ${value.length} items>`;\n return '<redacted>';\n}\n\nfunction redactValue(key: string, value: unknown, depth: number, seen: WeakSet<object>): unknown {\n if (REDACTED_KEYS.has(key)) return describeRedacted(value);\n if (!value || typeof value !== 'object') return value;\n // Errors carry message/stack non-enumerably; recursing would empty them.\n // Typed arrays would turn into index-keyed objects. Both are forwarded whole.\n if (value instanceof Error || ArrayBuffer.isView(value)) return value;\n if (depth >= MAX_REDACT_DEPTH) return '<redacted depth limit>';\n if (seen.has(value)) return '<circular>';\n seen.add(value);\n if (Array.isArray(value)) {\n return value.map(item => redactValue(key, item, depth + 1, seen));\n }\n const out: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value as Record<string, unknown>)) {\n out[childKey] = redactValue(childKey, childValue, depth + 1, seen);\n }\n return out;\n}\n\n/** Replace sensitive values at any depth; everything else is forwarded verbatim. */\nexport function redactBleDebugLogData(\n data?: Record<string, unknown>\n): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const seen = new WeakSet<object>();\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n out[key] = redactValue(key, value, 0, seen);\n }\n return out;\n}\n","/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n","import type { ElectronBleConnectOptions } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Main-process allowlist entry for one vendor key. The renderer only names the vendor; which\n * peripherals it may see and which GATT profile and framing it may use are decided here, so an\n * IPC caller cannot turn the service into a generic BLE proxy. UUID arguments may arrive in any\n * case, with or without dashes.\n */\nexport interface ThirdPartyBleVendorProfile {\n /** Whether an advertised service UUID belongs to this vendor. */\n isServiceUuid(uuid: string): boolean;\n /** Whether a local name belongs to this vendor; the handler passes a length-capped name. */\n isName?(name: string): boolean;\n /** Whether this GATT profile and write framing is one the vendor's firmware uses. */\n acceptsConnect(options: ElectronBleConnectOptions): boolean;\n}\n\nexport const normalizeBleUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\nconst isUuidString = (value: unknown): value is string => typeof value === 'string';\n\nconst isIntegerInRange = (value: unknown, min: number, max: number): value is number =>\n typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;\n\n// Duplicated from @onekeyfe/hwk-trezor-adapter (TREZOR_BLE_UUIDS, TREZOR_BLE_PACKET_SIZE) to keep\n// this package free of vendor dependencies; the Trezor connector tests pin the two together.\nconst TREZOR_SERVICE_UUID = normalizeBleUuid('8c000001-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_WRITE_UUID = normalizeBleUuid('8c000002-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_NOTIFY_UUID = normalizeBleUuid('8c000003-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_PACKET_SIZE = 244;\nconst TREZOR_MAX_CHUNK_DELAY_MS = 50;\nconst TREZOR_NAME_BRAND = /\\bTrezor\\b/i;\nconst TREZOR_NAME_MODEL = /\\bSafe\\s*7\\b|\\bT3W1\\b/i;\n\nconst trezorProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => isUuidString(uuid) && normalizeBleUuid(uuid) === TREZOR_SERVICE_UUID,\n isName: name => TREZOR_NAME_BRAND.test(name) && TREZOR_NAME_MODEL.test(name),\n acceptsConnect: options => {\n const { write } = options;\n return (\n isUuidString(options.serviceUuid) &&\n isUuidString(options.writeUuid) &&\n isUuidString(options.notifyUuid) &&\n normalizeBleUuid(options.serviceUuid) === TREZOR_SERVICE_UUID &&\n normalizeBleUuid(options.writeUuid) === TREZOR_WRITE_UUID &&\n normalizeBleUuid(options.notifyUuid) === TREZOR_NOTIFY_UUID &&\n typeof write === 'object' &&\n write !== null &&\n write.mode === 'padded' &&\n write.chunkSize === TREZOR_PACKET_SIZE &&\n (write.chunkDelayMs === undefined ||\n isIntegerInRange(write.chunkDelayMs, 0, TREZOR_MAX_CHUNK_DELAY_MS)) &&\n write.maxLength === undefined\n );\n },\n};\n\n// DMK's GATT family 13d63400-2c97-MMMM-000R-4c6564676572: MMMM is the model (0004 Nano X,\n// 6004 Stax, 3004 Flex, 8004/9004 Nano Gen5), R the role. Any model segment is accepted so a\n// new model works without a release here.\nconst LEDGER_UUID = /^13d634002c97([0-9a-f]{4})000([0-3])4c6564676572$/;\nconst LEDGER_ROLE_SERVICE = '0';\nconst LEDGER_ROLE_NOTIFY = '1';\nconst LEDGER_ROLE_WRITE = '2';\nconst LEDGER_ROLE_WRITE_CMD = '3';\n// The 0x08 handshake reports the frame size in one byte.\nconst LEDGER_MAX_FRAME_SIZE = 255;\n\nconst parseLedgerUuid = (uuid: unknown): { model: string; role: string } | undefined => {\n if (!isUuidString(uuid)) return undefined;\n const parsed = LEDGER_UUID.exec(normalizeBleUuid(uuid));\n return parsed ? { model: parsed[1], role: parsed[2] } : undefined;\n};\n\nconst ledgerProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => parseLedgerUuid(uuid)?.role === LEDGER_ROLE_SERVICE,\n acceptsConnect: options => {\n const service = parseLedgerUuid(options.serviceUuid);\n const notify = parseLedgerUuid(options.notifyUuid);\n const write = parseLedgerUuid(options.writeUuid);\n const framing = options.write;\n return (\n service?.role === LEDGER_ROLE_SERVICE &&\n notify?.role === LEDGER_ROLE_NOTIFY &&\n (write?.role === LEDGER_ROLE_WRITE || write?.role === LEDGER_ROLE_WRITE_CMD) &&\n notify.model === service.model &&\n write.model === service.model &&\n typeof framing === 'object' &&\n framing !== null &&\n framing.mode === 'raw' &&\n isIntegerInRange(framing.maxLength, 1, LEDGER_MAX_FRAME_SIZE) &&\n framing.chunkSize === undefined &&\n framing.chunkDelayMs === undefined\n );\n },\n};\n\n/**\n * Vendors the desktop BLE service serves by default, keyed by the vendor string the connectors\n * send. A host passing `vendors` replaces this map, so spread it to add a vendor.\n */\nexport const DEFAULT_THIRD_PARTY_BLE_VENDORS: Readonly<Record<string, ThirdPartyBleVendorProfile>> =\n Object.freeze({\n ledger: ledgerProfile,\n trezor: trezorProfile,\n });\n","import { type BleDebugLogLevel, type BleDebugLogger, redactBleDebugLogData } from './debugLog';\nimport {\n THIRD_PARTY_BLE_DEVICE_TTL_MS,\n THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,\n THIRD_PARTY_BLE_SCAN_DURATION_MS,\n THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,\n} from './constants';\nimport { DEFAULT_THIRD_PARTY_BLE_VENDORS, normalizeBleUuid } from './vendorProfiles';\n\nimport type { ThirdPartyBleVendorProfile } from './vendorProfiles';\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Subset of @stoprocent/noble we touch. We type as `any` to keep the package\n * installable without the native module — the default factory loads it on first use.\n */\nexport interface NobleLike {\n state: string;\n on(event: string, handler: (...args: any[]) => void): NobleLike;\n removeListener(event: string, handler: (...args: any[]) => void): NobleLike;\n startScanningAsync(serviceUuids: string[], allowDuplicates: boolean): Promise<void>;\n stopScanningAsync(): Promise<void>;\n stop?(): void;\n /**\n * Connect by id with no scan; both backends emit `discover` as a side effect\n * (win `BLEManager::Connect`, mac `retrievePeripheralsWithIdentifiers`).\n */\n connectAsync?(idOrAddress: string): Promise<NoblePeripheralLike | undefined>;\n cancelConnect?(idOrAddress: string): void;\n reset?(): Promise<void>;\n}\n\nexport interface NoblePeripheralLike {\n id: string;\n // Every advertisement field, so the host can look for a cross-transport\n // identity (e.g. a serial in manufacturerData) without another scan.\n advertisement: {\n localName?: string;\n serviceUuids?: string[];\n manufacturerData?: Buffer;\n serviceData?: Array<{ uuid: string; data: Buffer }>;\n txPowerLevel?: number;\n serviceSolicitationUuids?: string[];\n };\n /** Some noble builds expose the BLE MAC/address separately from `id`. */\n address?: string;\n addressType?: string;\n connectable?: boolean;\n rssi: number;\n state: string;\n connectAsync(): Promise<void>;\n cancelConnect?(): void;\n disconnectAsync(): Promise<void>;\n discoverSomeServicesAndCharacteristicsAsync(\n serviceUuids: string[],\n characteristicUuids: string[]\n ): Promise<{ characteristics: NobleCharacteristicLike[] }>;\n /** Read live RSSI from the connected peripheral. Returns dBm. */\n updateRssiAsync?(): Promise<number>;\n on(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n removeListener(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n}\n\nexport interface NobleCharacteristicLike {\n uuid: string;\n subscribeAsync(): Promise<void>;\n unsubscribeAsync(): Promise<void>;\n writeAsync(data: Buffer, withoutResponse: boolean): Promise<void>;\n on(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n removeListener(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n}\n\nexport type NobleFactory = () => NobleLike;\n\nconst DEFAULT_NOBLE_FACTORY: NobleFactory = () => {\n // Lazy require so packagers don't bundle the native module at import time.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const noble = require('@stoprocent/noble') as NobleLike;\n return noble;\n};\n\ninterface ConnectClaim {\n abandoned: boolean;\n cancelScan?: () => void;\n cancelNative?: () => void;\n}\n\ninterface DeviceEntry {\n /** Registered vendor key; results and links never mix across it. */\n vendor?: string;\n write?: ElectronBleConnectOptions['write'];\n peripheral: NoblePeripheralLike;\n writeChar?: NobleCharacteristicLike;\n notifyChar?: NobleCharacteristicLike;\n notifyHandler?: (data: Buffer, isNotification: boolean) => void;\n disconnectHandler?: () => void;\n}\n\n// Longer than any real vendor name, and bounds the work a vendor name rule does per advertisement.\nconst BLE_NAME_MATCH_MAX_LENGTH = 64;\n\n/**\n * Stands in for the scan filter we cannot use (see `scan`). Only the vendor profile decides; the\n * caller's service UUIDs can narrow the profile's set, and caller name patterns are never run.\n */\nconst matchesPeripheral = (\n p: NoblePeripheralLike,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n): boolean => {\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n advertised.some(\n uuid =>\n typeof uuid === 'string' &&\n profile.isServiceUuid(uuid) &&\n (!narrowTo || narrowTo.has(normalizeBleUuid(uuid)))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!profile.isName || typeof name !== 'string' || !name) return false;\n return profile.isName(name.slice(0, BLE_NAME_MATCH_MAX_LENGTH));\n};\n\n/** Renderer `serviceUuids` may only narrow the vendor's set; anything malformed is ignored. */\nconst requestedServiceUuids = (options?: ElectronBleScanOptions): Set<string> | undefined => {\n const requested = options?.match ? options.match.serviceUuids : options?.serviceUuids;\n if (!Array.isArray(requested)) return undefined;\n const uuids = requested.filter((uuid): uuid is string => typeof uuid === 'string');\n return uuids.length ? new Set(uuids.map(normalizeBleUuid)) : undefined;\n};\n\n/** Hex-encodes buffers so the info survives the IPC structured clone. */\nconst peripheralToInfo = (p: NoblePeripheralLike): ThirdPartyBleDeviceInfo => {\n const adv = p.advertisement ?? {};\n return {\n id: p.id,\n name: adv.localName,\n localName: adv.localName,\n rssi: p.rssi,\n isConnectable: p.connectable ?? null,\n advertisedServiceUuids: adv.serviceUuids,\n serviceSolicitationUuids: adv.serviceSolicitationUuids,\n txPowerLevel: adv.txPowerLevel,\n manufacturerDataHex: adv.manufacturerData\n ? Buffer.from(adv.manufacturerData).toString('hex')\n : undefined,\n serviceData: adv.serviceData?.map(entry => ({\n uuid: entry.uuid,\n dataHex: Buffer.from(entry.data).toString('hex'),\n })),\n address: p.address,\n addressType: p.addressType,\n state: p.state,\n };\n};\n\nconst delay = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));\n\n/** Waits for `work` or `ms`, whichever comes first; never rejects on timeout. */\nconst raceTimeout = async (\n work: Promise<unknown> | undefined,\n ms: number,\n onTimeout?: () => void\n): Promise<void> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n work,\n new Promise<void>(resolve => {\n timer = setTimeout(() => {\n onTimeout?.();\n resolve();\n }, ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n};\n\n// Radio-settle wait between stopping the scan and opening a GATT connection.\nconst BLE_CONNECT_SETTLE_MS = 300;\n// noble has no connect timeout. SMP pairing timeout (30s, the OS dialog's\n// limit) + 1s, so a first-time pairing is never cut off before the OS gives up.\nconst BLE_CONNECT_TIMEOUT_MS = 31_000;\n// Cap on a cleanup disconnectAsync, which hangs on a just-failed connect.\nconst BLE_DISCONNECT_TIMEOUT_MS = 2_000;\n\n/** Floor between noble rebuilds, so a persistent failure can't thrash. */\nconst NOBLE_RECOVER_COOLDOWN_MS = 10_000;\n\nexport interface NobleBleHandlerOptions {\n /** Override for tests; defaults to `require('@stoprocent/noble')`. */\n nobleFactory?: NobleFactory;\n /** Override the overall connect timeout (tests only; defaults to 31s). */\n connectTimeoutMs?: number;\n logger?: BleDebugLogger;\n /**\n * Vendors served over IPC, keyed by the `vendor` the renderer sends. Replaces the default map\n * (spread `DEFAULT_THIRD_PARTY_BLE_VENDORS` to add one); unknown vendors are rejected.\n */\n vendors?: Record<string, ThirdPartyBleVendorProfile>;\n}\n\n/** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */\nexport class NobleBleHandler {\n private _noble: NobleLike | undefined;\n\n private readonly _factory: NobleFactory;\n\n private readonly _connectTimeoutMs: number;\n\n private readonly _logger?: NobleBleHandlerOptions['logger'];\n\n private readonly _vendors: ReadonlyMap<string, ThirdPartyBleVendorProfile>;\n\n private readonly _discovered = new Map<string, NoblePeripheralLike>();\n\n // id -> last advertisement time (snapshot TTL).\n private readonly _lastSeen = new Map<string, number>();\n\n private readonly _connected = new Map<string, DeviceEntry>();\n\n private _discoverHandler?: (peripheral: NoblePeripheralLike) => void;\n\n private _scanning = false;\n\n private readonly _scanOwners = new Set<string | symbol | undefined>();\n\n private _scanTransition: Promise<void> = Promise.resolve();\n\n private _idleStopTimer?: ReturnType<typeof setTimeout>;\n\n private _onNotification?: (id: string, hexData: string) => void;\n\n private _onDeviceDisconnected?: (id: string) => void;\n\n private _initialized = false;\n\n private _disposed = false;\n\n private _disposePromise?: Promise<void>;\n\n private _releasePromise?: Promise<void>;\n\n private _initPromise?: Promise<void>;\n\n private readonly _nobleInstances = new Set<NobleLike>();\n\n private readonly _pendingCancellations = new Set<() => void>();\n\n private readonly _connectAttempts = new Set<{\n id: string;\n vendor: string;\n abandon: (error: Error) => void;\n cancelNative: () => void;\n settled: Promise<unknown>;\n }>();\n\n private _nativeReleased = false;\n\n private _lastNobleRecoverAt?: number;\n\n constructor(options: NobleBleHandlerOptions = {}) {\n this._factory = options.nobleFactory ?? DEFAULT_NOBLE_FACTORY;\n this._connectTimeoutMs = options.connectTimeoutMs ?? BLE_CONNECT_TIMEOUT_MS;\n this._logger = options.logger;\n // A Map, so an IPC vendor like `__proto__` cannot reach an inherited property.\n this._vendors = new Map(Object.entries(options.vendors ?? DEFAULT_THIRD_PARTY_BLE_VENDORS));\n }\n\n private _resolveVendor(vendor: unknown): {\n vendor: string;\n profile: ThirdPartyBleVendorProfile;\n } {\n const profile = typeof vendor === 'string' ? this._vendors.get(vendor) : undefined;\n if (typeof vendor !== 'string' || !profile) throw new Error('Unsupported BLE vendor');\n return { vendor, profile };\n }\n\n setNotificationListener(handler: (id: string, hexData: string) => void): void {\n this._onNotification = handler;\n }\n\n setDisconnectedListener(handler: (id: string) => void): void {\n this._onDeviceDisconnected = handler;\n }\n\n async init(): Promise<void> {\n this._assertActive();\n if (this._initialized) return;\n if (this._initPromise) return this._initPromise;\n this._initPromise = (async () => {\n this._noble ??= this._factory();\n this._nobleInstances.add(this._noble);\n this._discoverHandler ??= peripheral => {\n this._discovered.set(peripheral.id, peripheral);\n this._lastSeen.set(peripheral.id, Date.now());\n };\n this._noble.removeListener('discover', this._discoverHandler);\n this._noble.on('discover', this._discoverHandler);\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._assertActive();\n this._initialized = true;\n })();\n try {\n await this._initPromise;\n } finally {\n this._initPromise = undefined;\n }\n }\n\n async checkAvailability(): Promise<ThirdPartyBleAvailability> {\n this._assertActive();\n if (!this._noble) {\n try {\n this._noble = this._factory();\n this._nobleInstances.add(this._noble);\n } catch {\n return { available: false, state: 'unsupported', initialized: false };\n }\n }\n const state = this._noble?.state ?? 'unknown';\n return {\n available: state === 'poweredOn',\n state,\n initialized: this._initialized,\n };\n }\n\n /**\n * Lazy-start an unfiltered scan and return the current snapshot. noble-win filters per\n * packet, and a Safe 7 ADV carries only its name (UUID is in the scan response).\n */\n async scan(options?: ElectronBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]> {\n // Before any radio or owner bookkeeping: an unregistered caller gets nothing.\n const { vendor, profile } = this._resolveVendor(options?.vendor);\n // Claim before init: a stopScan/cancelPairing during the power-on wait must\n // find this owner, or the scan revives once the adapter powers on.\n this._scanOwners.add(vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(vendor);\n throw error;\n }\n if (!this._scanOwners.has(vendor)) return [];\n try {\n await this._setScanning(true);\n } catch (error) {\n this._log('warn', 'scan.start.error', { error: String(error) });\n await this._recoverNobleIfStuck(String(error));\n }\n this._assertActive();\n this._armIdleStop();\n const devices = this._snapshot(vendor, profile, requestedServiceUuids(options));\n // raw=0: nothing on air; raw>0: the match criteria dropped it. Counts only,\n // so bystanders' devices from the unfiltered scan stay out of support logs.\n if (devices.length === 0) {\n this._log('warn', 'scan.empty', {\n raw: this._discovered.size,\n kept: 0,\n named: [...this._discovered.values()].filter(p => p.advertisement?.localName).length,\n });\n }\n return devices;\n }\n\n /**\n * In-range devices matching the caller, minus TTL-expired ones. `_discovered`\n * is only a cache; a missing device can still be reached by `_directConnect`.\n */\n private _snapshot(\n vendor: string,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n ): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const result: ThirdPartyBleDeviceInfo[] = [];\n for (const [id, peripheral] of this._discovered) {\n if (now - (this._lastSeen.get(id) ?? 0) > THIRD_PARTY_BLE_DEVICE_TTL_MS) {\n this._discovered.delete(id);\n this._lastSeen.delete(id);\n continue;\n }\n if (!matchesPeripheral(peripheral, profile, narrowTo)) continue;\n result.push(peripheralToInfo(peripheral));\n }\n // A linked device stops advertising and ages out above, so merge held links\n // back in (field-verified on Safe 7: holding the link silences it).\n for (const [id, entry] of this._connected) {\n if (entry.vendor !== vendor) continue;\n if (result.some(info => info.id === id)) continue;\n result.push(peripheralToInfo(entry.peripheral));\n }\n return result;\n }\n\n /** A noble instance with its own bindings, so a stuck one can be replaced. */\n private _createFreshNoble(): NobleLike {\n const candidate = this._factory() as NobleLike & {\n withBindings?: () => NobleLike;\n };\n return typeof candidate.withBindings === 'function' ? candidate.withBindings() : candidate;\n }\n\n /**\n * Rebuild noble when Windows BLE re-enumeration latched its RadioWatcher at\n * `unsupported`; fresh bindings restart the watcher without an app restart.\n */\n private async _recoverNobleIfStuck(reason: string): Promise<void> {\n if (this._disposed) return;\n const state = this._noble?.state;\n if (state === 'poweredOn' || !this._initialized) return;\n // Never tear down bindings out from under a live link.\n if (this._connected.size > 0) {\n this._log('warn', 'noble.recover.skip', {\n reason,\n state,\n connected: this._connected.size,\n });\n return;\n }\n const now = Date.now();\n if (this._lastNobleRecoverAt && now - this._lastNobleRecoverAt < NOBLE_RECOVER_COOLDOWN_MS) {\n return;\n }\n this._lastNobleRecoverAt = now;\n this._log('warn', 'noble.recover.start', { reason, state });\n try {\n const previous = this._noble;\n if (previous && this._discoverHandler) {\n previous.removeListener('discover', this._discoverHandler);\n }\n // The default factory returns the stuck module singleton; `withBindings()`\n // mints a new instance. Injected factories are assumed to return fresh ones.\n const fresh = this._createFreshNoble();\n this._noble = fresh;\n this._nobleInstances.add(fresh);\n if (this._discoverHandler) {\n fresh.on('discover', this._discoverHandler);\n }\n this._scanning = false;\n this._discovered.clear();\n this._lastSeen.clear();\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._log('warn', 'noble.recover.done', { state: this._noble?.state });\n } catch (error) {\n // Recovery is best-effort; the caller already reported the scan failure.\n this._log('warn', 'noble.recover.error', { error: String(error) });\n }\n }\n\n private _armIdleStop(): void {\n if (this._disposed) return;\n this._clearIdleStop();\n this._idleStopTimer = setTimeout(() => {\n void this._stopContinuousScan();\n }, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS);\n }\n\n private _clearIdleStop(): void {\n if (this._idleStopTimer) {\n clearTimeout(this._idleStopTimer);\n this._idleStopTimer = undefined;\n }\n }\n\n // Keep native start/stop callbacks ordered: a late stop must finish before\n // a newer scan starts, even when callers belong to different vendors.\n private _setScanning(scanning: boolean): Promise<void> {\n const transition = this._scanTransition.then(async () => {\n if (scanning) {\n this._assertActive();\n if (this._scanOwners.size === 0) return;\n }\n if (this._scanning === scanning) return;\n this._scanning = scanning;\n try {\n if (scanning) {\n await this._requireNoble().startScanningAsync([], true);\n this._log('warn', 'scan.start', { allowDuplicates: true });\n } else {\n await this._noble?.stopScanningAsync();\n }\n } catch (error) {\n this._scanning = false;\n if (scanning) throw error;\n }\n });\n this._scanTransition = transition.catch(() => undefined);\n return transition;\n }\n\n /** Stop scanning but keep the discovered cache (used before connect). */\n private async _pauseScan(): Promise<void> {\n this._clearIdleStop();\n await this._setScanning(false);\n }\n\n /** Stop scanning and forget discovered devices (idle timeout / teardown). */\n private async _stopContinuousScan(): Promise<void> {\n this._scanOwners.clear();\n this._discovered.clear();\n this._lastSeen.clear();\n await this._pauseScan();\n }\n\n /** A vendor releasing discovery must not stop another vendor's scan. */\n async stopScan(vendor?: string): Promise<void> {\n if (vendor !== undefined) {\n this._scanOwners.delete(vendor);\n if (this._scanOwners.size > 0) return;\n }\n await this._stopContinuousScan();\n }\n\n /**\n * Look up a previously-scanned device by id (no extra BLE traffic).\n * Returns null if the device hasn't been seen by a recent scan.\n */\n getDevice(id: string): ThirdPartyBleDeviceInfo | null {\n const p = this._discovered.get(id);\n // The scan cache holds every advertiser in range; only a registered vendor's device is exposed.\n if (!p || ![...this._vendors.values()].some(profile => matchesPeripheral(p, profile))) {\n return null;\n }\n return peripheralToInfo(p);\n }\n\n /** Live RSSI (dBm) of a connected peripheral, else the cached scan-time value. */\n async readRssi(id: string): Promise<number> {\n const entry = this._requireEntry(id);\n if (entry.peripheral.updateRssiAsync) {\n return entry.peripheral.updateRssiAsync();\n }\n return entry.peripheral.rssi;\n }\n\n /** Abandon matching pairing attempts without touching another owner's links. */\n async cancelPairing(options?: { vendor: string; id?: string }): Promise<void> {\n const matches = (id: string, vendor?: string) =>\n options === undefined ||\n (vendor === options.vendor && (options.id === undefined || id === options.id));\n for (const attempt of this._connectAttempts) {\n if (!matches(attempt.id, attempt.vendor)) continue;\n attempt.abandon(new Error(`connect cancelled: ${attempt.id}`));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'cancelPairing.cancelConnect.error', {\n id: attempt.id,\n error: String(error),\n });\n }\n }\n if (options?.id === undefined) await this.stopScan(options?.vendor);\n for (const [id, entry] of this._connected) {\n if (matches(id, entry.vendor)) await this.disconnect(id).catch(() => undefined);\n }\n }\n\n /**\n * Scan until `id` is discovered, releasing only its own scan ownership. A\n * bonded Safe 7 goes silent, so callers fall back to `_directConnect`.\n */\n private async _scanUntilFound(\n id: string,\n timeoutMs: number,\n claim: ConnectClaim\n ): Promise<NoblePeripheralLike | undefined> {\n await this.init();\n if (claim.abandoned) return undefined;\n const existing = this._discovered.get(id);\n if (existing) return existing;\n const noble = this._requireNoble();\n const owner = Symbol('connect-scan');\n this._scanOwners.add(owner);\n return new Promise<NoblePeripheralLike | undefined>(resolve => {\n let done = false;\n const finish = (p?: NoblePeripheralLike) => {\n if (done) return;\n done = true;\n this._pendingCancellations.delete(cancel);\n clearTimeout(timer);\n noble.removeListener('discover', onDiscover);\n if (claim.cancelScan === cancel) claim.cancelScan = undefined;\n this._scanOwners.delete(owner);\n if (this._scanOwners.size === 0) {\n void this._pauseScan().then(() => resolve(p));\n } else {\n resolve(p);\n }\n };\n const onDiscover = (peripheral: NoblePeripheralLike) => {\n this._discovered.set(peripheral.id, peripheral);\n if (peripheral.id === id) finish(peripheral);\n };\n const timer = setTimeout(() => finish(this._discovered.get(id)), timeoutMs);\n const cancel = () => finish();\n this._pendingCancellations.add(cancel);\n claim.cancelScan = cancel;\n noble.on('discover', onDiscover);\n void this._setScanning(true).catch(() => finish());\n });\n }\n\n /**\n * Connect by id without advertisement: the only route to a linked, silent device.\n * Returns undefined instead of throwing so the caller reports \"device not found\".\n */\n private async _directConnect(id: string): Promise<NoblePeripheralLike | undefined> {\n const noble = this._requireNoble();\n if (typeof noble.connectAsync !== 'function') {\n // Distinguishes a stub noble from \"the device wasn't there\" in the log.\n this._log('warn', 'connect.direct.unavailable', { id });\n return undefined;\n }\n // warn, not info: reaching a silent bonded device this way is unproven on\n // real hardware, so it must always be logged.\n this._log('warn', 'connect.direct.start', { id });\n const startedAt = Date.now();\n try {\n // Bounded by the connect() timeout; noble-mac never resolves when it\n // cannot retrieve the peripheral.\n const peripheral = await noble.connectAsync(id);\n const resolved = peripheral ?? this._discovered.get(id);\n this._log('warn', 'connect.direct.done', {\n id,\n elapsedMs: Date.now() - startedAt,\n found: Boolean(resolved),\n // Anything but 'connected' later surfaces as a service-discovery error.\n state: resolved?.state,\n fromNoble: Boolean(peripheral),\n });\n return resolved;\n } catch (error) {\n this._log('warn', 'connect.direct.error', {\n id,\n elapsedMs: Date.now() - startedAt,\n error: String(error),\n });\n return undefined;\n }\n }\n\n // noble's disconnectAsync hangs after a failed connect (CoreBluetooth never\n // sends the disconnect event), so bound it.\n private async _safeDisconnect(peripheral: NoblePeripheralLike): Promise<void> {\n if (this._nativeReleased) return;\n await raceTimeout(\n peripheral.disconnectAsync().catch(() => undefined),\n BLE_DISCONNECT_TIMEOUT_MS\n );\n }\n\n // One timeout covers connect and service discovery; the connector maps\n // `timed out` (unreachable) and `connection failed` (stale bond) differently.\n async connect(\n id: string,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n if (\n !options ||\n !options.vendor ||\n ![options.serviceUuid, options.writeUuid, options.notifyUuid].every(\n uuid => typeof uuid === 'string' && /^[0-9a-f]{32}$/i.test(normalizeBleUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\n }\n // Before any native work: only a registered vendor's own GATT profile and framing.\n if (!this._resolveVendor(options.vendor).profile.acceptsConnect(options)) {\n throw new Error(`BLE GATT profile not allowed for ${options.vendor}`);\n }\n this._assertActive();\n // Promise.race cannot stop _connectInner; the claim makes a late success tear\n // its link down instead of committing an unowned one that silences the device.\n const claim: ConnectClaim = { abandoned: false };\n // Shared by the timeout and cancelPairing.\n let abandon!: (error: Error) => void;\n const abandoned = new Promise<never>((_, reject) => {\n abandon = (error: Error) => {\n claim.abandoned = true;\n reject(error);\n claim.cancelScan?.();\n };\n });\n const timer = setTimeout(\n () => abandon(new Error(`connect timed out after ${this._connectTimeoutMs}ms`)),\n this._connectTimeoutMs\n );\n const attempt = {\n id,\n vendor: options.vendor,\n abandon,\n cancelNative: () => claim.cancelNative?.(),\n settled: Promise.resolve<unknown>(undefined),\n };\n this._connectAttempts.add(attempt);\n const nativeOperation = this._connectInner(id, claim, options);\n const caller = (async () => {\n try {\n return await Promise.race([nativeOperation, abandoned]);\n } catch (error) {\n const peripheral = this._discovered.get(id);\n // Tear down only a link nobody owns, same guard as _connectInner.abortIfAbandoned.\n if (peripheral && !this._connected.has(id)) await this._safeDisconnect(peripheral);\n throw error;\n } finally {\n clearTimeout(timer);\n }\n })();\n // A rejected caller can still have a native connect or disconnect in flight.\n attempt.settled = Promise.allSettled([nativeOperation, caller]).finally(() => {\n this._connectAttempts.delete(attempt);\n // The connect cleared the idle stop; re-arm it so an owner left behind (a picker that\n // connected without stopScan) still expires instead of keeping the radio on.\n if (this._scanOwners.size > 0) this._armIdleStop();\n // Resume the radio only for a connect-scan (symbol) owner; a vendor's\n // idle scan owner must not restart it during pairing.\n const connectScanWaiting = [...this._scanOwners].some(owner => typeof owner === 'symbol');\n if (connectScanWaiting) {\n void this._setScanning(true).catch(() => undefined);\n }\n });\n return caller;\n }\n\n private async _connectInner(\n id: string,\n claim: ConnectClaim,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n let route: 'cache' | 'scan' | 'direct' | 'none' = 'cache';\n let peripheral: NoblePeripheralLike | undefined;\n // Stops the flow before it commits an unowned link; the rejection itself is\n // unobservable because Promise.race already settled.\n const abortIfAbandoned = async (stage: string) => {\n if (!claim.abandoned && !this._disposed) return;\n // A link still held in _connected belongs to a previous connect.\n if (peripheral && peripheral.state === 'connected' && !this._connected.has(id)) {\n await this._safeDisconnect(peripheral);\n }\n this._log('warn', 'connect.abandoned', { id, route, stage });\n throw new Error(`connect abandoned after timeout: ${id}`);\n };\n await this.init();\n await abortIfAbandoned('init');\n // Stop scanning (keep the cache) and let the radio settle before connecting.\n await this._pauseScan();\n await delay(BLE_CONNECT_SETTLE_MS);\n await abortIfAbandoned('settle');\n this._assertActive();\n peripheral = this._discovered.get(id);\n if (!peripheral) {\n route = 'scan';\n peripheral = await this._scanUntilFound(id, THIRD_PARTY_BLE_SCAN_DURATION_MS, claim);\n await abortIfAbandoned('scan');\n }\n if (!peripheral) {\n route = 'direct';\n // Deliberately after the scan: noble-mac never resolves connect-by-id for a\n // peripheral CoreBluetooth cannot retrieve (`NobleMac::Connect`).\n const native = this._requireNoble();\n claim.cancelNative = () => native.cancelConnect?.(id);\n peripheral = await this._directConnect(id);\n }\n if (!peripheral) {\n // discoveredCount=0 points at the radio/scan; counts only, see scan.empty.\n this._log('warn', 'connect.notFound', {\n id,\n route: 'none',\n discoveredCount: this._discovered.size,\n });\n throw new Error(`BLE device not found: ${id}`);\n }\n\n await abortIfAbandoned('resolve');\n\n const wasConnected = peripheral.state === 'connected';\n const connectingPeripheral = peripheral;\n const native = this._requireNoble();\n claim.cancelNative = () => {\n if (connectingPeripheral.state === 'connecting' && connectingPeripheral.cancelConnect) {\n connectingPeripheral.cancelConnect();\n } else {\n native.cancelConnect?.(id);\n }\n };\n this._log('warn', 'connect.route', {\n id,\n route,\n wasConnected,\n name: peripheral.advertisement?.localName,\n });\n if (!wasConnected) {\n await peripheral.connectAsync();\n await abortIfAbandoned('link');\n }\n\n try {\n const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(\n [options.serviceUuid],\n [options.writeUuid, options.notifyUuid]\n );\n const writeUuid = normalizeBleUuid(options.writeUuid);\n const notifyUuid = normalizeBleUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeBleUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeBleUuid(c.uuid) === notifyUuid);\n if (!writeChar || !notifyChar) {\n throw new Error(`BLE characteristics not found on device ${id}`);\n }\n // Last gate before commit: service discovery can also outlast the timeout.\n await abortIfAbandoned('discovery');\n\n const disconnectHandler = () => {\n // One peripheral outlives many links; a stale handler must not report\n // the current link as unexpectedly dropped.\n if (this._connected.get(id)?.disconnectHandler !== disconnectHandler) return;\n this._cleanupDevice(id, /* unexpected */ true);\n };\n peripheral.on('disconnect', disconnectHandler);\n\n this._connected.set(id, {\n peripheral,\n writeChar,\n notifyChar,\n disconnectHandler,\n vendor: options.vendor,\n write: options.write,\n });\n this._log('info', 'connect.done', { id, name: peripheral.advertisement.localName });\n return { id, name: peripheral.advertisement.localName };\n } catch (error) {\n // Don't leak a GATT link this call opened.\n if (!wasConnected) await this._safeDisconnect(peripheral);\n throw error;\n }\n }\n\n async disconnect(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry) return;\n if (entry.disconnectHandler) {\n // Suppress the unexpected-disconnect event for explicit disconnects.\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n try {\n await entry.peripheral.disconnectAsync();\n } catch (error) {\n this._log('warn', 'disconnect.error', { id, error: String(error) });\n }\n this._cleanupDevice(id, /* unexpected */ false, entry);\n }\n\n async subscribe(id: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.notifyChar) throw new Error(`BLE notify characteristic missing for ${id}`);\n if (entry.notifyHandler) return;\n const handler = (data: Buffer) => {\n this._onNotification?.(id, data.toString('hex'));\n };\n entry.notifyHandler = handler;\n entry.notifyChar.on('data', handler);\n await entry.notifyChar.subscribeAsync();\n }\n\n async unsubscribe(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry?.notifyChar) return;\n if (entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n entry.notifyHandler = undefined;\n }\n try {\n await entry.notifyChar.unsubscribeAsync();\n } catch (error) {\n this._log('warn', 'unsubscribe.error', { id, error: String(error) });\n }\n }\n\n async write(id: string, hexData: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.writeChar) throw new Error(`BLE write characteristic missing for ${id}`);\n const buffer = Buffer.from(hexData, 'hex');\n const framing = entry.write;\n if (!framing) throw new Error(`No BLE write framing recorded for ${id}`);\n\n if (framing.mode === 'raw') {\n // Self-framed, so padding corrupts it. Ledger negotiates the size per link\n // (0x08 handshake) in one byte, so it never exceeds 255.\n const maxLength = framing.maxLength ?? buffer.length;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hexData) || buffer.length > maxLength) {\n throw new Error(`Invalid BLE frame for ${entry.vendor ?? 'device'}`);\n }\n await entry.writeChar.writeAsync(buffer, false);\n return;\n }\n\n const { chunkSize } = framing;\n if (!chunkSize) throw new Error(`Padded BLE writes need a chunkSize for ${id}`);\n for (let offset = 0; offset < buffer.length; offset += chunkSize) {\n this._assertActive();\n const slice = buffer.subarray(offset, offset + chunkSize);\n // Firmware drops a short final packet (then RetriesExceeded), so every chunk\n // is zero-filled to full size, as in trezor-suite transport-bluetooth.\n const chunk = Buffer.alloc(chunkSize);\n slice.copy(chunk);\n // OneKey uses writeWithResponse for stability; mirror that.\n await entry.writeChar.writeAsync(chunk, false);\n if (offset + chunkSize < buffer.length && framing.chunkDelayMs) {\n await delay(framing.chunkDelayMs);\n }\n }\n }\n\n /** Retire a renderer's handler without stopping a process-wide native manager. */\n dispose(): Promise<void> {\n if (this._disposePromise) return this._disposePromise;\n this._disposed = true;\n this._clearIdleStop();\n this._scanning = false;\n this._onNotification = undefined;\n this._onDeviceDisconnected = undefined;\n const connections = Array.from(this._connectAttempts);\n for (const attempt of connections) {\n attempt.abandon(new Error('Desktop BLE is shutting down'));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'dispose.cancelConnect.error', { error: String(error) });\n }\n }\n for (const cancel of this._pendingCancellations) cancel();\n if (this._noble && this._discoverHandler) {\n this._noble.removeListener('discover', this._discoverHandler);\n }\n const entries = Array.from(this._connected.entries());\n for (const [id] of entries) this._cleanupDevice(id, false);\n this._disposePromise = (async () => {\n try {\n await raceTimeout(\n Promise.allSettled([\n ...connections.map(attempt => attempt.settled),\n ...Array.from(this._nobleInstances, async instance => instance.stopScanningAsync()),\n ...entries.map(async ([, entry]) => {\n try {\n await raceTimeout(\n entry.notifyChar?.unsubscribeAsync().catch(() => undefined),\n 250\n );\n } finally {\n await this._safeDisconnect(entry.peripheral);\n }\n }),\n ]),\n 3500,\n () => this._log('warn', 'dispose.timeout')\n );\n } finally {\n this._discovered.clear();\n this._lastSeen.clear();\n this._initialized = false;\n }\n })();\n return this._disposePromise;\n }\n\n /**\n * Terminal native release, including recovered instances. A host sharing noble\n * must defer stop() until every transport disposed, and dedupe the instances.\n */\n disposeForAppQuit(\n releaseNoble: (instance: { stop?(): void }) => void = instance => instance.stop?.()\n ): Promise<void> {\n if (!this._releasePromise) {\n this._releasePromise = this.dispose().finally(() => {\n this._nativeReleased = true;\n let releaseError: Error | undefined;\n for (const instance of this._nobleInstances) {\n try {\n releaseNoble(instance);\n } catch (error) {\n releaseError = error instanceof Error ? error : new Error(String(error));\n }\n }\n this._nobleInstances.clear();\n if (releaseError) throw releaseError;\n this._log('info', 'dispose.native.done');\n });\n }\n return this._releasePromise;\n }\n\n private _assertActive(): void {\n if (this._disposed) throw new Error('Desktop BLE is shutting down');\n }\n\n private _cleanupDevice(id: string, unexpected: boolean, expected?: DeviceEntry): void {\n const entry = this._connected.get(id);\n if (!entry) return;\n // A reconnect may have replaced the entry while the caller awaited.\n // A stale caller must not tear down the link it no longer owns.\n if (expected && entry !== expected) return;\n if (entry.notifyChar && entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n }\n // Otherwise a later explicit disconnect is reported as unexpected.\n if (entry.disconnectHandler) {\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n this._connected.delete(id);\n if (unexpected) {\n this._log('warn', 'disconnect.unexpected', { id });\n this._onDeviceDisconnected?.(id);\n }\n }\n\n private _requireEntry(id: string): DeviceEntry {\n this._assertActive();\n const entry = this._connected.get(id);\n if (!entry) throw new Error(`BLE device is not connected: ${id}`);\n return entry;\n }\n\n private _requireNoble(): NobleLike {\n this._assertActive();\n if (!this._noble) throw new Error('Desktop BLE: noble was not initialized');\n return this._noble;\n }\n\n private async _waitForPoweredOn(timeoutMs: number): Promise<void> {\n const noble = this._requireNoble();\n if (noble.state === 'poweredOn') return;\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n noble.removeListener('stateChange', handler);\n this._pendingCancellations.delete(cancel);\n };\n const cancel = () => {\n cleanup();\n reject(new Error('Desktop BLE is shutting down'));\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(\n new Error(\n `Desktop BLE: noble did not reach poweredOn within ${timeoutMs}ms (last state: ${noble.state})`\n )\n );\n }, timeoutMs);\n const handler = (state: string) => {\n if (state === 'poweredOn') {\n cleanup();\n resolve();\n } else if (state === 'unsupported' || state === 'unauthorized') {\n cleanup();\n reject(new Error(`Desktop BLE: noble state ${state}`));\n }\n };\n this._pendingCancellations.add(cancel);\n noble.on('stateChange', handler);\n });\n }\n\n private _log(level: BleDebugLogLevel, event: string, data?: Record<string, unknown>): void {\n this._logger?.({\n level,\n scope: 'desktop-noble-ble',\n event,\n data: redactBleDebugLogData(data),\n });\n }\n}\n","import { THIRD_PARTY_BLE_CHANNELS } from './constants';\nimport { NobleBleHandler } from './NobleBleHandler';\n\nimport type { NobleBleHandlerOptions } from './NobleBleHandler';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/** Minimal slice of Electron's `WebContents` we use (kept duck-typed so we\n * don't take a hard dep on `electron`). */\nexport interface WebContentsLike {\n send(channel: string, ...args: unknown[]): void;\n on?(event: string, listener: (...args: any[]) => void): void;\n}\n\n/** Minimal slice of Electron's `ipcMain` we use. */\nexport interface IpcMainLike {\n handle(\n channel: string,\n listener: (event: unknown, ...args: any[]) => Promise<unknown> | unknown\n ): void;\n removeHandler(channel: string): void;\n}\n\nexport interface InitThirdPartyBleSupportOptions extends NobleBleHandlerOptions {\n /** Inject your own `ipcMain` (defaults to `require('electron').ipcMain`). */\n ipcMain?: IpcMainLike;\n}\n\nexport interface ThirdPartyBleSupportHandle {\n handler: NobleBleHandler;\n dispose(): Promise<void>;\n disposeForAppQuit(releaseNoble?: (instance: { stop?(): void }) => void): Promise<void>;\n}\n\nconst DEFAULT_IPC_MAIN: () => IpcMainLike = () => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { ipcMain } = require('electron') as { ipcMain: IpcMainLike };\n return ipcMain;\n};\n\n/**\n * Wire a `NobleBleHandler` to Electron IPC once `BrowserWindow` is ready. Call dispose()\n * when retiring a renderer and disposeForAppQuit() before Node teardown.\n */\nexport function initThirdPartyBleSupport(\n webContents: WebContentsLike,\n options: InitThirdPartyBleSupportOptions = {}\n): ThirdPartyBleSupportHandle {\n const ipcMain = options.ipcMain ?? DEFAULT_IPC_MAIN();\n const handler = new NobleBleHandler(options);\n let disposed = false;\n\n handler.setNotificationListener((id, hexData) => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.notification, id, hexData);\n });\n handler.setDisconnectedListener(id => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.disconnected, id);\n });\n\n const handle = <T>(channel: string, fn: (...args: any[]) => Promise<T> | T): void => {\n ipcMain.handle(channel, async (_event, ...args) => {\n if (disposed) throw new Error('Third-party BLE is shutting down');\n return fn(...args);\n });\n };\n\n handle(THIRD_PARTY_BLE_CHANNELS.scan, (options?: ElectronBleScanOptions) =>\n handler.scan(options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.stopScan, (vendor?: string) => handler.stopScan(vendor));\n handle(THIRD_PARTY_BLE_CHANNELS.connect, (id: string, options: ElectronBleConnectOptions) =>\n handler.connect(id, options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.disconnect, (id: string) => handler.disconnect(id));\n handle(THIRD_PARTY_BLE_CHANNELS.write, (id: string, hexData: string) =>\n handler.write(id, hexData)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.subscribe, (id: string) => handler.subscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.unsubscribe, (id: string) => handler.unsubscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.availability, () => handler.checkAvailability());\n handle(THIRD_PARTY_BLE_CHANNELS.getDevice, (id: string) => handler.getDevice(id));\n handle(THIRD_PARTY_BLE_CHANNELS.readRssi, (id: string) => handler.readRssi(id));\n handle(THIRD_PARTY_BLE_CHANNELS.cancelPairing, (options?: { vendor: string; id?: string }) =>\n handler.cancelPairing(options)\n );\n\n const removeHandlers = () => {\n if (disposed) return;\n disposed = true;\n for (const channel of Object.values(THIRD_PARTY_BLE_CHANNELS)) {\n ipcMain.removeHandler(channel);\n }\n };\n\n return {\n handler,\n dispose: () => {\n removeHandlers();\n return handler.dispose();\n },\n disposeForAppQuit: releaseNoble => {\n removeHandlers();\n return handler.disposeForAppQuit(releaseNoble);\n },\n };\n}\n"],"mappings":"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","names":["native","options"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/debugLog.ts","../src/constants.ts","../src/vendorProfiles.ts","../src/NobleBleHandler.ts","../src/main.ts"],"sourcesContent":["export { NobleBleHandler } from './NobleBleHandler';\nexport type { NobleBleHandlerOptions } from './NobleBleHandler';\n\nexport { DEFAULT_THIRD_PARTY_BLE_VENDORS } from './vendorProfiles';\nexport type { ThirdPartyBleVendorProfile } from './vendorProfiles';\n\nexport { initThirdPartyBleSupport } from './main';\nexport type {\n InitThirdPartyBleSupportOptions,\n IpcMainLike,\n ThirdPartyBleSupportHandle,\n} from './main';\n\nexport {\n THIRD_PARTY_BLE_CHANNELS,\n THIRD_PARTY_BLE_SCAN_DURATION_MS,\n THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,\n THIRD_PARTY_BLE_DEVICE_TTL_MS,\n THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,\n} from './constants';\nexport type { ThirdPartyBleChannel } from './constants';\n\nexport type {\n ThirdPartyBleApi,\n ThirdPartyBleAvailability,\n ThirdPartyBleDeviceInfo,\n} from './types/desktop-api';\n\nexport type { BleDebugLogEntry, BleDebugLogLevel, BleDebugLogger } from './debugLog';\nexport { redactBleDebugLogData } from './debugLog';\n","export type BleDebugLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type BleDebugLogEntry = {\n level: BleDebugLogLevel;\n scope: string;\n event: string;\n data?: Record<string, unknown>;\n};\n\nexport type BleDebugLogger = (entry: BleDebugLogEntry) => void;\n\n/**\n * Keys whose values never belong in a log, kept narrow to real secrets and\n * key material. Raw frames (`packetHex`, `hexData`, `payload`) stay only because\n * they are Trezor THP ciphertext or Ledger APDUs; a vendor with a cleartext\n * protocol must move them into this list.\n */\nconst REDACTED_KEYS: ReadonlySet<string> = new Set([\n // Secrets the user types or the device derives.\n 'pin',\n 'passphrase',\n 'mnemonic',\n 'seed',\n 'seedPhrase',\n 'entropy',\n // Private key material. Public keys stay: device identity, address\n // derivation and fingerprint mismatches are all diagnosed from them.\n 'privateKey',\n 'privKey',\n 'secretKey',\n 'hostKey',\n 'trezorKey',\n 'host_static_key',\n // Pairing / session credentials. The THP static key pair is part of the\n // pairing credential, so it goes here rather than with the public keys.\n 'trezor_static_public_key',\n 'credential',\n 'credentials',\n 'sessionKey',\n 'token',\n 'accessToken',\n 'authToken',\n]);\n\n/** Guards against a cyclic or pathological `data` bag stalling the log call. */\nconst MAX_REDACT_DEPTH = 8;\n\n/**\n * Keep the shape of what was dropped so two logs still line up: a 32-byte\n * value that became 33 bytes is visible without the value itself.\n */\nfunction describeRedacted(value: unknown): string {\n if (typeof value === 'string') return `<redacted ${value.length} chars>`;\n if (ArrayBuffer.isView(value)) return `<redacted ${value.byteLength} bytes>`;\n if (Array.isArray(value)) return `<redacted ${value.length} items>`;\n return '<redacted>';\n}\n\nfunction redactValue(key: string, value: unknown, depth: number, seen: WeakSet<object>): unknown {\n if (REDACTED_KEYS.has(key)) return describeRedacted(value);\n if (!value || typeof value !== 'object') return value;\n // Errors carry message/stack non-enumerably; recursing would empty them.\n // Typed arrays would turn into index-keyed objects. Both are forwarded whole.\n if (value instanceof Error || ArrayBuffer.isView(value)) return value;\n if (depth >= MAX_REDACT_DEPTH) return '<redacted depth limit>';\n if (seen.has(value)) return '<circular>';\n seen.add(value);\n if (Array.isArray(value)) {\n return value.map(item => redactValue(key, item, depth + 1, seen));\n }\n const out: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value as Record<string, unknown>)) {\n out[childKey] = redactValue(childKey, childValue, depth + 1, seen);\n }\n return out;\n}\n\n/** Replace sensitive values at any depth; everything else is forwarded verbatim. */\nexport function redactBleDebugLogData(\n data?: Record<string, unknown>\n): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const seen = new WeakSet<object>();\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n out[key] = redactValue(key, value, 0, seen);\n }\n return out;\n}\n","/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n\n/** Time an availability check waits for a fresh noble to report its first state. */\nexport const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3_000;\n","import type { ElectronBleConnectOptions } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Main-process allowlist entry for one vendor key. The renderer only names the vendor; which\n * peripherals it may see and which GATT profile and framing it may use are decided here, so an\n * IPC caller cannot turn the service into a generic BLE proxy. UUID arguments may arrive in any\n * case, with or without dashes.\n */\nexport interface ThirdPartyBleVendorProfile {\n /** Whether an advertised service UUID belongs to this vendor. */\n isServiceUuid(uuid: string): boolean;\n /** Whether a local name belongs to this vendor; the handler passes a length-capped name. */\n isName?(name: string): boolean;\n /** Whether this GATT profile and write framing is one the vendor's firmware uses. */\n acceptsConnect(options: ElectronBleConnectOptions): boolean;\n}\n\nexport const normalizeBleUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\nconst isUuidString = (value: unknown): value is string => typeof value === 'string';\n\nconst isIntegerInRange = (value: unknown, min: number, max: number): value is number =>\n typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;\n\n// Duplicated from @onekeyfe/hwk-trezor-adapter (TREZOR_BLE_UUIDS, TREZOR_BLE_PACKET_SIZE) to keep\n// this package free of vendor dependencies; the Trezor connector tests pin the two together.\nconst TREZOR_SERVICE_UUID = normalizeBleUuid('8c000001-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_WRITE_UUID = normalizeBleUuid('8c000002-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_NOTIFY_UUID = normalizeBleUuid('8c000003-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_PACKET_SIZE = 244;\nconst TREZOR_MAX_CHUNK_DELAY_MS = 50;\nconst TREZOR_NAME_BRAND = /\\bTrezor\\b/i;\nconst TREZOR_NAME_MODEL = /\\bSafe\\s*7\\b|\\bT3W1\\b/i;\n\nconst trezorProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => isUuidString(uuid) && normalizeBleUuid(uuid) === TREZOR_SERVICE_UUID,\n isName: name => TREZOR_NAME_BRAND.test(name) && TREZOR_NAME_MODEL.test(name),\n acceptsConnect: options => {\n const { write } = options;\n return (\n isUuidString(options.serviceUuid) &&\n isUuidString(options.writeUuid) &&\n isUuidString(options.notifyUuid) &&\n normalizeBleUuid(options.serviceUuid) === TREZOR_SERVICE_UUID &&\n normalizeBleUuid(options.writeUuid) === TREZOR_WRITE_UUID &&\n normalizeBleUuid(options.notifyUuid) === TREZOR_NOTIFY_UUID &&\n typeof write === 'object' &&\n write !== null &&\n write.mode === 'padded' &&\n write.chunkSize === TREZOR_PACKET_SIZE &&\n (write.chunkDelayMs === undefined ||\n isIntegerInRange(write.chunkDelayMs, 0, TREZOR_MAX_CHUNK_DELAY_MS)) &&\n write.maxLength === undefined\n );\n },\n};\n\n// DMK's GATT family 13d63400-2c97-MMMM-000R-4c6564676572: MMMM is the model (0004 Nano X,\n// 6004 Stax, 3004 Flex, 8004/9004 Nano Gen5), R the role. Any model segment is accepted so a\n// new model works without a release here.\nconst LEDGER_UUID = /^13d634002c97([0-9a-f]{4})000([0-3])4c6564676572$/;\nconst LEDGER_ROLE_SERVICE = '0';\nconst LEDGER_ROLE_NOTIFY = '1';\nconst LEDGER_ROLE_WRITE = '2';\nconst LEDGER_ROLE_WRITE_CMD = '3';\n// The 0x08 handshake reports the frame size in one byte.\nconst LEDGER_MAX_FRAME_SIZE = 255;\n\nconst parseLedgerUuid = (uuid: unknown): { model: string; role: string } | undefined => {\n if (!isUuidString(uuid)) return undefined;\n const parsed = LEDGER_UUID.exec(normalizeBleUuid(uuid));\n return parsed ? { model: parsed[1], role: parsed[2] } : undefined;\n};\n\nconst ledgerProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => parseLedgerUuid(uuid)?.role === LEDGER_ROLE_SERVICE,\n acceptsConnect: options => {\n const service = parseLedgerUuid(options.serviceUuid);\n const notify = parseLedgerUuid(options.notifyUuid);\n const write = parseLedgerUuid(options.writeUuid);\n const framing = options.write;\n return (\n service?.role === LEDGER_ROLE_SERVICE &&\n notify?.role === LEDGER_ROLE_NOTIFY &&\n (write?.role === LEDGER_ROLE_WRITE || write?.role === LEDGER_ROLE_WRITE_CMD) &&\n notify.model === service.model &&\n write.model === service.model &&\n typeof framing === 'object' &&\n framing !== null &&\n framing.mode === 'raw' &&\n isIntegerInRange(framing.maxLength, 1, LEDGER_MAX_FRAME_SIZE) &&\n framing.chunkSize === undefined &&\n framing.chunkDelayMs === undefined\n );\n },\n};\n\n/**\n * Vendors the desktop BLE service serves by default, keyed by the vendor string the connectors\n * send. A host passing `vendors` replaces this map, so spread it to add a vendor.\n */\nexport const DEFAULT_THIRD_PARTY_BLE_VENDORS: Readonly<Record<string, ThirdPartyBleVendorProfile>> =\n Object.freeze({\n ledger: ledgerProfile,\n trezor: trezorProfile,\n });\n","import { type BleDebugLogLevel, type BleDebugLogger, redactBleDebugLogData } from './debugLog';\nimport {\n THIRD_PARTY_BLE_DEVICE_TTL_MS,\n THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,\n THIRD_PARTY_BLE_SCAN_DURATION_MS,\n THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,\n THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS,\n} from './constants';\nimport { DEFAULT_THIRD_PARTY_BLE_VENDORS, normalizeBleUuid } from './vendorProfiles';\n\nimport type { ThirdPartyBleVendorProfile } from './vendorProfiles';\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Subset of @stoprocent/noble we touch. We type as `any` to keep the package\n * installable without the native module — the default factory loads it on first use.\n */\nexport interface NobleLike {\n state: string;\n on(event: string, handler: (...args: any[]) => void): NobleLike;\n removeListener(event: string, handler: (...args: any[]) => void): NobleLike;\n startScanningAsync(serviceUuids: string[], allowDuplicates: boolean): Promise<void>;\n stopScanningAsync(): Promise<void>;\n stop?(): void;\n /**\n * Connect by id with no scan; both backends emit `discover` as a side effect\n * (win `BLEManager::Connect`, mac `retrievePeripheralsWithIdentifiers`).\n */\n connectAsync?(idOrAddress: string): Promise<NoblePeripheralLike | undefined>;\n cancelConnect?(idOrAddress: string): void;\n reset?(): Promise<void>;\n}\n\nexport interface NoblePeripheralLike {\n id: string;\n // Every advertisement field, so the host can look for a cross-transport\n // identity (e.g. a serial in manufacturerData) without another scan.\n advertisement: {\n localName?: string;\n serviceUuids?: string[];\n manufacturerData?: Buffer;\n serviceData?: Array<{ uuid: string; data: Buffer }>;\n txPowerLevel?: number;\n serviceSolicitationUuids?: string[];\n };\n /** Some noble builds expose the BLE MAC/address separately from `id`. */\n address?: string;\n addressType?: string;\n connectable?: boolean;\n rssi: number;\n state: string;\n connectAsync(): Promise<void>;\n cancelConnect?(): void;\n disconnectAsync(): Promise<void>;\n discoverSomeServicesAndCharacteristicsAsync(\n serviceUuids: string[],\n characteristicUuids: string[]\n ): Promise<{ characteristics: NobleCharacteristicLike[] }>;\n /** Read live RSSI from the connected peripheral. Returns dBm. */\n updateRssiAsync?(): Promise<number>;\n on(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n removeListener(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n}\n\nexport interface NobleCharacteristicLike {\n uuid: string;\n subscribeAsync(): Promise<void>;\n unsubscribeAsync(): Promise<void>;\n writeAsync(data: Buffer, withoutResponse: boolean): Promise<void>;\n on(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n removeListener(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n}\n\nexport type NobleFactory = () => NobleLike;\n\nconst DEFAULT_NOBLE_FACTORY: NobleFactory = () => {\n // Lazy require so packagers don't bundle the native module at import time.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const noble = require('@stoprocent/noble') as NobleLike;\n return noble;\n};\n\ninterface ConnectClaim {\n abandoned: boolean;\n cancelScan?: () => void;\n cancelNative?: () => void;\n}\n\ninterface DeviceEntry {\n /** Registered vendor key; results and links never mix across it. */\n vendor?: string;\n write?: ElectronBleConnectOptions['write'];\n peripheral: NoblePeripheralLike;\n writeChar?: NobleCharacteristicLike;\n notifyChar?: NobleCharacteristicLike;\n notifyHandler?: (data: Buffer, isNotification: boolean) => void;\n disconnectHandler?: () => void;\n}\n\n// Longer than any real vendor name, and bounds the work a vendor name rule does per advertisement.\nconst BLE_NAME_MATCH_MAX_LENGTH = 64;\n\n/**\n * Stands in for the scan filter we cannot use (see `scan`). Only the vendor profile decides; the\n * caller's service UUIDs can narrow the profile's set, and caller name patterns are never run.\n */\nconst matchesPeripheral = (\n p: NoblePeripheralLike,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n): boolean => {\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n advertised.some(\n uuid =>\n typeof uuid === 'string' &&\n profile.isServiceUuid(uuid) &&\n (!narrowTo || narrowTo.has(normalizeBleUuid(uuid)))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!profile.isName || typeof name !== 'string' || !name) return false;\n return profile.isName(name.slice(0, BLE_NAME_MATCH_MAX_LENGTH));\n};\n\n/** Renderer `serviceUuids` may only narrow the vendor's set; anything malformed is ignored. */\nconst requestedServiceUuids = (options?: ElectronBleScanOptions): Set<string> | undefined => {\n const requested = options?.match ? options.match.serviceUuids : options?.serviceUuids;\n if (!Array.isArray(requested)) return undefined;\n const uuids = requested.filter((uuid): uuid is string => typeof uuid === 'string');\n return uuids.length ? new Set(uuids.map(normalizeBleUuid)) : undefined;\n};\n\n/** Hex-encodes buffers so the info survives the IPC structured clone. */\nconst peripheralToInfo = (p: NoblePeripheralLike): ThirdPartyBleDeviceInfo => {\n const adv = p.advertisement ?? {};\n return {\n id: p.id,\n name: adv.localName,\n localName: adv.localName,\n rssi: p.rssi,\n isConnectable: p.connectable ?? null,\n advertisedServiceUuids: adv.serviceUuids,\n serviceSolicitationUuids: adv.serviceSolicitationUuids,\n txPowerLevel: adv.txPowerLevel,\n manufacturerDataHex: adv.manufacturerData\n ? Buffer.from(adv.manufacturerData).toString('hex')\n : undefined,\n serviceData: adv.serviceData?.map(entry => ({\n uuid: entry.uuid,\n dataHex: Buffer.from(entry.data).toString('hex'),\n })),\n address: p.address,\n addressType: p.addressType,\n state: p.state,\n };\n};\n\nconst delay = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));\n\n/** Waits for `work` or `ms`, whichever comes first; never rejects on timeout. */\nconst raceTimeout = async (\n work: Promise<unknown> | undefined,\n ms: number,\n onTimeout?: () => void\n): Promise<void> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n work,\n new Promise<void>(resolve => {\n timer = setTimeout(() => {\n onTimeout?.();\n resolve();\n }, ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n};\n\n// Radio-settle wait between stopping the scan and opening a GATT connection.\nconst BLE_CONNECT_SETTLE_MS = 300;\n// noble has no connect timeout. SMP pairing timeout (30s, the OS dialog's\n// limit) + 1s, so a first-time pairing is never cut off before the OS gives up.\nconst BLE_CONNECT_TIMEOUT_MS = 31_000;\n// Cap on a cleanup disconnectAsync, which hangs on a just-failed connect.\nconst BLE_DISCONNECT_TIMEOUT_MS = 2_000;\n\n/** Floor between noble rebuilds, so a persistent failure can't thrash. */\nconst NOBLE_RECOVER_COOLDOWN_MS = 10_000;\n\nexport interface NobleBleHandlerOptions {\n /** Override for tests; defaults to `require('@stoprocent/noble')`. */\n nobleFactory?: NobleFactory;\n /** Override the overall connect timeout (tests only; defaults to 31s). */\n connectTimeoutMs?: number;\n logger?: BleDebugLogger;\n /**\n * Vendors served over IPC, keyed by the `vendor` the renderer sends. Replaces the default map\n * (spread `DEFAULT_THIRD_PARTY_BLE_VENDORS` to add one); unknown vendors are rejected.\n */\n vendors?: Record<string, ThirdPartyBleVendorProfile>;\n}\n\n/** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */\nexport class NobleBleHandler {\n private _noble: NobleLike | undefined;\n\n private readonly _factory: NobleFactory;\n\n private readonly _connectTimeoutMs: number;\n\n private readonly _logger?: NobleBleHandlerOptions['logger'];\n\n private readonly _vendors: ReadonlyMap<string, ThirdPartyBleVendorProfile>;\n\n private readonly _discovered = new Map<string, NoblePeripheralLike>();\n\n // id -> last advertisement time (snapshot TTL).\n private readonly _lastSeen = new Map<string, number>();\n\n private readonly _connected = new Map<string, DeviceEntry>();\n\n private _discoverHandler?: (peripheral: NoblePeripheralLike) => void;\n\n private _scanning = false;\n\n private readonly _scanOwners = new Set<string | symbol | undefined>();\n\n private _scanTransition: Promise<void> = Promise.resolve();\n\n private _idleStopTimer?: ReturnType<typeof setTimeout>;\n\n private _onNotification?: (id: string, hexData: string) => void;\n\n private _onDeviceDisconnected?: (id: string) => void;\n\n private _initialized = false;\n\n private _disposed = false;\n\n private _disposePromise?: Promise<void>;\n\n private _releasePromise?: Promise<void>;\n\n private _initPromise?: Promise<void>;\n\n private readonly _nobleInstances = new Set<NobleLike>();\n\n private readonly _pendingCancellations = new Set<() => void>();\n\n private readonly _connectAttempts = new Set<{\n id: string;\n vendor: string;\n abandon: (error: Error) => void;\n cancelNative: () => void;\n settled: Promise<unknown>;\n }>();\n\n private _nativeReleased = false;\n\n private _lastNobleRecoverAt?: number;\n\n constructor(options: NobleBleHandlerOptions = {}) {\n this._factory = options.nobleFactory ?? DEFAULT_NOBLE_FACTORY;\n this._connectTimeoutMs = options.connectTimeoutMs ?? BLE_CONNECT_TIMEOUT_MS;\n this._logger = options.logger;\n // A Map, so an IPC vendor like `__proto__` cannot reach an inherited property.\n this._vendors = new Map(Object.entries(options.vendors ?? DEFAULT_THIRD_PARTY_BLE_VENDORS));\n }\n\n private _resolveVendor(vendor: unknown): {\n vendor: string;\n profile: ThirdPartyBleVendorProfile;\n } {\n const profile = typeof vendor === 'string' ? this._vendors.get(vendor) : undefined;\n if (typeof vendor !== 'string' || !profile) throw new Error('Unsupported BLE vendor');\n return { vendor, profile };\n }\n\n setNotificationListener(handler: (id: string, hexData: string) => void): void {\n this._onNotification = handler;\n }\n\n setDisconnectedListener(handler: (id: string) => void): void {\n this._onDeviceDisconnected = handler;\n }\n\n async init(): Promise<void> {\n this._assertActive();\n if (this._initialized) return;\n if (this._initPromise) return this._initPromise;\n this._initPromise = (async () => {\n this._noble ??= this._factory();\n this._nobleInstances.add(this._noble);\n this._discoverHandler ??= peripheral => {\n this._discovered.set(peripheral.id, peripheral);\n this._lastSeen.set(peripheral.id, Date.now());\n };\n this._noble.removeListener('discover', this._discoverHandler);\n this._noble.on('discover', this._discoverHandler);\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._assertActive();\n this._initialized = true;\n })();\n try {\n await this._initPromise;\n } finally {\n this._initPromise = undefined;\n }\n }\n\n async checkAvailability(): Promise<ThirdPartyBleAvailability> {\n this._assertActive();\n if (!this._noble) {\n try {\n this._noble = this._factory();\n this._nobleInstances.add(this._noble);\n } catch {\n return { available: false, state: 'unsupported', initialized: false };\n }\n }\n await this._waitForKnownState(THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS);\n this._assertActive();\n const state = this._noble?.state ?? 'unknown';\n return {\n available: state === 'poweredOn',\n state,\n initialized: this._initialized,\n };\n }\n\n /**\n * Lazy-start an unfiltered scan and return the current snapshot. noble-win filters per\n * packet, and a Safe 7 ADV carries only its name (UUID is in the scan response).\n */\n async scan(options?: ElectronBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]> {\n // Before any radio or owner bookkeeping: an unregistered caller gets nothing.\n const { vendor, profile } = this._resolveVendor(options?.vendor);\n // Claim before init: a stopScan/cancelPairing during the power-on wait must\n // find this owner, or the scan revives once the adapter powers on.\n this._scanOwners.add(vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(vendor);\n throw error;\n }\n if (!this._scanOwners.has(vendor)) return [];\n try {\n await this._setScanning(true);\n } catch (error) {\n this._log('warn', 'scan.start.error', { error: String(error) });\n await this._recoverNobleIfStuck(String(error));\n }\n this._assertActive();\n this._armIdleStop();\n const devices = this._snapshot(vendor, profile, requestedServiceUuids(options));\n // raw=0: nothing on air; raw>0: the match criteria dropped it. Counts only,\n // so bystanders' devices from the unfiltered scan stay out of support logs.\n if (devices.length === 0) {\n this._log('warn', 'scan.empty', {\n raw: this._discovered.size,\n kept: 0,\n named: [...this._discovered.values()].filter(p => p.advertisement?.localName).length,\n });\n }\n return devices;\n }\n\n /**\n * In-range devices matching the caller, minus TTL-expired ones. `_discovered`\n * is only a cache; a missing device can still be reached by `_directConnect`.\n */\n private _snapshot(\n vendor: string,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n ): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const result: ThirdPartyBleDeviceInfo[] = [];\n for (const [id, peripheral] of this._discovered) {\n if (now - (this._lastSeen.get(id) ?? 0) > THIRD_PARTY_BLE_DEVICE_TTL_MS) {\n this._discovered.delete(id);\n this._lastSeen.delete(id);\n continue;\n }\n if (!matchesPeripheral(peripheral, profile, narrowTo)) continue;\n result.push(peripheralToInfo(peripheral));\n }\n // A linked device stops advertising and ages out above, so merge held links\n // back in (field-verified on Safe 7: holding the link silences it).\n for (const [id, entry] of this._connected) {\n if (entry.vendor !== vendor) continue;\n if (result.some(info => info.id === id)) continue;\n result.push(peripheralToInfo(entry.peripheral));\n }\n return result;\n }\n\n /** A noble instance with its own bindings, so a stuck one can be replaced. */\n private _createFreshNoble(): NobleLike {\n const candidate = this._factory() as NobleLike & {\n withBindings?: () => NobleLike;\n };\n return typeof candidate.withBindings === 'function' ? candidate.withBindings() : candidate;\n }\n\n /**\n * Rebuild noble when Windows BLE re-enumeration latched its RadioWatcher at\n * `unsupported`; fresh bindings restart the watcher without an app restart.\n */\n private async _recoverNobleIfStuck(reason: string): Promise<void> {\n if (this._disposed) return;\n const state = this._noble?.state;\n if (state === 'poweredOn' || !this._initialized) return;\n // Never tear down bindings out from under a live link.\n if (this._connected.size > 0) {\n this._log('warn', 'noble.recover.skip', {\n reason,\n state,\n connected: this._connected.size,\n });\n return;\n }\n const now = Date.now();\n if (this._lastNobleRecoverAt && now - this._lastNobleRecoverAt < NOBLE_RECOVER_COOLDOWN_MS) {\n return;\n }\n this._lastNobleRecoverAt = now;\n this._log('warn', 'noble.recover.start', { reason, state });\n try {\n const previous = this._noble;\n if (previous && this._discoverHandler) {\n previous.removeListener('discover', this._discoverHandler);\n }\n // The default factory returns the stuck module singleton; `withBindings()`\n // mints a new instance. Injected factories are assumed to return fresh ones.\n const fresh = this._createFreshNoble();\n this._noble = fresh;\n this._nobleInstances.add(fresh);\n if (this._discoverHandler) {\n fresh.on('discover', this._discoverHandler);\n }\n this._scanning = false;\n this._discovered.clear();\n this._lastSeen.clear();\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._log('warn', 'noble.recover.done', { state: this._noble?.state });\n } catch (error) {\n // Recovery is best-effort; the caller already reported the scan failure.\n this._log('warn', 'noble.recover.error', { error: String(error) });\n }\n }\n\n private _armIdleStop(): void {\n if (this._disposed) return;\n this._clearIdleStop();\n this._idleStopTimer = setTimeout(() => {\n void this._stopContinuousScan();\n }, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS);\n }\n\n private _clearIdleStop(): void {\n if (this._idleStopTimer) {\n clearTimeout(this._idleStopTimer);\n this._idleStopTimer = undefined;\n }\n }\n\n // Keep native start/stop callbacks ordered: a late stop must finish before\n // a newer scan starts, even when callers belong to different vendors.\n private _setScanning(scanning: boolean): Promise<void> {\n const transition = this._scanTransition.then(async () => {\n if (scanning) {\n this._assertActive();\n if (this._scanOwners.size === 0) return;\n }\n if (this._scanning === scanning) return;\n this._scanning = scanning;\n try {\n if (scanning) {\n await this._requireNoble().startScanningAsync([], true);\n this._log('warn', 'scan.start', { allowDuplicates: true });\n } else {\n await this._noble?.stopScanningAsync();\n }\n } catch (error) {\n this._scanning = false;\n if (scanning) throw error;\n }\n });\n this._scanTransition = transition.catch(() => undefined);\n return transition;\n }\n\n /** Stop scanning but keep the discovered cache (used before connect). */\n private async _pauseScan(): Promise<void> {\n this._clearIdleStop();\n await this._setScanning(false);\n }\n\n /** Stop scanning and forget discovered devices (idle timeout / teardown). */\n private async _stopContinuousScan(): Promise<void> {\n this._scanOwners.clear();\n this._discovered.clear();\n this._lastSeen.clear();\n await this._pauseScan();\n }\n\n /** A vendor releasing discovery must not stop another vendor's scan. */\n async stopScan(vendor?: string): Promise<void> {\n if (vendor !== undefined) {\n this._scanOwners.delete(vendor);\n if (this._scanOwners.size > 0) return;\n }\n await this._stopContinuousScan();\n }\n\n /**\n * Look up a previously-scanned device by id (no extra BLE traffic).\n * Returns null if the device hasn't been seen by a recent scan.\n */\n getDevice(id: string): ThirdPartyBleDeviceInfo | null {\n const p = this._discovered.get(id);\n // The scan cache holds every advertiser in range; only a registered vendor's device is exposed.\n if (!p || ![...this._vendors.values()].some(profile => matchesPeripheral(p, profile))) {\n return null;\n }\n return peripheralToInfo(p);\n }\n\n /** Live RSSI (dBm) of a connected peripheral, else the cached scan-time value. */\n async readRssi(id: string): Promise<number> {\n const entry = this._requireEntry(id);\n if (entry.peripheral.updateRssiAsync) {\n return entry.peripheral.updateRssiAsync();\n }\n return entry.peripheral.rssi;\n }\n\n /** Abandon matching pairing attempts without touching another owner's links. */\n async cancelPairing(options?: { vendor: string; id?: string }): Promise<void> {\n const matches = (id: string, vendor?: string) =>\n options === undefined ||\n (vendor === options.vendor && (options.id === undefined || id === options.id));\n for (const attempt of this._connectAttempts) {\n if (!matches(attempt.id, attempt.vendor)) continue;\n attempt.abandon(new Error(`connect cancelled: ${attempt.id}`));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'cancelPairing.cancelConnect.error', {\n id: attempt.id,\n error: String(error),\n });\n }\n }\n if (options?.id === undefined) await this.stopScan(options?.vendor);\n for (const [id, entry] of this._connected) {\n if (matches(id, entry.vendor)) await this.disconnect(id).catch(() => undefined);\n }\n }\n\n /**\n * Scan until `id` is discovered, releasing only its own scan ownership. A\n * bonded Safe 7 goes silent, so callers fall back to `_directConnect`.\n */\n private async _scanUntilFound(\n id: string,\n timeoutMs: number,\n claim: ConnectClaim\n ): Promise<NoblePeripheralLike | undefined> {\n await this.init();\n if (claim.abandoned) return undefined;\n const existing = this._discovered.get(id);\n if (existing) return existing;\n const noble = this._requireNoble();\n const owner = Symbol('connect-scan');\n this._scanOwners.add(owner);\n return new Promise<NoblePeripheralLike | undefined>(resolve => {\n let done = false;\n const finish = (p?: NoblePeripheralLike) => {\n if (done) return;\n done = true;\n this._pendingCancellations.delete(cancel);\n clearTimeout(timer);\n noble.removeListener('discover', onDiscover);\n if (claim.cancelScan === cancel) claim.cancelScan = undefined;\n this._scanOwners.delete(owner);\n if (this._scanOwners.size === 0) {\n void this._pauseScan().then(() => resolve(p));\n } else {\n resolve(p);\n }\n };\n const onDiscover = (peripheral: NoblePeripheralLike) => {\n this._discovered.set(peripheral.id, peripheral);\n if (peripheral.id === id) finish(peripheral);\n };\n const timer = setTimeout(() => finish(this._discovered.get(id)), timeoutMs);\n const cancel = () => finish();\n this._pendingCancellations.add(cancel);\n claim.cancelScan = cancel;\n noble.on('discover', onDiscover);\n void this._setScanning(true).catch(() => finish());\n });\n }\n\n /**\n * Connect by id without advertisement: the only route to a linked, silent device.\n * Returns undefined instead of throwing so the caller reports \"device not found\".\n */\n private async _directConnect(id: string): Promise<NoblePeripheralLike | undefined> {\n const noble = this._requireNoble();\n if (typeof noble.connectAsync !== 'function') {\n // Distinguishes a stub noble from \"the device wasn't there\" in the log.\n this._log('warn', 'connect.direct.unavailable', { id });\n return undefined;\n }\n // warn, not info: reaching a silent bonded device this way is unproven on\n // real hardware, so it must always be logged.\n this._log('warn', 'connect.direct.start', { id });\n const startedAt = Date.now();\n try {\n // Bounded by the connect() timeout; noble-mac never resolves when it\n // cannot retrieve the peripheral.\n const peripheral = await noble.connectAsync(id);\n const resolved = peripheral ?? this._discovered.get(id);\n this._log('warn', 'connect.direct.done', {\n id,\n elapsedMs: Date.now() - startedAt,\n found: Boolean(resolved),\n // Anything but 'connected' later surfaces as a service-discovery error.\n state: resolved?.state,\n fromNoble: Boolean(peripheral),\n });\n return resolved;\n } catch (error) {\n this._log('warn', 'connect.direct.error', {\n id,\n elapsedMs: Date.now() - startedAt,\n error: String(error),\n });\n return undefined;\n }\n }\n\n // noble's disconnectAsync hangs after a failed connect (CoreBluetooth never\n // sends the disconnect event), so bound it.\n private async _safeDisconnect(peripheral: NoblePeripheralLike): Promise<void> {\n if (this._nativeReleased) return;\n await raceTimeout(\n peripheral.disconnectAsync().catch(() => undefined),\n BLE_DISCONNECT_TIMEOUT_MS\n );\n }\n\n // One timeout covers connect and service discovery; the connector maps\n // `timed out` (unreachable) and `connection failed` (stale bond) differently.\n async connect(\n id: string,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n if (\n !options ||\n !options.vendor ||\n ![options.serviceUuid, options.writeUuid, options.notifyUuid].every(\n uuid => typeof uuid === 'string' && /^[0-9a-f]{32}$/i.test(normalizeBleUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\n }\n // Before any native work: only a registered vendor's own GATT profile and framing.\n if (!this._resolveVendor(options.vendor).profile.acceptsConnect(options)) {\n throw new Error(`BLE GATT profile not allowed for ${options.vendor}`);\n }\n this._assertActive();\n // Promise.race cannot stop _connectInner; the claim makes a late success tear\n // its link down instead of committing an unowned one that silences the device.\n const claim: ConnectClaim = { abandoned: false };\n // Shared by the timeout and cancelPairing.\n let abandon!: (error: Error) => void;\n const abandoned = new Promise<never>((_, reject) => {\n abandon = (error: Error) => {\n claim.abandoned = true;\n reject(error);\n claim.cancelScan?.();\n };\n });\n const timer = setTimeout(\n () => abandon(new Error(`connect timed out after ${this._connectTimeoutMs}ms`)),\n this._connectTimeoutMs\n );\n const attempt = {\n id,\n vendor: options.vendor,\n abandon,\n cancelNative: () => claim.cancelNative?.(),\n settled: Promise.resolve<unknown>(undefined),\n };\n this._connectAttempts.add(attempt);\n const nativeOperation = this._connectInner(id, claim, options);\n const caller = (async () => {\n try {\n return await Promise.race([nativeOperation, abandoned]);\n } catch (error) {\n const peripheral = this._discovered.get(id);\n // Tear down only a link nobody owns, same guard as _connectInner.abortIfAbandoned.\n if (peripheral && !this._connected.has(id)) await this._safeDisconnect(peripheral);\n throw error;\n } finally {\n clearTimeout(timer);\n }\n })();\n // A rejected caller can still have a native connect or disconnect in flight.\n attempt.settled = Promise.allSettled([nativeOperation, caller]).finally(() => {\n this._connectAttempts.delete(attempt);\n // The connect cleared the idle stop; re-arm it so an owner left behind (a picker that\n // connected without stopScan) still expires instead of keeping the radio on.\n if (this._scanOwners.size > 0) this._armIdleStop();\n // Resume the radio only for a connect-scan (symbol) owner; a vendor's\n // idle scan owner must not restart it during pairing.\n const connectScanWaiting = [...this._scanOwners].some(owner => typeof owner === 'symbol');\n if (connectScanWaiting) {\n void this._setScanning(true).catch(() => undefined);\n }\n });\n return caller;\n }\n\n private async _connectInner(\n id: string,\n claim: ConnectClaim,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n let route: 'cache' | 'scan' | 'direct' | 'none' = 'cache';\n let peripheral: NoblePeripheralLike | undefined;\n // Stops the flow before it commits an unowned link; the rejection itself is\n // unobservable because Promise.race already settled.\n const abortIfAbandoned = async (stage: string) => {\n if (!claim.abandoned && !this._disposed) return;\n // A link still held in _connected belongs to a previous connect.\n if (peripheral && peripheral.state === 'connected' && !this._connected.has(id)) {\n await this._safeDisconnect(peripheral);\n }\n this._log('warn', 'connect.abandoned', { id, route, stage });\n throw new Error(`connect abandoned after timeout: ${id}`);\n };\n await this.init();\n await abortIfAbandoned('init');\n // Stop scanning (keep the cache) and let the radio settle before connecting.\n await this._pauseScan();\n await delay(BLE_CONNECT_SETTLE_MS);\n await abortIfAbandoned('settle');\n this._assertActive();\n peripheral = this._discovered.get(id);\n if (!peripheral) {\n route = 'scan';\n peripheral = await this._scanUntilFound(id, THIRD_PARTY_BLE_SCAN_DURATION_MS, claim);\n await abortIfAbandoned('scan');\n }\n if (!peripheral) {\n route = 'direct';\n // Deliberately after the scan: noble-mac never resolves connect-by-id for a\n // peripheral CoreBluetooth cannot retrieve (`NobleMac::Connect`).\n const native = this._requireNoble();\n claim.cancelNative = () => native.cancelConnect?.(id);\n peripheral = await this._directConnect(id);\n }\n if (!peripheral) {\n // discoveredCount=0 points at the radio/scan; counts only, see scan.empty.\n this._log('warn', 'connect.notFound', {\n id,\n route: 'none',\n discoveredCount: this._discovered.size,\n });\n throw new Error(`BLE device not found: ${id}`);\n }\n\n await abortIfAbandoned('resolve');\n\n const wasConnected = peripheral.state === 'connected';\n const connectingPeripheral = peripheral;\n const native = this._requireNoble();\n claim.cancelNative = () => {\n if (connectingPeripheral.state === 'connecting' && connectingPeripheral.cancelConnect) {\n connectingPeripheral.cancelConnect();\n } else {\n native.cancelConnect?.(id);\n }\n };\n this._log('warn', 'connect.route', {\n id,\n route,\n wasConnected,\n name: peripheral.advertisement?.localName,\n });\n if (!wasConnected) {\n await peripheral.connectAsync();\n await abortIfAbandoned('link');\n }\n\n try {\n const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(\n [options.serviceUuid],\n [options.writeUuid, options.notifyUuid]\n );\n const writeUuid = normalizeBleUuid(options.writeUuid);\n const notifyUuid = normalizeBleUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeBleUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeBleUuid(c.uuid) === notifyUuid);\n if (!writeChar || !notifyChar) {\n throw new Error(`BLE characteristics not found on device ${id}`);\n }\n // Last gate before commit: service discovery can also outlast the timeout.\n await abortIfAbandoned('discovery');\n\n const disconnectHandler = () => {\n // One peripheral outlives many links; a stale handler must not report\n // the current link as unexpectedly dropped.\n if (this._connected.get(id)?.disconnectHandler !== disconnectHandler) return;\n this._cleanupDevice(id, /* unexpected */ true);\n };\n peripheral.on('disconnect', disconnectHandler);\n\n this._connected.set(id, {\n peripheral,\n writeChar,\n notifyChar,\n disconnectHandler,\n vendor: options.vendor,\n write: options.write,\n });\n this._log('info', 'connect.done', { id, name: peripheral.advertisement.localName });\n return { id, name: peripheral.advertisement.localName };\n } catch (error) {\n // Don't leak a GATT link this call opened.\n if (!wasConnected) await this._safeDisconnect(peripheral);\n throw error;\n }\n }\n\n async disconnect(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry) return;\n if (entry.disconnectHandler) {\n // Suppress the unexpected-disconnect event for explicit disconnects.\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n try {\n await entry.peripheral.disconnectAsync();\n } catch (error) {\n this._log('warn', 'disconnect.error', { id, error: String(error) });\n }\n this._cleanupDevice(id, /* unexpected */ false, entry);\n }\n\n async subscribe(id: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.notifyChar) throw new Error(`BLE notify characteristic missing for ${id}`);\n if (entry.notifyHandler) return;\n const handler = (data: Buffer) => {\n this._onNotification?.(id, data.toString('hex'));\n };\n entry.notifyHandler = handler;\n entry.notifyChar.on('data', handler);\n await entry.notifyChar.subscribeAsync();\n }\n\n async unsubscribe(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry?.notifyChar) return;\n if (entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n entry.notifyHandler = undefined;\n }\n try {\n await entry.notifyChar.unsubscribeAsync();\n } catch (error) {\n this._log('warn', 'unsubscribe.error', { id, error: String(error) });\n }\n }\n\n async write(id: string, hexData: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.writeChar) throw new Error(`BLE write characteristic missing for ${id}`);\n const buffer = Buffer.from(hexData, 'hex');\n const framing = entry.write;\n if (!framing) throw new Error(`No BLE write framing recorded for ${id}`);\n\n if (framing.mode === 'raw') {\n // Self-framed, so padding corrupts it. Ledger negotiates the size per link\n // (0x08 handshake) in one byte, so it never exceeds 255.\n const maxLength = framing.maxLength ?? buffer.length;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hexData) || buffer.length > maxLength) {\n throw new Error(`Invalid BLE frame for ${entry.vendor ?? 'device'}`);\n }\n await entry.writeChar.writeAsync(buffer, false);\n return;\n }\n\n const { chunkSize } = framing;\n if (!chunkSize) throw new Error(`Padded BLE writes need a chunkSize for ${id}`);\n for (let offset = 0; offset < buffer.length; offset += chunkSize) {\n this._assertActive();\n const slice = buffer.subarray(offset, offset + chunkSize);\n // Firmware drops a short final packet (then RetriesExceeded), so every chunk\n // is zero-filled to full size, as in trezor-suite transport-bluetooth.\n const chunk = Buffer.alloc(chunkSize);\n slice.copy(chunk);\n // OneKey uses writeWithResponse for stability; mirror that.\n await entry.writeChar.writeAsync(chunk, false);\n if (offset + chunkSize < buffer.length && framing.chunkDelayMs) {\n await delay(framing.chunkDelayMs);\n }\n }\n }\n\n /** Retire a renderer's handler without stopping a process-wide native manager. */\n dispose(): Promise<void> {\n if (this._disposePromise) return this._disposePromise;\n this._disposed = true;\n this._clearIdleStop();\n this._scanning = false;\n this._onNotification = undefined;\n this._onDeviceDisconnected = undefined;\n const connections = Array.from(this._connectAttempts);\n for (const attempt of connections) {\n attempt.abandon(new Error('Desktop BLE is shutting down'));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'dispose.cancelConnect.error', { error: String(error) });\n }\n }\n for (const cancel of this._pendingCancellations) cancel();\n if (this._noble && this._discoverHandler) {\n this._noble.removeListener('discover', this._discoverHandler);\n }\n const entries = Array.from(this._connected.entries());\n for (const [id] of entries) this._cleanupDevice(id, false);\n this._disposePromise = (async () => {\n try {\n await raceTimeout(\n Promise.allSettled([\n ...connections.map(attempt => attempt.settled),\n ...Array.from(this._nobleInstances, async instance => instance.stopScanningAsync()),\n ...entries.map(async ([, entry]) => {\n try {\n await raceTimeout(\n entry.notifyChar?.unsubscribeAsync().catch(() => undefined),\n 250\n );\n } finally {\n await this._safeDisconnect(entry.peripheral);\n }\n }),\n ]),\n 3500,\n () => this._log('warn', 'dispose.timeout')\n );\n } finally {\n this._discovered.clear();\n this._lastSeen.clear();\n this._initialized = false;\n }\n })();\n return this._disposePromise;\n }\n\n /**\n * Terminal native release, including recovered instances. A host sharing noble\n * must defer stop() until every transport disposed, and dedupe the instances.\n */\n disposeForAppQuit(\n releaseNoble: (instance: { stop?(): void }) => void = instance => instance.stop?.()\n ): Promise<void> {\n if (!this._releasePromise) {\n this._releasePromise = this.dispose().finally(() => {\n this._nativeReleased = true;\n let releaseError: Error | undefined;\n for (const instance of this._nobleInstances) {\n try {\n releaseNoble(instance);\n } catch (error) {\n releaseError = error instanceof Error ? error : new Error(String(error));\n }\n }\n this._nobleInstances.clear();\n if (releaseError) throw releaseError;\n this._log('info', 'dispose.native.done');\n });\n }\n return this._releasePromise;\n }\n\n private _assertActive(): void {\n if (this._disposed) throw new Error('Desktop BLE is shutting down');\n }\n\n private _cleanupDevice(id: string, unexpected: boolean, expected?: DeviceEntry): void {\n const entry = this._connected.get(id);\n if (!entry) return;\n // A reconnect may have replaced the entry while the caller awaited.\n // A stale caller must not tear down the link it no longer owns.\n if (expected && entry !== expected) return;\n if (entry.notifyChar && entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n }\n // Otherwise a later explicit disconnect is reported as unexpected.\n if (entry.disconnectHandler) {\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n this._connected.delete(id);\n if (unexpected) {\n this._log('warn', 'disconnect.unexpected', { id });\n this._onDeviceDisconnected?.(id);\n }\n }\n\n private _requireEntry(id: string): DeviceEntry {\n this._assertActive();\n const entry = this._connected.get(id);\n if (!entry) throw new Error(`BLE device is not connected: ${id}`);\n return entry;\n }\n\n private _requireNoble(): NobleLike {\n this._assertActive();\n if (!this._noble) throw new Error('Desktop BLE: noble was not initialized');\n return this._noble;\n }\n\n /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */\n private async _waitForKnownState(timeoutMs: number): Promise<void> {\n const noble = this._requireNoble();\n if (noble.state !== 'unknown') return;\n await new Promise<void>(resolve => {\n const settle = () => {\n clearTimeout(timer);\n noble.removeListener('stateChange', settle);\n this._pendingCancellations.delete(settle);\n resolve();\n };\n const timer = setTimeout(settle, timeoutMs);\n this._pendingCancellations.add(settle);\n noble.on('stateChange', settle);\n });\n }\n\n private async _waitForPoweredOn(timeoutMs: number): Promise<void> {\n const noble = this._requireNoble();\n if (noble.state === 'poweredOn') return;\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n noble.removeListener('stateChange', handler);\n this._pendingCancellations.delete(cancel);\n };\n const cancel = () => {\n cleanup();\n reject(new Error('Desktop BLE is shutting down'));\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(\n new Error(\n `Desktop BLE: noble did not reach poweredOn within ${timeoutMs}ms (last state: ${noble.state})`\n )\n );\n }, timeoutMs);\n const handler = (state: string) => {\n if (state === 'poweredOn') {\n cleanup();\n resolve();\n } else if (state === 'unsupported' || state === 'unauthorized') {\n cleanup();\n reject(new Error(`Desktop BLE: noble state ${state}`));\n }\n };\n this._pendingCancellations.add(cancel);\n noble.on('stateChange', handler);\n });\n }\n\n private _log(level: BleDebugLogLevel, event: string, data?: Record<string, unknown>): void {\n this._logger?.({\n level,\n scope: 'desktop-noble-ble',\n event,\n data: redactBleDebugLogData(data),\n });\n }\n}\n","import { THIRD_PARTY_BLE_CHANNELS } from './constants';\nimport { NobleBleHandler } from './NobleBleHandler';\n\nimport type { NobleBleHandlerOptions } from './NobleBleHandler';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/** Minimal slice of Electron's `WebContents` we use (kept duck-typed so we\n * don't take a hard dep on `electron`). */\nexport interface WebContentsLike {\n send(channel: string, ...args: unknown[]): void;\n on?(event: string, listener: (...args: any[]) => void): void;\n}\n\n/** Minimal slice of Electron's `ipcMain` we use. */\nexport interface IpcMainLike {\n handle(\n channel: string,\n listener: (event: unknown, ...args: any[]) => Promise<unknown> | unknown\n ): void;\n removeHandler(channel: string): void;\n}\n\nexport interface InitThirdPartyBleSupportOptions extends NobleBleHandlerOptions {\n /** Inject your own `ipcMain` (defaults to `require('electron').ipcMain`). */\n ipcMain?: IpcMainLike;\n}\n\nexport interface ThirdPartyBleSupportHandle {\n handler: NobleBleHandler;\n dispose(): Promise<void>;\n disposeForAppQuit(releaseNoble?: (instance: { stop?(): void }) => void): Promise<void>;\n}\n\nconst DEFAULT_IPC_MAIN: () => IpcMainLike = () => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { ipcMain } = require('electron') as { ipcMain: IpcMainLike };\n return ipcMain;\n};\n\n/**\n * Wire a `NobleBleHandler` to Electron IPC once `BrowserWindow` is ready. Call dispose()\n * when retiring a renderer and disposeForAppQuit() before Node teardown.\n */\nexport function initThirdPartyBleSupport(\n webContents: WebContentsLike,\n options: InitThirdPartyBleSupportOptions = {}\n): ThirdPartyBleSupportHandle {\n const ipcMain = options.ipcMain ?? DEFAULT_IPC_MAIN();\n const handler = new NobleBleHandler(options);\n let disposed = false;\n\n handler.setNotificationListener((id, hexData) => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.notification, id, hexData);\n });\n handler.setDisconnectedListener(id => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.disconnected, id);\n });\n\n const handle = <T>(channel: string, fn: (...args: any[]) => Promise<T> | T): void => {\n ipcMain.handle(channel, async (_event, ...args) => {\n if (disposed) throw new Error('Third-party BLE is shutting down');\n return fn(...args);\n });\n };\n\n handle(THIRD_PARTY_BLE_CHANNELS.scan, (options?: ElectronBleScanOptions) =>\n handler.scan(options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.stopScan, (vendor?: string) => handler.stopScan(vendor));\n handle(THIRD_PARTY_BLE_CHANNELS.connect, (id: string, options: ElectronBleConnectOptions) =>\n handler.connect(id, options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.disconnect, (id: string) => handler.disconnect(id));\n handle(THIRD_PARTY_BLE_CHANNELS.write, (id: string, hexData: string) =>\n handler.write(id, hexData)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.subscribe, (id: string) => handler.subscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.unsubscribe, (id: string) => handler.unsubscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.availability, () => handler.checkAvailability());\n handle(THIRD_PARTY_BLE_CHANNELS.getDevice, (id: string) => handler.getDevice(id));\n handle(THIRD_PARTY_BLE_CHANNELS.readRssi, (id: string) => handler.readRssi(id));\n handle(THIRD_PARTY_BLE_CHANNELS.cancelPairing, (options?: { vendor: string; id?: string }) =>\n handler.cancelPairing(options)\n );\n\n const removeHandlers = () => {\n if (disposed) return;\n disposed = true;\n for (const channel of Object.values(THIRD_PARTY_BLE_CHANNELS)) {\n ipcMain.removeHandler(channel);\n }\n };\n\n return {\n handler,\n dispose: () => {\n removeHandlers();\n return handler.dispose();\n },\n disposeForAppQuit: releaseNoble => {\n removeHandlers();\n return handler.disposeForAppQuit(releaseNoble);\n },\n };\n}\n"],"mappings":"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","names":["native","options"]}
package/dist/index.mjs CHANGED
@@ -4,8 +4,9 @@ import {
4
4
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
5
5
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
6
6
  THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
7
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS,
7
8
  __require
8
- } from "./chunk-A5AENDC2.mjs";
9
+ } from "./chunk-ILABAQJY.mjs";
9
10
 
10
11
  // src/debugLog.ts
11
12
  var REDACTED_KEYS = /* @__PURE__ */ new Set([
@@ -242,6 +243,8 @@ var NobleBleHandler = class {
242
243
  return { available: false, state: "unsupported", initialized: false };
243
244
  }
244
245
  }
246
+ await this._waitForKnownState(THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS);
247
+ this._assertActive();
245
248
  const state = this._noble?.state ?? "unknown";
246
249
  return {
247
250
  available: state === "poweredOn",
@@ -853,6 +856,22 @@ var NobleBleHandler = class {
853
856
  if (!this._noble) throw new Error("Desktop BLE: noble was not initialized");
854
857
  return this._noble;
855
858
  }
859
+ /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */
860
+ async _waitForKnownState(timeoutMs) {
861
+ const noble = this._requireNoble();
862
+ if (noble.state !== "unknown") return;
863
+ await new Promise((resolve) => {
864
+ const settle = () => {
865
+ clearTimeout(timer);
866
+ noble.removeListener("stateChange", settle);
867
+ this._pendingCancellations.delete(settle);
868
+ resolve();
869
+ };
870
+ const timer = setTimeout(settle, timeoutMs);
871
+ this._pendingCancellations.add(settle);
872
+ noble.on("stateChange", settle);
873
+ });
874
+ }
856
875
  async _waitForPoweredOn(timeoutMs) {
857
876
  const noble = this._requireNoble();
858
877
  if (noble.state === "poweredOn") return;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/debugLog.ts","../src/vendorProfiles.ts","../src/NobleBleHandler.ts","../src/main.ts"],"sourcesContent":["export type BleDebugLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type BleDebugLogEntry = {\n level: BleDebugLogLevel;\n scope: string;\n event: string;\n data?: Record<string, unknown>;\n};\n\nexport type BleDebugLogger = (entry: BleDebugLogEntry) => void;\n\n/**\n * Keys whose values never belong in a log, kept narrow to real secrets and\n * key material. Raw frames (`packetHex`, `hexData`, `payload`) stay only because\n * they are Trezor THP ciphertext or Ledger APDUs; a vendor with a cleartext\n * protocol must move them into this list.\n */\nconst REDACTED_KEYS: ReadonlySet<string> = new Set([\n // Secrets the user types or the device derives.\n 'pin',\n 'passphrase',\n 'mnemonic',\n 'seed',\n 'seedPhrase',\n 'entropy',\n // Private key material. Public keys stay: device identity, address\n // derivation and fingerprint mismatches are all diagnosed from them.\n 'privateKey',\n 'privKey',\n 'secretKey',\n 'hostKey',\n 'trezorKey',\n 'host_static_key',\n // Pairing / session credentials. The THP static key pair is part of the\n // pairing credential, so it goes here rather than with the public keys.\n 'trezor_static_public_key',\n 'credential',\n 'credentials',\n 'sessionKey',\n 'token',\n 'accessToken',\n 'authToken',\n]);\n\n/** Guards against a cyclic or pathological `data` bag stalling the log call. */\nconst MAX_REDACT_DEPTH = 8;\n\n/**\n * Keep the shape of what was dropped so two logs still line up: a 32-byte\n * value that became 33 bytes is visible without the value itself.\n */\nfunction describeRedacted(value: unknown): string {\n if (typeof value === 'string') return `<redacted ${value.length} chars>`;\n if (ArrayBuffer.isView(value)) return `<redacted ${value.byteLength} bytes>`;\n if (Array.isArray(value)) return `<redacted ${value.length} items>`;\n return '<redacted>';\n}\n\nfunction redactValue(key: string, value: unknown, depth: number, seen: WeakSet<object>): unknown {\n if (REDACTED_KEYS.has(key)) return describeRedacted(value);\n if (!value || typeof value !== 'object') return value;\n // Errors carry message/stack non-enumerably; recursing would empty them.\n // Typed arrays would turn into index-keyed objects. Both are forwarded whole.\n if (value instanceof Error || ArrayBuffer.isView(value)) return value;\n if (depth >= MAX_REDACT_DEPTH) return '<redacted depth limit>';\n if (seen.has(value)) return '<circular>';\n seen.add(value);\n if (Array.isArray(value)) {\n return value.map(item => redactValue(key, item, depth + 1, seen));\n }\n const out: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value as Record<string, unknown>)) {\n out[childKey] = redactValue(childKey, childValue, depth + 1, seen);\n }\n return out;\n}\n\n/** Replace sensitive values at any depth; everything else is forwarded verbatim. */\nexport function redactBleDebugLogData(\n data?: Record<string, unknown>\n): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const seen = new WeakSet<object>();\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n out[key] = redactValue(key, value, 0, seen);\n }\n return out;\n}\n","import type { ElectronBleConnectOptions } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Main-process allowlist entry for one vendor key. The renderer only names the vendor; which\n * peripherals it may see and which GATT profile and framing it may use are decided here, so an\n * IPC caller cannot turn the service into a generic BLE proxy. UUID arguments may arrive in any\n * case, with or without dashes.\n */\nexport interface ThirdPartyBleVendorProfile {\n /** Whether an advertised service UUID belongs to this vendor. */\n isServiceUuid(uuid: string): boolean;\n /** Whether a local name belongs to this vendor; the handler passes a length-capped name. */\n isName?(name: string): boolean;\n /** Whether this GATT profile and write framing is one the vendor's firmware uses. */\n acceptsConnect(options: ElectronBleConnectOptions): boolean;\n}\n\nexport const normalizeBleUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\nconst isUuidString = (value: unknown): value is string => typeof value === 'string';\n\nconst isIntegerInRange = (value: unknown, min: number, max: number): value is number =>\n typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;\n\n// Duplicated from @onekeyfe/hwk-trezor-adapter (TREZOR_BLE_UUIDS, TREZOR_BLE_PACKET_SIZE) to keep\n// this package free of vendor dependencies; the Trezor connector tests pin the two together.\nconst TREZOR_SERVICE_UUID = normalizeBleUuid('8c000001-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_WRITE_UUID = normalizeBleUuid('8c000002-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_NOTIFY_UUID = normalizeBleUuid('8c000003-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_PACKET_SIZE = 244;\nconst TREZOR_MAX_CHUNK_DELAY_MS = 50;\nconst TREZOR_NAME_BRAND = /\\bTrezor\\b/i;\nconst TREZOR_NAME_MODEL = /\\bSafe\\s*7\\b|\\bT3W1\\b/i;\n\nconst trezorProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => isUuidString(uuid) && normalizeBleUuid(uuid) === TREZOR_SERVICE_UUID,\n isName: name => TREZOR_NAME_BRAND.test(name) && TREZOR_NAME_MODEL.test(name),\n acceptsConnect: options => {\n const { write } = options;\n return (\n isUuidString(options.serviceUuid) &&\n isUuidString(options.writeUuid) &&\n isUuidString(options.notifyUuid) &&\n normalizeBleUuid(options.serviceUuid) === TREZOR_SERVICE_UUID &&\n normalizeBleUuid(options.writeUuid) === TREZOR_WRITE_UUID &&\n normalizeBleUuid(options.notifyUuid) === TREZOR_NOTIFY_UUID &&\n typeof write === 'object' &&\n write !== null &&\n write.mode === 'padded' &&\n write.chunkSize === TREZOR_PACKET_SIZE &&\n (write.chunkDelayMs === undefined ||\n isIntegerInRange(write.chunkDelayMs, 0, TREZOR_MAX_CHUNK_DELAY_MS)) &&\n write.maxLength === undefined\n );\n },\n};\n\n// DMK's GATT family 13d63400-2c97-MMMM-000R-4c6564676572: MMMM is the model (0004 Nano X,\n// 6004 Stax, 3004 Flex, 8004/9004 Nano Gen5), R the role. Any model segment is accepted so a\n// new model works without a release here.\nconst LEDGER_UUID = /^13d634002c97([0-9a-f]{4})000([0-3])4c6564676572$/;\nconst LEDGER_ROLE_SERVICE = '0';\nconst LEDGER_ROLE_NOTIFY = '1';\nconst LEDGER_ROLE_WRITE = '2';\nconst LEDGER_ROLE_WRITE_CMD = '3';\n// The 0x08 handshake reports the frame size in one byte.\nconst LEDGER_MAX_FRAME_SIZE = 255;\n\nconst parseLedgerUuid = (uuid: unknown): { model: string; role: string } | undefined => {\n if (!isUuidString(uuid)) return undefined;\n const parsed = LEDGER_UUID.exec(normalizeBleUuid(uuid));\n return parsed ? { model: parsed[1], role: parsed[2] } : undefined;\n};\n\nconst ledgerProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => parseLedgerUuid(uuid)?.role === LEDGER_ROLE_SERVICE,\n acceptsConnect: options => {\n const service = parseLedgerUuid(options.serviceUuid);\n const notify = parseLedgerUuid(options.notifyUuid);\n const write = parseLedgerUuid(options.writeUuid);\n const framing = options.write;\n return (\n service?.role === LEDGER_ROLE_SERVICE &&\n notify?.role === LEDGER_ROLE_NOTIFY &&\n (write?.role === LEDGER_ROLE_WRITE || write?.role === LEDGER_ROLE_WRITE_CMD) &&\n notify.model === service.model &&\n write.model === service.model &&\n typeof framing === 'object' &&\n framing !== null &&\n framing.mode === 'raw' &&\n isIntegerInRange(framing.maxLength, 1, LEDGER_MAX_FRAME_SIZE) &&\n framing.chunkSize === undefined &&\n framing.chunkDelayMs === undefined\n );\n },\n};\n\n/**\n * Vendors the desktop BLE service serves by default, keyed by the vendor string the connectors\n * send. A host passing `vendors` replaces this map, so spread it to add a vendor.\n */\nexport const DEFAULT_THIRD_PARTY_BLE_VENDORS: Readonly<Record<string, ThirdPartyBleVendorProfile>> =\n Object.freeze({\n ledger: ledgerProfile,\n trezor: trezorProfile,\n });\n","import { type BleDebugLogLevel, type BleDebugLogger, redactBleDebugLogData } from './debugLog';\nimport {\n THIRD_PARTY_BLE_DEVICE_TTL_MS,\n THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,\n THIRD_PARTY_BLE_SCAN_DURATION_MS,\n THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,\n} from './constants';\nimport { DEFAULT_THIRD_PARTY_BLE_VENDORS, normalizeBleUuid } from './vendorProfiles';\n\nimport type { ThirdPartyBleVendorProfile } from './vendorProfiles';\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Subset of @stoprocent/noble we touch. We type as `any` to keep the package\n * installable without the native module — the default factory loads it on first use.\n */\nexport interface NobleLike {\n state: string;\n on(event: string, handler: (...args: any[]) => void): NobleLike;\n removeListener(event: string, handler: (...args: any[]) => void): NobleLike;\n startScanningAsync(serviceUuids: string[], allowDuplicates: boolean): Promise<void>;\n stopScanningAsync(): Promise<void>;\n stop?(): void;\n /**\n * Connect by id with no scan; both backends emit `discover` as a side effect\n * (win `BLEManager::Connect`, mac `retrievePeripheralsWithIdentifiers`).\n */\n connectAsync?(idOrAddress: string): Promise<NoblePeripheralLike | undefined>;\n cancelConnect?(idOrAddress: string): void;\n reset?(): Promise<void>;\n}\n\nexport interface NoblePeripheralLike {\n id: string;\n // Every advertisement field, so the host can look for a cross-transport\n // identity (e.g. a serial in manufacturerData) without another scan.\n advertisement: {\n localName?: string;\n serviceUuids?: string[];\n manufacturerData?: Buffer;\n serviceData?: Array<{ uuid: string; data: Buffer }>;\n txPowerLevel?: number;\n serviceSolicitationUuids?: string[];\n };\n /** Some noble builds expose the BLE MAC/address separately from `id`. */\n address?: string;\n addressType?: string;\n connectable?: boolean;\n rssi: number;\n state: string;\n connectAsync(): Promise<void>;\n cancelConnect?(): void;\n disconnectAsync(): Promise<void>;\n discoverSomeServicesAndCharacteristicsAsync(\n serviceUuids: string[],\n characteristicUuids: string[]\n ): Promise<{ characteristics: NobleCharacteristicLike[] }>;\n /** Read live RSSI from the connected peripheral. Returns dBm. */\n updateRssiAsync?(): Promise<number>;\n on(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n removeListener(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n}\n\nexport interface NobleCharacteristicLike {\n uuid: string;\n subscribeAsync(): Promise<void>;\n unsubscribeAsync(): Promise<void>;\n writeAsync(data: Buffer, withoutResponse: boolean): Promise<void>;\n on(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n removeListener(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n}\n\nexport type NobleFactory = () => NobleLike;\n\nconst DEFAULT_NOBLE_FACTORY: NobleFactory = () => {\n // Lazy require so packagers don't bundle the native module at import time.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const noble = require('@stoprocent/noble') as NobleLike;\n return noble;\n};\n\ninterface ConnectClaim {\n abandoned: boolean;\n cancelScan?: () => void;\n cancelNative?: () => void;\n}\n\ninterface DeviceEntry {\n /** Registered vendor key; results and links never mix across it. */\n vendor?: string;\n write?: ElectronBleConnectOptions['write'];\n peripheral: NoblePeripheralLike;\n writeChar?: NobleCharacteristicLike;\n notifyChar?: NobleCharacteristicLike;\n notifyHandler?: (data: Buffer, isNotification: boolean) => void;\n disconnectHandler?: () => void;\n}\n\n// Longer than any real vendor name, and bounds the work a vendor name rule does per advertisement.\nconst BLE_NAME_MATCH_MAX_LENGTH = 64;\n\n/**\n * Stands in for the scan filter we cannot use (see `scan`). Only the vendor profile decides; the\n * caller's service UUIDs can narrow the profile's set, and caller name patterns are never run.\n */\nconst matchesPeripheral = (\n p: NoblePeripheralLike,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n): boolean => {\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n advertised.some(\n uuid =>\n typeof uuid === 'string' &&\n profile.isServiceUuid(uuid) &&\n (!narrowTo || narrowTo.has(normalizeBleUuid(uuid)))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!profile.isName || typeof name !== 'string' || !name) return false;\n return profile.isName(name.slice(0, BLE_NAME_MATCH_MAX_LENGTH));\n};\n\n/** Renderer `serviceUuids` may only narrow the vendor's set; anything malformed is ignored. */\nconst requestedServiceUuids = (options?: ElectronBleScanOptions): Set<string> | undefined => {\n const requested = options?.match ? options.match.serviceUuids : options?.serviceUuids;\n if (!Array.isArray(requested)) return undefined;\n const uuids = requested.filter((uuid): uuid is string => typeof uuid === 'string');\n return uuids.length ? new Set(uuids.map(normalizeBleUuid)) : undefined;\n};\n\n/** Hex-encodes buffers so the info survives the IPC structured clone. */\nconst peripheralToInfo = (p: NoblePeripheralLike): ThirdPartyBleDeviceInfo => {\n const adv = p.advertisement ?? {};\n return {\n id: p.id,\n name: adv.localName,\n localName: adv.localName,\n rssi: p.rssi,\n isConnectable: p.connectable ?? null,\n advertisedServiceUuids: adv.serviceUuids,\n serviceSolicitationUuids: adv.serviceSolicitationUuids,\n txPowerLevel: adv.txPowerLevel,\n manufacturerDataHex: adv.manufacturerData\n ? Buffer.from(adv.manufacturerData).toString('hex')\n : undefined,\n serviceData: adv.serviceData?.map(entry => ({\n uuid: entry.uuid,\n dataHex: Buffer.from(entry.data).toString('hex'),\n })),\n address: p.address,\n addressType: p.addressType,\n state: p.state,\n };\n};\n\nconst delay = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));\n\n/** Waits for `work` or `ms`, whichever comes first; never rejects on timeout. */\nconst raceTimeout = async (\n work: Promise<unknown> | undefined,\n ms: number,\n onTimeout?: () => void\n): Promise<void> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n work,\n new Promise<void>(resolve => {\n timer = setTimeout(() => {\n onTimeout?.();\n resolve();\n }, ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n};\n\n// Radio-settle wait between stopping the scan and opening a GATT connection.\nconst BLE_CONNECT_SETTLE_MS = 300;\n// noble has no connect timeout. SMP pairing timeout (30s, the OS dialog's\n// limit) + 1s, so a first-time pairing is never cut off before the OS gives up.\nconst BLE_CONNECT_TIMEOUT_MS = 31_000;\n// Cap on a cleanup disconnectAsync, which hangs on a just-failed connect.\nconst BLE_DISCONNECT_TIMEOUT_MS = 2_000;\n\n/** Floor between noble rebuilds, so a persistent failure can't thrash. */\nconst NOBLE_RECOVER_COOLDOWN_MS = 10_000;\n\nexport interface NobleBleHandlerOptions {\n /** Override for tests; defaults to `require('@stoprocent/noble')`. */\n nobleFactory?: NobleFactory;\n /** Override the overall connect timeout (tests only; defaults to 31s). */\n connectTimeoutMs?: number;\n logger?: BleDebugLogger;\n /**\n * Vendors served over IPC, keyed by the `vendor` the renderer sends. Replaces the default map\n * (spread `DEFAULT_THIRD_PARTY_BLE_VENDORS` to add one); unknown vendors are rejected.\n */\n vendors?: Record<string, ThirdPartyBleVendorProfile>;\n}\n\n/** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */\nexport class NobleBleHandler {\n private _noble: NobleLike | undefined;\n\n private readonly _factory: NobleFactory;\n\n private readonly _connectTimeoutMs: number;\n\n private readonly _logger?: NobleBleHandlerOptions['logger'];\n\n private readonly _vendors: ReadonlyMap<string, ThirdPartyBleVendorProfile>;\n\n private readonly _discovered = new Map<string, NoblePeripheralLike>();\n\n // id -> last advertisement time (snapshot TTL).\n private readonly _lastSeen = new Map<string, number>();\n\n private readonly _connected = new Map<string, DeviceEntry>();\n\n private _discoverHandler?: (peripheral: NoblePeripheralLike) => void;\n\n private _scanning = false;\n\n private readonly _scanOwners = new Set<string | symbol | undefined>();\n\n private _scanTransition: Promise<void> = Promise.resolve();\n\n private _idleStopTimer?: ReturnType<typeof setTimeout>;\n\n private _onNotification?: (id: string, hexData: string) => void;\n\n private _onDeviceDisconnected?: (id: string) => void;\n\n private _initialized = false;\n\n private _disposed = false;\n\n private _disposePromise?: Promise<void>;\n\n private _releasePromise?: Promise<void>;\n\n private _initPromise?: Promise<void>;\n\n private readonly _nobleInstances = new Set<NobleLike>();\n\n private readonly _pendingCancellations = new Set<() => void>();\n\n private readonly _connectAttempts = new Set<{\n id: string;\n vendor: string;\n abandon: (error: Error) => void;\n cancelNative: () => void;\n settled: Promise<unknown>;\n }>();\n\n private _nativeReleased = false;\n\n private _lastNobleRecoverAt?: number;\n\n constructor(options: NobleBleHandlerOptions = {}) {\n this._factory = options.nobleFactory ?? DEFAULT_NOBLE_FACTORY;\n this._connectTimeoutMs = options.connectTimeoutMs ?? BLE_CONNECT_TIMEOUT_MS;\n this._logger = options.logger;\n // A Map, so an IPC vendor like `__proto__` cannot reach an inherited property.\n this._vendors = new Map(Object.entries(options.vendors ?? DEFAULT_THIRD_PARTY_BLE_VENDORS));\n }\n\n private _resolveVendor(vendor: unknown): {\n vendor: string;\n profile: ThirdPartyBleVendorProfile;\n } {\n const profile = typeof vendor === 'string' ? this._vendors.get(vendor) : undefined;\n if (typeof vendor !== 'string' || !profile) throw new Error('Unsupported BLE vendor');\n return { vendor, profile };\n }\n\n setNotificationListener(handler: (id: string, hexData: string) => void): void {\n this._onNotification = handler;\n }\n\n setDisconnectedListener(handler: (id: string) => void): void {\n this._onDeviceDisconnected = handler;\n }\n\n async init(): Promise<void> {\n this._assertActive();\n if (this._initialized) return;\n if (this._initPromise) return this._initPromise;\n this._initPromise = (async () => {\n this._noble ??= this._factory();\n this._nobleInstances.add(this._noble);\n this._discoverHandler ??= peripheral => {\n this._discovered.set(peripheral.id, peripheral);\n this._lastSeen.set(peripheral.id, Date.now());\n };\n this._noble.removeListener('discover', this._discoverHandler);\n this._noble.on('discover', this._discoverHandler);\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._assertActive();\n this._initialized = true;\n })();\n try {\n await this._initPromise;\n } finally {\n this._initPromise = undefined;\n }\n }\n\n async checkAvailability(): Promise<ThirdPartyBleAvailability> {\n this._assertActive();\n if (!this._noble) {\n try {\n this._noble = this._factory();\n this._nobleInstances.add(this._noble);\n } catch {\n return { available: false, state: 'unsupported', initialized: false };\n }\n }\n const state = this._noble?.state ?? 'unknown';\n return {\n available: state === 'poweredOn',\n state,\n initialized: this._initialized,\n };\n }\n\n /**\n * Lazy-start an unfiltered scan and return the current snapshot. noble-win filters per\n * packet, and a Safe 7 ADV carries only its name (UUID is in the scan response).\n */\n async scan(options?: ElectronBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]> {\n // Before any radio or owner bookkeeping: an unregistered caller gets nothing.\n const { vendor, profile } = this._resolveVendor(options?.vendor);\n // Claim before init: a stopScan/cancelPairing during the power-on wait must\n // find this owner, or the scan revives once the adapter powers on.\n this._scanOwners.add(vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(vendor);\n throw error;\n }\n if (!this._scanOwners.has(vendor)) return [];\n try {\n await this._setScanning(true);\n } catch (error) {\n this._log('warn', 'scan.start.error', { error: String(error) });\n await this._recoverNobleIfStuck(String(error));\n }\n this._assertActive();\n this._armIdleStop();\n const devices = this._snapshot(vendor, profile, requestedServiceUuids(options));\n // raw=0: nothing on air; raw>0: the match criteria dropped it. Counts only,\n // so bystanders' devices from the unfiltered scan stay out of support logs.\n if (devices.length === 0) {\n this._log('warn', 'scan.empty', {\n raw: this._discovered.size,\n kept: 0,\n named: [...this._discovered.values()].filter(p => p.advertisement?.localName).length,\n });\n }\n return devices;\n }\n\n /**\n * In-range devices matching the caller, minus TTL-expired ones. `_discovered`\n * is only a cache; a missing device can still be reached by `_directConnect`.\n */\n private _snapshot(\n vendor: string,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n ): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const result: ThirdPartyBleDeviceInfo[] = [];\n for (const [id, peripheral] of this._discovered) {\n if (now - (this._lastSeen.get(id) ?? 0) > THIRD_PARTY_BLE_DEVICE_TTL_MS) {\n this._discovered.delete(id);\n this._lastSeen.delete(id);\n continue;\n }\n if (!matchesPeripheral(peripheral, profile, narrowTo)) continue;\n result.push(peripheralToInfo(peripheral));\n }\n // A linked device stops advertising and ages out above, so merge held links\n // back in (field-verified on Safe 7: holding the link silences it).\n for (const [id, entry] of this._connected) {\n if (entry.vendor !== vendor) continue;\n if (result.some(info => info.id === id)) continue;\n result.push(peripheralToInfo(entry.peripheral));\n }\n return result;\n }\n\n /** A noble instance with its own bindings, so a stuck one can be replaced. */\n private _createFreshNoble(): NobleLike {\n const candidate = this._factory() as NobleLike & {\n withBindings?: () => NobleLike;\n };\n return typeof candidate.withBindings === 'function' ? candidate.withBindings() : candidate;\n }\n\n /**\n * Rebuild noble when Windows BLE re-enumeration latched its RadioWatcher at\n * `unsupported`; fresh bindings restart the watcher without an app restart.\n */\n private async _recoverNobleIfStuck(reason: string): Promise<void> {\n if (this._disposed) return;\n const state = this._noble?.state;\n if (state === 'poweredOn' || !this._initialized) return;\n // Never tear down bindings out from under a live link.\n if (this._connected.size > 0) {\n this._log('warn', 'noble.recover.skip', {\n reason,\n state,\n connected: this._connected.size,\n });\n return;\n }\n const now = Date.now();\n if (this._lastNobleRecoverAt && now - this._lastNobleRecoverAt < NOBLE_RECOVER_COOLDOWN_MS) {\n return;\n }\n this._lastNobleRecoverAt = now;\n this._log('warn', 'noble.recover.start', { reason, state });\n try {\n const previous = this._noble;\n if (previous && this._discoverHandler) {\n previous.removeListener('discover', this._discoverHandler);\n }\n // The default factory returns the stuck module singleton; `withBindings()`\n // mints a new instance. Injected factories are assumed to return fresh ones.\n const fresh = this._createFreshNoble();\n this._noble = fresh;\n this._nobleInstances.add(fresh);\n if (this._discoverHandler) {\n fresh.on('discover', this._discoverHandler);\n }\n this._scanning = false;\n this._discovered.clear();\n this._lastSeen.clear();\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._log('warn', 'noble.recover.done', { state: this._noble?.state });\n } catch (error) {\n // Recovery is best-effort; the caller already reported the scan failure.\n this._log('warn', 'noble.recover.error', { error: String(error) });\n }\n }\n\n private _armIdleStop(): void {\n if (this._disposed) return;\n this._clearIdleStop();\n this._idleStopTimer = setTimeout(() => {\n void this._stopContinuousScan();\n }, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS);\n }\n\n private _clearIdleStop(): void {\n if (this._idleStopTimer) {\n clearTimeout(this._idleStopTimer);\n this._idleStopTimer = undefined;\n }\n }\n\n // Keep native start/stop callbacks ordered: a late stop must finish before\n // a newer scan starts, even when callers belong to different vendors.\n private _setScanning(scanning: boolean): Promise<void> {\n const transition = this._scanTransition.then(async () => {\n if (scanning) {\n this._assertActive();\n if (this._scanOwners.size === 0) return;\n }\n if (this._scanning === scanning) return;\n this._scanning = scanning;\n try {\n if (scanning) {\n await this._requireNoble().startScanningAsync([], true);\n this._log('warn', 'scan.start', { allowDuplicates: true });\n } else {\n await this._noble?.stopScanningAsync();\n }\n } catch (error) {\n this._scanning = false;\n if (scanning) throw error;\n }\n });\n this._scanTransition = transition.catch(() => undefined);\n return transition;\n }\n\n /** Stop scanning but keep the discovered cache (used before connect). */\n private async _pauseScan(): Promise<void> {\n this._clearIdleStop();\n await this._setScanning(false);\n }\n\n /** Stop scanning and forget discovered devices (idle timeout / teardown). */\n private async _stopContinuousScan(): Promise<void> {\n this._scanOwners.clear();\n this._discovered.clear();\n this._lastSeen.clear();\n await this._pauseScan();\n }\n\n /** A vendor releasing discovery must not stop another vendor's scan. */\n async stopScan(vendor?: string): Promise<void> {\n if (vendor !== undefined) {\n this._scanOwners.delete(vendor);\n if (this._scanOwners.size > 0) return;\n }\n await this._stopContinuousScan();\n }\n\n /**\n * Look up a previously-scanned device by id (no extra BLE traffic).\n * Returns null if the device hasn't been seen by a recent scan.\n */\n getDevice(id: string): ThirdPartyBleDeviceInfo | null {\n const p = this._discovered.get(id);\n // The scan cache holds every advertiser in range; only a registered vendor's device is exposed.\n if (!p || ![...this._vendors.values()].some(profile => matchesPeripheral(p, profile))) {\n return null;\n }\n return peripheralToInfo(p);\n }\n\n /** Live RSSI (dBm) of a connected peripheral, else the cached scan-time value. */\n async readRssi(id: string): Promise<number> {\n const entry = this._requireEntry(id);\n if (entry.peripheral.updateRssiAsync) {\n return entry.peripheral.updateRssiAsync();\n }\n return entry.peripheral.rssi;\n }\n\n /** Abandon matching pairing attempts without touching another owner's links. */\n async cancelPairing(options?: { vendor: string; id?: string }): Promise<void> {\n const matches = (id: string, vendor?: string) =>\n options === undefined ||\n (vendor === options.vendor && (options.id === undefined || id === options.id));\n for (const attempt of this._connectAttempts) {\n if (!matches(attempt.id, attempt.vendor)) continue;\n attempt.abandon(new Error(`connect cancelled: ${attempt.id}`));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'cancelPairing.cancelConnect.error', {\n id: attempt.id,\n error: String(error),\n });\n }\n }\n if (options?.id === undefined) await this.stopScan(options?.vendor);\n for (const [id, entry] of this._connected) {\n if (matches(id, entry.vendor)) await this.disconnect(id).catch(() => undefined);\n }\n }\n\n /**\n * Scan until `id` is discovered, releasing only its own scan ownership. A\n * bonded Safe 7 goes silent, so callers fall back to `_directConnect`.\n */\n private async _scanUntilFound(\n id: string,\n timeoutMs: number,\n claim: ConnectClaim\n ): Promise<NoblePeripheralLike | undefined> {\n await this.init();\n if (claim.abandoned) return undefined;\n const existing = this._discovered.get(id);\n if (existing) return existing;\n const noble = this._requireNoble();\n const owner = Symbol('connect-scan');\n this._scanOwners.add(owner);\n return new Promise<NoblePeripheralLike | undefined>(resolve => {\n let done = false;\n const finish = (p?: NoblePeripheralLike) => {\n if (done) return;\n done = true;\n this._pendingCancellations.delete(cancel);\n clearTimeout(timer);\n noble.removeListener('discover', onDiscover);\n if (claim.cancelScan === cancel) claim.cancelScan = undefined;\n this._scanOwners.delete(owner);\n if (this._scanOwners.size === 0) {\n void this._pauseScan().then(() => resolve(p));\n } else {\n resolve(p);\n }\n };\n const onDiscover = (peripheral: NoblePeripheralLike) => {\n this._discovered.set(peripheral.id, peripheral);\n if (peripheral.id === id) finish(peripheral);\n };\n const timer = setTimeout(() => finish(this._discovered.get(id)), timeoutMs);\n const cancel = () => finish();\n this._pendingCancellations.add(cancel);\n claim.cancelScan = cancel;\n noble.on('discover', onDiscover);\n void this._setScanning(true).catch(() => finish());\n });\n }\n\n /**\n * Connect by id without advertisement: the only route to a linked, silent device.\n * Returns undefined instead of throwing so the caller reports \"device not found\".\n */\n private async _directConnect(id: string): Promise<NoblePeripheralLike | undefined> {\n const noble = this._requireNoble();\n if (typeof noble.connectAsync !== 'function') {\n // Distinguishes a stub noble from \"the device wasn't there\" in the log.\n this._log('warn', 'connect.direct.unavailable', { id });\n return undefined;\n }\n // warn, not info: reaching a silent bonded device this way is unproven on\n // real hardware, so it must always be logged.\n this._log('warn', 'connect.direct.start', { id });\n const startedAt = Date.now();\n try {\n // Bounded by the connect() timeout; noble-mac never resolves when it\n // cannot retrieve the peripheral.\n const peripheral = await noble.connectAsync(id);\n const resolved = peripheral ?? this._discovered.get(id);\n this._log('warn', 'connect.direct.done', {\n id,\n elapsedMs: Date.now() - startedAt,\n found: Boolean(resolved),\n // Anything but 'connected' later surfaces as a service-discovery error.\n state: resolved?.state,\n fromNoble: Boolean(peripheral),\n });\n return resolved;\n } catch (error) {\n this._log('warn', 'connect.direct.error', {\n id,\n elapsedMs: Date.now() - startedAt,\n error: String(error),\n });\n return undefined;\n }\n }\n\n // noble's disconnectAsync hangs after a failed connect (CoreBluetooth never\n // sends the disconnect event), so bound it.\n private async _safeDisconnect(peripheral: NoblePeripheralLike): Promise<void> {\n if (this._nativeReleased) return;\n await raceTimeout(\n peripheral.disconnectAsync().catch(() => undefined),\n BLE_DISCONNECT_TIMEOUT_MS\n );\n }\n\n // One timeout covers connect and service discovery; the connector maps\n // `timed out` (unreachable) and `connection failed` (stale bond) differently.\n async connect(\n id: string,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n if (\n !options ||\n !options.vendor ||\n ![options.serviceUuid, options.writeUuid, options.notifyUuid].every(\n uuid => typeof uuid === 'string' && /^[0-9a-f]{32}$/i.test(normalizeBleUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\n }\n // Before any native work: only a registered vendor's own GATT profile and framing.\n if (!this._resolveVendor(options.vendor).profile.acceptsConnect(options)) {\n throw new Error(`BLE GATT profile not allowed for ${options.vendor}`);\n }\n this._assertActive();\n // Promise.race cannot stop _connectInner; the claim makes a late success tear\n // its link down instead of committing an unowned one that silences the device.\n const claim: ConnectClaim = { abandoned: false };\n // Shared by the timeout and cancelPairing.\n let abandon!: (error: Error) => void;\n const abandoned = new Promise<never>((_, reject) => {\n abandon = (error: Error) => {\n claim.abandoned = true;\n reject(error);\n claim.cancelScan?.();\n };\n });\n const timer = setTimeout(\n () => abandon(new Error(`connect timed out after ${this._connectTimeoutMs}ms`)),\n this._connectTimeoutMs\n );\n const attempt = {\n id,\n vendor: options.vendor,\n abandon,\n cancelNative: () => claim.cancelNative?.(),\n settled: Promise.resolve<unknown>(undefined),\n };\n this._connectAttempts.add(attempt);\n const nativeOperation = this._connectInner(id, claim, options);\n const caller = (async () => {\n try {\n return await Promise.race([nativeOperation, abandoned]);\n } catch (error) {\n const peripheral = this._discovered.get(id);\n // Tear down only a link nobody owns, same guard as _connectInner.abortIfAbandoned.\n if (peripheral && !this._connected.has(id)) await this._safeDisconnect(peripheral);\n throw error;\n } finally {\n clearTimeout(timer);\n }\n })();\n // A rejected caller can still have a native connect or disconnect in flight.\n attempt.settled = Promise.allSettled([nativeOperation, caller]).finally(() => {\n this._connectAttempts.delete(attempt);\n // The connect cleared the idle stop; re-arm it so an owner left behind (a picker that\n // connected without stopScan) still expires instead of keeping the radio on.\n if (this._scanOwners.size > 0) this._armIdleStop();\n // Resume the radio only for a connect-scan (symbol) owner; a vendor's\n // idle scan owner must not restart it during pairing.\n const connectScanWaiting = [...this._scanOwners].some(owner => typeof owner === 'symbol');\n if (connectScanWaiting) {\n void this._setScanning(true).catch(() => undefined);\n }\n });\n return caller;\n }\n\n private async _connectInner(\n id: string,\n claim: ConnectClaim,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n let route: 'cache' | 'scan' | 'direct' | 'none' = 'cache';\n let peripheral: NoblePeripheralLike | undefined;\n // Stops the flow before it commits an unowned link; the rejection itself is\n // unobservable because Promise.race already settled.\n const abortIfAbandoned = async (stage: string) => {\n if (!claim.abandoned && !this._disposed) return;\n // A link still held in _connected belongs to a previous connect.\n if (peripheral && peripheral.state === 'connected' && !this._connected.has(id)) {\n await this._safeDisconnect(peripheral);\n }\n this._log('warn', 'connect.abandoned', { id, route, stage });\n throw new Error(`connect abandoned after timeout: ${id}`);\n };\n await this.init();\n await abortIfAbandoned('init');\n // Stop scanning (keep the cache) and let the radio settle before connecting.\n await this._pauseScan();\n await delay(BLE_CONNECT_SETTLE_MS);\n await abortIfAbandoned('settle');\n this._assertActive();\n peripheral = this._discovered.get(id);\n if (!peripheral) {\n route = 'scan';\n peripheral = await this._scanUntilFound(id, THIRD_PARTY_BLE_SCAN_DURATION_MS, claim);\n await abortIfAbandoned('scan');\n }\n if (!peripheral) {\n route = 'direct';\n // Deliberately after the scan: noble-mac never resolves connect-by-id for a\n // peripheral CoreBluetooth cannot retrieve (`NobleMac::Connect`).\n const native = this._requireNoble();\n claim.cancelNative = () => native.cancelConnect?.(id);\n peripheral = await this._directConnect(id);\n }\n if (!peripheral) {\n // discoveredCount=0 points at the radio/scan; counts only, see scan.empty.\n this._log('warn', 'connect.notFound', {\n id,\n route: 'none',\n discoveredCount: this._discovered.size,\n });\n throw new Error(`BLE device not found: ${id}`);\n }\n\n await abortIfAbandoned('resolve');\n\n const wasConnected = peripheral.state === 'connected';\n const connectingPeripheral = peripheral;\n const native = this._requireNoble();\n claim.cancelNative = () => {\n if (connectingPeripheral.state === 'connecting' && connectingPeripheral.cancelConnect) {\n connectingPeripheral.cancelConnect();\n } else {\n native.cancelConnect?.(id);\n }\n };\n this._log('warn', 'connect.route', {\n id,\n route,\n wasConnected,\n name: peripheral.advertisement?.localName,\n });\n if (!wasConnected) {\n await peripheral.connectAsync();\n await abortIfAbandoned('link');\n }\n\n try {\n const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(\n [options.serviceUuid],\n [options.writeUuid, options.notifyUuid]\n );\n const writeUuid = normalizeBleUuid(options.writeUuid);\n const notifyUuid = normalizeBleUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeBleUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeBleUuid(c.uuid) === notifyUuid);\n if (!writeChar || !notifyChar) {\n throw new Error(`BLE characteristics not found on device ${id}`);\n }\n // Last gate before commit: service discovery can also outlast the timeout.\n await abortIfAbandoned('discovery');\n\n const disconnectHandler = () => {\n // One peripheral outlives many links; a stale handler must not report\n // the current link as unexpectedly dropped.\n if (this._connected.get(id)?.disconnectHandler !== disconnectHandler) return;\n this._cleanupDevice(id, /* unexpected */ true);\n };\n peripheral.on('disconnect', disconnectHandler);\n\n this._connected.set(id, {\n peripheral,\n writeChar,\n notifyChar,\n disconnectHandler,\n vendor: options.vendor,\n write: options.write,\n });\n this._log('info', 'connect.done', { id, name: peripheral.advertisement.localName });\n return { id, name: peripheral.advertisement.localName };\n } catch (error) {\n // Don't leak a GATT link this call opened.\n if (!wasConnected) await this._safeDisconnect(peripheral);\n throw error;\n }\n }\n\n async disconnect(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry) return;\n if (entry.disconnectHandler) {\n // Suppress the unexpected-disconnect event for explicit disconnects.\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n try {\n await entry.peripheral.disconnectAsync();\n } catch (error) {\n this._log('warn', 'disconnect.error', { id, error: String(error) });\n }\n this._cleanupDevice(id, /* unexpected */ false, entry);\n }\n\n async subscribe(id: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.notifyChar) throw new Error(`BLE notify characteristic missing for ${id}`);\n if (entry.notifyHandler) return;\n const handler = (data: Buffer) => {\n this._onNotification?.(id, data.toString('hex'));\n };\n entry.notifyHandler = handler;\n entry.notifyChar.on('data', handler);\n await entry.notifyChar.subscribeAsync();\n }\n\n async unsubscribe(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry?.notifyChar) return;\n if (entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n entry.notifyHandler = undefined;\n }\n try {\n await entry.notifyChar.unsubscribeAsync();\n } catch (error) {\n this._log('warn', 'unsubscribe.error', { id, error: String(error) });\n }\n }\n\n async write(id: string, hexData: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.writeChar) throw new Error(`BLE write characteristic missing for ${id}`);\n const buffer = Buffer.from(hexData, 'hex');\n const framing = entry.write;\n if (!framing) throw new Error(`No BLE write framing recorded for ${id}`);\n\n if (framing.mode === 'raw') {\n // Self-framed, so padding corrupts it. Ledger negotiates the size per link\n // (0x08 handshake) in one byte, so it never exceeds 255.\n const maxLength = framing.maxLength ?? buffer.length;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hexData) || buffer.length > maxLength) {\n throw new Error(`Invalid BLE frame for ${entry.vendor ?? 'device'}`);\n }\n await entry.writeChar.writeAsync(buffer, false);\n return;\n }\n\n const { chunkSize } = framing;\n if (!chunkSize) throw new Error(`Padded BLE writes need a chunkSize for ${id}`);\n for (let offset = 0; offset < buffer.length; offset += chunkSize) {\n this._assertActive();\n const slice = buffer.subarray(offset, offset + chunkSize);\n // Firmware drops a short final packet (then RetriesExceeded), so every chunk\n // is zero-filled to full size, as in trezor-suite transport-bluetooth.\n const chunk = Buffer.alloc(chunkSize);\n slice.copy(chunk);\n // OneKey uses writeWithResponse for stability; mirror that.\n await entry.writeChar.writeAsync(chunk, false);\n if (offset + chunkSize < buffer.length && framing.chunkDelayMs) {\n await delay(framing.chunkDelayMs);\n }\n }\n }\n\n /** Retire a renderer's handler without stopping a process-wide native manager. */\n dispose(): Promise<void> {\n if (this._disposePromise) return this._disposePromise;\n this._disposed = true;\n this._clearIdleStop();\n this._scanning = false;\n this._onNotification = undefined;\n this._onDeviceDisconnected = undefined;\n const connections = Array.from(this._connectAttempts);\n for (const attempt of connections) {\n attempt.abandon(new Error('Desktop BLE is shutting down'));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'dispose.cancelConnect.error', { error: String(error) });\n }\n }\n for (const cancel of this._pendingCancellations) cancel();\n if (this._noble && this._discoverHandler) {\n this._noble.removeListener('discover', this._discoverHandler);\n }\n const entries = Array.from(this._connected.entries());\n for (const [id] of entries) this._cleanupDevice(id, false);\n this._disposePromise = (async () => {\n try {\n await raceTimeout(\n Promise.allSettled([\n ...connections.map(attempt => attempt.settled),\n ...Array.from(this._nobleInstances, async instance => instance.stopScanningAsync()),\n ...entries.map(async ([, entry]) => {\n try {\n await raceTimeout(\n entry.notifyChar?.unsubscribeAsync().catch(() => undefined),\n 250\n );\n } finally {\n await this._safeDisconnect(entry.peripheral);\n }\n }),\n ]),\n 3500,\n () => this._log('warn', 'dispose.timeout')\n );\n } finally {\n this._discovered.clear();\n this._lastSeen.clear();\n this._initialized = false;\n }\n })();\n return this._disposePromise;\n }\n\n /**\n * Terminal native release, including recovered instances. A host sharing noble\n * must defer stop() until every transport disposed, and dedupe the instances.\n */\n disposeForAppQuit(\n releaseNoble: (instance: { stop?(): void }) => void = instance => instance.stop?.()\n ): Promise<void> {\n if (!this._releasePromise) {\n this._releasePromise = this.dispose().finally(() => {\n this._nativeReleased = true;\n let releaseError: Error | undefined;\n for (const instance of this._nobleInstances) {\n try {\n releaseNoble(instance);\n } catch (error) {\n releaseError = error instanceof Error ? error : new Error(String(error));\n }\n }\n this._nobleInstances.clear();\n if (releaseError) throw releaseError;\n this._log('info', 'dispose.native.done');\n });\n }\n return this._releasePromise;\n }\n\n private _assertActive(): void {\n if (this._disposed) throw new Error('Desktop BLE is shutting down');\n }\n\n private _cleanupDevice(id: string, unexpected: boolean, expected?: DeviceEntry): void {\n const entry = this._connected.get(id);\n if (!entry) return;\n // A reconnect may have replaced the entry while the caller awaited.\n // A stale caller must not tear down the link it no longer owns.\n if (expected && entry !== expected) return;\n if (entry.notifyChar && entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n }\n // Otherwise a later explicit disconnect is reported as unexpected.\n if (entry.disconnectHandler) {\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n this._connected.delete(id);\n if (unexpected) {\n this._log('warn', 'disconnect.unexpected', { id });\n this._onDeviceDisconnected?.(id);\n }\n }\n\n private _requireEntry(id: string): DeviceEntry {\n this._assertActive();\n const entry = this._connected.get(id);\n if (!entry) throw new Error(`BLE device is not connected: ${id}`);\n return entry;\n }\n\n private _requireNoble(): NobleLike {\n this._assertActive();\n if (!this._noble) throw new Error('Desktop BLE: noble was not initialized');\n return this._noble;\n }\n\n private async _waitForPoweredOn(timeoutMs: number): Promise<void> {\n const noble = this._requireNoble();\n if (noble.state === 'poweredOn') return;\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n noble.removeListener('stateChange', handler);\n this._pendingCancellations.delete(cancel);\n };\n const cancel = () => {\n cleanup();\n reject(new Error('Desktop BLE is shutting down'));\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(\n new Error(\n `Desktop BLE: noble did not reach poweredOn within ${timeoutMs}ms (last state: ${noble.state})`\n )\n );\n }, timeoutMs);\n const handler = (state: string) => {\n if (state === 'poweredOn') {\n cleanup();\n resolve();\n } else if (state === 'unsupported' || state === 'unauthorized') {\n cleanup();\n reject(new Error(`Desktop BLE: noble state ${state}`));\n }\n };\n this._pendingCancellations.add(cancel);\n noble.on('stateChange', handler);\n });\n }\n\n private _log(level: BleDebugLogLevel, event: string, data?: Record<string, unknown>): void {\n this._logger?.({\n level,\n scope: 'desktop-noble-ble',\n event,\n data: redactBleDebugLogData(data),\n });\n }\n}\n","import { THIRD_PARTY_BLE_CHANNELS } from './constants';\nimport { NobleBleHandler } from './NobleBleHandler';\n\nimport type { NobleBleHandlerOptions } from './NobleBleHandler';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/** Minimal slice of Electron's `WebContents` we use (kept duck-typed so we\n * don't take a hard dep on `electron`). */\nexport interface WebContentsLike {\n send(channel: string, ...args: unknown[]): void;\n on?(event: string, listener: (...args: any[]) => void): void;\n}\n\n/** Minimal slice of Electron's `ipcMain` we use. */\nexport interface IpcMainLike {\n handle(\n channel: string,\n listener: (event: unknown, ...args: any[]) => Promise<unknown> | unknown\n ): void;\n removeHandler(channel: string): void;\n}\n\nexport interface InitThirdPartyBleSupportOptions extends NobleBleHandlerOptions {\n /** Inject your own `ipcMain` (defaults to `require('electron').ipcMain`). */\n ipcMain?: IpcMainLike;\n}\n\nexport interface ThirdPartyBleSupportHandle {\n handler: NobleBleHandler;\n dispose(): Promise<void>;\n disposeForAppQuit(releaseNoble?: (instance: { stop?(): void }) => void): Promise<void>;\n}\n\nconst DEFAULT_IPC_MAIN: () => IpcMainLike = () => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { ipcMain } = require('electron') as { ipcMain: IpcMainLike };\n return ipcMain;\n};\n\n/**\n * Wire a `NobleBleHandler` to Electron IPC once `BrowserWindow` is ready. Call dispose()\n * when retiring a renderer and disposeForAppQuit() before Node teardown.\n */\nexport function initThirdPartyBleSupport(\n webContents: WebContentsLike,\n options: InitThirdPartyBleSupportOptions = {}\n): ThirdPartyBleSupportHandle {\n const ipcMain = options.ipcMain ?? DEFAULT_IPC_MAIN();\n const handler = new NobleBleHandler(options);\n let disposed = false;\n\n handler.setNotificationListener((id, hexData) => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.notification, id, hexData);\n });\n handler.setDisconnectedListener(id => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.disconnected, id);\n });\n\n const handle = <T>(channel: string, fn: (...args: any[]) => Promise<T> | T): void => {\n ipcMain.handle(channel, async (_event, ...args) => {\n if (disposed) throw new Error('Third-party BLE is shutting down');\n return fn(...args);\n });\n };\n\n handle(THIRD_PARTY_BLE_CHANNELS.scan, (options?: ElectronBleScanOptions) =>\n handler.scan(options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.stopScan, (vendor?: string) => handler.stopScan(vendor));\n handle(THIRD_PARTY_BLE_CHANNELS.connect, (id: string, options: ElectronBleConnectOptions) =>\n handler.connect(id, options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.disconnect, (id: string) => handler.disconnect(id));\n handle(THIRD_PARTY_BLE_CHANNELS.write, (id: string, hexData: string) =>\n handler.write(id, hexData)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.subscribe, (id: string) => handler.subscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.unsubscribe, (id: string) => handler.unsubscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.availability, () => handler.checkAvailability());\n handle(THIRD_PARTY_BLE_CHANNELS.getDevice, (id: string) => handler.getDevice(id));\n handle(THIRD_PARTY_BLE_CHANNELS.readRssi, (id: string) => handler.readRssi(id));\n handle(THIRD_PARTY_BLE_CHANNELS.cancelPairing, (options?: { vendor: string; id?: string }) =>\n handler.cancelPairing(options)\n );\n\n const removeHandlers = () => {\n if (disposed) return;\n disposed = true;\n for (const channel of Object.values(THIRD_PARTY_BLE_CHANNELS)) {\n ipcMain.removeHandler(channel);\n }\n };\n\n return {\n handler,\n dispose: () => {\n removeHandlers();\n return handler.dispose();\n },\n disposeForAppQuit: releaseNoble => {\n removeHandlers();\n return handler.disposeForAppQuit(releaseNoble);\n },\n };\n}\n"],"mappings":"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","names":["native","options"]}
1
+ {"version":3,"sources":["../src/debugLog.ts","../src/vendorProfiles.ts","../src/NobleBleHandler.ts","../src/main.ts"],"sourcesContent":["export type BleDebugLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type BleDebugLogEntry = {\n level: BleDebugLogLevel;\n scope: string;\n event: string;\n data?: Record<string, unknown>;\n};\n\nexport type BleDebugLogger = (entry: BleDebugLogEntry) => void;\n\n/**\n * Keys whose values never belong in a log, kept narrow to real secrets and\n * key material. Raw frames (`packetHex`, `hexData`, `payload`) stay only because\n * they are Trezor THP ciphertext or Ledger APDUs; a vendor with a cleartext\n * protocol must move them into this list.\n */\nconst REDACTED_KEYS: ReadonlySet<string> = new Set([\n // Secrets the user types or the device derives.\n 'pin',\n 'passphrase',\n 'mnemonic',\n 'seed',\n 'seedPhrase',\n 'entropy',\n // Private key material. Public keys stay: device identity, address\n // derivation and fingerprint mismatches are all diagnosed from them.\n 'privateKey',\n 'privKey',\n 'secretKey',\n 'hostKey',\n 'trezorKey',\n 'host_static_key',\n // Pairing / session credentials. The THP static key pair is part of the\n // pairing credential, so it goes here rather than with the public keys.\n 'trezor_static_public_key',\n 'credential',\n 'credentials',\n 'sessionKey',\n 'token',\n 'accessToken',\n 'authToken',\n]);\n\n/** Guards against a cyclic or pathological `data` bag stalling the log call. */\nconst MAX_REDACT_DEPTH = 8;\n\n/**\n * Keep the shape of what was dropped so two logs still line up: a 32-byte\n * value that became 33 bytes is visible without the value itself.\n */\nfunction describeRedacted(value: unknown): string {\n if (typeof value === 'string') return `<redacted ${value.length} chars>`;\n if (ArrayBuffer.isView(value)) return `<redacted ${value.byteLength} bytes>`;\n if (Array.isArray(value)) return `<redacted ${value.length} items>`;\n return '<redacted>';\n}\n\nfunction redactValue(key: string, value: unknown, depth: number, seen: WeakSet<object>): unknown {\n if (REDACTED_KEYS.has(key)) return describeRedacted(value);\n if (!value || typeof value !== 'object') return value;\n // Errors carry message/stack non-enumerably; recursing would empty them.\n // Typed arrays would turn into index-keyed objects. Both are forwarded whole.\n if (value instanceof Error || ArrayBuffer.isView(value)) return value;\n if (depth >= MAX_REDACT_DEPTH) return '<redacted depth limit>';\n if (seen.has(value)) return '<circular>';\n seen.add(value);\n if (Array.isArray(value)) {\n return value.map(item => redactValue(key, item, depth + 1, seen));\n }\n const out: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value as Record<string, unknown>)) {\n out[childKey] = redactValue(childKey, childValue, depth + 1, seen);\n }\n return out;\n}\n\n/** Replace sensitive values at any depth; everything else is forwarded verbatim. */\nexport function redactBleDebugLogData(\n data?: Record<string, unknown>\n): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const seen = new WeakSet<object>();\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n out[key] = redactValue(key, value, 0, seen);\n }\n return out;\n}\n","import type { ElectronBleConnectOptions } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Main-process allowlist entry for one vendor key. The renderer only names the vendor; which\n * peripherals it may see and which GATT profile and framing it may use are decided here, so an\n * IPC caller cannot turn the service into a generic BLE proxy. UUID arguments may arrive in any\n * case, with or without dashes.\n */\nexport interface ThirdPartyBleVendorProfile {\n /** Whether an advertised service UUID belongs to this vendor. */\n isServiceUuid(uuid: string): boolean;\n /** Whether a local name belongs to this vendor; the handler passes a length-capped name. */\n isName?(name: string): boolean;\n /** Whether this GATT profile and write framing is one the vendor's firmware uses. */\n acceptsConnect(options: ElectronBleConnectOptions): boolean;\n}\n\nexport const normalizeBleUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\nconst isUuidString = (value: unknown): value is string => typeof value === 'string';\n\nconst isIntegerInRange = (value: unknown, min: number, max: number): value is number =>\n typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;\n\n// Duplicated from @onekeyfe/hwk-trezor-adapter (TREZOR_BLE_UUIDS, TREZOR_BLE_PACKET_SIZE) to keep\n// this package free of vendor dependencies; the Trezor connector tests pin the two together.\nconst TREZOR_SERVICE_UUID = normalizeBleUuid('8c000001-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_WRITE_UUID = normalizeBleUuid('8c000002-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_NOTIFY_UUID = normalizeBleUuid('8c000003-a59b-4d58-a9ad-073df69fa1b1');\nconst TREZOR_PACKET_SIZE = 244;\nconst TREZOR_MAX_CHUNK_DELAY_MS = 50;\nconst TREZOR_NAME_BRAND = /\\bTrezor\\b/i;\nconst TREZOR_NAME_MODEL = /\\bSafe\\s*7\\b|\\bT3W1\\b/i;\n\nconst trezorProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => isUuidString(uuid) && normalizeBleUuid(uuid) === TREZOR_SERVICE_UUID,\n isName: name => TREZOR_NAME_BRAND.test(name) && TREZOR_NAME_MODEL.test(name),\n acceptsConnect: options => {\n const { write } = options;\n return (\n isUuidString(options.serviceUuid) &&\n isUuidString(options.writeUuid) &&\n isUuidString(options.notifyUuid) &&\n normalizeBleUuid(options.serviceUuid) === TREZOR_SERVICE_UUID &&\n normalizeBleUuid(options.writeUuid) === TREZOR_WRITE_UUID &&\n normalizeBleUuid(options.notifyUuid) === TREZOR_NOTIFY_UUID &&\n typeof write === 'object' &&\n write !== null &&\n write.mode === 'padded' &&\n write.chunkSize === TREZOR_PACKET_SIZE &&\n (write.chunkDelayMs === undefined ||\n isIntegerInRange(write.chunkDelayMs, 0, TREZOR_MAX_CHUNK_DELAY_MS)) &&\n write.maxLength === undefined\n );\n },\n};\n\n// DMK's GATT family 13d63400-2c97-MMMM-000R-4c6564676572: MMMM is the model (0004 Nano X,\n// 6004 Stax, 3004 Flex, 8004/9004 Nano Gen5), R the role. Any model segment is accepted so a\n// new model works without a release here.\nconst LEDGER_UUID = /^13d634002c97([0-9a-f]{4})000([0-3])4c6564676572$/;\nconst LEDGER_ROLE_SERVICE = '0';\nconst LEDGER_ROLE_NOTIFY = '1';\nconst LEDGER_ROLE_WRITE = '2';\nconst LEDGER_ROLE_WRITE_CMD = '3';\n// The 0x08 handshake reports the frame size in one byte.\nconst LEDGER_MAX_FRAME_SIZE = 255;\n\nconst parseLedgerUuid = (uuid: unknown): { model: string; role: string } | undefined => {\n if (!isUuidString(uuid)) return undefined;\n const parsed = LEDGER_UUID.exec(normalizeBleUuid(uuid));\n return parsed ? { model: parsed[1], role: parsed[2] } : undefined;\n};\n\nconst ledgerProfile: ThirdPartyBleVendorProfile = {\n isServiceUuid: uuid => parseLedgerUuid(uuid)?.role === LEDGER_ROLE_SERVICE,\n acceptsConnect: options => {\n const service = parseLedgerUuid(options.serviceUuid);\n const notify = parseLedgerUuid(options.notifyUuid);\n const write = parseLedgerUuid(options.writeUuid);\n const framing = options.write;\n return (\n service?.role === LEDGER_ROLE_SERVICE &&\n notify?.role === LEDGER_ROLE_NOTIFY &&\n (write?.role === LEDGER_ROLE_WRITE || write?.role === LEDGER_ROLE_WRITE_CMD) &&\n notify.model === service.model &&\n write.model === service.model &&\n typeof framing === 'object' &&\n framing !== null &&\n framing.mode === 'raw' &&\n isIntegerInRange(framing.maxLength, 1, LEDGER_MAX_FRAME_SIZE) &&\n framing.chunkSize === undefined &&\n framing.chunkDelayMs === undefined\n );\n },\n};\n\n/**\n * Vendors the desktop BLE service serves by default, keyed by the vendor string the connectors\n * send. A host passing `vendors` replaces this map, so spread it to add a vendor.\n */\nexport const DEFAULT_THIRD_PARTY_BLE_VENDORS: Readonly<Record<string, ThirdPartyBleVendorProfile>> =\n Object.freeze({\n ledger: ledgerProfile,\n trezor: trezorProfile,\n });\n","import { type BleDebugLogLevel, type BleDebugLogger, redactBleDebugLogData } from './debugLog';\nimport {\n THIRD_PARTY_BLE_DEVICE_TTL_MS,\n THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,\n THIRD_PARTY_BLE_SCAN_DURATION_MS,\n THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,\n THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS,\n} from './constants';\nimport { DEFAULT_THIRD_PARTY_BLE_VENDORS, normalizeBleUuid } from './vendorProfiles';\n\nimport type { ThirdPartyBleVendorProfile } from './vendorProfiles';\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Subset of @stoprocent/noble we touch. We type as `any` to keep the package\n * installable without the native module — the default factory loads it on first use.\n */\nexport interface NobleLike {\n state: string;\n on(event: string, handler: (...args: any[]) => void): NobleLike;\n removeListener(event: string, handler: (...args: any[]) => void): NobleLike;\n startScanningAsync(serviceUuids: string[], allowDuplicates: boolean): Promise<void>;\n stopScanningAsync(): Promise<void>;\n stop?(): void;\n /**\n * Connect by id with no scan; both backends emit `discover` as a side effect\n * (win `BLEManager::Connect`, mac `retrievePeripheralsWithIdentifiers`).\n */\n connectAsync?(idOrAddress: string): Promise<NoblePeripheralLike | undefined>;\n cancelConnect?(idOrAddress: string): void;\n reset?(): Promise<void>;\n}\n\nexport interface NoblePeripheralLike {\n id: string;\n // Every advertisement field, so the host can look for a cross-transport\n // identity (e.g. a serial in manufacturerData) without another scan.\n advertisement: {\n localName?: string;\n serviceUuids?: string[];\n manufacturerData?: Buffer;\n serviceData?: Array<{ uuid: string; data: Buffer }>;\n txPowerLevel?: number;\n serviceSolicitationUuids?: string[];\n };\n /** Some noble builds expose the BLE MAC/address separately from `id`. */\n address?: string;\n addressType?: string;\n connectable?: boolean;\n rssi: number;\n state: string;\n connectAsync(): Promise<void>;\n cancelConnect?(): void;\n disconnectAsync(): Promise<void>;\n discoverSomeServicesAndCharacteristicsAsync(\n serviceUuids: string[],\n characteristicUuids: string[]\n ): Promise<{ characteristics: NobleCharacteristicLike[] }>;\n /** Read live RSSI from the connected peripheral. Returns dBm. */\n updateRssiAsync?(): Promise<number>;\n on(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n removeListener(event: string, handler: (...args: any[]) => void): NoblePeripheralLike;\n}\n\nexport interface NobleCharacteristicLike {\n uuid: string;\n subscribeAsync(): Promise<void>;\n unsubscribeAsync(): Promise<void>;\n writeAsync(data: Buffer, withoutResponse: boolean): Promise<void>;\n on(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n removeListener(\n event: 'data',\n handler: (data: Buffer, isNotification: boolean) => void\n ): NobleCharacteristicLike;\n}\n\nexport type NobleFactory = () => NobleLike;\n\nconst DEFAULT_NOBLE_FACTORY: NobleFactory = () => {\n // Lazy require so packagers don't bundle the native module at import time.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const noble = require('@stoprocent/noble') as NobleLike;\n return noble;\n};\n\ninterface ConnectClaim {\n abandoned: boolean;\n cancelScan?: () => void;\n cancelNative?: () => void;\n}\n\ninterface DeviceEntry {\n /** Registered vendor key; results and links never mix across it. */\n vendor?: string;\n write?: ElectronBleConnectOptions['write'];\n peripheral: NoblePeripheralLike;\n writeChar?: NobleCharacteristicLike;\n notifyChar?: NobleCharacteristicLike;\n notifyHandler?: (data: Buffer, isNotification: boolean) => void;\n disconnectHandler?: () => void;\n}\n\n// Longer than any real vendor name, and bounds the work a vendor name rule does per advertisement.\nconst BLE_NAME_MATCH_MAX_LENGTH = 64;\n\n/**\n * Stands in for the scan filter we cannot use (see `scan`). Only the vendor profile decides; the\n * caller's service UUIDs can narrow the profile's set, and caller name patterns are never run.\n */\nconst matchesPeripheral = (\n p: NoblePeripheralLike,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n): boolean => {\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n advertised.some(\n uuid =>\n typeof uuid === 'string' &&\n profile.isServiceUuid(uuid) &&\n (!narrowTo || narrowTo.has(normalizeBleUuid(uuid)))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!profile.isName || typeof name !== 'string' || !name) return false;\n return profile.isName(name.slice(0, BLE_NAME_MATCH_MAX_LENGTH));\n};\n\n/** Renderer `serviceUuids` may only narrow the vendor's set; anything malformed is ignored. */\nconst requestedServiceUuids = (options?: ElectronBleScanOptions): Set<string> | undefined => {\n const requested = options?.match ? options.match.serviceUuids : options?.serviceUuids;\n if (!Array.isArray(requested)) return undefined;\n const uuids = requested.filter((uuid): uuid is string => typeof uuid === 'string');\n return uuids.length ? new Set(uuids.map(normalizeBleUuid)) : undefined;\n};\n\n/** Hex-encodes buffers so the info survives the IPC structured clone. */\nconst peripheralToInfo = (p: NoblePeripheralLike): ThirdPartyBleDeviceInfo => {\n const adv = p.advertisement ?? {};\n return {\n id: p.id,\n name: adv.localName,\n localName: adv.localName,\n rssi: p.rssi,\n isConnectable: p.connectable ?? null,\n advertisedServiceUuids: adv.serviceUuids,\n serviceSolicitationUuids: adv.serviceSolicitationUuids,\n txPowerLevel: adv.txPowerLevel,\n manufacturerDataHex: adv.manufacturerData\n ? Buffer.from(adv.manufacturerData).toString('hex')\n : undefined,\n serviceData: adv.serviceData?.map(entry => ({\n uuid: entry.uuid,\n dataHex: Buffer.from(entry.data).toString('hex'),\n })),\n address: p.address,\n addressType: p.addressType,\n state: p.state,\n };\n};\n\nconst delay = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));\n\n/** Waits for `work` or `ms`, whichever comes first; never rejects on timeout. */\nconst raceTimeout = async (\n work: Promise<unknown> | undefined,\n ms: number,\n onTimeout?: () => void\n): Promise<void> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n work,\n new Promise<void>(resolve => {\n timer = setTimeout(() => {\n onTimeout?.();\n resolve();\n }, ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n};\n\n// Radio-settle wait between stopping the scan and opening a GATT connection.\nconst BLE_CONNECT_SETTLE_MS = 300;\n// noble has no connect timeout. SMP pairing timeout (30s, the OS dialog's\n// limit) + 1s, so a first-time pairing is never cut off before the OS gives up.\nconst BLE_CONNECT_TIMEOUT_MS = 31_000;\n// Cap on a cleanup disconnectAsync, which hangs on a just-failed connect.\nconst BLE_DISCONNECT_TIMEOUT_MS = 2_000;\n\n/** Floor between noble rebuilds, so a persistent failure can't thrash. */\nconst NOBLE_RECOVER_COOLDOWN_MS = 10_000;\n\nexport interface NobleBleHandlerOptions {\n /** Override for tests; defaults to `require('@stoprocent/noble')`. */\n nobleFactory?: NobleFactory;\n /** Override the overall connect timeout (tests only; defaults to 31s). */\n connectTimeoutMs?: number;\n logger?: BleDebugLogger;\n /**\n * Vendors served over IPC, keyed by the `vendor` the renderer sends. Replaces the default map\n * (spread `DEFAULT_THIRD_PARTY_BLE_VENDORS` to add one); unknown vendors are rejected.\n */\n vendors?: Record<string, ThirdPartyBleVendorProfile>;\n}\n\n/** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */\nexport class NobleBleHandler {\n private _noble: NobleLike | undefined;\n\n private readonly _factory: NobleFactory;\n\n private readonly _connectTimeoutMs: number;\n\n private readonly _logger?: NobleBleHandlerOptions['logger'];\n\n private readonly _vendors: ReadonlyMap<string, ThirdPartyBleVendorProfile>;\n\n private readonly _discovered = new Map<string, NoblePeripheralLike>();\n\n // id -> last advertisement time (snapshot TTL).\n private readonly _lastSeen = new Map<string, number>();\n\n private readonly _connected = new Map<string, DeviceEntry>();\n\n private _discoverHandler?: (peripheral: NoblePeripheralLike) => void;\n\n private _scanning = false;\n\n private readonly _scanOwners = new Set<string | symbol | undefined>();\n\n private _scanTransition: Promise<void> = Promise.resolve();\n\n private _idleStopTimer?: ReturnType<typeof setTimeout>;\n\n private _onNotification?: (id: string, hexData: string) => void;\n\n private _onDeviceDisconnected?: (id: string) => void;\n\n private _initialized = false;\n\n private _disposed = false;\n\n private _disposePromise?: Promise<void>;\n\n private _releasePromise?: Promise<void>;\n\n private _initPromise?: Promise<void>;\n\n private readonly _nobleInstances = new Set<NobleLike>();\n\n private readonly _pendingCancellations = new Set<() => void>();\n\n private readonly _connectAttempts = new Set<{\n id: string;\n vendor: string;\n abandon: (error: Error) => void;\n cancelNative: () => void;\n settled: Promise<unknown>;\n }>();\n\n private _nativeReleased = false;\n\n private _lastNobleRecoverAt?: number;\n\n constructor(options: NobleBleHandlerOptions = {}) {\n this._factory = options.nobleFactory ?? DEFAULT_NOBLE_FACTORY;\n this._connectTimeoutMs = options.connectTimeoutMs ?? BLE_CONNECT_TIMEOUT_MS;\n this._logger = options.logger;\n // A Map, so an IPC vendor like `__proto__` cannot reach an inherited property.\n this._vendors = new Map(Object.entries(options.vendors ?? DEFAULT_THIRD_PARTY_BLE_VENDORS));\n }\n\n private _resolveVendor(vendor: unknown): {\n vendor: string;\n profile: ThirdPartyBleVendorProfile;\n } {\n const profile = typeof vendor === 'string' ? this._vendors.get(vendor) : undefined;\n if (typeof vendor !== 'string' || !profile) throw new Error('Unsupported BLE vendor');\n return { vendor, profile };\n }\n\n setNotificationListener(handler: (id: string, hexData: string) => void): void {\n this._onNotification = handler;\n }\n\n setDisconnectedListener(handler: (id: string) => void): void {\n this._onDeviceDisconnected = handler;\n }\n\n async init(): Promise<void> {\n this._assertActive();\n if (this._initialized) return;\n if (this._initPromise) return this._initPromise;\n this._initPromise = (async () => {\n this._noble ??= this._factory();\n this._nobleInstances.add(this._noble);\n this._discoverHandler ??= peripheral => {\n this._discovered.set(peripheral.id, peripheral);\n this._lastSeen.set(peripheral.id, Date.now());\n };\n this._noble.removeListener('discover', this._discoverHandler);\n this._noble.on('discover', this._discoverHandler);\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._assertActive();\n this._initialized = true;\n })();\n try {\n await this._initPromise;\n } finally {\n this._initPromise = undefined;\n }\n }\n\n async checkAvailability(): Promise<ThirdPartyBleAvailability> {\n this._assertActive();\n if (!this._noble) {\n try {\n this._noble = this._factory();\n this._nobleInstances.add(this._noble);\n } catch {\n return { available: false, state: 'unsupported', initialized: false };\n }\n }\n await this._waitForKnownState(THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS);\n this._assertActive();\n const state = this._noble?.state ?? 'unknown';\n return {\n available: state === 'poweredOn',\n state,\n initialized: this._initialized,\n };\n }\n\n /**\n * Lazy-start an unfiltered scan and return the current snapshot. noble-win filters per\n * packet, and a Safe 7 ADV carries only its name (UUID is in the scan response).\n */\n async scan(options?: ElectronBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]> {\n // Before any radio or owner bookkeeping: an unregistered caller gets nothing.\n const { vendor, profile } = this._resolveVendor(options?.vendor);\n // Claim before init: a stopScan/cancelPairing during the power-on wait must\n // find this owner, or the scan revives once the adapter powers on.\n this._scanOwners.add(vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(vendor);\n throw error;\n }\n if (!this._scanOwners.has(vendor)) return [];\n try {\n await this._setScanning(true);\n } catch (error) {\n this._log('warn', 'scan.start.error', { error: String(error) });\n await this._recoverNobleIfStuck(String(error));\n }\n this._assertActive();\n this._armIdleStop();\n const devices = this._snapshot(vendor, profile, requestedServiceUuids(options));\n // raw=0: nothing on air; raw>0: the match criteria dropped it. Counts only,\n // so bystanders' devices from the unfiltered scan stay out of support logs.\n if (devices.length === 0) {\n this._log('warn', 'scan.empty', {\n raw: this._discovered.size,\n kept: 0,\n named: [...this._discovered.values()].filter(p => p.advertisement?.localName).length,\n });\n }\n return devices;\n }\n\n /**\n * In-range devices matching the caller, minus TTL-expired ones. `_discovered`\n * is only a cache; a missing device can still be reached by `_directConnect`.\n */\n private _snapshot(\n vendor: string,\n profile: ThirdPartyBleVendorProfile,\n narrowTo?: Set<string>\n ): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const result: ThirdPartyBleDeviceInfo[] = [];\n for (const [id, peripheral] of this._discovered) {\n if (now - (this._lastSeen.get(id) ?? 0) > THIRD_PARTY_BLE_DEVICE_TTL_MS) {\n this._discovered.delete(id);\n this._lastSeen.delete(id);\n continue;\n }\n if (!matchesPeripheral(peripheral, profile, narrowTo)) continue;\n result.push(peripheralToInfo(peripheral));\n }\n // A linked device stops advertising and ages out above, so merge held links\n // back in (field-verified on Safe 7: holding the link silences it).\n for (const [id, entry] of this._connected) {\n if (entry.vendor !== vendor) continue;\n if (result.some(info => info.id === id)) continue;\n result.push(peripheralToInfo(entry.peripheral));\n }\n return result;\n }\n\n /** A noble instance with its own bindings, so a stuck one can be replaced. */\n private _createFreshNoble(): NobleLike {\n const candidate = this._factory() as NobleLike & {\n withBindings?: () => NobleLike;\n };\n return typeof candidate.withBindings === 'function' ? candidate.withBindings() : candidate;\n }\n\n /**\n * Rebuild noble when Windows BLE re-enumeration latched its RadioWatcher at\n * `unsupported`; fresh bindings restart the watcher without an app restart.\n */\n private async _recoverNobleIfStuck(reason: string): Promise<void> {\n if (this._disposed) return;\n const state = this._noble?.state;\n if (state === 'poweredOn' || !this._initialized) return;\n // Never tear down bindings out from under a live link.\n if (this._connected.size > 0) {\n this._log('warn', 'noble.recover.skip', {\n reason,\n state,\n connected: this._connected.size,\n });\n return;\n }\n const now = Date.now();\n if (this._lastNobleRecoverAt && now - this._lastNobleRecoverAt < NOBLE_RECOVER_COOLDOWN_MS) {\n return;\n }\n this._lastNobleRecoverAt = now;\n this._log('warn', 'noble.recover.start', { reason, state });\n try {\n const previous = this._noble;\n if (previous && this._discoverHandler) {\n previous.removeListener('discover', this._discoverHandler);\n }\n // The default factory returns the stuck module singleton; `withBindings()`\n // mints a new instance. Injected factories are assumed to return fresh ones.\n const fresh = this._createFreshNoble();\n this._noble = fresh;\n this._nobleInstances.add(fresh);\n if (this._discoverHandler) {\n fresh.on('discover', this._discoverHandler);\n }\n this._scanning = false;\n this._discovered.clear();\n this._lastSeen.clear();\n await this._waitForPoweredOn(THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS);\n this._log('warn', 'noble.recover.done', { state: this._noble?.state });\n } catch (error) {\n // Recovery is best-effort; the caller already reported the scan failure.\n this._log('warn', 'noble.recover.error', { error: String(error) });\n }\n }\n\n private _armIdleStop(): void {\n if (this._disposed) return;\n this._clearIdleStop();\n this._idleStopTimer = setTimeout(() => {\n void this._stopContinuousScan();\n }, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS);\n }\n\n private _clearIdleStop(): void {\n if (this._idleStopTimer) {\n clearTimeout(this._idleStopTimer);\n this._idleStopTimer = undefined;\n }\n }\n\n // Keep native start/stop callbacks ordered: a late stop must finish before\n // a newer scan starts, even when callers belong to different vendors.\n private _setScanning(scanning: boolean): Promise<void> {\n const transition = this._scanTransition.then(async () => {\n if (scanning) {\n this._assertActive();\n if (this._scanOwners.size === 0) return;\n }\n if (this._scanning === scanning) return;\n this._scanning = scanning;\n try {\n if (scanning) {\n await this._requireNoble().startScanningAsync([], true);\n this._log('warn', 'scan.start', { allowDuplicates: true });\n } else {\n await this._noble?.stopScanningAsync();\n }\n } catch (error) {\n this._scanning = false;\n if (scanning) throw error;\n }\n });\n this._scanTransition = transition.catch(() => undefined);\n return transition;\n }\n\n /** Stop scanning but keep the discovered cache (used before connect). */\n private async _pauseScan(): Promise<void> {\n this._clearIdleStop();\n await this._setScanning(false);\n }\n\n /** Stop scanning and forget discovered devices (idle timeout / teardown). */\n private async _stopContinuousScan(): Promise<void> {\n this._scanOwners.clear();\n this._discovered.clear();\n this._lastSeen.clear();\n await this._pauseScan();\n }\n\n /** A vendor releasing discovery must not stop another vendor's scan. */\n async stopScan(vendor?: string): Promise<void> {\n if (vendor !== undefined) {\n this._scanOwners.delete(vendor);\n if (this._scanOwners.size > 0) return;\n }\n await this._stopContinuousScan();\n }\n\n /**\n * Look up a previously-scanned device by id (no extra BLE traffic).\n * Returns null if the device hasn't been seen by a recent scan.\n */\n getDevice(id: string): ThirdPartyBleDeviceInfo | null {\n const p = this._discovered.get(id);\n // The scan cache holds every advertiser in range; only a registered vendor's device is exposed.\n if (!p || ![...this._vendors.values()].some(profile => matchesPeripheral(p, profile))) {\n return null;\n }\n return peripheralToInfo(p);\n }\n\n /** Live RSSI (dBm) of a connected peripheral, else the cached scan-time value. */\n async readRssi(id: string): Promise<number> {\n const entry = this._requireEntry(id);\n if (entry.peripheral.updateRssiAsync) {\n return entry.peripheral.updateRssiAsync();\n }\n return entry.peripheral.rssi;\n }\n\n /** Abandon matching pairing attempts without touching another owner's links. */\n async cancelPairing(options?: { vendor: string; id?: string }): Promise<void> {\n const matches = (id: string, vendor?: string) =>\n options === undefined ||\n (vendor === options.vendor && (options.id === undefined || id === options.id));\n for (const attempt of this._connectAttempts) {\n if (!matches(attempt.id, attempt.vendor)) continue;\n attempt.abandon(new Error(`connect cancelled: ${attempt.id}`));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'cancelPairing.cancelConnect.error', {\n id: attempt.id,\n error: String(error),\n });\n }\n }\n if (options?.id === undefined) await this.stopScan(options?.vendor);\n for (const [id, entry] of this._connected) {\n if (matches(id, entry.vendor)) await this.disconnect(id).catch(() => undefined);\n }\n }\n\n /**\n * Scan until `id` is discovered, releasing only its own scan ownership. A\n * bonded Safe 7 goes silent, so callers fall back to `_directConnect`.\n */\n private async _scanUntilFound(\n id: string,\n timeoutMs: number,\n claim: ConnectClaim\n ): Promise<NoblePeripheralLike | undefined> {\n await this.init();\n if (claim.abandoned) return undefined;\n const existing = this._discovered.get(id);\n if (existing) return existing;\n const noble = this._requireNoble();\n const owner = Symbol('connect-scan');\n this._scanOwners.add(owner);\n return new Promise<NoblePeripheralLike | undefined>(resolve => {\n let done = false;\n const finish = (p?: NoblePeripheralLike) => {\n if (done) return;\n done = true;\n this._pendingCancellations.delete(cancel);\n clearTimeout(timer);\n noble.removeListener('discover', onDiscover);\n if (claim.cancelScan === cancel) claim.cancelScan = undefined;\n this._scanOwners.delete(owner);\n if (this._scanOwners.size === 0) {\n void this._pauseScan().then(() => resolve(p));\n } else {\n resolve(p);\n }\n };\n const onDiscover = (peripheral: NoblePeripheralLike) => {\n this._discovered.set(peripheral.id, peripheral);\n if (peripheral.id === id) finish(peripheral);\n };\n const timer = setTimeout(() => finish(this._discovered.get(id)), timeoutMs);\n const cancel = () => finish();\n this._pendingCancellations.add(cancel);\n claim.cancelScan = cancel;\n noble.on('discover', onDiscover);\n void this._setScanning(true).catch(() => finish());\n });\n }\n\n /**\n * Connect by id without advertisement: the only route to a linked, silent device.\n * Returns undefined instead of throwing so the caller reports \"device not found\".\n */\n private async _directConnect(id: string): Promise<NoblePeripheralLike | undefined> {\n const noble = this._requireNoble();\n if (typeof noble.connectAsync !== 'function') {\n // Distinguishes a stub noble from \"the device wasn't there\" in the log.\n this._log('warn', 'connect.direct.unavailable', { id });\n return undefined;\n }\n // warn, not info: reaching a silent bonded device this way is unproven on\n // real hardware, so it must always be logged.\n this._log('warn', 'connect.direct.start', { id });\n const startedAt = Date.now();\n try {\n // Bounded by the connect() timeout; noble-mac never resolves when it\n // cannot retrieve the peripheral.\n const peripheral = await noble.connectAsync(id);\n const resolved = peripheral ?? this._discovered.get(id);\n this._log('warn', 'connect.direct.done', {\n id,\n elapsedMs: Date.now() - startedAt,\n found: Boolean(resolved),\n // Anything but 'connected' later surfaces as a service-discovery error.\n state: resolved?.state,\n fromNoble: Boolean(peripheral),\n });\n return resolved;\n } catch (error) {\n this._log('warn', 'connect.direct.error', {\n id,\n elapsedMs: Date.now() - startedAt,\n error: String(error),\n });\n return undefined;\n }\n }\n\n // noble's disconnectAsync hangs after a failed connect (CoreBluetooth never\n // sends the disconnect event), so bound it.\n private async _safeDisconnect(peripheral: NoblePeripheralLike): Promise<void> {\n if (this._nativeReleased) return;\n await raceTimeout(\n peripheral.disconnectAsync().catch(() => undefined),\n BLE_DISCONNECT_TIMEOUT_MS\n );\n }\n\n // One timeout covers connect and service discovery; the connector maps\n // `timed out` (unreachable) and `connection failed` (stale bond) differently.\n async connect(\n id: string,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n if (\n !options ||\n !options.vendor ||\n ![options.serviceUuid, options.writeUuid, options.notifyUuid].every(\n uuid => typeof uuid === 'string' && /^[0-9a-f]{32}$/i.test(normalizeBleUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\n }\n // Before any native work: only a registered vendor's own GATT profile and framing.\n if (!this._resolveVendor(options.vendor).profile.acceptsConnect(options)) {\n throw new Error(`BLE GATT profile not allowed for ${options.vendor}`);\n }\n this._assertActive();\n // Promise.race cannot stop _connectInner; the claim makes a late success tear\n // its link down instead of committing an unowned one that silences the device.\n const claim: ConnectClaim = { abandoned: false };\n // Shared by the timeout and cancelPairing.\n let abandon!: (error: Error) => void;\n const abandoned = new Promise<never>((_, reject) => {\n abandon = (error: Error) => {\n claim.abandoned = true;\n reject(error);\n claim.cancelScan?.();\n };\n });\n const timer = setTimeout(\n () => abandon(new Error(`connect timed out after ${this._connectTimeoutMs}ms`)),\n this._connectTimeoutMs\n );\n const attempt = {\n id,\n vendor: options.vendor,\n abandon,\n cancelNative: () => claim.cancelNative?.(),\n settled: Promise.resolve<unknown>(undefined),\n };\n this._connectAttempts.add(attempt);\n const nativeOperation = this._connectInner(id, claim, options);\n const caller = (async () => {\n try {\n return await Promise.race([nativeOperation, abandoned]);\n } catch (error) {\n const peripheral = this._discovered.get(id);\n // Tear down only a link nobody owns, same guard as _connectInner.abortIfAbandoned.\n if (peripheral && !this._connected.has(id)) await this._safeDisconnect(peripheral);\n throw error;\n } finally {\n clearTimeout(timer);\n }\n })();\n // A rejected caller can still have a native connect or disconnect in flight.\n attempt.settled = Promise.allSettled([nativeOperation, caller]).finally(() => {\n this._connectAttempts.delete(attempt);\n // The connect cleared the idle stop; re-arm it so an owner left behind (a picker that\n // connected without stopScan) still expires instead of keeping the radio on.\n if (this._scanOwners.size > 0) this._armIdleStop();\n // Resume the radio only for a connect-scan (symbol) owner; a vendor's\n // idle scan owner must not restart it during pairing.\n const connectScanWaiting = [...this._scanOwners].some(owner => typeof owner === 'symbol');\n if (connectScanWaiting) {\n void this._setScanning(true).catch(() => undefined);\n }\n });\n return caller;\n }\n\n private async _connectInner(\n id: string,\n claim: ConnectClaim,\n options: ElectronBleConnectOptions\n ): Promise<{ id: string; name?: string }> {\n let route: 'cache' | 'scan' | 'direct' | 'none' = 'cache';\n let peripheral: NoblePeripheralLike | undefined;\n // Stops the flow before it commits an unowned link; the rejection itself is\n // unobservable because Promise.race already settled.\n const abortIfAbandoned = async (stage: string) => {\n if (!claim.abandoned && !this._disposed) return;\n // A link still held in _connected belongs to a previous connect.\n if (peripheral && peripheral.state === 'connected' && !this._connected.has(id)) {\n await this._safeDisconnect(peripheral);\n }\n this._log('warn', 'connect.abandoned', { id, route, stage });\n throw new Error(`connect abandoned after timeout: ${id}`);\n };\n await this.init();\n await abortIfAbandoned('init');\n // Stop scanning (keep the cache) and let the radio settle before connecting.\n await this._pauseScan();\n await delay(BLE_CONNECT_SETTLE_MS);\n await abortIfAbandoned('settle');\n this._assertActive();\n peripheral = this._discovered.get(id);\n if (!peripheral) {\n route = 'scan';\n peripheral = await this._scanUntilFound(id, THIRD_PARTY_BLE_SCAN_DURATION_MS, claim);\n await abortIfAbandoned('scan');\n }\n if (!peripheral) {\n route = 'direct';\n // Deliberately after the scan: noble-mac never resolves connect-by-id for a\n // peripheral CoreBluetooth cannot retrieve (`NobleMac::Connect`).\n const native = this._requireNoble();\n claim.cancelNative = () => native.cancelConnect?.(id);\n peripheral = await this._directConnect(id);\n }\n if (!peripheral) {\n // discoveredCount=0 points at the radio/scan; counts only, see scan.empty.\n this._log('warn', 'connect.notFound', {\n id,\n route: 'none',\n discoveredCount: this._discovered.size,\n });\n throw new Error(`BLE device not found: ${id}`);\n }\n\n await abortIfAbandoned('resolve');\n\n const wasConnected = peripheral.state === 'connected';\n const connectingPeripheral = peripheral;\n const native = this._requireNoble();\n claim.cancelNative = () => {\n if (connectingPeripheral.state === 'connecting' && connectingPeripheral.cancelConnect) {\n connectingPeripheral.cancelConnect();\n } else {\n native.cancelConnect?.(id);\n }\n };\n this._log('warn', 'connect.route', {\n id,\n route,\n wasConnected,\n name: peripheral.advertisement?.localName,\n });\n if (!wasConnected) {\n await peripheral.connectAsync();\n await abortIfAbandoned('link');\n }\n\n try {\n const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(\n [options.serviceUuid],\n [options.writeUuid, options.notifyUuid]\n );\n const writeUuid = normalizeBleUuid(options.writeUuid);\n const notifyUuid = normalizeBleUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeBleUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeBleUuid(c.uuid) === notifyUuid);\n if (!writeChar || !notifyChar) {\n throw new Error(`BLE characteristics not found on device ${id}`);\n }\n // Last gate before commit: service discovery can also outlast the timeout.\n await abortIfAbandoned('discovery');\n\n const disconnectHandler = () => {\n // One peripheral outlives many links; a stale handler must not report\n // the current link as unexpectedly dropped.\n if (this._connected.get(id)?.disconnectHandler !== disconnectHandler) return;\n this._cleanupDevice(id, /* unexpected */ true);\n };\n peripheral.on('disconnect', disconnectHandler);\n\n this._connected.set(id, {\n peripheral,\n writeChar,\n notifyChar,\n disconnectHandler,\n vendor: options.vendor,\n write: options.write,\n });\n this._log('info', 'connect.done', { id, name: peripheral.advertisement.localName });\n return { id, name: peripheral.advertisement.localName };\n } catch (error) {\n // Don't leak a GATT link this call opened.\n if (!wasConnected) await this._safeDisconnect(peripheral);\n throw error;\n }\n }\n\n async disconnect(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry) return;\n if (entry.disconnectHandler) {\n // Suppress the unexpected-disconnect event for explicit disconnects.\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n try {\n await entry.peripheral.disconnectAsync();\n } catch (error) {\n this._log('warn', 'disconnect.error', { id, error: String(error) });\n }\n this._cleanupDevice(id, /* unexpected */ false, entry);\n }\n\n async subscribe(id: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.notifyChar) throw new Error(`BLE notify characteristic missing for ${id}`);\n if (entry.notifyHandler) return;\n const handler = (data: Buffer) => {\n this._onNotification?.(id, data.toString('hex'));\n };\n entry.notifyHandler = handler;\n entry.notifyChar.on('data', handler);\n await entry.notifyChar.subscribeAsync();\n }\n\n async unsubscribe(id: string): Promise<void> {\n const entry = this._connected.get(id);\n if (!entry?.notifyChar) return;\n if (entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n entry.notifyHandler = undefined;\n }\n try {\n await entry.notifyChar.unsubscribeAsync();\n } catch (error) {\n this._log('warn', 'unsubscribe.error', { id, error: String(error) });\n }\n }\n\n async write(id: string, hexData: string): Promise<void> {\n const entry = this._requireEntry(id);\n if (!entry.writeChar) throw new Error(`BLE write characteristic missing for ${id}`);\n const buffer = Buffer.from(hexData, 'hex');\n const framing = entry.write;\n if (!framing) throw new Error(`No BLE write framing recorded for ${id}`);\n\n if (framing.mode === 'raw') {\n // Self-framed, so padding corrupts it. Ledger negotiates the size per link\n // (0x08 handshake) in one byte, so it never exceeds 255.\n const maxLength = framing.maxLength ?? buffer.length;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hexData) || buffer.length > maxLength) {\n throw new Error(`Invalid BLE frame for ${entry.vendor ?? 'device'}`);\n }\n await entry.writeChar.writeAsync(buffer, false);\n return;\n }\n\n const { chunkSize } = framing;\n if (!chunkSize) throw new Error(`Padded BLE writes need a chunkSize for ${id}`);\n for (let offset = 0; offset < buffer.length; offset += chunkSize) {\n this._assertActive();\n const slice = buffer.subarray(offset, offset + chunkSize);\n // Firmware drops a short final packet (then RetriesExceeded), so every chunk\n // is zero-filled to full size, as in trezor-suite transport-bluetooth.\n const chunk = Buffer.alloc(chunkSize);\n slice.copy(chunk);\n // OneKey uses writeWithResponse for stability; mirror that.\n await entry.writeChar.writeAsync(chunk, false);\n if (offset + chunkSize < buffer.length && framing.chunkDelayMs) {\n await delay(framing.chunkDelayMs);\n }\n }\n }\n\n /** Retire a renderer's handler without stopping a process-wide native manager. */\n dispose(): Promise<void> {\n if (this._disposePromise) return this._disposePromise;\n this._disposed = true;\n this._clearIdleStop();\n this._scanning = false;\n this._onNotification = undefined;\n this._onDeviceDisconnected = undefined;\n const connections = Array.from(this._connectAttempts);\n for (const attempt of connections) {\n attempt.abandon(new Error('Desktop BLE is shutting down'));\n try {\n attempt.cancelNative();\n } catch (error) {\n this._log('warn', 'dispose.cancelConnect.error', { error: String(error) });\n }\n }\n for (const cancel of this._pendingCancellations) cancel();\n if (this._noble && this._discoverHandler) {\n this._noble.removeListener('discover', this._discoverHandler);\n }\n const entries = Array.from(this._connected.entries());\n for (const [id] of entries) this._cleanupDevice(id, false);\n this._disposePromise = (async () => {\n try {\n await raceTimeout(\n Promise.allSettled([\n ...connections.map(attempt => attempt.settled),\n ...Array.from(this._nobleInstances, async instance => instance.stopScanningAsync()),\n ...entries.map(async ([, entry]) => {\n try {\n await raceTimeout(\n entry.notifyChar?.unsubscribeAsync().catch(() => undefined),\n 250\n );\n } finally {\n await this._safeDisconnect(entry.peripheral);\n }\n }),\n ]),\n 3500,\n () => this._log('warn', 'dispose.timeout')\n );\n } finally {\n this._discovered.clear();\n this._lastSeen.clear();\n this._initialized = false;\n }\n })();\n return this._disposePromise;\n }\n\n /**\n * Terminal native release, including recovered instances. A host sharing noble\n * must defer stop() until every transport disposed, and dedupe the instances.\n */\n disposeForAppQuit(\n releaseNoble: (instance: { stop?(): void }) => void = instance => instance.stop?.()\n ): Promise<void> {\n if (!this._releasePromise) {\n this._releasePromise = this.dispose().finally(() => {\n this._nativeReleased = true;\n let releaseError: Error | undefined;\n for (const instance of this._nobleInstances) {\n try {\n releaseNoble(instance);\n } catch (error) {\n releaseError = error instanceof Error ? error : new Error(String(error));\n }\n }\n this._nobleInstances.clear();\n if (releaseError) throw releaseError;\n this._log('info', 'dispose.native.done');\n });\n }\n return this._releasePromise;\n }\n\n private _assertActive(): void {\n if (this._disposed) throw new Error('Desktop BLE is shutting down');\n }\n\n private _cleanupDevice(id: string, unexpected: boolean, expected?: DeviceEntry): void {\n const entry = this._connected.get(id);\n if (!entry) return;\n // A reconnect may have replaced the entry while the caller awaited.\n // A stale caller must not tear down the link it no longer owns.\n if (expected && entry !== expected) return;\n if (entry.notifyChar && entry.notifyHandler) {\n entry.notifyChar.removeListener('data', entry.notifyHandler);\n }\n // Otherwise a later explicit disconnect is reported as unexpected.\n if (entry.disconnectHandler) {\n entry.peripheral.removeListener('disconnect', entry.disconnectHandler);\n }\n this._connected.delete(id);\n if (unexpected) {\n this._log('warn', 'disconnect.unexpected', { id });\n this._onDeviceDisconnected?.(id);\n }\n }\n\n private _requireEntry(id: string): DeviceEntry {\n this._assertActive();\n const entry = this._connected.get(id);\n if (!entry) throw new Error(`BLE device is not connected: ${id}`);\n return entry;\n }\n\n private _requireNoble(): NobleLike {\n this._assertActive();\n if (!this._noble) throw new Error('Desktop BLE: noble was not initialized');\n return this._noble;\n }\n\n /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */\n private async _waitForKnownState(timeoutMs: number): Promise<void> {\n const noble = this._requireNoble();\n if (noble.state !== 'unknown') return;\n await new Promise<void>(resolve => {\n const settle = () => {\n clearTimeout(timer);\n noble.removeListener('stateChange', settle);\n this._pendingCancellations.delete(settle);\n resolve();\n };\n const timer = setTimeout(settle, timeoutMs);\n this._pendingCancellations.add(settle);\n noble.on('stateChange', settle);\n });\n }\n\n private async _waitForPoweredOn(timeoutMs: number): Promise<void> {\n const noble = this._requireNoble();\n if (noble.state === 'poweredOn') return;\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n noble.removeListener('stateChange', handler);\n this._pendingCancellations.delete(cancel);\n };\n const cancel = () => {\n cleanup();\n reject(new Error('Desktop BLE is shutting down'));\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(\n new Error(\n `Desktop BLE: noble did not reach poweredOn within ${timeoutMs}ms (last state: ${noble.state})`\n )\n );\n }, timeoutMs);\n const handler = (state: string) => {\n if (state === 'poweredOn') {\n cleanup();\n resolve();\n } else if (state === 'unsupported' || state === 'unauthorized') {\n cleanup();\n reject(new Error(`Desktop BLE: noble state ${state}`));\n }\n };\n this._pendingCancellations.add(cancel);\n noble.on('stateChange', handler);\n });\n }\n\n private _log(level: BleDebugLogLevel, event: string, data?: Record<string, unknown>): void {\n this._logger?.({\n level,\n scope: 'desktop-noble-ble',\n event,\n data: redactBleDebugLogData(data),\n });\n }\n}\n","import { THIRD_PARTY_BLE_CHANNELS } from './constants';\nimport { NobleBleHandler } from './NobleBleHandler';\n\nimport type { NobleBleHandlerOptions } from './NobleBleHandler';\nimport type { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/** Minimal slice of Electron's `WebContents` we use (kept duck-typed so we\n * don't take a hard dep on `electron`). */\nexport interface WebContentsLike {\n send(channel: string, ...args: unknown[]): void;\n on?(event: string, listener: (...args: any[]) => void): void;\n}\n\n/** Minimal slice of Electron's `ipcMain` we use. */\nexport interface IpcMainLike {\n handle(\n channel: string,\n listener: (event: unknown, ...args: any[]) => Promise<unknown> | unknown\n ): void;\n removeHandler(channel: string): void;\n}\n\nexport interface InitThirdPartyBleSupportOptions extends NobleBleHandlerOptions {\n /** Inject your own `ipcMain` (defaults to `require('electron').ipcMain`). */\n ipcMain?: IpcMainLike;\n}\n\nexport interface ThirdPartyBleSupportHandle {\n handler: NobleBleHandler;\n dispose(): Promise<void>;\n disposeForAppQuit(releaseNoble?: (instance: { stop?(): void }) => void): Promise<void>;\n}\n\nconst DEFAULT_IPC_MAIN: () => IpcMainLike = () => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { ipcMain } = require('electron') as { ipcMain: IpcMainLike };\n return ipcMain;\n};\n\n/**\n * Wire a `NobleBleHandler` to Electron IPC once `BrowserWindow` is ready. Call dispose()\n * when retiring a renderer and disposeForAppQuit() before Node teardown.\n */\nexport function initThirdPartyBleSupport(\n webContents: WebContentsLike,\n options: InitThirdPartyBleSupportOptions = {}\n): ThirdPartyBleSupportHandle {\n const ipcMain = options.ipcMain ?? DEFAULT_IPC_MAIN();\n const handler = new NobleBleHandler(options);\n let disposed = false;\n\n handler.setNotificationListener((id, hexData) => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.notification, id, hexData);\n });\n handler.setDisconnectedListener(id => {\n webContents.send(THIRD_PARTY_BLE_CHANNELS.disconnected, id);\n });\n\n const handle = <T>(channel: string, fn: (...args: any[]) => Promise<T> | T): void => {\n ipcMain.handle(channel, async (_event, ...args) => {\n if (disposed) throw new Error('Third-party BLE is shutting down');\n return fn(...args);\n });\n };\n\n handle(THIRD_PARTY_BLE_CHANNELS.scan, (options?: ElectronBleScanOptions) =>\n handler.scan(options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.stopScan, (vendor?: string) => handler.stopScan(vendor));\n handle(THIRD_PARTY_BLE_CHANNELS.connect, (id: string, options: ElectronBleConnectOptions) =>\n handler.connect(id, options)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.disconnect, (id: string) => handler.disconnect(id));\n handle(THIRD_PARTY_BLE_CHANNELS.write, (id: string, hexData: string) =>\n handler.write(id, hexData)\n );\n handle(THIRD_PARTY_BLE_CHANNELS.subscribe, (id: string) => handler.subscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.unsubscribe, (id: string) => handler.unsubscribe(id));\n handle(THIRD_PARTY_BLE_CHANNELS.availability, () => handler.checkAvailability());\n handle(THIRD_PARTY_BLE_CHANNELS.getDevice, (id: string) => handler.getDevice(id));\n handle(THIRD_PARTY_BLE_CHANNELS.readRssi, (id: string) => handler.readRssi(id));\n handle(THIRD_PARTY_BLE_CHANNELS.cancelPairing, (options?: { vendor: string; id?: string }) =>\n handler.cancelPairing(options)\n );\n\n const removeHandlers = () => {\n if (disposed) return;\n disposed = true;\n for (const channel of Object.values(THIRD_PARTY_BLE_CHANNELS)) {\n ipcMain.removeHandler(channel);\n }\n };\n\n return {\n handler,\n dispose: () => {\n removeHandlers();\n return handler.dispose();\n },\n disposeForAppQuit: releaseNoble => {\n removeHandlers();\n return handler.disposeForAppQuit(releaseNoble);\n },\n };\n}\n"],"mappings":"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","names":["native","options"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hwk-desktop-noble-ble",
3
- "version": "1.2.8-alpha.52",
3
+ "version": "1.2.8-alpha.54",
4
4
  "description": "Main-process BLE service (noble) shared by third-party hardware connectors on desktop.",
5
5
  "author": "OneKey",
6
6
  "license": "MIT",
@@ -58,7 +58,7 @@
58
58
  "connector"
59
59
  ],
60
60
  "dependencies": {
61
- "@onekeyfe/hwk-adapter-core": "1.2.8-alpha.52"
61
+ "@onekeyfe/hwk-adapter-core": "1.2.8-alpha.54"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@stoprocent/noble": "*",
@@ -77,5 +77,5 @@
77
77
  "tsup": "^8.0.0",
78
78
  "typescript": "5.1.6"
79
79
  },
80
- "gitHead": "389c588beb19de26451c729bb09bc8e7ce6b0717"
80
+ "gitHead": "167d8127232ef23d345ccc0c12fd0c21fcaf6552"
81
81
  }