@ledgerhq/hw-transport-node-hid-singleton 6.30.2-nightly.1 → 6.30.2
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +8 -12
- package/README.md +6 -28
- package/lib/TransportNodeHid.d.ts +8 -19
- package/lib/TransportNodeHid.d.ts.map +1 -1
- package/lib/TransportNodeHid.js +83 -102
- package/lib/TransportNodeHid.js.map +1 -1
- package/lib-es/TransportNodeHid.d.ts +8 -19
- package/lib-es/TransportNodeHid.d.ts.map +1 -1
- package/lib-es/TransportNodeHid.js +84 -103
- package/lib-es/TransportNodeHid.js.map +1 -1
- package/package.json +3 -3
- package/src/TransportNodeHid.ts +76 -122
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
> @ledgerhq/hw-transport-node-hid-singleton@6.30.2
|
|
2
|
+
> @ledgerhq/hw-transport-node-hid-singleton@6.30.2 build /home/runner/work/ledger-live/ledger-live/libs/ledgerjs/packages/hw-transport-node-hid-singleton
|
|
3
3
|
> tsc && tsc -m ES6 --outDir lib-es
|
|
4
4
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
# @ledgerhq/hw-transport-node-hid-singleton
|
|
2
2
|
|
|
3
|
-
## 6.30.2
|
|
3
|
+
## 6.30.2
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
- [#
|
|
8
|
-
|
|
9
|
-
- Refactoring of the disconnect after inactivity of the transport implementation
|
|
10
|
-
hw-transport-node-hid-singleton
|
|
11
|
-
- Better logs and documentation
|
|
7
|
+
- [#5750](https://github.com/LedgerHQ/ledger-live/pull/5750) [`16b4d7a`](https://github.com/LedgerHQ/ledger-live/commit/16b4d7ab4702022d4967f3c054d3c62a76716947) Thanks [@aussedatlo](https://github.com/aussedatlo)! - Arrange verbosity and improve typing
|
|
12
8
|
|
|
13
|
-
- Updated dependencies [[`
|
|
14
|
-
- @ledgerhq/hw-transport
|
|
15
|
-
- @ledgerhq/hw-transport@6.
|
|
9
|
+
- Updated dependencies [[`16b4d7a`](https://github.com/LedgerHQ/ledger-live/commit/16b4d7ab4702022d4967f3c054d3c62a76716947)]:
|
|
10
|
+
- @ledgerhq/hw-transport@6.30.2
|
|
11
|
+
- @ledgerhq/hw-transport-node-hid-noevents@6.29.2
|
|
16
12
|
|
|
17
|
-
## 6.30.2-
|
|
13
|
+
## 6.30.2-next.0
|
|
18
14
|
|
|
19
15
|
### Patch Changes
|
|
20
16
|
|
|
21
17
|
- [#5750](https://github.com/LedgerHQ/ledger-live/pull/5750) [`16b4d7a`](https://github.com/LedgerHQ/ledger-live/commit/16b4d7ab4702022d4967f3c054d3c62a76716947) Thanks [@aussedatlo](https://github.com/aussedatlo)! - Arrange verbosity and improve typing
|
|
22
18
|
|
|
23
19
|
- Updated dependencies [[`16b4d7a`](https://github.com/LedgerHQ/ledger-live/commit/16b4d7ab4702022d4967f3c054d3c62a76716947)]:
|
|
24
|
-
- @ledgerhq/hw-transport@6.30.2-
|
|
25
|
-
- @ledgerhq/hw-transport-node-hid-noevents@6.29.2-
|
|
20
|
+
- @ledgerhq/hw-transport@6.30.2-next.0
|
|
21
|
+
- @ledgerhq/hw-transport-node-hid-noevents@6.29.2-next.0
|
|
26
22
|
|
|
27
23
|
## 6.30.1
|
|
28
24
|
|
package/README.md
CHANGED
|
@@ -34,16 +34,14 @@ For a smooth and quick integration:
|
|
|
34
34
|
* [Examples](#examples)
|
|
35
35
|
* [exchange](#exchange)
|
|
36
36
|
* [Parameters](#parameters-1)
|
|
37
|
-
* [close](#close)
|
|
38
37
|
* [isSupported](#issupported)
|
|
39
38
|
* [list](#list)
|
|
40
39
|
* [listen](#listen)
|
|
41
40
|
* [Parameters](#parameters-2)
|
|
42
|
-
* [
|
|
41
|
+
* [autoDisconnect](#autodisconnect)
|
|
43
42
|
* [disconnect](#disconnect)
|
|
44
43
|
* [open](#open)
|
|
45
44
|
* [Parameters](#parameters-3)
|
|
46
|
-
* [onDisconnect](#ondisconnect)
|
|
47
45
|
|
|
48
46
|
### TransportNodeHidSingleton
|
|
49
47
|
|
|
@@ -68,7 +66,7 @@ TransportNodeHid.create().then(transport => ...)
|
|
|
68
66
|
|
|
69
67
|
#### exchange
|
|
70
68
|
|
|
71
|
-
|
|
69
|
+
Exchange with the device using APDU protocol.
|
|
72
70
|
|
|
73
71
|
##### Parameters
|
|
74
72
|
|
|
@@ -76,15 +74,6 @@ Exchanges with the device using APDU protocol
|
|
|
76
74
|
|
|
77
75
|
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** a promise of apdu response
|
|
78
76
|
|
|
79
|
-
#### close
|
|
80
|
-
|
|
81
|
-
Closes the transport instance by triggering a disconnection after some inactivity (no new `open`).
|
|
82
|
-
|
|
83
|
-
Intentionally not disconnecting the device/closing the hid connection directly:
|
|
84
|
-
The HID connection will only be closed after some inactivity.
|
|
85
|
-
|
|
86
|
-
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<void>** 
|
|
87
|
-
|
|
88
77
|
#### isSupported
|
|
89
78
|
|
|
90
79
|
#### list
|
|
@@ -97,18 +86,15 @@ Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/
|
|
|
97
86
|
|
|
98
87
|
Returns **Subscription** 
|
|
99
88
|
|
|
100
|
-
####
|
|
89
|
+
#### autoDisconnect
|
|
101
90
|
|
|
102
|
-
|
|
91
|
+
convenience wrapper for auto-disconnect logic
|
|
103
92
|
|
|
104
|
-
|
|
93
|
+
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<void>** 
|
|
105
94
|
|
|
106
95
|
#### disconnect
|
|
107
96
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
If you want to try to re-use the same transport instance at the next action (when calling `open` again), you can use
|
|
111
|
-
the transport instance `close` method: it will only enable a disconnect after some inactivity.
|
|
97
|
+
globally disconnect the transport singleton
|
|
112
98
|
|
|
113
99
|
#### open
|
|
114
100
|
|
|
@@ -126,11 +112,3 @@ Legacy: `_descriptor` is needed to follow the Transport definition
|
|
|
126
112
|
* `context` **TraceContext?** 
|
|
127
113
|
|
|
128
114
|
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[TransportNodeHidSingleton](#transportnodehidsingleton)>** 
|
|
129
|
-
|
|
130
|
-
### onDisconnect
|
|
131
|
-
|
|
132
|
-
Disconnect event received from the transport instance.
|
|
133
|
-
|
|
134
|
-
It could be after a disconnection coming from the HID library (e.g. device unplugged) or from the transport instance itself (e.g. close).
|
|
135
|
-
Clearing the singleton instance.
|
|
136
|
-
Currently, only 1 device at a time is supported.
|
|
@@ -3,7 +3,7 @@ import HID from "node-hid";
|
|
|
3
3
|
import TransportNodeHidNoEvents from "@ledgerhq/hw-transport-node-hid-noevents";
|
|
4
4
|
import type { Observer, DescriptorEvent, Subscription } from "@ledgerhq/hw-transport";
|
|
5
5
|
import { TraceContext } from "@ledgerhq/logs";
|
|
6
|
-
export type ListenDescriptorEvent = DescriptorEvent<
|
|
6
|
+
export type ListenDescriptorEvent = DescriptorEvent<any>;
|
|
7
7
|
/**
|
|
8
8
|
* node-hid Transport implementation
|
|
9
9
|
* @example
|
|
@@ -12,6 +12,7 @@ export type ListenDescriptorEvent = DescriptorEvent<string>;
|
|
|
12
12
|
* TransportNodeHid.create().then(transport => ...)
|
|
13
13
|
*/
|
|
14
14
|
export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
|
|
15
|
+
preventAutoDisconnect: boolean;
|
|
15
16
|
constructor(device: HID.HID, { context }?: {
|
|
16
17
|
context?: TraceContext;
|
|
17
18
|
});
|
|
@@ -26,21 +27,14 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
|
|
|
26
27
|
/**
|
|
27
28
|
*/
|
|
28
29
|
static listen: (observer: Observer<ListenDescriptorEvent>) => Subscription;
|
|
29
|
-
static disconnectAfterInactivityTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
30
|
-
static clearDisconnectAfterInactivityTimeout(): void;
|
|
31
30
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* Currently, there is only one transport instance (for only one device connected via USB).
|
|
31
|
+
* convenience wrapper for auto-disconnect logic
|
|
35
32
|
*/
|
|
36
|
-
static
|
|
33
|
+
static autoDisconnect(): Promise<void>;
|
|
37
34
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* If you want to try to re-use the same transport instance at the next action (when calling `open` again), you can use
|
|
41
|
-
* the transport instance `close` method: it will only enable a disconnect after some inactivity.
|
|
35
|
+
* globally disconnect the transport singleton
|
|
42
36
|
*/
|
|
43
|
-
static disconnect(): void
|
|
37
|
+
static disconnect(): Promise<void>;
|
|
44
38
|
/**
|
|
45
39
|
* Connects to the first Ledger device connected via USB
|
|
46
40
|
*
|
|
@@ -50,19 +44,14 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
|
|
|
50
44
|
* Legacy: `_descriptor` is needed to follow the Transport definition
|
|
51
45
|
*/
|
|
52
46
|
static open(_descriptor: string, _timeoutMs?: number, context?: TraceContext): Promise<TransportNodeHidSingleton>;
|
|
47
|
+
setAllowAutoDisconnect(allow: boolean): void;
|
|
53
48
|
/**
|
|
54
|
-
*
|
|
49
|
+
* Exchange with the device using APDU protocol.
|
|
55
50
|
*
|
|
56
51
|
* @param apdu
|
|
57
52
|
* @returns a promise of apdu response
|
|
58
53
|
*/
|
|
59
54
|
exchange(apdu: Buffer): Promise<Buffer>;
|
|
60
|
-
/**
|
|
61
|
-
* Closes the transport instance by triggering a disconnection after some inactivity (no new `open`).
|
|
62
|
-
*
|
|
63
|
-
* Intentionally not disconnecting the device/closing the hid connection directly:
|
|
64
|
-
* The HID connection will only be closed after some inactivity.
|
|
65
|
-
*/
|
|
66
55
|
close(): Promise<void>;
|
|
67
56
|
}
|
|
68
57
|
//# sourceMappingURL=TransportNodeHid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransportNodeHid.d.ts","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":";AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,wBAAwC,MAAM,0CAA0C,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAe,YAAY,
|
|
1
|
+
{"version":3,"file":"TransportNodeHid.d.ts","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":";AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,wBAAwC,MAAM,0CAA0C,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAe,YAAY,EAAO,MAAM,gBAAgB,CAAC;AAyBhE,MAAM,MAAM,qBAAqB,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,wBAAwB;IAC7E,qBAAqB,UAAS;gBAElB,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,YAAY,CAAA;KAAO;IAIzE;;OAEG;IACH,MAAM,CAAC,WAAW,yBAAwC;IAE1D;;OAEG;IACH,MAAM,CAAC,IAAI,qBAAiC;IAE5C;OACG;IACH,MAAM,CAAC,MAAM,aAAc,SAAS,qBAAqB,CAAC,KAAG,YAAY,CAqDvE;IAEF;;OAEG;WACU,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5C;;OAEG;WACU,UAAU;IASvB;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CACT,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,yBAAyB,CAAC;IAyCrC,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI5C;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAMvB"}
|
package/lib/TransportNodeHid.js
CHANGED
|
@@ -22,6 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
25
34
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
36
|
};
|
|
@@ -34,7 +43,17 @@ const errors_1 = require("@ledgerhq/errors");
|
|
|
34
43
|
const listenDevices_1 = require("./listenDevices");
|
|
35
44
|
const LOG_TYPE = "hid-verbose";
|
|
36
45
|
let transportInstance = null;
|
|
37
|
-
const
|
|
46
|
+
const DISCONNECT_TIMEOUT = 5000;
|
|
47
|
+
let disconnectTimeout;
|
|
48
|
+
const clearDisconnectTimeout = () => {
|
|
49
|
+
if (disconnectTimeout) {
|
|
50
|
+
clearTimeout(disconnectTimeout);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const setDisconnectTimeout = () => {
|
|
54
|
+
clearDisconnectTimeout();
|
|
55
|
+
disconnectTimeout = setTimeout(() => TransportNodeHidSingleton.autoDisconnect(), DISCONNECT_TIMEOUT);
|
|
56
|
+
};
|
|
38
57
|
/**
|
|
39
58
|
* node-hid Transport implementation
|
|
40
59
|
* @example
|
|
@@ -45,56 +64,36 @@ const DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS = 5000;
|
|
|
45
64
|
class TransportNodeHidSingleton extends hw_transport_node_hid_noevents_1.default {
|
|
46
65
|
constructor(device, { context } = {}) {
|
|
47
66
|
super(device, { context, logType: LOG_TYPE });
|
|
48
|
-
|
|
49
|
-
static clearDisconnectAfterInactivityTimeout() {
|
|
50
|
-
if (TransportNodeHidSingleton.disconnectAfterInactivityTimeout) {
|
|
51
|
-
clearTimeout(TransportNodeHidSingleton.disconnectAfterInactivityTimeout);
|
|
52
|
-
}
|
|
67
|
+
this.preventAutoDisconnect = false;
|
|
53
68
|
}
|
|
54
69
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* Currently, there is only one transport instance (for only one device connected via USB).
|
|
70
|
+
* convenience wrapper for auto-disconnect logic
|
|
58
71
|
*/
|
|
59
|
-
static
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
type: LOG_TYPE,
|
|
64
|
-
message: "Disconnecting after inactivity, if not prevented",
|
|
65
|
-
data: {
|
|
66
|
-
hasInstance: Boolean(transportInstance),
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
if (transportInstance) {
|
|
72
|
+
static autoDisconnect() {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
if (transportInstance && !transportInstance.preventAutoDisconnect) {
|
|
75
|
+
(0, logs_1.log)("hid-verbose", "triggering auto disconnect");
|
|
70
76
|
TransportNodeHidSingleton.disconnect();
|
|
71
77
|
}
|
|
72
|
-
|
|
78
|
+
else if (transportInstance) {
|
|
79
|
+
// If we have disabled the auto-disconnect, try again in DISCONNECT_TIMEOUT
|
|
80
|
+
clearDisconnectTimeout();
|
|
81
|
+
setDisconnectTimeout();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
73
84
|
}
|
|
74
85
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* If you want to try to re-use the same transport instance at the next action (when calling `open` again), you can use
|
|
78
|
-
* the transport instance `close` method: it will only enable a disconnect after some inactivity.
|
|
86
|
+
* globally disconnect the transport singleton
|
|
79
87
|
*/
|
|
80
88
|
static disconnect() {
|
|
81
|
-
(0,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
if (transportInstance) {
|
|
91
|
+
transportInstance.device.close();
|
|
92
|
+
transportInstance.emit("disconnect");
|
|
93
|
+
transportInstance = null;
|
|
94
|
+
}
|
|
95
|
+
clearDisconnectTimeout();
|
|
87
96
|
});
|
|
88
|
-
TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
|
|
89
|
-
if (transportInstance) {
|
|
90
|
-
transportInstance.device.close();
|
|
91
|
-
(0, logs_1.trace)({
|
|
92
|
-
type: LOG_TYPE,
|
|
93
|
-
message: `Closed HID communication with device. Emitting "disconnect" event from static disconnect and clearing singleton instance`,
|
|
94
|
-
});
|
|
95
|
-
transportInstance.emit("disconnect");
|
|
96
|
-
transportInstance = null;
|
|
97
|
-
}
|
|
98
97
|
}
|
|
99
98
|
/**
|
|
100
99
|
* Connects to the first Ledger device connected via USB
|
|
@@ -106,79 +105,61 @@ class TransportNodeHidSingleton extends hw_transport_node_hid_noevents_1.default
|
|
|
106
105
|
*/
|
|
107
106
|
static open(_descriptor, _timeoutMs, context) {
|
|
108
107
|
const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
tracer.trace("Reusing already opened transport instance");
|
|
112
|
-
return Promise.resolve(transportInstance);
|
|
113
|
-
}
|
|
114
|
-
const devicesDetectedDuringOpen = (0, hw_transport_node_hid_noevents_1.getDevices)();
|
|
115
|
-
tracer.trace(`Devices detected during open: ${devicesDetectedDuringOpen.length}`, {
|
|
116
|
-
devicesDetectedDuringOpen,
|
|
117
|
-
});
|
|
118
|
-
if (devicesDetectedDuringOpen.length === 0) {
|
|
119
|
-
return Promise.reject(new errors_1.CantOpenDevice("No device found"));
|
|
120
|
-
}
|
|
121
|
-
const device = devicesDetectedDuringOpen[0];
|
|
122
|
-
tracer.trace("Found a device, creating HID transport instance ...", { device });
|
|
123
|
-
let HIDDevice;
|
|
124
|
-
try {
|
|
125
|
-
HIDDevice = new node_hid_1.default.HID(device.path);
|
|
126
|
-
}
|
|
127
|
-
catch (error) {
|
|
128
|
-
tracer.trace(`Error while connecting to device: ${error}`, { error });
|
|
129
|
-
return Promise.reject(error);
|
|
130
|
-
}
|
|
131
|
-
transportInstance = new TransportNodeHidSingleton(HIDDevice, {
|
|
132
|
-
context,
|
|
133
|
-
});
|
|
134
|
-
const clearDeviceEventsListener = (0, listenDevices_1.listenDevices)(() => { }, () => {
|
|
135
|
-
// Assumes any ledger disconnection concerns current transport
|
|
108
|
+
clearDisconnectTimeout();
|
|
109
|
+
return Promise.resolve().then(() => {
|
|
136
110
|
if (transportInstance) {
|
|
137
|
-
tracer.trace("
|
|
138
|
-
transportInstance
|
|
111
|
+
tracer.trace("Reusing already opened transport instance");
|
|
112
|
+
return transportInstance;
|
|
139
113
|
}
|
|
114
|
+
const device = (0, hw_transport_node_hid_noevents_1.getDevices)()[0];
|
|
115
|
+
if (!device)
|
|
116
|
+
throw new errors_1.CantOpenDevice("no device found");
|
|
117
|
+
tracer.trace("Found a device, creating HID transport instance ...", { device });
|
|
118
|
+
transportInstance = new TransportNodeHidSingleton(new node_hid_1.default.HID(device.path), {
|
|
119
|
+
context,
|
|
120
|
+
});
|
|
121
|
+
const unlisten = (0, listenDevices_1.listenDevices)(() => { }, () => {
|
|
122
|
+
// Assumes any ledger disconnection concerns current transport
|
|
123
|
+
if (transportInstance) {
|
|
124
|
+
transportInstance.emit("disconnect");
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const onDisconnect = () => {
|
|
128
|
+
if (!transportInstance)
|
|
129
|
+
return;
|
|
130
|
+
tracer.trace("Device was disconnected, clearing transport instance ...");
|
|
131
|
+
transportInstance.off("disconnect", onDisconnect);
|
|
132
|
+
transportInstance = null;
|
|
133
|
+
unlisten();
|
|
134
|
+
};
|
|
135
|
+
transportInstance.on("disconnect", onDisconnect);
|
|
136
|
+
return transportInstance;
|
|
140
137
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* It could be after a disconnection coming from the HID library (e.g. device unplugged) or from the transport instance itself (e.g. close).
|
|
145
|
-
* Clearing the singleton instance.
|
|
146
|
-
* Currently, only 1 device at a time is supported.
|
|
147
|
-
*/
|
|
148
|
-
const onDisconnect = () => {
|
|
149
|
-
if (!transportInstance) {
|
|
150
|
-
tracer.trace("disconnect event without transport instance, ignoring ...");
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
this.clearDisconnectAfterInactivityTimeout();
|
|
154
|
-
transportInstance.off("disconnect", onDisconnect);
|
|
155
|
-
transportInstance = null;
|
|
156
|
-
clearDeviceEventsListener();
|
|
157
|
-
};
|
|
158
|
-
transportInstance.on("disconnect", onDisconnect);
|
|
159
|
-
return Promise.resolve(transportInstance);
|
|
138
|
+
}
|
|
139
|
+
setAllowAutoDisconnect(allow) {
|
|
140
|
+
this.preventAutoDisconnect = !allow;
|
|
160
141
|
}
|
|
161
142
|
/**
|
|
162
|
-
*
|
|
143
|
+
* Exchange with the device using APDU protocol.
|
|
163
144
|
*
|
|
164
145
|
* @param apdu
|
|
165
146
|
* @returns a promise of apdu response
|
|
166
147
|
*/
|
|
167
148
|
exchange(apdu) {
|
|
168
|
-
|
|
149
|
+
const _super = Object.create(null, {
|
|
150
|
+
exchange: { get: () => super.exchange }
|
|
151
|
+
});
|
|
152
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
clearDisconnectTimeout();
|
|
154
|
+
const result = yield _super.exchange.call(this, apdu);
|
|
155
|
+
setDisconnectTimeout();
|
|
156
|
+
return result;
|
|
157
|
+
});
|
|
169
158
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Closes the transport instance by triggering a disconnection after some inactivity (no new `open`).
|
|
172
|
-
*
|
|
173
|
-
* Intentionally not disconnecting the device/closing the hid connection directly:
|
|
174
|
-
* The HID connection will only be closed after some inactivity.
|
|
175
|
-
*/
|
|
176
159
|
close() {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
});
|
|
181
|
-
TransportNodeHidSingleton.setDisconnectAfterInactivityTimeout();
|
|
160
|
+
// intentionally, a close will not effectively close the hid connection but
|
|
161
|
+
// will allow an auto-disconnection after some inactivity
|
|
162
|
+
this.preventAutoDisconnect = false;
|
|
182
163
|
return Promise.resolve();
|
|
183
164
|
}
|
|
184
165
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransportNodeHid.js","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TransportNodeHid.js","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import HID from "node-hid";
|
|
|
3
3
|
import TransportNodeHidNoEvents from "@ledgerhq/hw-transport-node-hid-noevents";
|
|
4
4
|
import type { Observer, DescriptorEvent, Subscription } from "@ledgerhq/hw-transport";
|
|
5
5
|
import { TraceContext } from "@ledgerhq/logs";
|
|
6
|
-
export type ListenDescriptorEvent = DescriptorEvent<
|
|
6
|
+
export type ListenDescriptorEvent = DescriptorEvent<any>;
|
|
7
7
|
/**
|
|
8
8
|
* node-hid Transport implementation
|
|
9
9
|
* @example
|
|
@@ -12,6 +12,7 @@ export type ListenDescriptorEvent = DescriptorEvent<string>;
|
|
|
12
12
|
* TransportNodeHid.create().then(transport => ...)
|
|
13
13
|
*/
|
|
14
14
|
export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
|
|
15
|
+
preventAutoDisconnect: boolean;
|
|
15
16
|
constructor(device: HID.HID, { context }?: {
|
|
16
17
|
context?: TraceContext;
|
|
17
18
|
});
|
|
@@ -26,21 +27,14 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
|
|
|
26
27
|
/**
|
|
27
28
|
*/
|
|
28
29
|
static listen: (observer: Observer<ListenDescriptorEvent>) => Subscription;
|
|
29
|
-
static disconnectAfterInactivityTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
30
|
-
static clearDisconnectAfterInactivityTimeout(): void;
|
|
31
30
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* Currently, there is only one transport instance (for only one device connected via USB).
|
|
31
|
+
* convenience wrapper for auto-disconnect logic
|
|
35
32
|
*/
|
|
36
|
-
static
|
|
33
|
+
static autoDisconnect(): Promise<void>;
|
|
37
34
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* If you want to try to re-use the same transport instance at the next action (when calling `open` again), you can use
|
|
41
|
-
* the transport instance `close` method: it will only enable a disconnect after some inactivity.
|
|
35
|
+
* globally disconnect the transport singleton
|
|
42
36
|
*/
|
|
43
|
-
static disconnect(): void
|
|
37
|
+
static disconnect(): Promise<void>;
|
|
44
38
|
/**
|
|
45
39
|
* Connects to the first Ledger device connected via USB
|
|
46
40
|
*
|
|
@@ -50,19 +44,14 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
|
|
|
50
44
|
* Legacy: `_descriptor` is needed to follow the Transport definition
|
|
51
45
|
*/
|
|
52
46
|
static open(_descriptor: string, _timeoutMs?: number, context?: TraceContext): Promise<TransportNodeHidSingleton>;
|
|
47
|
+
setAllowAutoDisconnect(allow: boolean): void;
|
|
53
48
|
/**
|
|
54
|
-
*
|
|
49
|
+
* Exchange with the device using APDU protocol.
|
|
55
50
|
*
|
|
56
51
|
* @param apdu
|
|
57
52
|
* @returns a promise of apdu response
|
|
58
53
|
*/
|
|
59
54
|
exchange(apdu: Buffer): Promise<Buffer>;
|
|
60
|
-
/**
|
|
61
|
-
* Closes the transport instance by triggering a disconnection after some inactivity (no new `open`).
|
|
62
|
-
*
|
|
63
|
-
* Intentionally not disconnecting the device/closing the hid connection directly:
|
|
64
|
-
* The HID connection will only be closed after some inactivity.
|
|
65
|
-
*/
|
|
66
55
|
close(): Promise<void>;
|
|
67
56
|
}
|
|
68
57
|
//# sourceMappingURL=TransportNodeHid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransportNodeHid.d.ts","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":";AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,wBAAwC,MAAM,0CAA0C,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAe,YAAY,
|
|
1
|
+
{"version":3,"file":"TransportNodeHid.d.ts","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":";AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,wBAAwC,MAAM,0CAA0C,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAe,YAAY,EAAO,MAAM,gBAAgB,CAAC;AAyBhE,MAAM,MAAM,qBAAqB,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,wBAAwB;IAC7E,qBAAqB,UAAS;gBAElB,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,YAAY,CAAA;KAAO;IAIzE;;OAEG;IACH,MAAM,CAAC,WAAW,yBAAwC;IAE1D;;OAEG;IACH,MAAM,CAAC,IAAI,qBAAiC;IAE5C;OACG;IACH,MAAM,CAAC,MAAM,aAAc,SAAS,qBAAqB,CAAC,KAAG,YAAY,CAqDvE;IAEF;;OAEG;WACU,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5C;;OAEG;WACU,UAAU;IASvB;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CACT,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,yBAAyB,CAAC;IAyCrC,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI5C;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAMvB"}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import HID from "node-hid";
|
|
2
11
|
import TransportNodeHidNoEvents, { getDevices } from "@ledgerhq/hw-transport-node-hid-noevents";
|
|
3
|
-
import { LocalTracer,
|
|
12
|
+
import { LocalTracer, log } from "@ledgerhq/logs";
|
|
4
13
|
import { identifyUSBProductId } from "@ledgerhq/devices";
|
|
5
14
|
import { CantOpenDevice } from "@ledgerhq/errors";
|
|
6
15
|
import { listenDevices } from "./listenDevices";
|
|
7
16
|
const LOG_TYPE = "hid-verbose";
|
|
8
17
|
let transportInstance = null;
|
|
9
|
-
const
|
|
18
|
+
const DISCONNECT_TIMEOUT = 5000;
|
|
19
|
+
let disconnectTimeout;
|
|
20
|
+
const clearDisconnectTimeout = () => {
|
|
21
|
+
if (disconnectTimeout) {
|
|
22
|
+
clearTimeout(disconnectTimeout);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const setDisconnectTimeout = () => {
|
|
26
|
+
clearDisconnectTimeout();
|
|
27
|
+
disconnectTimeout = setTimeout(() => TransportNodeHidSingleton.autoDisconnect(), DISCONNECT_TIMEOUT);
|
|
28
|
+
};
|
|
10
29
|
/**
|
|
11
30
|
* node-hid Transport implementation
|
|
12
31
|
* @example
|
|
@@ -17,56 +36,36 @@ const DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS = 5000;
|
|
|
17
36
|
class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
|
|
18
37
|
constructor(device, { context } = {}) {
|
|
19
38
|
super(device, { context, logType: LOG_TYPE });
|
|
20
|
-
|
|
21
|
-
static clearDisconnectAfterInactivityTimeout() {
|
|
22
|
-
if (TransportNodeHidSingleton.disconnectAfterInactivityTimeout) {
|
|
23
|
-
clearTimeout(TransportNodeHidSingleton.disconnectAfterInactivityTimeout);
|
|
24
|
-
}
|
|
39
|
+
this.preventAutoDisconnect = false;
|
|
25
40
|
}
|
|
26
41
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* Currently, there is only one transport instance (for only one device connected via USB).
|
|
42
|
+
* convenience wrapper for auto-disconnect logic
|
|
30
43
|
*/
|
|
31
|
-
static
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
type: LOG_TYPE,
|
|
36
|
-
message: "Disconnecting after inactivity, if not prevented",
|
|
37
|
-
data: {
|
|
38
|
-
hasInstance: Boolean(transportInstance),
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
if (transportInstance) {
|
|
44
|
+
static autoDisconnect() {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
if (transportInstance && !transportInstance.preventAutoDisconnect) {
|
|
47
|
+
log("hid-verbose", "triggering auto disconnect");
|
|
42
48
|
TransportNodeHidSingleton.disconnect();
|
|
43
49
|
}
|
|
44
|
-
|
|
50
|
+
else if (transportInstance) {
|
|
51
|
+
// If we have disabled the auto-disconnect, try again in DISCONNECT_TIMEOUT
|
|
52
|
+
clearDisconnectTimeout();
|
|
53
|
+
setDisconnectTimeout();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
45
56
|
}
|
|
46
57
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* If you want to try to re-use the same transport instance at the next action (when calling `open` again), you can use
|
|
50
|
-
* the transport instance `close` method: it will only enable a disconnect after some inactivity.
|
|
58
|
+
* globally disconnect the transport singleton
|
|
51
59
|
*/
|
|
52
60
|
static disconnect() {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
if (transportInstance) {
|
|
63
|
+
transportInstance.device.close();
|
|
64
|
+
transportInstance.emit("disconnect");
|
|
65
|
+
transportInstance = null;
|
|
66
|
+
}
|
|
67
|
+
clearDisconnectTimeout();
|
|
59
68
|
});
|
|
60
|
-
TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
|
|
61
|
-
if (transportInstance) {
|
|
62
|
-
transportInstance.device.close();
|
|
63
|
-
trace({
|
|
64
|
-
type: LOG_TYPE,
|
|
65
|
-
message: `Closed HID communication with device. Emitting "disconnect" event from static disconnect and clearing singleton instance`,
|
|
66
|
-
});
|
|
67
|
-
transportInstance.emit("disconnect");
|
|
68
|
-
transportInstance = null;
|
|
69
|
-
}
|
|
70
69
|
}
|
|
71
70
|
/**
|
|
72
71
|
* Connects to the first Ledger device connected via USB
|
|
@@ -78,79 +77,61 @@ class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
|
|
|
78
77
|
*/
|
|
79
78
|
static open(_descriptor, _timeoutMs, context) {
|
|
80
79
|
const tracer = new LocalTracer(LOG_TYPE, context);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
tracer.trace("Reusing already opened transport instance");
|
|
84
|
-
return Promise.resolve(transportInstance);
|
|
85
|
-
}
|
|
86
|
-
const devicesDetectedDuringOpen = getDevices();
|
|
87
|
-
tracer.trace(`Devices detected during open: ${devicesDetectedDuringOpen.length}`, {
|
|
88
|
-
devicesDetectedDuringOpen,
|
|
89
|
-
});
|
|
90
|
-
if (devicesDetectedDuringOpen.length === 0) {
|
|
91
|
-
return Promise.reject(new CantOpenDevice("No device found"));
|
|
92
|
-
}
|
|
93
|
-
const device = devicesDetectedDuringOpen[0];
|
|
94
|
-
tracer.trace("Found a device, creating HID transport instance ...", { device });
|
|
95
|
-
let HIDDevice;
|
|
96
|
-
try {
|
|
97
|
-
HIDDevice = new HID.HID(device.path);
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
tracer.trace(`Error while connecting to device: ${error}`, { error });
|
|
101
|
-
return Promise.reject(error);
|
|
102
|
-
}
|
|
103
|
-
transportInstance = new TransportNodeHidSingleton(HIDDevice, {
|
|
104
|
-
context,
|
|
105
|
-
});
|
|
106
|
-
const clearDeviceEventsListener = listenDevices(() => { }, () => {
|
|
107
|
-
// Assumes any ledger disconnection concerns current transport
|
|
80
|
+
clearDisconnectTimeout();
|
|
81
|
+
return Promise.resolve().then(() => {
|
|
108
82
|
if (transportInstance) {
|
|
109
|
-
tracer.trace("
|
|
110
|
-
transportInstance
|
|
83
|
+
tracer.trace("Reusing already opened transport instance");
|
|
84
|
+
return transportInstance;
|
|
111
85
|
}
|
|
86
|
+
const device = getDevices()[0];
|
|
87
|
+
if (!device)
|
|
88
|
+
throw new CantOpenDevice("no device found");
|
|
89
|
+
tracer.trace("Found a device, creating HID transport instance ...", { device });
|
|
90
|
+
transportInstance = new TransportNodeHidSingleton(new HID.HID(device.path), {
|
|
91
|
+
context,
|
|
92
|
+
});
|
|
93
|
+
const unlisten = listenDevices(() => { }, () => {
|
|
94
|
+
// Assumes any ledger disconnection concerns current transport
|
|
95
|
+
if (transportInstance) {
|
|
96
|
+
transportInstance.emit("disconnect");
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const onDisconnect = () => {
|
|
100
|
+
if (!transportInstance)
|
|
101
|
+
return;
|
|
102
|
+
tracer.trace("Device was disconnected, clearing transport instance ...");
|
|
103
|
+
transportInstance.off("disconnect", onDisconnect);
|
|
104
|
+
transportInstance = null;
|
|
105
|
+
unlisten();
|
|
106
|
+
};
|
|
107
|
+
transportInstance.on("disconnect", onDisconnect);
|
|
108
|
+
return transportInstance;
|
|
112
109
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
* It could be after a disconnection coming from the HID library (e.g. device unplugged) or from the transport instance itself (e.g. close).
|
|
117
|
-
* Clearing the singleton instance.
|
|
118
|
-
* Currently, only 1 device at a time is supported.
|
|
119
|
-
*/
|
|
120
|
-
const onDisconnect = () => {
|
|
121
|
-
if (!transportInstance) {
|
|
122
|
-
tracer.trace("disconnect event without transport instance, ignoring ...");
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
this.clearDisconnectAfterInactivityTimeout();
|
|
126
|
-
transportInstance.off("disconnect", onDisconnect);
|
|
127
|
-
transportInstance = null;
|
|
128
|
-
clearDeviceEventsListener();
|
|
129
|
-
};
|
|
130
|
-
transportInstance.on("disconnect", onDisconnect);
|
|
131
|
-
return Promise.resolve(transportInstance);
|
|
110
|
+
}
|
|
111
|
+
setAllowAutoDisconnect(allow) {
|
|
112
|
+
this.preventAutoDisconnect = !allow;
|
|
132
113
|
}
|
|
133
114
|
/**
|
|
134
|
-
*
|
|
115
|
+
* Exchange with the device using APDU protocol.
|
|
135
116
|
*
|
|
136
117
|
* @param apdu
|
|
137
118
|
* @returns a promise of apdu response
|
|
138
119
|
*/
|
|
139
120
|
exchange(apdu) {
|
|
140
|
-
|
|
121
|
+
const _super = Object.create(null, {
|
|
122
|
+
exchange: { get: () => super.exchange }
|
|
123
|
+
});
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
clearDisconnectTimeout();
|
|
126
|
+
const result = yield _super.exchange.call(this, apdu);
|
|
127
|
+
setDisconnectTimeout();
|
|
128
|
+
return result;
|
|
129
|
+
});
|
|
141
130
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Closes the transport instance by triggering a disconnection after some inactivity (no new `open`).
|
|
144
|
-
*
|
|
145
|
-
* Intentionally not disconnecting the device/closing the hid connection directly:
|
|
146
|
-
* The HID connection will only be closed after some inactivity.
|
|
147
|
-
*/
|
|
148
131
|
close() {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
});
|
|
153
|
-
TransportNodeHidSingleton.setDisconnectAfterInactivityTimeout();
|
|
132
|
+
// intentionally, a close will not effectively close the hid connection but
|
|
133
|
+
// will allow an auto-disconnection after some inactivity
|
|
134
|
+
this.preventAutoDisconnect = false;
|
|
154
135
|
return Promise.resolve();
|
|
155
136
|
}
|
|
156
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransportNodeHid.js","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,wBAAwB,EAAE,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAEhG,OAAO,EAAE,WAAW,EAAgB,
|
|
1
|
+
{"version":3,"file":"TransportNodeHid.js","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/hw-transport-node-hid-singleton",
|
|
3
|
-
"version": "6.30.2
|
|
3
|
+
"version": "6.30.2",
|
|
4
4
|
"description": "Ledger Hardware Wallet Node implementation of the communication layer, using node-hid and node-usb",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"node-hid": "^2.1.2",
|
|
31
31
|
"usb": "2.9.0",
|
|
32
32
|
"@ledgerhq/devices": "^8.2.0",
|
|
33
|
+
"@ledgerhq/hw-transport": "^6.30.2",
|
|
33
34
|
"@ledgerhq/errors": "^6.16.1",
|
|
34
|
-
"@ledgerhq/hw-transport": "^6.
|
|
35
|
-
"@ledgerhq/hw-transport-node-hid-noevents": "^6.29.2-nightly.1",
|
|
35
|
+
"@ledgerhq/hw-transport-node-hid-noevents": "^6.29.2",
|
|
36
36
|
"@ledgerhq/logs": "^6.12.0"
|
|
37
37
|
},
|
|
38
38
|
"gitHead": "dd0dea64b58e5a9125c8a422dcffd29e5ef6abec",
|
package/src/TransportNodeHid.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import HID from "node-hid";
|
|
2
2
|
import TransportNodeHidNoEvents, { getDevices } from "@ledgerhq/hw-transport-node-hid-noevents";
|
|
3
3
|
import type { Observer, DescriptorEvent, Subscription } from "@ledgerhq/hw-transport";
|
|
4
|
-
import { LocalTracer, TraceContext,
|
|
4
|
+
import { LocalTracer, TraceContext, log } from "@ledgerhq/logs";
|
|
5
5
|
import { identifyUSBProductId } from "@ledgerhq/devices";
|
|
6
6
|
import { CantOpenDevice } from "@ledgerhq/errors";
|
|
7
7
|
import { listenDevices } from "./listenDevices";
|
|
@@ -10,9 +10,23 @@ const LOG_TYPE = "hid-verbose";
|
|
|
10
10
|
|
|
11
11
|
let transportInstance: TransportNodeHidSingleton | null = null;
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const DISCONNECT_TIMEOUT = 5000;
|
|
14
|
+
let disconnectTimeout;
|
|
15
|
+
const clearDisconnectTimeout = () => {
|
|
16
|
+
if (disconnectTimeout) {
|
|
17
|
+
clearTimeout(disconnectTimeout);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const setDisconnectTimeout = () => {
|
|
22
|
+
clearDisconnectTimeout();
|
|
23
|
+
disconnectTimeout = setTimeout(
|
|
24
|
+
() => TransportNodeHidSingleton.autoDisconnect(),
|
|
25
|
+
DISCONNECT_TIMEOUT,
|
|
26
|
+
);
|
|
27
|
+
};
|
|
14
28
|
|
|
15
|
-
export type ListenDescriptorEvent = DescriptorEvent<
|
|
29
|
+
export type ListenDescriptorEvent = DescriptorEvent<any>;
|
|
16
30
|
|
|
17
31
|
/**
|
|
18
32
|
* node-hid Transport implementation
|
|
@@ -22,6 +36,8 @@ export type ListenDescriptorEvent = DescriptorEvent<string>;
|
|
|
22
36
|
* TransportNodeHid.create().then(transport => ...)
|
|
23
37
|
*/
|
|
24
38
|
export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
|
|
39
|
+
preventAutoDisconnect = false;
|
|
40
|
+
|
|
25
41
|
constructor(device: HID.HID, { context }: { context?: TraceContext } = {}) {
|
|
26
42
|
super(device, { context, logType: LOG_TYPE });
|
|
27
43
|
}
|
|
@@ -93,61 +109,30 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
|
|
|
93
109
|
};
|
|
94
110
|
};
|
|
95
111
|
|
|
96
|
-
static disconnectAfterInactivityTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
97
|
-
static clearDisconnectAfterInactivityTimeout() {
|
|
98
|
-
if (TransportNodeHidSingleton.disconnectAfterInactivityTimeout) {
|
|
99
|
-
clearTimeout(TransportNodeHidSingleton.disconnectAfterInactivityTimeout);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
112
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* Currently, there is only one transport instance (for only one device connected via USB).
|
|
113
|
+
* convenience wrapper for auto-disconnect logic
|
|
107
114
|
*/
|
|
108
|
-
static
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
if (transportInstance) {
|
|
120
|
-
TransportNodeHidSingleton.disconnect();
|
|
121
|
-
}
|
|
122
|
-
}, DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS);
|
|
115
|
+
static async autoDisconnect(): Promise<void> {
|
|
116
|
+
if (transportInstance && !transportInstance.preventAutoDisconnect) {
|
|
117
|
+
log("hid-verbose", "triggering auto disconnect");
|
|
118
|
+
TransportNodeHidSingleton.disconnect();
|
|
119
|
+
} else if (transportInstance) {
|
|
120
|
+
// If we have disabled the auto-disconnect, try again in DISCONNECT_TIMEOUT
|
|
121
|
+
clearDisconnectTimeout();
|
|
122
|
+
setDisconnectTimeout();
|
|
123
|
+
}
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* If you want to try to re-use the same transport instance at the next action (when calling `open` again), you can use
|
|
129
|
-
* the transport instance `close` method: it will only enable a disconnect after some inactivity.
|
|
127
|
+
* globally disconnect the transport singleton
|
|
130
128
|
*/
|
|
131
|
-
static disconnect() {
|
|
132
|
-
trace({
|
|
133
|
-
type: LOG_TYPE,
|
|
134
|
-
message: "Disconnecting from HID device",
|
|
135
|
-
data: {
|
|
136
|
-
hasInstance: Boolean(transportInstance),
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
|
|
141
|
-
|
|
129
|
+
static async disconnect() {
|
|
142
130
|
if (transportInstance) {
|
|
143
131
|
transportInstance.device.close();
|
|
144
|
-
trace({
|
|
145
|
-
type: LOG_TYPE,
|
|
146
|
-
message: `Closed HID communication with device. Emitting "disconnect" event from static disconnect and clearing singleton instance`,
|
|
147
|
-
});
|
|
148
132
|
transportInstance.emit("disconnect");
|
|
149
133
|
transportInstance = null;
|
|
150
134
|
}
|
|
135
|
+
clearDisconnectTimeout();
|
|
151
136
|
}
|
|
152
137
|
|
|
153
138
|
/**
|
|
@@ -164,97 +149,66 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
|
|
|
164
149
|
context?: TraceContext,
|
|
165
150
|
): Promise<TransportNodeHidSingleton> {
|
|
166
151
|
const tracer = new LocalTracer(LOG_TYPE, context);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (transportInstance) {
|
|
170
|
-
tracer.trace("Reusing already opened transport instance");
|
|
171
|
-
return Promise.resolve(transportInstance);
|
|
172
|
-
}
|
|
152
|
+
clearDisconnectTimeout();
|
|
173
153
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (devicesDetectedDuringOpen.length === 0) {
|
|
180
|
-
return Promise.reject(new CantOpenDevice("No device found"));
|
|
181
|
-
}
|
|
182
|
-
const device = devicesDetectedDuringOpen[0];
|
|
154
|
+
return Promise.resolve().then(() => {
|
|
155
|
+
if (transportInstance) {
|
|
156
|
+
tracer.trace("Reusing already opened transport instance");
|
|
157
|
+
return transportInstance;
|
|
158
|
+
}
|
|
183
159
|
|
|
184
|
-
|
|
160
|
+
const device = getDevices()[0];
|
|
161
|
+
if (!device) throw new CantOpenDevice("no device found");
|
|
185
162
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
tracer.trace(`Error while connecting to device: ${error}`, { error });
|
|
191
|
-
return Promise.reject(error);
|
|
192
|
-
}
|
|
163
|
+
tracer.trace("Found a device, creating HID transport instance ...", { device });
|
|
164
|
+
transportInstance = new TransportNodeHidSingleton(new HID.HID(device.path as string), {
|
|
165
|
+
context,
|
|
166
|
+
});
|
|
193
167
|
|
|
194
|
-
|
|
195
|
-
|
|
168
|
+
const unlisten = listenDevices(
|
|
169
|
+
() => {},
|
|
170
|
+
() => {
|
|
171
|
+
// Assumes any ledger disconnection concerns current transport
|
|
172
|
+
if (transportInstance) {
|
|
173
|
+
transportInstance.emit("disconnect");
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const onDisconnect = () => {
|
|
179
|
+
if (!transportInstance) return;
|
|
180
|
+
tracer.trace("Device was disconnected, clearing transport instance ...");
|
|
181
|
+
transportInstance.off("disconnect", onDisconnect);
|
|
182
|
+
transportInstance = null;
|
|
183
|
+
unlisten();
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
transportInstance.on("disconnect", onDisconnect);
|
|
187
|
+
return transportInstance;
|
|
196
188
|
});
|
|
189
|
+
}
|
|
197
190
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
() => {
|
|
201
|
-
// Assumes any ledger disconnection concerns current transport
|
|
202
|
-
if (transportInstance) {
|
|
203
|
-
tracer.trace("Listened to on remove device event. Emitting a disconnect");
|
|
204
|
-
transportInstance.emit("disconnect");
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Disconnect event received from the transport instance.
|
|
211
|
-
*
|
|
212
|
-
* It could be after a disconnection coming from the HID library (e.g. device unplugged) or from the transport instance itself (e.g. close).
|
|
213
|
-
* Clearing the singleton instance.
|
|
214
|
-
* Currently, only 1 device at a time is supported.
|
|
215
|
-
*/
|
|
216
|
-
const onDisconnect = () => {
|
|
217
|
-
if (!transportInstance) {
|
|
218
|
-
tracer.trace("disconnect event without transport instance, ignoring ...");
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
this.clearDisconnectAfterInactivityTimeout();
|
|
222
|
-
transportInstance.off("disconnect", onDisconnect);
|
|
223
|
-
transportInstance = null;
|
|
224
|
-
clearDeviceEventsListener();
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
transportInstance.on("disconnect", onDisconnect);
|
|
228
|
-
return Promise.resolve(transportInstance);
|
|
191
|
+
setAllowAutoDisconnect(allow: boolean): void {
|
|
192
|
+
this.preventAutoDisconnect = !allow;
|
|
229
193
|
}
|
|
230
194
|
|
|
231
195
|
/**
|
|
232
|
-
*
|
|
196
|
+
* Exchange with the device using APDU protocol.
|
|
233
197
|
*
|
|
234
198
|
* @param apdu
|
|
235
199
|
* @returns a promise of apdu response
|
|
236
200
|
*/
|
|
237
|
-
exchange(apdu: Buffer): Promise<Buffer> {
|
|
238
|
-
|
|
201
|
+
async exchange(apdu: Buffer): Promise<Buffer> {
|
|
202
|
+
clearDisconnectTimeout();
|
|
203
|
+
const result = await super.exchange(apdu);
|
|
204
|
+
setDisconnectTimeout();
|
|
205
|
+
return result;
|
|
239
206
|
}
|
|
240
207
|
|
|
241
|
-
/**
|
|
242
|
-
* Closes the transport instance by triggering a disconnection after some inactivity (no new `open`).
|
|
243
|
-
*
|
|
244
|
-
* Intentionally not disconnecting the device/closing the hid connection directly:
|
|
245
|
-
* The HID connection will only be closed after some inactivity.
|
|
246
|
-
*/
|
|
247
208
|
close(): Promise<void> {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
hasInstance: !!transportInstance,
|
|
252
|
-
disconnectAfterInactivityTimeoutMs: DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS,
|
|
253
|
-
},
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
TransportNodeHidSingleton.setDisconnectAfterInactivityTimeout();
|
|
257
|
-
|
|
209
|
+
// intentionally, a close will not effectively close the hid connection but
|
|
210
|
+
// will allow an auto-disconnection after some inactivity
|
|
211
|
+
this.preventAutoDisconnect = false;
|
|
258
212
|
return Promise.resolve();
|
|
259
213
|
}
|
|
260
214
|
}
|