@onekeyfe/hwk-desktop-noble-ble 1.2.6-alpha.0 → 1.2.8-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -4
package/dist/index.d.mts
CHANGED
|
@@ -77,7 +77,7 @@ interface ThirdPartyBleApi {
|
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* Subset of @stoprocent/noble we touch. We type as `any` to keep the package
|
|
80
|
-
* installable without the native module — it
|
|
80
|
+
* installable without the native module — the default factory loads it on first use.
|
|
81
81
|
*/
|
|
82
82
|
interface NobleLike {
|
|
83
83
|
state: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ interface ThirdPartyBleApi {
|
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* Subset of @stoprocent/noble we touch. We type as `any` to keep the package
|
|
80
|
-
* installable without the native module — it
|
|
80
|
+
* installable without the native module — the default factory loads it on first use.
|
|
81
81
|
*/
|
|
82
82
|
interface NobleLike {
|
|
83
83
|
state: string;
|
package/dist/index.js
CHANGED
|
@@ -563,9 +563,9 @@ var NobleBleHandler = class {
|
|
|
563
563
|
})();
|
|
564
564
|
attempt.settled = Promise.allSettled([nativeOperation, caller]).finally(() => {
|
|
565
565
|
this._connectAttempts.delete(attempt);
|
|
566
|
+
if (this._scanOwners.size > 0) this._armIdleStop();
|
|
566
567
|
const connectScanWaiting = [...this._scanOwners].some((owner) => typeof owner === "symbol");
|
|
567
568
|
if (connectScanWaiting) {
|
|
568
|
-
this._armIdleStop();
|
|
569
569
|
void this._setScanning(true).catch(() => void 0);
|
|
570
570
|
}
|
|
571
571
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/debugLog.ts","../src/constants.ts","../src/NobleBleHandler.ts","../src/main.ts"],"sourcesContent":["export { NobleBleHandler } from './NobleBleHandler';\nexport type { NobleBleHandlerOptions } from './NobleBleHandler';\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 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';\n\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type {\n ElectronBleConnectOptions,\n ElectronBleMatch,\n ElectronBleScanOptions,\n} 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 — it's loaded lazily inside main().\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 /** Opaque partition key from the caller; this handler never interprets 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\nconst normalizeUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\n/**\n * Matches a peripheral against caller-supplied criteria (service UUID or every\n * name pattern), standing in for the scan filter we cannot use (see `scan`).\n */\nconst matchesPeripheral = (p: NoblePeripheralLike, match?: ElectronBleMatch): boolean => {\n if (!match) return false;\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n match.serviceUuids?.length &&\n advertised.some(uuid =>\n match.serviceUuids?.some(wanted => normalizeUuid(wanted) === normalizeUuid(uuid))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!match.namePatterns?.length || !name) return false;\n return match.namePatterns.every(pattern => new RegExp(pattern, 'i').test(name));\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\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 _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 }\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 // 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(options?.vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(options?.vendor);\n throw error;\n }\n if (!this._scanOwners.has(options?.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(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(options?: ElectronBleScanOptions): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const match = options?.match ?? { serviceUuids: options?.serviceUuids };\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, match)) 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 !== options?.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 if (!p) return null;\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(normalizeUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\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 // 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 this._armIdleStop();\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 = normalizeUuid(options.writeUuid);\n const notifyUuid = normalizeUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeUuid(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\nexport { NobleBleHandler } from './NobleBleHandler';\nexport type {\n NobleBleHandlerOptions,\n NobleLike,\n NoblePeripheralLike,\n NobleCharacteristicLike,\n} from './NobleBleHandler';\nexport { THIRD_PARTY_BLE_CHANNELS } from './constants';\nexport type {\n ThirdPartyBleApi,\n ThirdPartyBleAvailability,\n ThirdPartyBleDeviceInfo,\n} from './types/desktop-api';\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/NobleBleHandler.ts","../src/main.ts"],"sourcesContent":["export { NobleBleHandler } from './NobleBleHandler';\nexport type { NobleBleHandlerOptions } from './NobleBleHandler';\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 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';\n\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type {\n ElectronBleConnectOptions,\n ElectronBleMatch,\n ElectronBleScanOptions,\n} 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 /** Opaque partition key from the caller; this handler never interprets 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\nconst normalizeUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\n/**\n * Matches a peripheral against caller-supplied criteria (service UUID or every\n * name pattern), standing in for the scan filter we cannot use (see `scan`).\n */\nconst matchesPeripheral = (p: NoblePeripheralLike, match?: ElectronBleMatch): boolean => {\n if (!match) return false;\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n match.serviceUuids?.length &&\n advertised.some(uuid =>\n match.serviceUuids?.some(wanted => normalizeUuid(wanted) === normalizeUuid(uuid))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!match.namePatterns?.length || !name) return false;\n return match.namePatterns.every(pattern => new RegExp(pattern, 'i').test(name));\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\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 _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 }\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 // 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(options?.vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(options?.vendor);\n throw error;\n }\n if (!this._scanOwners.has(options?.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(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(options?: ElectronBleScanOptions): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const match = options?.match ?? { serviceUuids: options?.serviceUuids };\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, match)) 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 !== options?.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 if (!p) return null;\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(normalizeUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\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 = normalizeUuid(options.writeUuid);\n const notifyUuid = normalizeUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeUuid(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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -516,9 +516,9 @@ var NobleBleHandler = class {
|
|
|
516
516
|
})();
|
|
517
517
|
attempt.settled = Promise.allSettled([nativeOperation, caller]).finally(() => {
|
|
518
518
|
this._connectAttempts.delete(attempt);
|
|
519
|
+
if (this._scanOwners.size > 0) this._armIdleStop();
|
|
519
520
|
const connectScanWaiting = [...this._scanOwners].some((owner) => typeof owner === "symbol");
|
|
520
521
|
if (connectScanWaiting) {
|
|
521
|
-
this._armIdleStop();
|
|
522
522
|
void this._setScanning(true).catch(() => void 0);
|
|
523
523
|
}
|
|
524
524
|
});
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/debugLog.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 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';\n\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type {\n ElectronBleConnectOptions,\n ElectronBleMatch,\n ElectronBleScanOptions,\n} 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 — it's loaded lazily inside main().\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 /** Opaque partition key from the caller; this handler never interprets 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\nconst normalizeUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\n/**\n * Matches a peripheral against caller-supplied criteria (service UUID or every\n * name pattern), standing in for the scan filter we cannot use (see `scan`).\n */\nconst matchesPeripheral = (p: NoblePeripheralLike, match?: ElectronBleMatch): boolean => {\n if (!match) return false;\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n match.serviceUuids?.length &&\n advertised.some(uuid =>\n match.serviceUuids?.some(wanted => normalizeUuid(wanted) === normalizeUuid(uuid))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!match.namePatterns?.length || !name) return false;\n return match.namePatterns.every(pattern => new RegExp(pattern, 'i').test(name));\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\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 _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 }\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 // 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(options?.vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(options?.vendor);\n throw error;\n }\n if (!this._scanOwners.has(options?.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(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(options?: ElectronBleScanOptions): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const match = options?.match ?? { serviceUuids: options?.serviceUuids };\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, match)) 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 !== options?.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 if (!p) return null;\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(normalizeUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\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 // 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 this._armIdleStop();\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 = normalizeUuid(options.writeUuid);\n const notifyUuid = normalizeUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeUuid(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\nexport { NobleBleHandler } from './NobleBleHandler';\nexport type {\n NobleBleHandlerOptions,\n NobleLike,\n NoblePeripheralLike,\n NobleCharacteristicLike,\n} from './NobleBleHandler';\nexport { THIRD_PARTY_BLE_CHANNELS } from './constants';\nexport type {\n ThirdPartyBleApi,\n ThirdPartyBleAvailability,\n ThirdPartyBleDeviceInfo,\n} from './types/desktop-api';\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/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 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';\n\nimport type { ThirdPartyBleAvailability, ThirdPartyBleDeviceInfo } from './types/desktop-api';\nimport type {\n ElectronBleConnectOptions,\n ElectronBleMatch,\n ElectronBleScanOptions,\n} 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 /** Opaque partition key from the caller; this handler never interprets 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\nconst normalizeUuid = (uuid: string): string => uuid.replace(/-/g, '').toLowerCase();\n\n/**\n * Matches a peripheral against caller-supplied criteria (service UUID or every\n * name pattern), standing in for the scan filter we cannot use (see `scan`).\n */\nconst matchesPeripheral = (p: NoblePeripheralLike, match?: ElectronBleMatch): boolean => {\n if (!match) return false;\n const adv = p.advertisement ?? {};\n const advertised = adv.serviceUuids ?? [];\n if (\n match.serviceUuids?.length &&\n advertised.some(uuid =>\n match.serviceUuids?.some(wanted => normalizeUuid(wanted) === normalizeUuid(uuid))\n )\n ) {\n return true;\n }\n const name = adv.localName;\n if (!match.namePatterns?.length || !name) return false;\n return match.namePatterns.every(pattern => new RegExp(pattern, 'i').test(name));\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\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 _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 }\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 // 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(options?.vendor);\n try {\n await this.init();\n } catch (error) {\n this._scanOwners.delete(options?.vendor);\n throw error;\n }\n if (!this._scanOwners.has(options?.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(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(options?: ElectronBleScanOptions): ThirdPartyBleDeviceInfo[] {\n const now = Date.now();\n const match = options?.match ?? { serviceUuids: options?.serviceUuids };\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, match)) 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 !== options?.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 if (!p) return null;\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(normalizeUuid(uuid))\n )\n ) {\n throw new Error('Invalid BLE GATT profile');\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 = normalizeUuid(options.writeUuid);\n const notifyUuid = normalizeUuid(options.notifyUuid);\n const writeChar = characteristics.find(c => normalizeUuid(c.uuid) === writeUuid);\n const notifyChar = characteristics.find(c => normalizeUuid(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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hwk-desktop-noble-ble",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8-alpha.13",
|
|
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,8 +58,7 @@
|
|
|
58
58
|
"connector"
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@onekeyfe/hwk-adapter-core": "1.2.
|
|
62
|
-
"buffer": "^6.0.3"
|
|
61
|
+
"@onekeyfe/hwk-adapter-core": "1.2.8-alpha.13"
|
|
63
62
|
},
|
|
64
63
|
"peerDependencies": {
|
|
65
64
|
"@stoprocent/noble": "*",
|
|
@@ -78,5 +77,5 @@
|
|
|
78
77
|
"tsup": "^8.0.0",
|
|
79
78
|
"typescript": "5.1.6"
|
|
80
79
|
},
|
|
81
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "ed22eddce37ce0562b329c42a6985b50949ca412"
|
|
82
81
|
}
|