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

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 CHANGED
@@ -20,26 +20,23 @@ declare class LedgerElectronBleTransport implements Transport {
20
20
  startDiscovering(): rxjs.Observable<TransportDiscoveredDevice>;
21
21
  listenToAvailableDevices(): rxjs.Observable<TransportDiscoveredDevice[]>;
22
22
  stopDiscovering(): Promise<void>;
23
+ /**
24
+ * Abandon connects still in flight in the main process. The pending
25
+ * `bridge.connect` then rejects and connect()'s own error path cleans up.
26
+ */
27
+ cancelPairing(deviceId?: string): Promise<void>;
23
28
  connect({ deviceId, onDisconnect, }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']>;
24
29
  disconnect({ connectedDevice, }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']>;
25
30
  }
26
31
 
27
32
  declare const LEDGER_BLE_VENDOR = "ledger";
28
33
  /**
29
- * Frame size bounds for the 0x08 handshake, kept together because they are two
30
- * halves of one rule: the device reports the size it can take in a single
31
- * byte, so 255 is the most it can ask for, and 20 is the floor we raise a
32
- * smaller answer to. The negotiated value in between is used as-is — capping
33
- * it would negotiate and then discard the answer, leaving every transfer at
34
- * roughly 7x the frame count.
34
+ * 0x08 handshake frame bounds: the device answers in one byte (255 ceiling) and 20
35
+ * is the floor. Values between are used as-is; capping would ~7x the frame count.
35
36
  */
36
37
  declare const LEDGER_BLE_MIN_FRAME_SIZE = 20;
37
38
  declare const LEDGER_BLE_MAX_FRAME_SIZE = 255;
38
- /**
39
- * Ledger GATT uuids are per device model, so unlike Trezor's fixed profile
40
- * they are looked up from the DMK data source at discovery time and passed in
41
- * here. Only the vendor key and the framing rule are constant.
42
- */
39
+ /** Ledger GATT uuids are per model, so callers pass them in from DMK's data source. */
43
40
  declare function ledgerBleMatch(serviceUuids: string[]): ElectronBleMatch;
44
41
  declare function ledgerBleConnectProfile(gatt: {
45
42
  serviceUuid: string;
package/dist/index.d.ts CHANGED
@@ -20,26 +20,23 @@ declare class LedgerElectronBleTransport implements Transport {
20
20
  startDiscovering(): rxjs.Observable<TransportDiscoveredDevice>;
21
21
  listenToAvailableDevices(): rxjs.Observable<TransportDiscoveredDevice[]>;
22
22
  stopDiscovering(): Promise<void>;
23
+ /**
24
+ * Abandon connects still in flight in the main process. The pending
25
+ * `bridge.connect` then rejects and connect()'s own error path cleans up.
26
+ */
27
+ cancelPairing(deviceId?: string): Promise<void>;
23
28
  connect({ deviceId, onDisconnect, }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']>;
24
29
  disconnect({ connectedDevice, }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']>;
25
30
  }
26
31
 
27
32
  declare const LEDGER_BLE_VENDOR = "ledger";
28
33
  /**
29
- * Frame size bounds for the 0x08 handshake, kept together because they are two
30
- * halves of one rule: the device reports the size it can take in a single
31
- * byte, so 255 is the most it can ask for, and 20 is the floor we raise a
32
- * smaller answer to. The negotiated value in between is used as-is — capping
33
- * it would negotiate and then discard the answer, leaving every transfer at
34
- * roughly 7x the frame count.
34
+ * 0x08 handshake frame bounds: the device answers in one byte (255 ceiling) and 20
35
+ * is the floor. Values between are used as-is; capping would ~7x the frame count.
35
36
  */
36
37
  declare const LEDGER_BLE_MIN_FRAME_SIZE = 20;
37
38
  declare const LEDGER_BLE_MAX_FRAME_SIZE = 255;
38
- /**
39
- * Ledger GATT uuids are per device model, so unlike Trezor's fixed profile
40
- * they are looked up from the DMK data source at discovery time and passed in
41
- * here. Only the vendor key and the framing rule are constant.
42
- */
39
+ /** Ledger GATT uuids are per model, so callers pass them in from DMK's data source. */
43
40
  declare function ledgerBleMatch(serviceUuids: string[]): ElectronBleMatch;
44
41
  declare function ledgerBleConnectProfile(gatt: {
45
42
  serviceUuid: string;
package/dist/index.js CHANGED
@@ -120,7 +120,21 @@ var LedgerElectronBleTransport = class {
120
120
  }
121
121
  async stopDiscovering() {
122
122
  await this.scanPromise?.catch(() => void 0);
123
- await this.bridge.stopScan();
123
+ await this.bridge.stopScan(LEDGER_BLE_VENDOR);
124
+ }
125
+ /**
126
+ * Abandon connects still in flight in the main process. The pending
127
+ * `bridge.connect` then rejects and connect()'s own error path cleans up.
128
+ */
129
+ async cancelPairing(deviceId) {
130
+ const ids = [...this.connections.entries()].filter(
131
+ ([id, entry]) => !entry.connectedDevice && (deviceId === void 0 || id === deviceId)
132
+ ).map(([id]) => id);
133
+ await Promise.all(
134
+ ids.map(
135
+ (id) => this.bridge.cancelPairing?.({ vendor: LEDGER_BLE_VENDOR, id }).catch(() => void 0)
136
+ )
137
+ );
124
138
  }
125
139
  async connect({
126
140
  deviceId,
@@ -131,14 +145,13 @@ var LedgerElectronBleTransport = class {
131
145
  let removeNotification;
132
146
  let removeDisconnect;
133
147
  let closed = false;
134
- let rejectPending;
135
- let receive;
148
+ let pending;
136
149
  const close = () => {
137
150
  if (closed) return false;
138
151
  closed = true;
139
152
  removeNotification?.();
140
153
  removeDisconnect?.();
141
- rejectPending?.(new Error("Bluetooth connection ended"));
154
+ pending?.reject(new Error("Bluetooth connection ended"));
142
155
  return true;
143
156
  };
144
157
  const release = () => {
@@ -182,24 +195,25 @@ var LedgerElectronBleTransport = class {
182
195
  removeNotification = this.bridge.onNotification((id, hex) => {
183
196
  if (id !== deviceId || closed) return;
184
197
  if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {
185
- rejectPending?.(new Error("Malformed Bluetooth notification"));
198
+ pending?.reject(new Error("Malformed Bluetooth notification"));
186
199
  return;
187
200
  }
188
- receive?.((0, import_hwk_adapter_core.hexToBytes)(hex));
201
+ pending?.receive((0, import_hwk_adapter_core.hexToBytes)(hex));
189
202
  });
190
203
  await this.bridge.subscribe(deviceId);
191
204
  const exchange = async (frames, accept, timeoutMs) => {
192
205
  if (closed) throw new Error("Bluetooth connection ended");
193
- if (receive) throw new Error("Bluetooth exchange is busy");
206
+ if (pending) throw new Error("Bluetooth exchange is busy");
194
207
  let timer;
195
208
  try {
196
209
  return await new Promise((resolve, reject) => {
197
- rejectPending = reject;
198
- receive = (bytes) => accept(bytes, resolve, reject);
199
- timer = setTimeout(
200
- () => reject(new import_device_management_kit.SendApduTimeoutError("Bluetooth response timed out")),
201
- timeoutMs
202
- );
210
+ pending = { reject, receive: (bytes) => accept(bytes, resolve, reject) };
211
+ if (timeoutMs !== void 0) {
212
+ timer = setTimeout(
213
+ () => reject(new import_device_management_kit.SendApduTimeoutError("Bluetooth response timed out")),
214
+ timeoutMs
215
+ );
216
+ }
203
217
  void (async () => {
204
218
  for (const frame of frames) {
205
219
  if (closed) throw new Error("Bluetooth connection ended");
@@ -209,8 +223,7 @@ var LedgerElectronBleTransport = class {
209
223
  });
210
224
  } finally {
211
225
  if (timer !== void 0) clearTimeout(timer);
212
- receive = void 0;
213
- rejectPending = void 0;
226
+ pending = void 0;
214
227
  }
215
228
  };
216
229
  const frameSize = await exchange(
@@ -231,7 +244,7 @@ var LedgerElectronBleTransport = class {
231
244
  type: "BLE",
232
245
  sendApdu: async (apdu, _triggersDisconnection, abortTimeout) => {
233
246
  if (closed) return (0, import_purify_ts.Left)(new import_device_management_kit.DisconnectError());
234
- if (receive) return (0, import_purify_ts.Left)(new import_device_management_kit.SendApduConcurrencyError());
247
+ if (pending) return (0, import_purify_ts.Left)(new import_device_management_kit.SendApduConcurrencyError());
235
248
  const receiver = this.args.apduReceiverServiceFactory();
236
249
  try {
237
250
  const result = await exchange(
@@ -242,7 +255,9 @@ var LedgerElectronBleTransport = class {
242
255
  response.map((value) => resolve((0, import_purify_ts.Right)(value)));
243
256
  }
244
257
  }),
245
- abortTimeout ?? 12e4
258
+ // No default: a signing APDU waits on the user, and close() or a
259
+ // disconnect already fails a pending exchange.
260
+ abortTimeout
246
261
  );
247
262
  if (result.isLeft()) {
248
263
  await disconnect(true);
@@ -282,11 +297,29 @@ var LedgerElectronBleTransport = class {
282
297
  };
283
298
 
284
299
  // src/LedgerElectronBleConnector.ts
300
+ var LedgerElectronBleConnector = class extends import_hwk_ledger_adapter.LedgerConnectorBase {
301
+ constructor(bridge) {
302
+ const transportRef = {};
303
+ super(
304
+ async () => (args) => {
305
+ const transport = new LedgerElectronBleTransport(bridge, args);
306
+ transportRef.current = transport;
307
+ return transport;
308
+ },
309
+ { connectionType: "ble" }
310
+ );
311
+ this._transportRef = transportRef;
312
+ }
313
+ async cancel(sessionId) {
314
+ await super.cancel(sessionId);
315
+ try {
316
+ await this._transportRef.current?.cancelPairing(sessionId);
317
+ } catch {
318
+ }
319
+ }
320
+ };
285
321
  function createLedgerElectronBleConnector(bridge) {
286
- return new import_hwk_ledger_adapter.LedgerConnectorBase(
287
- async () => (args) => new LedgerElectronBleTransport(bridge, args),
288
- { connectionType: "ble" }
289
- );
322
+ return new LedgerElectronBleConnector(bridge);
290
323
  }
291
324
  // Annotate the CommonJS export names for ESM import in node:
292
325
  0 && (module.exports = {
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 // 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":[]}
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\nclass LedgerElectronBleConnector extends LedgerConnectorBase {\n private readonly _transportRef: { current?: LedgerElectronBleTransport };\n\n constructor(bridge: ElectronBleApi) {\n const transportRef: { current?: LedgerElectronBleTransport } = {};\n super(\n async () => (args: TransportArgs) => {\n const transport = new LedgerElectronBleTransport(bridge, args);\n transportRef.current = transport;\n return transport;\n },\n { connectionType: 'ble' }\n );\n this._transportRef = transportRef;\n }\n\n override async cancel(sessionId: string): Promise<void> {\n await super.cancel(sessionId);\n // Base cancel only settles DeviceAction cancellers; a connect stuck in the\n // main process must be abandoned there.\n try {\n await this._transportRef.current?.cancelPairing(sessionId);\n } catch {\n // Cancel must never throw.\n }\n }\n}\n\nexport function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase {\n return new LedgerElectronBleConnector(bridge);\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';\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(LEDGER_BLE_VENDOR);\n }\n\n /**\n * Abandon connects still in flight in the main process. The pending\n * `bridge.connect` then rejects and connect()'s own error path cleans up.\n */\n async cancelPairing(deviceId?: string): Promise<void> {\n const ids = [...this.connections.entries()]\n .filter(\n ([id, entry]) => !entry.connectedDevice && (deviceId === undefined || id === deviceId)\n )\n .map(([id]) => id);\n await Promise.all(\n ids.map(id =>\n this.bridge.cancelPairing?.({ vendor: LEDGER_BLE_VENDOR, id }).catch(() => undefined)\n )\n );\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 // The single in-flight exchange; notifications feed it, close() rejects it.\n let pending:\n | { receive: (bytes: Uint8Array) => void; reject: (error: unknown) => void }\n | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n pending?.reject(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 right after\n // pairing, and a missed event costs a full APDU timeout.\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 pending?.reject(new Error('Malformed Bluetooth notification'));\n return;\n }\n pending?.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 | undefined\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (pending) 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 pending = { reject, receive: bytes => accept(bytes, resolve, reject) };\n if (timeoutMs !== undefined) {\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\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 pending = 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 hw-transport-web-ble inferMTU: the answer only raises the floor.\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 (pending) 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 // No default: a signing APDU waits on the user, and close() or a\n // disconnect already fails a pending exchange.\n abortTimeout\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 * 0x08 handshake frame bounds: the device answers in one byte (255 ceiling) and 20\n * is the floor. Values between are used as-is; capping would ~7x the frame count.\n */\nexport const LEDGER_BLE_MIN_FRAME_SIZE = 20;\nexport const LEDGER_BLE_MAX_FRAME_SIZE = 255;\n\n/** Ledger GATT uuids are per model, so callers pass them in from DMK's data source. */\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,gCAAoC;;;ACApC,mCAQO;AACP,uBAA4B;AAC5B,kBAAsC;;;ACR/B,IAAM,oBAAoB;AAM1B,IAAM,4BAA4B;AAClC,IAAM,4BAA4B;AAGlC,SAAS,eAAe,cAA0C;AACvE,SAAO,EAAE,aAAa;AACxB;AAEO,SAAS,wBAAwB,MAIV;AAC5B,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,GAAG;AAAA;AAAA,IAEH,OAAO,EAAE,MAAM,OAAO,WAAW,0BAA0B;AAAA,EAC7D;AACF;;;ADVA,8BAA2B;AAW3B,IAAM,eAAe;AACrB,IAAM,gBAAgB,CAAC,SAAiB,KAAK,QAAQ,MAAM,EAAE,EAAE,YAAY;AAC3E,IAAM,QAAQ,CAAC,UACb,MAAM,KAAK,OAAO,UAAQ,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAEhE,IAAM,6BAAN,MAAsD;AAAA,EAuB3D,YAAY,QAAwB,MAA0C;AAtB9E,SAAiB,UAAU,oBAAI,IAG7B;AAEF,SAAiB,cAAc,oBAAI,IAMjC;AAYA,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,gBAAgB;AACd,WAAO;AAAA,EACT;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,EACT;AAAA,EAEQ,SAAS,MAAoE;AACnF,UAAM,WAAW,KAAK,KAAK,sBAAsB,0BAA0B;AAC3E,UAAM,UAAU,OAAO,OAAO,QAAQ,EAAE;AAAA,MAAK,eAC3C,KAAK,wBAAwB;AAAA,QAC3B,UAAQ,cAAc,IAAI,MAAM,cAAc,UAAU,WAAW;AAAA,MACrE;AAAA,IACF;AACA,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,SAAoC;AAAA,MACxC,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,aAAa,QAAQ;AAAA,MACrB,WAAW;AAAA,IACb;AACA,SAAK,QAAQ,IAAI,KAAK,IAAI,EAAE,QAAQ,QAAQ,CAAC;AAC7C,WAAO;AAAA,EACT;AAAA,EAEQ,WAAiD;AACvD,QAAI,KAAK,YAAa,QAAO,KAAK;AAClC,UAAM,QAAQ,YAAY;AACxB,YAAM,eAAe,MAAM,KAAK,OAAO,kBAAkB;AACzD,UAAI,CAAC,aAAa,UAAW,OAAM,IAAI,MAAM,0BAA0B,aAAa,KAAK,EAAE;AAC3F,YAAM,QAAQ,MAAM,KAAK,OAAO,KAAK;AAAA,QACnC,QAAQ;AAAA,QACR,OAAO,eAAe,KAAK,KAAK,sBAAsB,qBAAqB,CAAC;AAAA,MAC9E,CAAC;AACD,YAAM,UAAuC,CAAC;AAC9C,iBAAW,QAAQ,OAAO;AACxB,cAAM,SAAS,KAAK,SAAS,IAAI;AACjC,YAAI,OAAQ,SAAQ,KAAK,MAAM;AAAA,MACjC;AACA,aAAO;AAAA,IACT,GAAG;AACH,SAAK,cAAc;AACnB,SAAK,KACF,QAAQ,MAAM;AACb,UAAI,KAAK,gBAAgB,KAAM,MAAK,cAAc;AAAA,IACpD,CAAC,EACA,MAAM,MAAM,MAAS;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,mBAAmB;AACjB,eAAO,mBAAM,MAAM,KAAK,SAAS,CAAC,EAAE,SAAK,sBAAS,iBAAW,kBAAK,OAAO,CAAC,CAAC;AAAA,EAC7E;AAAA,EAEA,2BAA2B;AACzB,eAAO,mBAAM,MAAM,KAAK,SAAS,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,kBAAiC;AACrC,UAAM,KAAK,aAAa,MAAM,MAAM,MAAS;AAC7C,UAAM,KAAK,OAAO,SAAS,iBAAiB;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,UAAkC;AACpD,UAAM,MAAM,CAAC,GAAG,KAAK,YAAY,QAAQ,CAAC,EACvC;AAAA,MACC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,oBAAoB,aAAa,UAAa,OAAO;AAAA,IAC/E,EACC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE;AACnB,UAAM,QAAQ;AAAA,MACZ,IAAI;AAAA,QAAI,QACN,KAAK,OAAO,gBAAgB,EAAE,QAAQ,mBAAmB,GAAG,CAAC,EAAE,MAAM,MAAM,MAAS;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,EACF,GAA0E;AACxE,QAAI,KAAK,YAAY,IAAI,QAAQ;AAC/B,iBAAO,uBAAK,IAAI,oDAAuB,6BAA6B,CAAC;AACvE,QAAI;AACJ,QAAI;AACJ,QAAI,SAAS;AAEb,QAAI;AAGJ,UAAM,QAAQ,MAAM;AAClB,UAAI,OAAQ,QAAO;AACnB,eAAS;AACT,2BAAqB;AACrB,yBAAmB;AACnB,eAAS,OAAO,IAAI,MAAM,4BAA4B,CAAC;AACvD,aAAO;AAAA,IACT;AACA,UAAM,UAAU,MAAM;AACpB,UAAI,KAAK,YAAY,IAAI,QAAQ,GAAG,UAAU,MAAO,MAAK,YAAY,OAAO,QAAQ;AAAA,IACvF;AACA,UAAM,aAAa,OAAO,WAAoB;AAC5C,UAAI,CAAC,MAAM,EAAG;AACd,YAAM,KAAK,OAAO,WAAW,QAAQ,EAAE,MAAM,MAAM,MAAS;AAC5D,cAAQ;AACR,UAAI,OAAQ,cAAa,QAAQ;AAAA,IACnC;AAEA,SAAK,YAAY,IAAI,UAAU,EAAE,MAAM,CAAC;AACxC,QAAI;AACF,YAAM,KAAK,gBAAgB;AAC3B,UAAI,CAAC,KAAK,QAAQ,IAAI,QAAQ,GAAG;AAC/B,cAAM,OAAO,MAAM,KAAK,OAAO,UAAU,QAAQ;AACjD,YAAI,KAAM,MAAK,SAAS,IAAI;AAAA,MAC9B;AACA,YAAM,QAAQ,KAAK,QAAQ,IAAI,QAAQ;AACvC,UAAI,CAAC,OAAO;AACV,cAAM;AACN,gBAAQ;AACR,mBAAO,uBAAK,IAAI,gDAAmB,CAAC;AAAA,MACtC;AAGA,yBAAmB,KAAK,OAAO,qBAAqB,QAAM;AACxD,YAAI,OAAO,SAAU;AACrB,YAAI,MAAM,GAAG;AACX,kBAAQ;AACR,uBAAa,QAAQ;AAAA,QACvB;AAAA,MACF,CAAC;AACD,YAAM,KAAK,OAAO;AAAA,QAChB;AAAA,QACA,wBAAwB;AAAA,UACtB,aAAa,MAAM,QAAQ;AAAA,UAC3B,WAAW,MAAM,QAAQ;AAAA,UACzB,YAAY,MAAM,QAAQ;AAAA,QAC5B,CAAC;AAAA,MACH;AACA,UAAI,OAAQ,OAAM,IAAI,MAAM,4BAA4B;AACxD,2BAAqB,KAAK,OAAO,eAAe,CAAC,IAAI,QAAQ;AAC3D,YAAI,OAAO,YAAY,OAAQ;AAC/B,YAAI,CAAC,sBAAsB,KAAK,GAAG,GAAG;AACpC,mBAAS,OAAO,IAAI,MAAM,kCAAkC,CAAC;AAC7D;AAAA,QACF;AACA,iBAAS,YAAQ,oCAAW,GAAG,CAAC;AAAA,MAClC,CAAC;AACD,YAAM,KAAK,OAAO,UAAU,QAAQ;AAEpC,YAAM,WAAW,OACf,QACA,QAKA,cACe;AACf,YAAI,OAAQ,OAAM,IAAI,MAAM,4BAA4B;AACxD,YAAI,QAAS,OAAM,IAAI,MAAM,4BAA4B;AACzD,YAAI;AACJ,YAAI;AACF,iBAAO,MAAM,IAAI,QAAW,CAAC,SAAS,WAAW;AAC/C,sBAAU,EAAE,QAAQ,SAAS,WAAS,OAAO,OAAO,SAAS,MAAM,EAAE;AACrE,gBAAI,cAAc,QAAW;AAC3B,sBAAQ;AAAA,gBACN,MAAM,OAAO,IAAI,kDAAqB,8BAA8B,CAAC;AAAA,gBACrE;AAAA,cACF;AAAA,YACF;AACA,kBAAM,YAAY;AAChB,yBAAW,SAAS,QAAQ;AAC1B,oBAAI,OAAQ,OAAM,IAAI,MAAM,4BAA4B;AACxD,sBAAM,KAAK,OAAO,MAAM,UAAU,MAAM,KAAK,CAAC;AAAA,cAChD;AAAA,YACF,GAAG,EAAE,MAAM,MAAM;AAAA,UACnB,CAAC;AAAA,QACH,UAAE;AACA,cAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,oBAAU;AAAA,QACZ;AAAA,MACF;AAEA,YAAM,YAAY,MAAM;AAAA,QACtB,CAAC,WAAW,GAAG,GAAM,GAAG,GAAG,GAAG,CAAC,CAAC;AAAA,QAChC,CAAC,OAAO,SAAS,WAAW;AAC1B,cAAI,MAAM,SAAS,KAAK,MAAM,CAAC,MAAM,KAAQ,MAAM,CAAC,IAAI,GAAG;AACzD,mBAAO,IAAI,MAAM,iCAAiC,CAAC;AAAA,UACrD,OAAO;AAEL,oBAAQ,KAAK,IAAI,2BAA2B,MAAM,CAAC,CAAC,CAAC;AAAA,UACvD;AAAA,QACF;AAAA,QACA;AAAA,MACF;AACA,UAAI,OAAQ,OAAM,IAAI,MAAM,4BAA4B;AACxD,YAAM,SAAS,KAAK,KAAK,yBAAyB,EAAE,UAAU,CAAC;AAC/D,YAAM,YAAY,IAAI,sDAAyB;AAAA,QAC7C,GAAG,MAAM;AAAA,QACT,MAAM;AAAA,QACN,UAAU,OAAO,MAAM,wBAAwB,iBAA0C;AACvF,cAAI,OAAQ,YAAO,uBAAK,IAAI,6CAAgB,CAAC;AAC7C,cAAI,QAAS,YAAO,uBAAK,IAAI,sDAAyB,CAAC;AACvD,gBAAM,WAAW,KAAK,KAAK,2BAA2B;AACtD,cAAI;AACF,kBAAM,SAAS,MAAM;AAAA,cACnB,OAAO,UAAU,IAAI,EAAE,IAAI,WAAS,MAAM,WAAW,CAAC;AAAA,cACtD,CAAC,OAAO,YACN,SAAS,YAAY,KAAK,EAAE,OAAO;AAAA,gBACjC,MAAM,WAAS,YAAQ,uBAAK,KAAK,CAAC;AAAA,gBAClC,OAAO,cAAY;AACjB,2BAAS,IAAI,WAAS,YAAQ,wBAAM,KAAK,CAAC,CAAC;AAAA,gBAC7C;AAAA,cACF,CAAC;AAAA;AAAA;AAAA,cAGH;AAAA,YACF;AACA,gBAAI,OAAO,OAAO,GAAG;AACnB,oBAAM,WAAW,IAAI;AAAA,YACvB;AACA,mBAAO;AAAA,UACT,SAAS,OAAO;AACd,kBAAM,WAAW,IAAI;AACrB,uBAAO,uBAAK,iBAAiB,oDAAuB,QAAQ,IAAI,6CAAgB,KAAK,CAAC;AAAA,UACxF;AAAA,QACF;AAAA,MACF,CAAC;AACD,YAAM,aAAa,KAAK,YAAY,IAAI,QAAQ;AAChD,UAAI,YAAY,UAAU,MAAO,YAAW,kBAAkB;AAC9D,iBAAO,wBAAM,SAAS;AAAA,IACxB,SAAS,OAAO;AACd,YAAM,WAAW,KAAK;AACtB,iBAAO,uBAAK,IAAI,oDAAuB,KAAK,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,MAAM,WAAW;AAAA,IACf;AAAA,EACF,GAAgF;AAC9E,UAAM,aAAa,KAAK,YAAY,IAAI,gBAAgB,EAAE;AAC1D,QAAI,YAAY,oBAAoB,mBAAmB,CAAC,WAAW,MAAM;AACvE,iBAAO,wBAAM,MAAS;AACxB,QAAI;AACF,YAAM,KAAK,OAAO,WAAW,gBAAgB,EAAE;AAC/C,iBAAO,wBAAM,MAAS;AAAA,IACxB,SAAS,OAAO;AACd,iBAAO,uBAAK,IAAI,6CAAgB,KAAK,CAAC;AAAA,IACxC,UAAE;AACA,UAAI,KAAK,YAAY,IAAI,gBAAgB,EAAE,MAAM,YAAY;AAC3D,aAAK,YAAY,OAAO,gBAAgB,EAAE;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACF;;;AD1TA,IAAM,6BAAN,cAAyC,8CAAoB;AAAA,EAG3D,YAAY,QAAwB;AAClC,UAAM,eAAyD,CAAC;AAChE;AAAA,MACE,YAAY,CAAC,SAAwB;AACnC,cAAM,YAAY,IAAI,2BAA2B,QAAQ,IAAI;AAC7D,qBAAa,UAAU;AACvB,eAAO;AAAA,MACT;AAAA,MACA,EAAE,gBAAgB,MAAM;AAAA,IAC1B;AACA,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAe,OAAO,WAAkC;AACtD,UAAM,MAAM,OAAO,SAAS;AAG5B,QAAI;AACF,YAAM,KAAK,cAAc,SAAS,cAAc,SAAS;AAAA,IAC3D,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEO,SAAS,iCAAiC,QAA6C;AAC5F,SAAO,IAAI,2BAA2B,MAAM;AAC9C;","names":[]}
package/dist/index.mjs CHANGED
@@ -96,7 +96,21 @@ var LedgerElectronBleTransport = class {
96
96
  }
97
97
  async stopDiscovering() {
98
98
  await this.scanPromise?.catch(() => void 0);
99
- await this.bridge.stopScan();
99
+ await this.bridge.stopScan(LEDGER_BLE_VENDOR);
100
+ }
101
+ /**
102
+ * Abandon connects still in flight in the main process. The pending
103
+ * `bridge.connect` then rejects and connect()'s own error path cleans up.
104
+ */
105
+ async cancelPairing(deviceId) {
106
+ const ids = [...this.connections.entries()].filter(
107
+ ([id, entry]) => !entry.connectedDevice && (deviceId === void 0 || id === deviceId)
108
+ ).map(([id]) => id);
109
+ await Promise.all(
110
+ ids.map(
111
+ (id) => this.bridge.cancelPairing?.({ vendor: LEDGER_BLE_VENDOR, id }).catch(() => void 0)
112
+ )
113
+ );
100
114
  }
101
115
  async connect({
102
116
  deviceId,
@@ -107,14 +121,13 @@ var LedgerElectronBleTransport = class {
107
121
  let removeNotification;
108
122
  let removeDisconnect;
109
123
  let closed = false;
110
- let rejectPending;
111
- let receive;
124
+ let pending;
112
125
  const close = () => {
113
126
  if (closed) return false;
114
127
  closed = true;
115
128
  removeNotification?.();
116
129
  removeDisconnect?.();
117
- rejectPending?.(new Error("Bluetooth connection ended"));
130
+ pending?.reject(new Error("Bluetooth connection ended"));
118
131
  return true;
119
132
  };
120
133
  const release = () => {
@@ -158,24 +171,25 @@ var LedgerElectronBleTransport = class {
158
171
  removeNotification = this.bridge.onNotification((id, hex) => {
159
172
  if (id !== deviceId || closed) return;
160
173
  if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {
161
- rejectPending?.(new Error("Malformed Bluetooth notification"));
174
+ pending?.reject(new Error("Malformed Bluetooth notification"));
162
175
  return;
163
176
  }
164
- receive?.(hexToBytes(hex));
177
+ pending?.receive(hexToBytes(hex));
165
178
  });
166
179
  await this.bridge.subscribe(deviceId);
167
180
  const exchange = async (frames, accept, timeoutMs) => {
168
181
  if (closed) throw new Error("Bluetooth connection ended");
169
- if (receive) throw new Error("Bluetooth exchange is busy");
182
+ if (pending) throw new Error("Bluetooth exchange is busy");
170
183
  let timer;
171
184
  try {
172
185
  return await new Promise((resolve, reject) => {
173
- rejectPending = reject;
174
- receive = (bytes) => accept(bytes, resolve, reject);
175
- timer = setTimeout(
176
- () => reject(new SendApduTimeoutError("Bluetooth response timed out")),
177
- timeoutMs
178
- );
186
+ pending = { reject, receive: (bytes) => accept(bytes, resolve, reject) };
187
+ if (timeoutMs !== void 0) {
188
+ timer = setTimeout(
189
+ () => reject(new SendApduTimeoutError("Bluetooth response timed out")),
190
+ timeoutMs
191
+ );
192
+ }
179
193
  void (async () => {
180
194
  for (const frame of frames) {
181
195
  if (closed) throw new Error("Bluetooth connection ended");
@@ -185,8 +199,7 @@ var LedgerElectronBleTransport = class {
185
199
  });
186
200
  } finally {
187
201
  if (timer !== void 0) clearTimeout(timer);
188
- receive = void 0;
189
- rejectPending = void 0;
202
+ pending = void 0;
190
203
  }
191
204
  };
192
205
  const frameSize = await exchange(
@@ -207,7 +220,7 @@ var LedgerElectronBleTransport = class {
207
220
  type: "BLE",
208
221
  sendApdu: async (apdu, _triggersDisconnection, abortTimeout) => {
209
222
  if (closed) return Left(new DisconnectError());
210
- if (receive) return Left(new SendApduConcurrencyError());
223
+ if (pending) return Left(new SendApduConcurrencyError());
211
224
  const receiver = this.args.apduReceiverServiceFactory();
212
225
  try {
213
226
  const result = await exchange(
@@ -218,7 +231,9 @@ var LedgerElectronBleTransport = class {
218
231
  response.map((value) => resolve(Right(value)));
219
232
  }
220
233
  }),
221
- abortTimeout ?? 12e4
234
+ // No default: a signing APDU waits on the user, and close() or a
235
+ // disconnect already fails a pending exchange.
236
+ abortTimeout
222
237
  );
223
238
  if (result.isLeft()) {
224
239
  await disconnect(true);
@@ -258,11 +273,29 @@ var LedgerElectronBleTransport = class {
258
273
  };
259
274
 
260
275
  // src/LedgerElectronBleConnector.ts
276
+ var LedgerElectronBleConnector = class extends LedgerConnectorBase {
277
+ constructor(bridge) {
278
+ const transportRef = {};
279
+ super(
280
+ async () => (args) => {
281
+ const transport = new LedgerElectronBleTransport(bridge, args);
282
+ transportRef.current = transport;
283
+ return transport;
284
+ },
285
+ { connectionType: "ble" }
286
+ );
287
+ this._transportRef = transportRef;
288
+ }
289
+ async cancel(sessionId) {
290
+ await super.cancel(sessionId);
291
+ try {
292
+ await this._transportRef.current?.cancelPairing(sessionId);
293
+ } catch {
294
+ }
295
+ }
296
+ };
261
297
  function createLedgerElectronBleConnector(bridge) {
262
- return new LedgerConnectorBase(
263
- async () => (args) => new LedgerElectronBleTransport(bridge, args),
264
- { connectionType: "ble" }
265
- );
298
+ return new LedgerElectronBleConnector(bridge);
266
299
  }
267
300
  export {
268
301
  LEDGER_BLE_MAX_FRAME_SIZE,
@@ -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 // 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":[]}
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\nclass LedgerElectronBleConnector extends LedgerConnectorBase {\n private readonly _transportRef: { current?: LedgerElectronBleTransport };\n\n constructor(bridge: ElectronBleApi) {\n const transportRef: { current?: LedgerElectronBleTransport } = {};\n super(\n async () => (args: TransportArgs) => {\n const transport = new LedgerElectronBleTransport(bridge, args);\n transportRef.current = transport;\n return transport;\n },\n { connectionType: 'ble' }\n );\n this._transportRef = transportRef;\n }\n\n override async cancel(sessionId: string): Promise<void> {\n await super.cancel(sessionId);\n // Base cancel only settles DeviceAction cancellers; a connect stuck in the\n // main process must be abandoned there.\n try {\n await this._transportRef.current?.cancelPairing(sessionId);\n } catch {\n // Cancel must never throw.\n }\n }\n}\n\nexport function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase {\n return new LedgerElectronBleConnector(bridge);\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';\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(LEDGER_BLE_VENDOR);\n }\n\n /**\n * Abandon connects still in flight in the main process. The pending\n * `bridge.connect` then rejects and connect()'s own error path cleans up.\n */\n async cancelPairing(deviceId?: string): Promise<void> {\n const ids = [...this.connections.entries()]\n .filter(\n ([id, entry]) => !entry.connectedDevice && (deviceId === undefined || id === deviceId)\n )\n .map(([id]) => id);\n await Promise.all(\n ids.map(id =>\n this.bridge.cancelPairing?.({ vendor: LEDGER_BLE_VENDOR, id }).catch(() => undefined)\n )\n );\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 // The single in-flight exchange; notifications feed it, close() rejects it.\n let pending:\n | { receive: (bytes: Uint8Array) => void; reject: (error: unknown) => void }\n | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n pending?.reject(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 right after\n // pairing, and a missed event costs a full APDU timeout.\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 pending?.reject(new Error('Malformed Bluetooth notification'));\n return;\n }\n pending?.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 | undefined\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (pending) 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 pending = { reject, receive: bytes => accept(bytes, resolve, reject) };\n if (timeoutMs !== undefined) {\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\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 pending = 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 hw-transport-web-ble inferMTU: the answer only raises the floor.\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 (pending) 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 // No default: a signing APDU waits on the user, and close() or a\n // disconnect already fails a pending exchange.\n abortTimeout\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 * 0x08 handshake frame bounds: the device answers in one byte (255 ceiling) and 20\n * is the floor. Values between are used as-is; capping would ~7x the frame count.\n */\nexport const LEDGER_BLE_MIN_FRAME_SIZE = 20;\nexport const LEDGER_BLE_MAX_FRAME_SIZE = 255;\n\n/** Ledger GATT uuids are per model, so callers pass them in from DMK's data source. */\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.4",
3
+ "version": "1.2.5-alpha.0",
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.4",
53
- "@onekeyfe/hwk-ledger-adapter": "1.2.3-alpha.4",
52
+ "@onekeyfe/hwk-adapter-core": "1.2.5-alpha.0",
53
+ "@onekeyfe/hwk-ledger-adapter": "1.2.5-alpha.0",
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": "705729baaec54b86a4fc28cce310a85b67b82730"
62
+ "gitHead": "e21d1c9a0afad287e854e568976ee62550a06b3e"
63
63
  }