@onekeyfe/hwk-ledger-connector-electron-ble 1.2.3-alpha.8 → 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 +8 -11
- package/dist/index.d.ts +8 -11
- package/dist/index.js +54 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +54 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
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
|
-
*
|
|
30
|
-
*
|
|
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
|
-
*
|
|
30
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
198
|
+
pending?.reject(new Error("Malformed Bluetooth notification"));
|
|
186
199
|
return;
|
|
187
200
|
}
|
|
188
|
-
receive
|
|
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 (
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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":"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","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
|
|
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
|
-
|
|
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
|
-
|
|
174
|
+
pending?.reject(new Error("Malformed Bluetooth notification"));
|
|
162
175
|
return;
|
|
163
176
|
}
|
|
164
|
-
receive
|
|
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 (
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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,
|
package/dist/index.mjs.map
CHANGED
|
@@ -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
|
+
"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.
|
|
53
|
-
"@onekeyfe/hwk-ledger-adapter": "1.2.
|
|
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": "
|
|
62
|
+
"gitHead": "e21d1c9a0afad287e854e568976ee62550a06b3e"
|
|
63
63
|
}
|