@onekeyfe/hwk-ledger-connector-electron-ble 1.2.3-alpha.2 → 1.2.3-alpha.4

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.js CHANGED
@@ -163,6 +163,13 @@ var LedgerElectronBleTransport = class {
163
163
  release();
164
164
  return (0, import_purify_ts.Left)(new import_device_management_kit.UnknownDeviceError());
165
165
  }
166
+ removeDisconnect = this.bridge.onDeviceDisconnected((id) => {
167
+ if (id !== deviceId) return;
168
+ if (close()) {
169
+ release();
170
+ onDisconnect(deviceId);
171
+ }
172
+ });
166
173
  await this.bridge.connect(
167
174
  deviceId,
168
175
  ledgerBleConnectProfile({
@@ -171,13 +178,7 @@ var LedgerElectronBleTransport = class {
171
178
  notifyUuid: known.profile.notifyUuid
172
179
  })
173
180
  );
174
- removeDisconnect = this.bridge.onDeviceDisconnected((id) => {
175
- if (id !== deviceId) return;
176
- if (close()) {
177
- release();
178
- onDisconnect(deviceId);
179
- }
180
- });
181
+ if (closed) throw new Error("Bluetooth connection ended");
181
182
  removeNotification = this.bridge.onNotification((id, hex) => {
182
183
  if (id !== deviceId || closed) return;
183
184
  if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/LedgerElectronBleConnector.ts","../src/LedgerElectronBleTransport.ts","../src/bleProfile.ts"],"sourcesContent":["export { createLedgerElectronBleConnector } from './LedgerElectronBleConnector';\nexport { LedgerElectronBleTransport } from './LedgerElectronBleTransport';\nexport type { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';\n\nexport {\n LEDGER_BLE_VENDOR,\n LEDGER_BLE_MIN_FRAME_SIZE,\n LEDGER_BLE_MAX_FRAME_SIZE,\n ledgerBleMatch,\n ledgerBleConnectProfile,\n} from './bleProfile';\n","import { LedgerConnectorBase } from '@onekeyfe/hwk-ledger-adapter';\nimport { LedgerElectronBleTransport } from './LedgerElectronBleTransport';\n\nimport type { TransportArgs } from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi } from '@onekeyfe/hwk-adapter-core';\n\nexport function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase {\n return new LedgerConnectorBase(\n async () => (args: TransportArgs) => new LedgerElectronBleTransport(bridge, args),\n { connectionType: 'ble' }\n );\n}\n","import {\n DisconnectError,\n GeneralDmkError,\n OpeningConnectionError,\n SendApduConcurrencyError,\n SendApduTimeoutError,\n TransportConnectedDevice,\n UnknownDeviceError,\n} from '@ledgerhq/device-management-kit';\nimport { Left, Right } from 'purify-ts';\nimport { defer, from, mergeMap } from 'rxjs';\nimport {\n LEDGER_BLE_MIN_FRAME_SIZE,\n LEDGER_BLE_VENDOR,\n ledgerBleConnectProfile,\n ledgerBleMatch,\n} from './bleProfile';\nimport { hexToBytes } from '@onekeyfe/hwk-adapter-core';\n\nimport type {\n BleDeviceInfos,\n SendApduResult,\n Transport,\n TransportArgs,\n TransportDiscoveredDevice,\n} from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';\n\nconst TRANSPORT_ID = 'ELECTRON_BLE';\n// Ledger's own transports open at the ATT default and then raise it from the\n// device's answer to the 0x08 handshake below. Keep the same floor so a device\n// that reports nothing useful still works.\nconst normalizeUuid = (uuid: string) => uuid.replace(/-/g, '').toLowerCase();\nconst toHex = (bytes: Uint8Array) =>\n Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');\n\nexport class LedgerElectronBleTransport implements Transport {\n private readonly devices = new Map<\n string,\n { device: TransportDiscoveredDevice; profile: BleDeviceInfos }\n >();\n\n private readonly connections = new Map<\n string,\n {\n close: () => boolean;\n connectedDevice?: TransportConnectedDevice;\n }\n >();\n\n private scanPromise: Promise<TransportDiscoveredDevice[]> | undefined;\n\n private readonly bridge: ElectronBleApi;\n\n private readonly args: Pick<\n TransportArgs,\n 'deviceModelDataSource' | 'apduSenderServiceFactory' | 'apduReceiverServiceFactory'\n >;\n\n constructor(bridge: ElectronBleApi, args: LedgerElectronBleTransport['args']) {\n this.bridge = bridge;\n this.args = args;\n }\n\n getIdentifier() {\n return TRANSPORT_ID;\n }\n\n isSupported() {\n return true;\n }\n\n private remember(info: ElectronBleDeviceInfo): TransportDiscoveredDevice | undefined {\n const profiles = this.args.deviceModelDataSource.getBluetoothServicesInfos();\n const profile = Object.values(profiles).find(candidate =>\n info.advertisedServiceUuids?.some(\n uuid => normalizeUuid(uuid) === normalizeUuid(candidate.serviceUuid)\n )\n );\n if (!profile) return undefined;\n const device: TransportDiscoveredDevice = {\n id: info.id,\n name: info.name,\n rssi: info.rssi,\n deviceModel: profile.deviceModel,\n transport: TRANSPORT_ID,\n };\n this.devices.set(info.id, { device, profile });\n return device;\n }\n\n private discover(): Promise<TransportDiscoveredDevice[]> {\n if (this.scanPromise) return this.scanPromise;\n const scan = (async () => {\n const availability = await this.bridge.checkAvailability();\n if (!availability.available) throw new Error(`Bluetooth unavailable: ${availability.state}`);\n const infos = await this.bridge.scan({\n vendor: LEDGER_BLE_VENDOR,\n match: ledgerBleMatch(this.args.deviceModelDataSource.getBluetoothServices()),\n });\n const devices: TransportDiscoveredDevice[] = [];\n for (const info of infos) {\n const device = this.remember(info);\n if (device) devices.push(device);\n }\n return devices;\n })();\n this.scanPromise = scan;\n void scan\n .finally(() => {\n if (this.scanPromise === scan) this.scanPromise = undefined;\n })\n .catch(() => undefined);\n return scan;\n }\n\n startDiscovering() {\n return defer(() => this.discover()).pipe(mergeMap(devices => from(devices)));\n }\n\n listenToAvailableDevices() {\n return defer(() => this.discover());\n }\n\n async stopDiscovering(): Promise<void> {\n await this.scanPromise?.catch(() => undefined);\n await this.bridge.stopScan();\n }\n\n async connect({\n deviceId,\n onDisconnect,\n }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']> {\n if (this.connections.has(deviceId))\n return Left(new OpeningConnectionError('Device is already connected'));\n let removeNotification: (() => void) | undefined;\n let removeDisconnect: (() => void) | undefined;\n let closed = false;\n let rejectPending: ((error: unknown) => void) | undefined;\n let receive: ((bytes: Uint8Array) => void) | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n rejectPending?.(new Error('Bluetooth connection ended'));\n return true;\n };\n const release = () => {\n if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);\n };\n const disconnect = async (notify: boolean) => {\n if (!close()) return;\n await this.bridge.disconnect(deviceId).catch(() => undefined);\n release();\n if (notify) onDisconnect(deviceId);\n };\n // Reserve before the first await so concurrent acquires cannot share native GATT state.\n this.connections.set(deviceId, { close });\n try {\n await this.stopDiscovering();\n if (!this.devices.has(deviceId)) {\n const info = await this.bridge.getDevice(deviceId);\n if (info) this.remember(info);\n }\n const known = this.devices.get(deviceId);\n if (!known) {\n close();\n release();\n return Left(new UnknownDeviceError());\n }\n await this.bridge.connect(\n deviceId,\n ledgerBleConnectProfile({\n serviceUuid: known.profile.serviceUuid,\n writeUuid: known.profile.writeUuid,\n notifyUuid: known.profile.notifyUuid,\n })\n );\n removeDisconnect = this.bridge.onDeviceDisconnected(id => {\n if (id !== deviceId) return;\n if (close()) {\n release();\n onDisconnect(deviceId);\n }\n });\n removeNotification = this.bridge.onNotification((id, hex) => {\n if (id !== deviceId || closed) return;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {\n rejectPending?.(new Error('Malformed Bluetooth notification'));\n return;\n }\n receive?.(hexToBytes(hex));\n });\n await this.bridge.subscribe(deviceId);\n\n const exchange = async <T>(\n frames: Uint8Array[],\n accept: (\n bytes: Uint8Array,\n resolve: (value: T) => void,\n reject: (error: unknown) => void\n ) => void,\n timeoutMs: number\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (receive) throw new Error('Bluetooth exchange is busy');\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await new Promise<T>((resolve, reject) => {\n rejectPending = reject;\n receive = bytes => accept(bytes, resolve, reject);\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\n void (async () => {\n for (const frame of frames) {\n if (closed) throw new Error('Bluetooth connection ended');\n await this.bridge.write(deviceId, toHex(frame));\n }\n })().catch(reject);\n });\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n receive = undefined;\n rejectPending = undefined;\n }\n };\n\n const frameSize = await exchange<number>(\n [Uint8Array.of(0x08, 0, 0, 0, 0)],\n (bytes, resolve, reject) => {\n if (bytes.length < 6 || bytes[0] !== 0x08 || bytes[5] < 6) {\n reject(new Error('Invalid Ledger BLE MTU response'));\n } else {\n // Matches @ledgerhq/hw-transport-web-ble inferMTU: the device\n // reports the frame size it can take, and it is only ever used to\n // raise the floor. Capping it here would negotiate and then throw\n // the answer away, leaving every transfer at ~7x the frame count.\n resolve(Math.max(LEDGER_BLE_MIN_FRAME_SIZE, bytes[5]));\n }\n },\n 30_000\n );\n if (closed) throw new Error('Bluetooth connection ended');\n const sender = this.args.apduSenderServiceFactory({ frameSize });\n const connected = new TransportConnectedDevice({\n ...known.device,\n type: 'BLE',\n sendApdu: async (apdu, _triggersDisconnection, abortTimeout): Promise<SendApduResult> => {\n if (closed) return Left(new DisconnectError());\n if (receive) return Left(new SendApduConcurrencyError());\n const receiver = this.args.apduReceiverServiceFactory();\n try {\n const result = await exchange<SendApduResult>(\n sender.getFrames(apdu).map(frame => frame.getRawData()),\n (bytes, resolve) =>\n receiver.handleFrame(bytes).caseOf({\n Left: error => resolve(Left(error)),\n Right: response => {\n response.map(value => resolve(Right(value)));\n },\n }),\n abortTimeout ?? 120_000\n );\n if (result.isLeft()) {\n await disconnect(true);\n }\n return result;\n } catch (error) {\n await disconnect(true);\n return Left(error instanceof SendApduTimeoutError ? error : new GeneralDmkError(error));\n }\n },\n });\n const connection = this.connections.get(deviceId);\n if (connection?.close === close) connection.connectedDevice = connected;\n return Right(connected);\n } catch (error) {\n await disconnect(false);\n return Left(new OpeningConnectionError(error));\n }\n }\n\n async disconnect({\n connectedDevice,\n }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']> {\n const connection = this.connections.get(connectedDevice.id);\n if (connection?.connectedDevice !== connectedDevice || !connection.close())\n return Right(undefined);\n try {\n await this.bridge.disconnect(connectedDevice.id);\n return Right(undefined);\n } catch (error) {\n return Left(new DisconnectError(error));\n } finally {\n if (this.connections.get(connectedDevice.id) === connection) {\n this.connections.delete(connectedDevice.id);\n }\n }\n }\n}\n","import type { ElectronBleConnectOptions, ElectronBleMatch } from '@onekeyfe/hwk-adapter-core';\n\nexport const LEDGER_BLE_VENDOR = 'ledger';\n\n/**\n * Frame size bounds for the 0x08 handshake, kept together because they are two\n * halves of one rule: the device reports the size it can take in a single\n * byte, so 255 is the most it can ask for, and 20 is the floor we raise a\n * smaller answer to. The negotiated value in between is used as-is — capping\n * it would negotiate and then discard the answer, leaving every transfer at\n * roughly 7x the frame count.\n */\nexport const LEDGER_BLE_MIN_FRAME_SIZE = 20;\nexport const LEDGER_BLE_MAX_FRAME_SIZE = 255;\n\n/**\n * Ledger GATT uuids are per device model, so unlike Trezor's fixed profile\n * they are looked up from the DMK data source at discovery time and passed in\n * here. Only the vendor key and the framing rule are constant.\n */\nexport function ledgerBleMatch(serviceUuids: string[]): ElectronBleMatch {\n return { serviceUuids };\n}\n\nexport function ledgerBleConnectProfile(gatt: {\n serviceUuid: string;\n writeUuid: string;\n notifyUuid: string;\n}): ElectronBleConnectOptions {\n return {\n vendor: LEDGER_BLE_VENDOR,\n ...gatt,\n // Ledger frames carry their own length and sequence; padding corrupts them.\n write: { mode: 'raw', maxLength: LEDGER_BLE_MAX_FRAME_SIZE },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/LedgerElectronBleConnector.ts","../src/LedgerElectronBleTransport.ts","../src/bleProfile.ts"],"sourcesContent":["export { createLedgerElectronBleConnector } from './LedgerElectronBleConnector';\nexport { LedgerElectronBleTransport } from './LedgerElectronBleTransport';\nexport type { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';\n\nexport {\n LEDGER_BLE_VENDOR,\n LEDGER_BLE_MIN_FRAME_SIZE,\n LEDGER_BLE_MAX_FRAME_SIZE,\n ledgerBleMatch,\n ledgerBleConnectProfile,\n} from './bleProfile';\n","import { LedgerConnectorBase } from '@onekeyfe/hwk-ledger-adapter';\nimport { LedgerElectronBleTransport } from './LedgerElectronBleTransport';\n\nimport type { TransportArgs } from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi } from '@onekeyfe/hwk-adapter-core';\n\nexport function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase {\n return new LedgerConnectorBase(\n async () => (args: TransportArgs) => new LedgerElectronBleTransport(bridge, args),\n { connectionType: 'ble' }\n );\n}\n","import {\n DisconnectError,\n GeneralDmkError,\n OpeningConnectionError,\n SendApduConcurrencyError,\n SendApduTimeoutError,\n TransportConnectedDevice,\n UnknownDeviceError,\n} from '@ledgerhq/device-management-kit';\nimport { Left, Right } from 'purify-ts';\nimport { defer, from, mergeMap } from 'rxjs';\nimport {\n LEDGER_BLE_MIN_FRAME_SIZE,\n LEDGER_BLE_VENDOR,\n ledgerBleConnectProfile,\n ledgerBleMatch,\n} from './bleProfile';\nimport { hexToBytes } from '@onekeyfe/hwk-adapter-core';\n\nimport type {\n BleDeviceInfos,\n SendApduResult,\n Transport,\n TransportArgs,\n TransportDiscoveredDevice,\n} from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';\n\nconst TRANSPORT_ID = 'ELECTRON_BLE';\n// Ledger's own transports open at the ATT default and then raise it from the\n// device's answer to the 0x08 handshake below. Keep the same floor so a device\n// that reports nothing useful still works.\nconst normalizeUuid = (uuid: string) => uuid.replace(/-/g, '').toLowerCase();\nconst toHex = (bytes: Uint8Array) =>\n Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');\n\nexport class LedgerElectronBleTransport implements Transport {\n private readonly devices = new Map<\n string,\n { device: TransportDiscoveredDevice; profile: BleDeviceInfos }\n >();\n\n private readonly connections = new Map<\n string,\n {\n close: () => boolean;\n connectedDevice?: TransportConnectedDevice;\n }\n >();\n\n private scanPromise: Promise<TransportDiscoveredDevice[]> | undefined;\n\n private readonly bridge: ElectronBleApi;\n\n private readonly args: Pick<\n TransportArgs,\n 'deviceModelDataSource' | 'apduSenderServiceFactory' | 'apduReceiverServiceFactory'\n >;\n\n constructor(bridge: ElectronBleApi, args: LedgerElectronBleTransport['args']) {\n this.bridge = bridge;\n this.args = args;\n }\n\n getIdentifier() {\n return TRANSPORT_ID;\n }\n\n isSupported() {\n return true;\n }\n\n private remember(info: ElectronBleDeviceInfo): TransportDiscoveredDevice | undefined {\n const profiles = this.args.deviceModelDataSource.getBluetoothServicesInfos();\n const profile = Object.values(profiles).find(candidate =>\n info.advertisedServiceUuids?.some(\n uuid => normalizeUuid(uuid) === normalizeUuid(candidate.serviceUuid)\n )\n );\n if (!profile) return undefined;\n const device: TransportDiscoveredDevice = {\n id: info.id,\n name: info.name,\n rssi: info.rssi,\n deviceModel: profile.deviceModel,\n transport: TRANSPORT_ID,\n };\n this.devices.set(info.id, { device, profile });\n return device;\n }\n\n private discover(): Promise<TransportDiscoveredDevice[]> {\n if (this.scanPromise) return this.scanPromise;\n const scan = (async () => {\n const availability = await this.bridge.checkAvailability();\n if (!availability.available) throw new Error(`Bluetooth unavailable: ${availability.state}`);\n const infos = await this.bridge.scan({\n vendor: LEDGER_BLE_VENDOR,\n match: ledgerBleMatch(this.args.deviceModelDataSource.getBluetoothServices()),\n });\n const devices: TransportDiscoveredDevice[] = [];\n for (const info of infos) {\n const device = this.remember(info);\n if (device) devices.push(device);\n }\n return devices;\n })();\n this.scanPromise = scan;\n void scan\n .finally(() => {\n if (this.scanPromise === scan) this.scanPromise = undefined;\n })\n .catch(() => undefined);\n return scan;\n }\n\n startDiscovering() {\n return defer(() => this.discover()).pipe(mergeMap(devices => from(devices)));\n }\n\n listenToAvailableDevices() {\n return defer(() => this.discover());\n }\n\n async stopDiscovering(): Promise<void> {\n await this.scanPromise?.catch(() => undefined);\n await this.bridge.stopScan();\n }\n\n async connect({\n deviceId,\n onDisconnect,\n }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']> {\n if (this.connections.has(deviceId))\n return Left(new OpeningConnectionError('Device is already connected'));\n let removeNotification: (() => void) | undefined;\n let removeDisconnect: (() => void) | undefined;\n let closed = false;\n let rejectPending: ((error: unknown) => void) | undefined;\n let receive: ((bytes: Uint8Array) => void) | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n rejectPending?.(new Error('Bluetooth connection ended'));\n return true;\n };\n const release = () => {\n if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);\n };\n const disconnect = async (notify: boolean) => {\n if (!close()) return;\n await this.bridge.disconnect(deviceId).catch(() => undefined);\n release();\n if (notify) onDisconnect(deviceId);\n };\n // Reserve before the first await so concurrent acquires cannot share native GATT state.\n this.connections.set(deviceId, { close });\n try {\n await this.stopDiscovering();\n if (!this.devices.has(deviceId)) {\n const info = await this.bridge.getDevice(deviceId);\n if (info) this.remember(info);\n }\n const known = this.devices.get(deviceId);\n if (!known) {\n close();\n release();\n return Left(new UnknownDeviceError());\n }\n // Registered before connect: a Ledger often drops the link the moment\n // pairing completes, and an event missed here costs a full APDU timeout.\n // The handler filters by id, and every failure path below closes it.\n removeDisconnect = this.bridge.onDeviceDisconnected(id => {\n if (id !== deviceId) return;\n if (close()) {\n release();\n onDisconnect(deviceId);\n }\n });\n await this.bridge.connect(\n deviceId,\n ledgerBleConnectProfile({\n serviceUuid: known.profile.serviceUuid,\n writeUuid: known.profile.writeUuid,\n notifyUuid: known.profile.notifyUuid,\n })\n );\n if (closed) throw new Error('Bluetooth connection ended');\n removeNotification = this.bridge.onNotification((id, hex) => {\n if (id !== deviceId || closed) return;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {\n rejectPending?.(new Error('Malformed Bluetooth notification'));\n return;\n }\n receive?.(hexToBytes(hex));\n });\n await this.bridge.subscribe(deviceId);\n\n const exchange = async <T>(\n frames: Uint8Array[],\n accept: (\n bytes: Uint8Array,\n resolve: (value: T) => void,\n reject: (error: unknown) => void\n ) => void,\n timeoutMs: number\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (receive) throw new Error('Bluetooth exchange is busy');\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await new Promise<T>((resolve, reject) => {\n rejectPending = reject;\n receive = bytes => accept(bytes, resolve, reject);\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\n void (async () => {\n for (const frame of frames) {\n if (closed) throw new Error('Bluetooth connection ended');\n await this.bridge.write(deviceId, toHex(frame));\n }\n })().catch(reject);\n });\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n receive = undefined;\n rejectPending = undefined;\n }\n };\n\n const frameSize = await exchange<number>(\n [Uint8Array.of(0x08, 0, 0, 0, 0)],\n (bytes, resolve, reject) => {\n if (bytes.length < 6 || bytes[0] !== 0x08 || bytes[5] < 6) {\n reject(new Error('Invalid Ledger BLE MTU response'));\n } else {\n // Matches @ledgerhq/hw-transport-web-ble inferMTU: the device\n // reports the frame size it can take, and it is only ever used to\n // raise the floor. Capping it here would negotiate and then throw\n // the answer away, leaving every transfer at ~7x the frame count.\n resolve(Math.max(LEDGER_BLE_MIN_FRAME_SIZE, bytes[5]));\n }\n },\n 30_000\n );\n if (closed) throw new Error('Bluetooth connection ended');\n const sender = this.args.apduSenderServiceFactory({ frameSize });\n const connected = new TransportConnectedDevice({\n ...known.device,\n type: 'BLE',\n sendApdu: async (apdu, _triggersDisconnection, abortTimeout): Promise<SendApduResult> => {\n if (closed) return Left(new DisconnectError());\n if (receive) return Left(new SendApduConcurrencyError());\n const receiver = this.args.apduReceiverServiceFactory();\n try {\n const result = await exchange<SendApduResult>(\n sender.getFrames(apdu).map(frame => frame.getRawData()),\n (bytes, resolve) =>\n receiver.handleFrame(bytes).caseOf({\n Left: error => resolve(Left(error)),\n Right: response => {\n response.map(value => resolve(Right(value)));\n },\n }),\n abortTimeout ?? 120_000\n );\n if (result.isLeft()) {\n await disconnect(true);\n }\n return result;\n } catch (error) {\n await disconnect(true);\n return Left(error instanceof SendApduTimeoutError ? error : new GeneralDmkError(error));\n }\n },\n });\n const connection = this.connections.get(deviceId);\n if (connection?.close === close) connection.connectedDevice = connected;\n return Right(connected);\n } catch (error) {\n await disconnect(false);\n return Left(new OpeningConnectionError(error));\n }\n }\n\n async disconnect({\n connectedDevice,\n }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']> {\n const connection = this.connections.get(connectedDevice.id);\n if (connection?.connectedDevice !== connectedDevice || !connection.close())\n return Right(undefined);\n try {\n await this.bridge.disconnect(connectedDevice.id);\n return Right(undefined);\n } catch (error) {\n return Left(new DisconnectError(error));\n } finally {\n if (this.connections.get(connectedDevice.id) === connection) {\n this.connections.delete(connectedDevice.id);\n }\n }\n }\n}\n","import type { ElectronBleConnectOptions, ElectronBleMatch } from '@onekeyfe/hwk-adapter-core';\n\nexport const LEDGER_BLE_VENDOR = 'ledger';\n\n/**\n * Frame size bounds for the 0x08 handshake, kept together because they are two\n * halves of one rule: the device reports the size it can take in a single\n * byte, so 255 is the most it can ask for, and 20 is the floor we raise a\n * smaller answer to. The negotiated value in between is used as-is — capping\n * it would negotiate and then discard the answer, leaving every transfer at\n * roughly 7x the frame count.\n */\nexport const LEDGER_BLE_MIN_FRAME_SIZE = 20;\nexport const LEDGER_BLE_MAX_FRAME_SIZE = 255;\n\n/**\n * Ledger GATT uuids are per device model, so unlike Trezor's fixed profile\n * they are looked up from the DMK data source at discovery time and passed in\n * here. Only the vendor key and the framing rule are constant.\n */\nexport function ledgerBleMatch(serviceUuids: string[]): ElectronBleMatch {\n return { serviceUuids };\n}\n\nexport function ledgerBleConnectProfile(gatt: {\n serviceUuid: string;\n writeUuid: string;\n notifyUuid: string;\n}): ElectronBleConnectOptions {\n return {\n vendor: LEDGER_BLE_VENDOR,\n ...gatt,\n // Ledger frames carry their own length and sequence; padding corrupts them.\n write: { mode: 'raw', maxLength: LEDGER_BLE_MAX_FRAME_SIZE },\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -139,6 +139,13 @@ var LedgerElectronBleTransport = class {
139
139
  release();
140
140
  return Left(new UnknownDeviceError());
141
141
  }
142
+ removeDisconnect = this.bridge.onDeviceDisconnected((id) => {
143
+ if (id !== deviceId) return;
144
+ if (close()) {
145
+ release();
146
+ onDisconnect(deviceId);
147
+ }
148
+ });
142
149
  await this.bridge.connect(
143
150
  deviceId,
144
151
  ledgerBleConnectProfile({
@@ -147,13 +154,7 @@ var LedgerElectronBleTransport = class {
147
154
  notifyUuid: known.profile.notifyUuid
148
155
  })
149
156
  );
150
- removeDisconnect = this.bridge.onDeviceDisconnected((id) => {
151
- if (id !== deviceId) return;
152
- if (close()) {
153
- release();
154
- onDisconnect(deviceId);
155
- }
156
- });
157
+ if (closed) throw new Error("Bluetooth connection ended");
157
158
  removeNotification = this.bridge.onNotification((id, hex) => {
158
159
  if (id !== deviceId || closed) return;
159
160
  if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/LedgerElectronBleConnector.ts","../src/LedgerElectronBleTransport.ts","../src/bleProfile.ts"],"sourcesContent":["import { LedgerConnectorBase } from '@onekeyfe/hwk-ledger-adapter';\nimport { LedgerElectronBleTransport } from './LedgerElectronBleTransport';\n\nimport type { TransportArgs } from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi } from '@onekeyfe/hwk-adapter-core';\n\nexport function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase {\n return new LedgerConnectorBase(\n async () => (args: TransportArgs) => new LedgerElectronBleTransport(bridge, args),\n { connectionType: 'ble' }\n );\n}\n","import {\n DisconnectError,\n GeneralDmkError,\n OpeningConnectionError,\n SendApduConcurrencyError,\n SendApduTimeoutError,\n TransportConnectedDevice,\n UnknownDeviceError,\n} from '@ledgerhq/device-management-kit';\nimport { Left, Right } from 'purify-ts';\nimport { defer, from, mergeMap } from 'rxjs';\nimport {\n LEDGER_BLE_MIN_FRAME_SIZE,\n LEDGER_BLE_VENDOR,\n ledgerBleConnectProfile,\n ledgerBleMatch,\n} from './bleProfile';\nimport { hexToBytes } from '@onekeyfe/hwk-adapter-core';\n\nimport type {\n BleDeviceInfos,\n SendApduResult,\n Transport,\n TransportArgs,\n TransportDiscoveredDevice,\n} from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';\n\nconst TRANSPORT_ID = 'ELECTRON_BLE';\n// Ledger's own transports open at the ATT default and then raise it from the\n// device's answer to the 0x08 handshake below. Keep the same floor so a device\n// that reports nothing useful still works.\nconst normalizeUuid = (uuid: string) => uuid.replace(/-/g, '').toLowerCase();\nconst toHex = (bytes: Uint8Array) =>\n Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');\n\nexport class LedgerElectronBleTransport implements Transport {\n private readonly devices = new Map<\n string,\n { device: TransportDiscoveredDevice; profile: BleDeviceInfos }\n >();\n\n private readonly connections = new Map<\n string,\n {\n close: () => boolean;\n connectedDevice?: TransportConnectedDevice;\n }\n >();\n\n private scanPromise: Promise<TransportDiscoveredDevice[]> | undefined;\n\n private readonly bridge: ElectronBleApi;\n\n private readonly args: Pick<\n TransportArgs,\n 'deviceModelDataSource' | 'apduSenderServiceFactory' | 'apduReceiverServiceFactory'\n >;\n\n constructor(bridge: ElectronBleApi, args: LedgerElectronBleTransport['args']) {\n this.bridge = bridge;\n this.args = args;\n }\n\n getIdentifier() {\n return TRANSPORT_ID;\n }\n\n isSupported() {\n return true;\n }\n\n private remember(info: ElectronBleDeviceInfo): TransportDiscoveredDevice | undefined {\n const profiles = this.args.deviceModelDataSource.getBluetoothServicesInfos();\n const profile = Object.values(profiles).find(candidate =>\n info.advertisedServiceUuids?.some(\n uuid => normalizeUuid(uuid) === normalizeUuid(candidate.serviceUuid)\n )\n );\n if (!profile) return undefined;\n const device: TransportDiscoveredDevice = {\n id: info.id,\n name: info.name,\n rssi: info.rssi,\n deviceModel: profile.deviceModel,\n transport: TRANSPORT_ID,\n };\n this.devices.set(info.id, { device, profile });\n return device;\n }\n\n private discover(): Promise<TransportDiscoveredDevice[]> {\n if (this.scanPromise) return this.scanPromise;\n const scan = (async () => {\n const availability = await this.bridge.checkAvailability();\n if (!availability.available) throw new Error(`Bluetooth unavailable: ${availability.state}`);\n const infos = await this.bridge.scan({\n vendor: LEDGER_BLE_VENDOR,\n match: ledgerBleMatch(this.args.deviceModelDataSource.getBluetoothServices()),\n });\n const devices: TransportDiscoveredDevice[] = [];\n for (const info of infos) {\n const device = this.remember(info);\n if (device) devices.push(device);\n }\n return devices;\n })();\n this.scanPromise = scan;\n void scan\n .finally(() => {\n if (this.scanPromise === scan) this.scanPromise = undefined;\n })\n .catch(() => undefined);\n return scan;\n }\n\n startDiscovering() {\n return defer(() => this.discover()).pipe(mergeMap(devices => from(devices)));\n }\n\n listenToAvailableDevices() {\n return defer(() => this.discover());\n }\n\n async stopDiscovering(): Promise<void> {\n await this.scanPromise?.catch(() => undefined);\n await this.bridge.stopScan();\n }\n\n async connect({\n deviceId,\n onDisconnect,\n }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']> {\n if (this.connections.has(deviceId))\n return Left(new OpeningConnectionError('Device is already connected'));\n let removeNotification: (() => void) | undefined;\n let removeDisconnect: (() => void) | undefined;\n let closed = false;\n let rejectPending: ((error: unknown) => void) | undefined;\n let receive: ((bytes: Uint8Array) => void) | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n rejectPending?.(new Error('Bluetooth connection ended'));\n return true;\n };\n const release = () => {\n if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);\n };\n const disconnect = async (notify: boolean) => {\n if (!close()) return;\n await this.bridge.disconnect(deviceId).catch(() => undefined);\n release();\n if (notify) onDisconnect(deviceId);\n };\n // Reserve before the first await so concurrent acquires cannot share native GATT state.\n this.connections.set(deviceId, { close });\n try {\n await this.stopDiscovering();\n if (!this.devices.has(deviceId)) {\n const info = await this.bridge.getDevice(deviceId);\n if (info) this.remember(info);\n }\n const known = this.devices.get(deviceId);\n if (!known) {\n close();\n release();\n return Left(new UnknownDeviceError());\n }\n await this.bridge.connect(\n deviceId,\n ledgerBleConnectProfile({\n serviceUuid: known.profile.serviceUuid,\n writeUuid: known.profile.writeUuid,\n notifyUuid: known.profile.notifyUuid,\n })\n );\n removeDisconnect = this.bridge.onDeviceDisconnected(id => {\n if (id !== deviceId) return;\n if (close()) {\n release();\n onDisconnect(deviceId);\n }\n });\n removeNotification = this.bridge.onNotification((id, hex) => {\n if (id !== deviceId || closed) return;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {\n rejectPending?.(new Error('Malformed Bluetooth notification'));\n return;\n }\n receive?.(hexToBytes(hex));\n });\n await this.bridge.subscribe(deviceId);\n\n const exchange = async <T>(\n frames: Uint8Array[],\n accept: (\n bytes: Uint8Array,\n resolve: (value: T) => void,\n reject: (error: unknown) => void\n ) => void,\n timeoutMs: number\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (receive) throw new Error('Bluetooth exchange is busy');\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await new Promise<T>((resolve, reject) => {\n rejectPending = reject;\n receive = bytes => accept(bytes, resolve, reject);\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\n void (async () => {\n for (const frame of frames) {\n if (closed) throw new Error('Bluetooth connection ended');\n await this.bridge.write(deviceId, toHex(frame));\n }\n })().catch(reject);\n });\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n receive = undefined;\n rejectPending = undefined;\n }\n };\n\n const frameSize = await exchange<number>(\n [Uint8Array.of(0x08, 0, 0, 0, 0)],\n (bytes, resolve, reject) => {\n if (bytes.length < 6 || bytes[0] !== 0x08 || bytes[5] < 6) {\n reject(new Error('Invalid Ledger BLE MTU response'));\n } else {\n // Matches @ledgerhq/hw-transport-web-ble inferMTU: the device\n // reports the frame size it can take, and it is only ever used to\n // raise the floor. Capping it here would negotiate and then throw\n // the answer away, leaving every transfer at ~7x the frame count.\n resolve(Math.max(LEDGER_BLE_MIN_FRAME_SIZE, bytes[5]));\n }\n },\n 30_000\n );\n if (closed) throw new Error('Bluetooth connection ended');\n const sender = this.args.apduSenderServiceFactory({ frameSize });\n const connected = new TransportConnectedDevice({\n ...known.device,\n type: 'BLE',\n sendApdu: async (apdu, _triggersDisconnection, abortTimeout): Promise<SendApduResult> => {\n if (closed) return Left(new DisconnectError());\n if (receive) return Left(new SendApduConcurrencyError());\n const receiver = this.args.apduReceiverServiceFactory();\n try {\n const result = await exchange<SendApduResult>(\n sender.getFrames(apdu).map(frame => frame.getRawData()),\n (bytes, resolve) =>\n receiver.handleFrame(bytes).caseOf({\n Left: error => resolve(Left(error)),\n Right: response => {\n response.map(value => resolve(Right(value)));\n },\n }),\n abortTimeout ?? 120_000\n );\n if (result.isLeft()) {\n await disconnect(true);\n }\n return result;\n } catch (error) {\n await disconnect(true);\n return Left(error instanceof SendApduTimeoutError ? error : new GeneralDmkError(error));\n }\n },\n });\n const connection = this.connections.get(deviceId);\n if (connection?.close === close) connection.connectedDevice = connected;\n return Right(connected);\n } catch (error) {\n await disconnect(false);\n return Left(new OpeningConnectionError(error));\n }\n }\n\n async disconnect({\n connectedDevice,\n }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']> {\n const connection = this.connections.get(connectedDevice.id);\n if (connection?.connectedDevice !== connectedDevice || !connection.close())\n return Right(undefined);\n try {\n await this.bridge.disconnect(connectedDevice.id);\n return Right(undefined);\n } catch (error) {\n return Left(new DisconnectError(error));\n } finally {\n if (this.connections.get(connectedDevice.id) === connection) {\n this.connections.delete(connectedDevice.id);\n }\n }\n }\n}\n","import type { ElectronBleConnectOptions, ElectronBleMatch } from '@onekeyfe/hwk-adapter-core';\n\nexport const LEDGER_BLE_VENDOR = 'ledger';\n\n/**\n * Frame size bounds for the 0x08 handshake, kept together because they are two\n * halves of one rule: the device reports the size it can take in a single\n * byte, so 255 is the most it can ask for, and 20 is the floor we raise a\n * smaller answer to. The negotiated value in between is used as-is — capping\n * it would negotiate and then discard the answer, leaving every transfer at\n * roughly 7x the frame count.\n */\nexport const LEDGER_BLE_MIN_FRAME_SIZE = 20;\nexport const LEDGER_BLE_MAX_FRAME_SIZE = 255;\n\n/**\n * Ledger GATT uuids are per device model, so unlike Trezor's fixed profile\n * they are looked up from the DMK data source at discovery time and passed in\n * here. Only the vendor key and the framing rule are constant.\n */\nexport function ledgerBleMatch(serviceUuids: string[]): ElectronBleMatch {\n return { serviceUuids };\n}\n\nexport function ledgerBleConnectProfile(gatt: {\n serviceUuid: string;\n writeUuid: string;\n notifyUuid: string;\n}): ElectronBleConnectOptions {\n return {\n vendor: LEDGER_BLE_VENDOR,\n ...gatt,\n // Ledger frames carry their own length and sequence; padding corrupts them.\n write: { mode: 'raw', maxLength: LEDGER_BLE_MAX_FRAME_SIZE },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/LedgerElectronBleConnector.ts","../src/LedgerElectronBleTransport.ts","../src/bleProfile.ts"],"sourcesContent":["import { LedgerConnectorBase } from '@onekeyfe/hwk-ledger-adapter';\nimport { LedgerElectronBleTransport } from './LedgerElectronBleTransport';\n\nimport type { TransportArgs } from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi } from '@onekeyfe/hwk-adapter-core';\n\nexport function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase {\n return new LedgerConnectorBase(\n async () => (args: TransportArgs) => new LedgerElectronBleTransport(bridge, args),\n { connectionType: 'ble' }\n );\n}\n","import {\n DisconnectError,\n GeneralDmkError,\n OpeningConnectionError,\n SendApduConcurrencyError,\n SendApduTimeoutError,\n TransportConnectedDevice,\n UnknownDeviceError,\n} from '@ledgerhq/device-management-kit';\nimport { Left, Right } from 'purify-ts';\nimport { defer, from, mergeMap } from 'rxjs';\nimport {\n LEDGER_BLE_MIN_FRAME_SIZE,\n LEDGER_BLE_VENDOR,\n ledgerBleConnectProfile,\n ledgerBleMatch,\n} from './bleProfile';\nimport { hexToBytes } from '@onekeyfe/hwk-adapter-core';\n\nimport type {\n BleDeviceInfos,\n SendApduResult,\n Transport,\n TransportArgs,\n TransportDiscoveredDevice,\n} from '@ledgerhq/device-management-kit';\nimport type { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';\n\nconst TRANSPORT_ID = 'ELECTRON_BLE';\n// Ledger's own transports open at the ATT default and then raise it from the\n// device's answer to the 0x08 handshake below. Keep the same floor so a device\n// that reports nothing useful still works.\nconst normalizeUuid = (uuid: string) => uuid.replace(/-/g, '').toLowerCase();\nconst toHex = (bytes: Uint8Array) =>\n Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');\n\nexport class LedgerElectronBleTransport implements Transport {\n private readonly devices = new Map<\n string,\n { device: TransportDiscoveredDevice; profile: BleDeviceInfos }\n >();\n\n private readonly connections = new Map<\n string,\n {\n close: () => boolean;\n connectedDevice?: TransportConnectedDevice;\n }\n >();\n\n private scanPromise: Promise<TransportDiscoveredDevice[]> | undefined;\n\n private readonly bridge: ElectronBleApi;\n\n private readonly args: Pick<\n TransportArgs,\n 'deviceModelDataSource' | 'apduSenderServiceFactory' | 'apduReceiverServiceFactory'\n >;\n\n constructor(bridge: ElectronBleApi, args: LedgerElectronBleTransport['args']) {\n this.bridge = bridge;\n this.args = args;\n }\n\n getIdentifier() {\n return TRANSPORT_ID;\n }\n\n isSupported() {\n return true;\n }\n\n private remember(info: ElectronBleDeviceInfo): TransportDiscoveredDevice | undefined {\n const profiles = this.args.deviceModelDataSource.getBluetoothServicesInfos();\n const profile = Object.values(profiles).find(candidate =>\n info.advertisedServiceUuids?.some(\n uuid => normalizeUuid(uuid) === normalizeUuid(candidate.serviceUuid)\n )\n );\n if (!profile) return undefined;\n const device: TransportDiscoveredDevice = {\n id: info.id,\n name: info.name,\n rssi: info.rssi,\n deviceModel: profile.deviceModel,\n transport: TRANSPORT_ID,\n };\n this.devices.set(info.id, { device, profile });\n return device;\n }\n\n private discover(): Promise<TransportDiscoveredDevice[]> {\n if (this.scanPromise) return this.scanPromise;\n const scan = (async () => {\n const availability = await this.bridge.checkAvailability();\n if (!availability.available) throw new Error(`Bluetooth unavailable: ${availability.state}`);\n const infos = await this.bridge.scan({\n vendor: LEDGER_BLE_VENDOR,\n match: ledgerBleMatch(this.args.deviceModelDataSource.getBluetoothServices()),\n });\n const devices: TransportDiscoveredDevice[] = [];\n for (const info of infos) {\n const device = this.remember(info);\n if (device) devices.push(device);\n }\n return devices;\n })();\n this.scanPromise = scan;\n void scan\n .finally(() => {\n if (this.scanPromise === scan) this.scanPromise = undefined;\n })\n .catch(() => undefined);\n return scan;\n }\n\n startDiscovering() {\n return defer(() => this.discover()).pipe(mergeMap(devices => from(devices)));\n }\n\n listenToAvailableDevices() {\n return defer(() => this.discover());\n }\n\n async stopDiscovering(): Promise<void> {\n await this.scanPromise?.catch(() => undefined);\n await this.bridge.stopScan();\n }\n\n async connect({\n deviceId,\n onDisconnect,\n }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']> {\n if (this.connections.has(deviceId))\n return Left(new OpeningConnectionError('Device is already connected'));\n let removeNotification: (() => void) | undefined;\n let removeDisconnect: (() => void) | undefined;\n let closed = false;\n let rejectPending: ((error: unknown) => void) | undefined;\n let receive: ((bytes: Uint8Array) => void) | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n rejectPending?.(new Error('Bluetooth connection ended'));\n return true;\n };\n const release = () => {\n if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);\n };\n const disconnect = async (notify: boolean) => {\n if (!close()) return;\n await this.bridge.disconnect(deviceId).catch(() => undefined);\n release();\n if (notify) onDisconnect(deviceId);\n };\n // Reserve before the first await so concurrent acquires cannot share native GATT state.\n this.connections.set(deviceId, { close });\n try {\n await this.stopDiscovering();\n if (!this.devices.has(deviceId)) {\n const info = await this.bridge.getDevice(deviceId);\n if (info) this.remember(info);\n }\n const known = this.devices.get(deviceId);\n if (!known) {\n close();\n release();\n return Left(new UnknownDeviceError());\n }\n // Registered before connect: a Ledger often drops the link the moment\n // pairing completes, and an event missed here costs a full APDU timeout.\n // The handler filters by id, and every failure path below closes it.\n removeDisconnect = this.bridge.onDeviceDisconnected(id => {\n if (id !== deviceId) return;\n if (close()) {\n release();\n onDisconnect(deviceId);\n }\n });\n await this.bridge.connect(\n deviceId,\n ledgerBleConnectProfile({\n serviceUuid: known.profile.serviceUuid,\n writeUuid: known.profile.writeUuid,\n notifyUuid: known.profile.notifyUuid,\n })\n );\n if (closed) throw new Error('Bluetooth connection ended');\n removeNotification = this.bridge.onNotification((id, hex) => {\n if (id !== deviceId || closed) return;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {\n rejectPending?.(new Error('Malformed Bluetooth notification'));\n return;\n }\n receive?.(hexToBytes(hex));\n });\n await this.bridge.subscribe(deviceId);\n\n const exchange = async <T>(\n frames: Uint8Array[],\n accept: (\n bytes: Uint8Array,\n resolve: (value: T) => void,\n reject: (error: unknown) => void\n ) => void,\n timeoutMs: number\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (receive) throw new Error('Bluetooth exchange is busy');\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await new Promise<T>((resolve, reject) => {\n rejectPending = reject;\n receive = bytes => accept(bytes, resolve, reject);\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\n void (async () => {\n for (const frame of frames) {\n if (closed) throw new Error('Bluetooth connection ended');\n await this.bridge.write(deviceId, toHex(frame));\n }\n })().catch(reject);\n });\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n receive = undefined;\n rejectPending = undefined;\n }\n };\n\n const frameSize = await exchange<number>(\n [Uint8Array.of(0x08, 0, 0, 0, 0)],\n (bytes, resolve, reject) => {\n if (bytes.length < 6 || bytes[0] !== 0x08 || bytes[5] < 6) {\n reject(new Error('Invalid Ledger BLE MTU response'));\n } else {\n // Matches @ledgerhq/hw-transport-web-ble inferMTU: the device\n // reports the frame size it can take, and it is only ever used to\n // raise the floor. Capping it here would negotiate and then throw\n // the answer away, leaving every transfer at ~7x the frame count.\n resolve(Math.max(LEDGER_BLE_MIN_FRAME_SIZE, bytes[5]));\n }\n },\n 30_000\n );\n if (closed) throw new Error('Bluetooth connection ended');\n const sender = this.args.apduSenderServiceFactory({ frameSize });\n const connected = new TransportConnectedDevice({\n ...known.device,\n type: 'BLE',\n sendApdu: async (apdu, _triggersDisconnection, abortTimeout): Promise<SendApduResult> => {\n if (closed) return Left(new DisconnectError());\n if (receive) return Left(new SendApduConcurrencyError());\n const receiver = this.args.apduReceiverServiceFactory();\n try {\n const result = await exchange<SendApduResult>(\n sender.getFrames(apdu).map(frame => frame.getRawData()),\n (bytes, resolve) =>\n receiver.handleFrame(bytes).caseOf({\n Left: error => resolve(Left(error)),\n Right: response => {\n response.map(value => resolve(Right(value)));\n },\n }),\n abortTimeout ?? 120_000\n );\n if (result.isLeft()) {\n await disconnect(true);\n }\n return result;\n } catch (error) {\n await disconnect(true);\n return Left(error instanceof SendApduTimeoutError ? error : new GeneralDmkError(error));\n }\n },\n });\n const connection = this.connections.get(deviceId);\n if (connection?.close === close) connection.connectedDevice = connected;\n return Right(connected);\n } catch (error) {\n await disconnect(false);\n return Left(new OpeningConnectionError(error));\n }\n }\n\n async disconnect({\n connectedDevice,\n }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']> {\n const connection = this.connections.get(connectedDevice.id);\n if (connection?.connectedDevice !== connectedDevice || !connection.close())\n return Right(undefined);\n try {\n await this.bridge.disconnect(connectedDevice.id);\n return Right(undefined);\n } catch (error) {\n return Left(new DisconnectError(error));\n } finally {\n if (this.connections.get(connectedDevice.id) === connection) {\n this.connections.delete(connectedDevice.id);\n }\n }\n }\n}\n","import type { ElectronBleConnectOptions, ElectronBleMatch } from '@onekeyfe/hwk-adapter-core';\n\nexport const LEDGER_BLE_VENDOR = 'ledger';\n\n/**\n * Frame size bounds for the 0x08 handshake, kept together because they are two\n * halves of one rule: the device reports the size it can take in a single\n * byte, so 255 is the most it can ask for, and 20 is the floor we raise a\n * smaller answer to. The negotiated value in between is used as-is — capping\n * it would negotiate and then discard the answer, leaving every transfer at\n * roughly 7x the frame count.\n */\nexport const LEDGER_BLE_MIN_FRAME_SIZE = 20;\nexport const LEDGER_BLE_MAX_FRAME_SIZE = 255;\n\n/**\n * Ledger GATT uuids are per device model, so unlike Trezor's fixed profile\n * they are looked up from the DMK data source at discovery time and passed in\n * here. Only the vendor key and the framing rule are constant.\n */\nexport function ledgerBleMatch(serviceUuids: string[]): ElectronBleMatch {\n return { serviceUuids };\n}\n\nexport function ledgerBleConnectProfile(gatt: {\n serviceUuid: string;\n writeUuid: string;\n notifyUuid: string;\n}): ElectronBleConnectOptions {\n return {\n vendor: LEDGER_BLE_VENDOR,\n ...gatt,\n // Ledger frames carry their own length and sequence; padding corrupts them.\n write: { mode: 'raw', maxLength: LEDGER_BLE_MAX_FRAME_SIZE },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hwk-ledger-connector-electron-ble",
3
- "version": "1.2.3-alpha.2",
3
+ "version": "1.2.3-alpha.4",
4
4
  "description": "IConnector implementation for Ledger hardware wallets via Electron BLE",
5
5
  "author": "OneKey",
6
6
  "license": "MIT",
@@ -49,8 +49,8 @@
49
49
  ],
50
50
  "dependencies": {
51
51
  "@ledgerhq/device-management-kit": "1.9.0",
52
- "@onekeyfe/hwk-adapter-core": "1.2.3-alpha.2",
53
- "@onekeyfe/hwk-ledger-adapter": "1.2.3-alpha.2",
52
+ "@onekeyfe/hwk-adapter-core": "1.2.3-alpha.4",
53
+ "@onekeyfe/hwk-ledger-adapter": "1.2.3-alpha.4",
54
54
  "purify-ts": "2.1.0",
55
55
  "rxjs": "7.8.2"
56
56
  },
@@ -59,5 +59,5 @@
59
59
  "tsup": "^8.0.0",
60
60
  "typescript": "5.1.6"
61
61
  },
62
- "gitHead": "8b790c78394debdd3d9999db2f6a56235efb7a18"
62
+ "gitHead": "705729baaec54b86a4fc28cce310a85b67b82730"
63
63
  }