@onekeyfe/hwk-ledger-connector-electron-ble 1.2.3-alpha.1

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/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # Ledger Electron BLE connector
2
+
3
+ Ledger BLE discovery, MTU negotiation and DMK APDU transport for Electron.
4
+ React Native applications should continue using `@onekeyfe/hwk-ledger-connector-ble`.
5
+
6
+ ## Renderer / background SDK
7
+
8
+ ```ts
9
+ import { createLedgerElectronBleConnector } from '@onekeyfe/hwk-ledger-connector-electron-ble';
10
+
11
+ const connector = createLedgerElectronBleConnector(window.desktopApi.thirdPartyBle);
12
+ ```
13
+
14
+ The host supplies an `ElectronBleApi` preload bridge. The root entry does not load
15
+ Electron or noble. Native Bluetooth access stays in the Electron main process.
16
+
17
+ ## Host-owned Bluetooth
18
+
19
+ Reuse the host's third-party BLE interface. Discovery passes the Ledger service
20
+ UUIDs and vendor filter to `bridge.scan`. Connection passes the selected service,
21
+ write and notify characteristic UUIDs to `bridge.connect`. Profiles come from
22
+ the Ledger DMK device-model data source; USB VID/PID are not used.
23
+
24
+ This package does not depend on the Trezor connector, Electron or noble, and does
25
+ not register IPC handlers or instantiate a native Bluetooth backend. The host
26
+ owns the shared native instance, preload IPC, sender validation and pairing
27
+ authorization. Ledger MTU negotiation and APDU framing remain in this package.
28
+
29
+ ## Validation
30
+
31
+ Run `yarn test --runInBand`, `yarn build`, and `yarn verify:types` in this package.
32
+ The transport tests use a synthetic bridge; they do not establish physical-device
33
+ compatibility. Hardware validation must cover discovery, initialized request /
34
+ response, disconnect, and reconnect for each vendor.
@@ -0,0 +1,27 @@
1
+ import { LedgerConnectorBase } from '@onekeyfe/hwk-ledger-adapter';
2
+ import { ElectronBleApi } from '@onekeyfe/hwk-adapter-core';
3
+ export { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';
4
+ import * as rxjs from 'rxjs';
5
+ import { Transport, TransportDiscoveredDevice } from '@ledgerhq/device-management-kit';
6
+
7
+ declare function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase;
8
+
9
+ declare class LedgerElectronBleTransport implements Transport {
10
+ private readonly devices;
11
+ private readonly connections;
12
+ private scanPromise;
13
+ private readonly bridge;
14
+ private readonly args;
15
+ constructor(bridge: ElectronBleApi, args: LedgerElectronBleTransport['args']);
16
+ getIdentifier(): string;
17
+ isSupported(): boolean;
18
+ private remember;
19
+ private discover;
20
+ startDiscovering(): rxjs.Observable<TransportDiscoveredDevice>;
21
+ listenToAvailableDevices(): rxjs.Observable<TransportDiscoveredDevice[]>;
22
+ stopDiscovering(): Promise<void>;
23
+ connect({ deviceId, onDisconnect, }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']>;
24
+ disconnect({ connectedDevice, }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']>;
25
+ }
26
+
27
+ export { LedgerElectronBleTransport, createLedgerElectronBleConnector };
@@ -0,0 +1,27 @@
1
+ import { LedgerConnectorBase } from '@onekeyfe/hwk-ledger-adapter';
2
+ import { ElectronBleApi } from '@onekeyfe/hwk-adapter-core';
3
+ export { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';
4
+ import * as rxjs from 'rxjs';
5
+ import { Transport, TransportDiscoveredDevice } from '@ledgerhq/device-management-kit';
6
+
7
+ declare function createLedgerElectronBleConnector(bridge: ElectronBleApi): LedgerConnectorBase;
8
+
9
+ declare class LedgerElectronBleTransport implements Transport {
10
+ private readonly devices;
11
+ private readonly connections;
12
+ private scanPromise;
13
+ private readonly bridge;
14
+ private readonly args;
15
+ constructor(bridge: ElectronBleApi, args: LedgerElectronBleTransport['args']);
16
+ getIdentifier(): string;
17
+ isSupported(): boolean;
18
+ private remember;
19
+ private discover;
20
+ startDiscovering(): rxjs.Observable<TransportDiscoveredDevice>;
21
+ listenToAvailableDevices(): rxjs.Observable<TransportDiscoveredDevice[]>;
22
+ stopDiscovering(): Promise<void>;
23
+ connect({ deviceId, onDisconnect, }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']>;
24
+ disconnect({ connectedDevice, }: Parameters<Transport['disconnect']>[0]): ReturnType<Transport['disconnect']>;
25
+ }
26
+
27
+ export { LedgerElectronBleTransport, createLedgerElectronBleConnector };
package/dist/index.js ADDED
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from2, except, desc) => {
11
+ if (from2 && typeof from2 === "object" || typeof from2 === "function") {
12
+ for (let key of __getOwnPropNames(from2))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ LedgerElectronBleTransport: () => LedgerElectronBleTransport,
24
+ createLedgerElectronBleConnector: () => createLedgerElectronBleConnector
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/LedgerElectronBleConnector.ts
29
+ var import_hwk_ledger_adapter = require("@onekeyfe/hwk-ledger-adapter");
30
+
31
+ // src/LedgerElectronBleTransport.ts
32
+ var import_device_management_kit = require("@ledgerhq/device-management-kit");
33
+ var import_purify_ts = require("purify-ts");
34
+ var import_rxjs = require("rxjs");
35
+ var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
36
+ var TRANSPORT_ID = "ELECTRON_BLE";
37
+ var FRAME_SIZE = 20;
38
+ var normalizeUuid = (uuid) => uuid.replace(/-/g, "").toLowerCase();
39
+ var toHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
40
+ var LedgerElectronBleTransport = class {
41
+ constructor(bridge, args) {
42
+ this.devices = /* @__PURE__ */ new Map();
43
+ this.connections = /* @__PURE__ */ new Map();
44
+ this.bridge = bridge;
45
+ this.args = args;
46
+ }
47
+ getIdentifier() {
48
+ return TRANSPORT_ID;
49
+ }
50
+ isSupported() {
51
+ return true;
52
+ }
53
+ remember(info) {
54
+ const profiles = this.args.deviceModelDataSource.getBluetoothServicesInfos();
55
+ const profile = Object.values(profiles).find(
56
+ (candidate) => info.advertisedServiceUuids?.some(
57
+ (uuid) => normalizeUuid(uuid) === normalizeUuid(candidate.serviceUuid)
58
+ )
59
+ );
60
+ if (!profile) return void 0;
61
+ const device = {
62
+ id: info.id,
63
+ name: info.name,
64
+ rssi: info.rssi,
65
+ deviceModel: profile.deviceModel,
66
+ transport: TRANSPORT_ID
67
+ };
68
+ this.devices.set(info.id, { device, profile });
69
+ return device;
70
+ }
71
+ discover() {
72
+ if (this.scanPromise) return this.scanPromise;
73
+ const scan = (async () => {
74
+ const availability = await this.bridge.checkAvailability();
75
+ if (!availability.available) throw new Error(`Bluetooth unavailable: ${availability.state}`);
76
+ const infos = await this.bridge.scan({
77
+ vendor: "ledger",
78
+ serviceUuids: this.args.deviceModelDataSource.getBluetoothServices()
79
+ });
80
+ const devices = [];
81
+ for (const info of infos) {
82
+ const device = this.remember(info);
83
+ if (device) devices.push(device);
84
+ }
85
+ return devices;
86
+ })();
87
+ this.scanPromise = scan;
88
+ void scan.finally(() => {
89
+ if (this.scanPromise === scan) this.scanPromise = void 0;
90
+ }).catch(() => void 0);
91
+ return scan;
92
+ }
93
+ startDiscovering() {
94
+ return (0, import_rxjs.defer)(() => this.discover()).pipe((0, import_rxjs.mergeMap)((devices) => (0, import_rxjs.from)(devices)));
95
+ }
96
+ listenToAvailableDevices() {
97
+ return (0, import_rxjs.defer)(() => this.discover());
98
+ }
99
+ async stopDiscovering() {
100
+ await this.scanPromise?.catch(() => void 0);
101
+ await this.bridge.stopScan();
102
+ }
103
+ async connect({
104
+ deviceId,
105
+ onDisconnect
106
+ }) {
107
+ if (this.connections.has(deviceId))
108
+ return (0, import_purify_ts.Left)(new import_device_management_kit.OpeningConnectionError("Device is already connected"));
109
+ let removeNotification;
110
+ let removeDisconnect;
111
+ let closed = false;
112
+ let rejectPending;
113
+ let receive;
114
+ const close = () => {
115
+ if (closed) return false;
116
+ closed = true;
117
+ removeNotification?.();
118
+ removeDisconnect?.();
119
+ rejectPending?.(new Error("Bluetooth connection ended"));
120
+ return true;
121
+ };
122
+ const release = () => {
123
+ if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);
124
+ };
125
+ const disconnect = async (notify) => {
126
+ if (!close()) return;
127
+ await this.bridge.disconnect(deviceId).catch(() => void 0);
128
+ release();
129
+ if (notify) onDisconnect(deviceId);
130
+ };
131
+ this.connections.set(deviceId, { close });
132
+ try {
133
+ await this.stopDiscovering();
134
+ if (!this.devices.has(deviceId)) {
135
+ const info = await this.bridge.getDevice(deviceId);
136
+ if (info) this.remember(info);
137
+ }
138
+ const known = this.devices.get(deviceId);
139
+ if (!known) {
140
+ close();
141
+ release();
142
+ return (0, import_purify_ts.Left)(new import_device_management_kit.UnknownDeviceError());
143
+ }
144
+ await this.bridge.connect(deviceId, {
145
+ vendor: "ledger",
146
+ serviceUuid: known.profile.serviceUuid,
147
+ writeUuid: known.profile.writeUuid,
148
+ notifyUuid: known.profile.notifyUuid
149
+ });
150
+ removeDisconnect = this.bridge.onDeviceDisconnected((id) => {
151
+ if (id !== deviceId) return;
152
+ if (close()) {
153
+ release();
154
+ onDisconnect(deviceId);
155
+ }
156
+ });
157
+ removeNotification = this.bridge.onNotification((id, hex) => {
158
+ if (id !== deviceId || closed) return;
159
+ if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {
160
+ rejectPending?.(new Error("Malformed Bluetooth notification"));
161
+ return;
162
+ }
163
+ receive?.((0, import_hwk_adapter_core.hexToBytes)(hex));
164
+ });
165
+ await this.bridge.subscribe(deviceId);
166
+ const exchange = async (frames, accept, timeoutMs) => {
167
+ if (closed) throw new Error("Bluetooth connection ended");
168
+ if (receive) throw new Error("Bluetooth exchange is busy");
169
+ let timer;
170
+ try {
171
+ return await new Promise((resolve, reject) => {
172
+ rejectPending = reject;
173
+ receive = (bytes) => accept(bytes, resolve, reject);
174
+ timer = setTimeout(
175
+ () => reject(new import_device_management_kit.SendApduTimeoutError("Bluetooth response timed out")),
176
+ timeoutMs
177
+ );
178
+ void (async () => {
179
+ for (const frame of frames) {
180
+ if (closed) throw new Error("Bluetooth connection ended");
181
+ await this.bridge.write(deviceId, toHex(frame));
182
+ }
183
+ })().catch(reject);
184
+ });
185
+ } finally {
186
+ if (timer !== void 0) clearTimeout(timer);
187
+ receive = void 0;
188
+ rejectPending = void 0;
189
+ }
190
+ };
191
+ const frameSize = await exchange(
192
+ [Uint8Array.of(8, 0, 0, 0, 0)],
193
+ (bytes, resolve, reject) => {
194
+ if (bytes.length < 6 || bytes[0] !== 8 || bytes[5] < 6) {
195
+ reject(new Error("Invalid Ledger BLE MTU response"));
196
+ } else resolve(Math.min(FRAME_SIZE, bytes[5]));
197
+ },
198
+ 3e4
199
+ );
200
+ if (closed) throw new Error("Bluetooth connection ended");
201
+ const sender = this.args.apduSenderServiceFactory({ frameSize });
202
+ const connected = new import_device_management_kit.TransportConnectedDevice({
203
+ ...known.device,
204
+ type: "BLE",
205
+ sendApdu: async (apdu, _triggersDisconnection, abortTimeout) => {
206
+ if (closed) return (0, import_purify_ts.Left)(new import_device_management_kit.DisconnectError());
207
+ if (receive) return (0, import_purify_ts.Left)(new import_device_management_kit.SendApduConcurrencyError());
208
+ const receiver = this.args.apduReceiverServiceFactory();
209
+ try {
210
+ const result = await exchange(
211
+ sender.getFrames(apdu).map((frame) => frame.getRawData()),
212
+ (bytes, resolve) => receiver.handleFrame(bytes).caseOf({
213
+ Left: (error) => resolve((0, import_purify_ts.Left)(error)),
214
+ Right: (response) => {
215
+ response.map((value) => resolve((0, import_purify_ts.Right)(value)));
216
+ }
217
+ }),
218
+ abortTimeout ?? 12e4
219
+ );
220
+ if (result.isLeft()) {
221
+ await disconnect(true);
222
+ }
223
+ return result;
224
+ } catch (error) {
225
+ await disconnect(true);
226
+ return (0, import_purify_ts.Left)(error instanceof import_device_management_kit.SendApduTimeoutError ? error : new import_device_management_kit.GeneralDmkError(error));
227
+ }
228
+ }
229
+ });
230
+ const connection = this.connections.get(deviceId);
231
+ if (connection?.close === close) connection.connectedDevice = connected;
232
+ return (0, import_purify_ts.Right)(connected);
233
+ } catch (error) {
234
+ await disconnect(false);
235
+ return (0, import_purify_ts.Left)(new import_device_management_kit.OpeningConnectionError(error));
236
+ }
237
+ }
238
+ async disconnect({
239
+ connectedDevice
240
+ }) {
241
+ const connection = this.connections.get(connectedDevice.id);
242
+ if (connection?.connectedDevice !== connectedDevice || !connection.close())
243
+ return (0, import_purify_ts.Right)(void 0);
244
+ try {
245
+ await this.bridge.disconnect(connectedDevice.id);
246
+ return (0, import_purify_ts.Right)(void 0);
247
+ } catch (error) {
248
+ return (0, import_purify_ts.Left)(new import_device_management_kit.DisconnectError(error));
249
+ } finally {
250
+ if (this.connections.get(connectedDevice.id) === connection) {
251
+ this.connections.delete(connectedDevice.id);
252
+ }
253
+ }
254
+ }
255
+ };
256
+
257
+ // src/LedgerElectronBleConnector.ts
258
+ function createLedgerElectronBleConnector(bridge) {
259
+ return new import_hwk_ledger_adapter.LedgerConnectorBase(
260
+ async () => (args) => new LedgerElectronBleTransport(bridge, args),
261
+ { connectionType: "ble" }
262
+ );
263
+ }
264
+ // Annotate the CommonJS export names for ESM import in node:
265
+ 0 && (module.exports = {
266
+ LedgerElectronBleTransport,
267
+ createLedgerElectronBleConnector
268
+ });
269
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/LedgerElectronBleConnector.ts","../src/LedgerElectronBleTransport.ts"],"sourcesContent":["export { createLedgerElectronBleConnector } from './LedgerElectronBleConnector';\nexport { LedgerElectronBleTransport } from './LedgerElectronBleTransport';\nexport type { ElectronBleApi, ElectronBleDeviceInfo } from '@onekeyfe/hwk-adapter-core';\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 { 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// The ATT default is safe on both native backends; never split a Ledger frame in main.\nconst FRAME_SIZE = 20;\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',\n serviceUuids: this.args.deviceModelDataSource.getBluetoothServices(),\n });\n const devices: TransportDiscoveredDevice[] = [];\n for (const info of infos) {\n const device = this.remember(info);\n if (device) devices.push(device);\n }\n return devices;\n })();\n this.scanPromise = scan;\n void scan\n .finally(() => {\n if (this.scanPromise === scan) this.scanPromise = undefined;\n })\n .catch(() => undefined);\n return scan;\n }\n\n startDiscovering() {\n return defer(() => this.discover()).pipe(mergeMap(devices => from(devices)));\n }\n\n listenToAvailableDevices() {\n return defer(() => this.discover());\n }\n\n async stopDiscovering(): Promise<void> {\n await this.scanPromise?.catch(() => undefined);\n await this.bridge.stopScan();\n }\n\n async connect({\n deviceId,\n onDisconnect,\n }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']> {\n if (this.connections.has(deviceId))\n return Left(new OpeningConnectionError('Device is already connected'));\n let removeNotification: (() => void) | undefined;\n let removeDisconnect: (() => void) | undefined;\n let closed = false;\n let rejectPending: ((error: unknown) => void) | undefined;\n let receive: ((bytes: Uint8Array) => void) | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n rejectPending?.(new Error('Bluetooth connection ended'));\n return true;\n };\n const release = () => {\n if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);\n };\n const disconnect = async (notify: boolean) => {\n if (!close()) return;\n await this.bridge.disconnect(deviceId).catch(() => undefined);\n release();\n if (notify) onDisconnect(deviceId);\n };\n // Reserve before the first await so concurrent acquires cannot share native GATT state.\n this.connections.set(deviceId, { close });\n try {\n await this.stopDiscovering();\n if (!this.devices.has(deviceId)) {\n const info = await this.bridge.getDevice(deviceId);\n if (info) this.remember(info);\n }\n const known = this.devices.get(deviceId);\n if (!known) {\n close();\n release();\n return Left(new UnknownDeviceError());\n }\n await this.bridge.connect(deviceId, {\n vendor: 'ledger',\n serviceUuid: known.profile.serviceUuid,\n writeUuid: known.profile.writeUuid,\n notifyUuid: known.profile.notifyUuid,\n });\n removeDisconnect = this.bridge.onDeviceDisconnected(id => {\n if (id !== deviceId) return;\n if (close()) {\n release();\n onDisconnect(deviceId);\n }\n });\n removeNotification = this.bridge.onNotification((id, hex) => {\n if (id !== deviceId || closed) return;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {\n rejectPending?.(new Error('Malformed Bluetooth notification'));\n return;\n }\n receive?.(hexToBytes(hex));\n });\n await this.bridge.subscribe(deviceId);\n\n const exchange = async <T>(\n frames: Uint8Array[],\n accept: (\n bytes: Uint8Array,\n resolve: (value: T) => void,\n reject: (error: unknown) => void\n ) => void,\n timeoutMs: number\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (receive) throw new Error('Bluetooth exchange is busy');\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await new Promise<T>((resolve, reject) => {\n rejectPending = reject;\n receive = bytes => accept(bytes, resolve, reject);\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\n void (async () => {\n for (const frame of frames) {\n if (closed) throw new Error('Bluetooth connection ended');\n await this.bridge.write(deviceId, toHex(frame));\n }\n })().catch(reject);\n });\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n receive = undefined;\n rejectPending = undefined;\n }\n };\n\n const frameSize = await exchange<number>(\n [Uint8Array.of(0x08, 0, 0, 0, 0)],\n (bytes, resolve, reject) => {\n if (bytes.length < 6 || bytes[0] !== 0x08 || bytes[5] < 6) {\n reject(new Error('Invalid Ledger BLE MTU response'));\n } else resolve(Math.min(FRAME_SIZE, bytes[5]));\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"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,249 @@
1
+ // src/LedgerElectronBleConnector.ts
2
+ import { LedgerConnectorBase } from "@onekeyfe/hwk-ledger-adapter";
3
+
4
+ // src/LedgerElectronBleTransport.ts
5
+ import {
6
+ DisconnectError,
7
+ GeneralDmkError,
8
+ OpeningConnectionError,
9
+ SendApduConcurrencyError,
10
+ SendApduTimeoutError,
11
+ TransportConnectedDevice,
12
+ UnknownDeviceError
13
+ } from "@ledgerhq/device-management-kit";
14
+ import { Left, Right } from "purify-ts";
15
+ import { defer, from, mergeMap } from "rxjs";
16
+ import { hexToBytes } from "@onekeyfe/hwk-adapter-core";
17
+ var TRANSPORT_ID = "ELECTRON_BLE";
18
+ var FRAME_SIZE = 20;
19
+ var normalizeUuid = (uuid) => uuid.replace(/-/g, "").toLowerCase();
20
+ var toHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
21
+ var LedgerElectronBleTransport = class {
22
+ constructor(bridge, args) {
23
+ this.devices = /* @__PURE__ */ new Map();
24
+ this.connections = /* @__PURE__ */ new Map();
25
+ this.bridge = bridge;
26
+ this.args = args;
27
+ }
28
+ getIdentifier() {
29
+ return TRANSPORT_ID;
30
+ }
31
+ isSupported() {
32
+ return true;
33
+ }
34
+ remember(info) {
35
+ const profiles = this.args.deviceModelDataSource.getBluetoothServicesInfos();
36
+ const profile = Object.values(profiles).find(
37
+ (candidate) => info.advertisedServiceUuids?.some(
38
+ (uuid) => normalizeUuid(uuid) === normalizeUuid(candidate.serviceUuid)
39
+ )
40
+ );
41
+ if (!profile) return void 0;
42
+ const device = {
43
+ id: info.id,
44
+ name: info.name,
45
+ rssi: info.rssi,
46
+ deviceModel: profile.deviceModel,
47
+ transport: TRANSPORT_ID
48
+ };
49
+ this.devices.set(info.id, { device, profile });
50
+ return device;
51
+ }
52
+ discover() {
53
+ if (this.scanPromise) return this.scanPromise;
54
+ const scan = (async () => {
55
+ const availability = await this.bridge.checkAvailability();
56
+ if (!availability.available) throw new Error(`Bluetooth unavailable: ${availability.state}`);
57
+ const infos = await this.bridge.scan({
58
+ vendor: "ledger",
59
+ serviceUuids: this.args.deviceModelDataSource.getBluetoothServices()
60
+ });
61
+ const devices = [];
62
+ for (const info of infos) {
63
+ const device = this.remember(info);
64
+ if (device) devices.push(device);
65
+ }
66
+ return devices;
67
+ })();
68
+ this.scanPromise = scan;
69
+ void scan.finally(() => {
70
+ if (this.scanPromise === scan) this.scanPromise = void 0;
71
+ }).catch(() => void 0);
72
+ return scan;
73
+ }
74
+ startDiscovering() {
75
+ return defer(() => this.discover()).pipe(mergeMap((devices) => from(devices)));
76
+ }
77
+ listenToAvailableDevices() {
78
+ return defer(() => this.discover());
79
+ }
80
+ async stopDiscovering() {
81
+ await this.scanPromise?.catch(() => void 0);
82
+ await this.bridge.stopScan();
83
+ }
84
+ async connect({
85
+ deviceId,
86
+ onDisconnect
87
+ }) {
88
+ if (this.connections.has(deviceId))
89
+ return Left(new OpeningConnectionError("Device is already connected"));
90
+ let removeNotification;
91
+ let removeDisconnect;
92
+ let closed = false;
93
+ let rejectPending;
94
+ let receive;
95
+ const close = () => {
96
+ if (closed) return false;
97
+ closed = true;
98
+ removeNotification?.();
99
+ removeDisconnect?.();
100
+ rejectPending?.(new Error("Bluetooth connection ended"));
101
+ return true;
102
+ };
103
+ const release = () => {
104
+ if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);
105
+ };
106
+ const disconnect = async (notify) => {
107
+ if (!close()) return;
108
+ await this.bridge.disconnect(deviceId).catch(() => void 0);
109
+ release();
110
+ if (notify) onDisconnect(deviceId);
111
+ };
112
+ this.connections.set(deviceId, { close });
113
+ try {
114
+ await this.stopDiscovering();
115
+ if (!this.devices.has(deviceId)) {
116
+ const info = await this.bridge.getDevice(deviceId);
117
+ if (info) this.remember(info);
118
+ }
119
+ const known = this.devices.get(deviceId);
120
+ if (!known) {
121
+ close();
122
+ release();
123
+ return Left(new UnknownDeviceError());
124
+ }
125
+ await this.bridge.connect(deviceId, {
126
+ vendor: "ledger",
127
+ serviceUuid: known.profile.serviceUuid,
128
+ writeUuid: known.profile.writeUuid,
129
+ notifyUuid: known.profile.notifyUuid
130
+ });
131
+ removeDisconnect = this.bridge.onDeviceDisconnected((id) => {
132
+ if (id !== deviceId) return;
133
+ if (close()) {
134
+ release();
135
+ onDisconnect(deviceId);
136
+ }
137
+ });
138
+ removeNotification = this.bridge.onNotification((id, hex) => {
139
+ if (id !== deviceId || closed) return;
140
+ if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {
141
+ rejectPending?.(new Error("Malformed Bluetooth notification"));
142
+ return;
143
+ }
144
+ receive?.(hexToBytes(hex));
145
+ });
146
+ await this.bridge.subscribe(deviceId);
147
+ const exchange = async (frames, accept, timeoutMs) => {
148
+ if (closed) throw new Error("Bluetooth connection ended");
149
+ if (receive) throw new Error("Bluetooth exchange is busy");
150
+ let timer;
151
+ try {
152
+ return await new Promise((resolve, reject) => {
153
+ rejectPending = reject;
154
+ receive = (bytes) => accept(bytes, resolve, reject);
155
+ timer = setTimeout(
156
+ () => reject(new SendApduTimeoutError("Bluetooth response timed out")),
157
+ timeoutMs
158
+ );
159
+ void (async () => {
160
+ for (const frame of frames) {
161
+ if (closed) throw new Error("Bluetooth connection ended");
162
+ await this.bridge.write(deviceId, toHex(frame));
163
+ }
164
+ })().catch(reject);
165
+ });
166
+ } finally {
167
+ if (timer !== void 0) clearTimeout(timer);
168
+ receive = void 0;
169
+ rejectPending = void 0;
170
+ }
171
+ };
172
+ const frameSize = await exchange(
173
+ [Uint8Array.of(8, 0, 0, 0, 0)],
174
+ (bytes, resolve, reject) => {
175
+ if (bytes.length < 6 || bytes[0] !== 8 || bytes[5] < 6) {
176
+ reject(new Error("Invalid Ledger BLE MTU response"));
177
+ } else resolve(Math.min(FRAME_SIZE, bytes[5]));
178
+ },
179
+ 3e4
180
+ );
181
+ if (closed) throw new Error("Bluetooth connection ended");
182
+ const sender = this.args.apduSenderServiceFactory({ frameSize });
183
+ const connected = new TransportConnectedDevice({
184
+ ...known.device,
185
+ type: "BLE",
186
+ sendApdu: async (apdu, _triggersDisconnection, abortTimeout) => {
187
+ if (closed) return Left(new DisconnectError());
188
+ if (receive) return Left(new SendApduConcurrencyError());
189
+ const receiver = this.args.apduReceiverServiceFactory();
190
+ try {
191
+ const result = await exchange(
192
+ sender.getFrames(apdu).map((frame) => frame.getRawData()),
193
+ (bytes, resolve) => receiver.handleFrame(bytes).caseOf({
194
+ Left: (error) => resolve(Left(error)),
195
+ Right: (response) => {
196
+ response.map((value) => resolve(Right(value)));
197
+ }
198
+ }),
199
+ abortTimeout ?? 12e4
200
+ );
201
+ if (result.isLeft()) {
202
+ await disconnect(true);
203
+ }
204
+ return result;
205
+ } catch (error) {
206
+ await disconnect(true);
207
+ return Left(error instanceof SendApduTimeoutError ? error : new GeneralDmkError(error));
208
+ }
209
+ }
210
+ });
211
+ const connection = this.connections.get(deviceId);
212
+ if (connection?.close === close) connection.connectedDevice = connected;
213
+ return Right(connected);
214
+ } catch (error) {
215
+ await disconnect(false);
216
+ return Left(new OpeningConnectionError(error));
217
+ }
218
+ }
219
+ async disconnect({
220
+ connectedDevice
221
+ }) {
222
+ const connection = this.connections.get(connectedDevice.id);
223
+ if (connection?.connectedDevice !== connectedDevice || !connection.close())
224
+ return Right(void 0);
225
+ try {
226
+ await this.bridge.disconnect(connectedDevice.id);
227
+ return Right(void 0);
228
+ } catch (error) {
229
+ return Left(new DisconnectError(error));
230
+ } finally {
231
+ if (this.connections.get(connectedDevice.id) === connection) {
232
+ this.connections.delete(connectedDevice.id);
233
+ }
234
+ }
235
+ }
236
+ };
237
+
238
+ // src/LedgerElectronBleConnector.ts
239
+ function createLedgerElectronBleConnector(bridge) {
240
+ return new LedgerConnectorBase(
241
+ async () => (args) => new LedgerElectronBleTransport(bridge, args),
242
+ { connectionType: "ble" }
243
+ );
244
+ }
245
+ export {
246
+ LedgerElectronBleTransport,
247
+ createLedgerElectronBleConnector
248
+ };
249
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/LedgerElectronBleConnector.ts","../src/LedgerElectronBleTransport.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 { 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// The ATT default is safe on both native backends; never split a Ledger frame in main.\nconst FRAME_SIZE = 20;\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',\n serviceUuids: this.args.deviceModelDataSource.getBluetoothServices(),\n });\n const devices: TransportDiscoveredDevice[] = [];\n for (const info of infos) {\n const device = this.remember(info);\n if (device) devices.push(device);\n }\n return devices;\n })();\n this.scanPromise = scan;\n void scan\n .finally(() => {\n if (this.scanPromise === scan) this.scanPromise = undefined;\n })\n .catch(() => undefined);\n return scan;\n }\n\n startDiscovering() {\n return defer(() => this.discover()).pipe(mergeMap(devices => from(devices)));\n }\n\n listenToAvailableDevices() {\n return defer(() => this.discover());\n }\n\n async stopDiscovering(): Promise<void> {\n await this.scanPromise?.catch(() => undefined);\n await this.bridge.stopScan();\n }\n\n async connect({\n deviceId,\n onDisconnect,\n }: Parameters<Transport['connect']>[0]): ReturnType<Transport['connect']> {\n if (this.connections.has(deviceId))\n return Left(new OpeningConnectionError('Device is already connected'));\n let removeNotification: (() => void) | undefined;\n let removeDisconnect: (() => void) | undefined;\n let closed = false;\n let rejectPending: ((error: unknown) => void) | undefined;\n let receive: ((bytes: Uint8Array) => void) | undefined;\n const close = () => {\n if (closed) return false;\n closed = true;\n removeNotification?.();\n removeDisconnect?.();\n rejectPending?.(new Error('Bluetooth connection ended'));\n return true;\n };\n const release = () => {\n if (this.connections.get(deviceId)?.close === close) this.connections.delete(deviceId);\n };\n const disconnect = async (notify: boolean) => {\n if (!close()) return;\n await this.bridge.disconnect(deviceId).catch(() => undefined);\n release();\n if (notify) onDisconnect(deviceId);\n };\n // Reserve before the first await so concurrent acquires cannot share native GATT state.\n this.connections.set(deviceId, { close });\n try {\n await this.stopDiscovering();\n if (!this.devices.has(deviceId)) {\n const info = await this.bridge.getDevice(deviceId);\n if (info) this.remember(info);\n }\n const known = this.devices.get(deviceId);\n if (!known) {\n close();\n release();\n return Left(new UnknownDeviceError());\n }\n await this.bridge.connect(deviceId, {\n vendor: 'ledger',\n serviceUuid: known.profile.serviceUuid,\n writeUuid: known.profile.writeUuid,\n notifyUuid: known.profile.notifyUuid,\n });\n removeDisconnect = this.bridge.onDeviceDisconnected(id => {\n if (id !== deviceId) return;\n if (close()) {\n release();\n onDisconnect(deviceId);\n }\n });\n removeNotification = this.bridge.onNotification((id, hex) => {\n if (id !== deviceId || closed) return;\n if (!/^(?:[0-9a-f]{2})+$/i.test(hex)) {\n rejectPending?.(new Error('Malformed Bluetooth notification'));\n return;\n }\n receive?.(hexToBytes(hex));\n });\n await this.bridge.subscribe(deviceId);\n\n const exchange = async <T>(\n frames: Uint8Array[],\n accept: (\n bytes: Uint8Array,\n resolve: (value: T) => void,\n reject: (error: unknown) => void\n ) => void,\n timeoutMs: number\n ): Promise<T> => {\n if (closed) throw new Error('Bluetooth connection ended');\n if (receive) throw new Error('Bluetooth exchange is busy');\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await new Promise<T>((resolve, reject) => {\n rejectPending = reject;\n receive = bytes => accept(bytes, resolve, reject);\n timer = setTimeout(\n () => reject(new SendApduTimeoutError('Bluetooth response timed out')),\n timeoutMs\n );\n void (async () => {\n for (const frame of frames) {\n if (closed) throw new Error('Bluetooth connection ended');\n await this.bridge.write(deviceId, toHex(frame));\n }\n })().catch(reject);\n });\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n receive = undefined;\n rejectPending = undefined;\n }\n };\n\n const frameSize = await exchange<number>(\n [Uint8Array.of(0x08, 0, 0, 0, 0)],\n (bytes, resolve, reject) => {\n if (bytes.length < 6 || bytes[0] !== 0x08 || bytes[5] < 6) {\n reject(new Error('Invalid Ledger BLE MTU response'));\n } else resolve(Math.min(FRAME_SIZE, bytes[5]));\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"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@onekeyfe/hwk-ledger-connector-electron-ble",
3
+ "version": "1.2.3-alpha.1",
4
+ "description": "IConnector implementation for Ledger hardware wallets via Electron BLE",
5
+ "author": "OneKey",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.mjs",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.mts",
14
+ "default": "./dist/index.mjs"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "dev": "tsup --watch",
28
+ "clean": "rimraf dist",
29
+ "test": "jest",
30
+ "verify:types": "node scripts/verify-package-types.js",
31
+ "prepack": "yarn build && yarn verify:types"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/OneKeyHQ/hardware-js-sdk.git",
39
+ "directory": "packages/hwk-ledger-connector-electron-ble"
40
+ },
41
+ "keywords": [
42
+ "ledger",
43
+ "electron",
44
+ "ble",
45
+ "bluetooth",
46
+ "hardware-wallet",
47
+ "onekey",
48
+ "connector"
49
+ ],
50
+ "dependencies": {
51
+ "@ledgerhq/device-management-kit": "1.9.0",
52
+ "@onekeyfe/hwk-adapter-core": "1.2.3-alpha.1",
53
+ "@onekeyfe/hwk-ledger-adapter": "1.2.3-alpha.1",
54
+ "purify-ts": "2.1.0",
55
+ "rxjs": "7.8.2"
56
+ },
57
+ "devDependencies": {
58
+ "rimraf": "^5.0.0",
59
+ "tsup": "^8.0.0",
60
+ "typescript": "5.1.6"
61
+ },
62
+ "gitHead": "fd4221863c00240fc4870fc50f8c517d97426de2"
63
+ }