@ledgerhq/hw-transport-node-hid-singleton 6.30.2 → 6.30.3-next.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ledgerhq/hw-transport-node-hid-singleton
2
2
 
3
+ ## 6.30.3-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#5749](https://github.com/LedgerHQ/ledger-live/pull/5749) [`eadebff`](https://github.com/LedgerHQ/ledger-live/commit/eadebff3fe58aef6a5befb033d5147afc49663d3) Thanks [@alexandremgo](https://github.com/alexandremgo)! - Fix: HID USB reconnection on LLD during the sync onboarding
8
+
9
+ - Refactoring of the disconnect after inactivity of the transport implementation
10
+ hw-transport-node-hid-singleton
11
+ - Better logs and documentation
12
+
13
+ - Updated dependencies [[`eadebff`](https://github.com/LedgerHQ/ledger-live/commit/eadebff3fe58aef6a5befb033d5147afc49663d3)]:
14
+ - @ledgerhq/hw-transport-node-hid-noevents@6.29.3-next.0
15
+ - @ledgerhq/hw-transport@6.30.3-next.0
16
+
3
17
  ## 6.30.2
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -34,14 +34,16 @@ For a smooth and quick integration:
34
34
  * [Examples](#examples)
35
35
  * [exchange](#exchange)
36
36
  * [Parameters](#parameters-1)
37
+ * [close](#close)
37
38
  * [isSupported](#issupported)
38
39
  * [list](#list)
39
40
  * [listen](#listen)
40
41
  * [Parameters](#parameters-2)
41
- * [autoDisconnect](#autodisconnect)
42
+ * [setDisconnectAfterInactivityTimeout](#setdisconnectafterinactivitytimeout)
42
43
  * [disconnect](#disconnect)
43
44
  * [open](#open)
44
45
  * [Parameters](#parameters-3)
46
+ * [onDisconnect](#ondisconnect)
45
47
 
46
48
  ### TransportNodeHidSingleton
47
49
 
@@ -66,7 +68,7 @@ TransportNodeHid.create().then(transport => ...)
66
68
 
67
69
  #### exchange
68
70
 
69
- Exchange with the device using APDU protocol.
71
+ Exchanges with the device using APDU protocol
70
72
 
71
73
  ##### Parameters
72
74
 
@@ -74,6 +76,15 @@ Exchange with the device using APDU protocol.
74
76
 
75
77
  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
76
78
 
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>**&#x20;
87
+
77
88
  #### isSupported
78
89
 
79
90
  #### list
@@ -86,15 +97,18 @@ Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/
86
97
 
87
98
  Returns **Subscription**&#x20;
88
99
 
89
- #### autoDisconnect
100
+ #### setDisconnectAfterInactivityTimeout
90
101
 
91
- convenience wrapper for auto-disconnect logic
102
+ Disconnects device from singleton instance after some inactivity (no new `open`).
92
103
 
93
- Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<void>**&#x20;
104
+ Currently, there is only one transport instance (for only one device connected via USB).
94
105
 
95
106
  #### disconnect
96
107
 
97
- globally disconnect the transport singleton
108
+ Disconnects from the HID device associated to the transport singleton.
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.
98
112
 
99
113
  #### open
100
114
 
@@ -112,3 +126,11 @@ Legacy: `_descriptor` is needed to follow the Transport definition
112
126
  * `context` **TraceContext?**&#x20;
113
127
 
114
128
  Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[TransportNodeHidSingleton](#transportnodehidsingleton)>**&#x20;
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<any>;
6
+ export type ListenDescriptorEvent = DescriptorEvent<string>;
7
7
  /**
8
8
  * node-hid Transport implementation
9
9
  * @example
@@ -12,7 +12,6 @@ export type ListenDescriptorEvent = DescriptorEvent<any>;
12
12
  * TransportNodeHid.create().then(transport => ...)
13
13
  */
14
14
  export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
15
- preventAutoDisconnect: boolean;
16
15
  constructor(device: HID.HID, { context }?: {
17
16
  context?: TraceContext;
18
17
  });
@@ -27,14 +26,21 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
27
26
  /**
28
27
  */
29
28
  static listen: (observer: Observer<ListenDescriptorEvent>) => Subscription;
29
+ static disconnectAfterInactivityTimeout: ReturnType<typeof setTimeout> | undefined;
30
+ static clearDisconnectAfterInactivityTimeout(): void;
30
31
  /**
31
- * convenience wrapper for auto-disconnect logic
32
+ * Disconnects device from singleton instance after some inactivity (no new `open`).
33
+ *
34
+ * Currently, there is only one transport instance (for only one device connected via USB).
32
35
  */
33
- static autoDisconnect(): Promise<void>;
36
+ static setDisconnectAfterInactivityTimeout(): void;
34
37
  /**
35
- * globally disconnect the transport singleton
38
+ * Disconnects from the HID device associated to the transport singleton.
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.
36
42
  */
37
- static disconnect(): Promise<void>;
43
+ static disconnect(): void;
38
44
  /**
39
45
  * Connects to the first Ledger device connected via USB
40
46
  *
@@ -44,14 +50,19 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
44
50
  * Legacy: `_descriptor` is needed to follow the Transport definition
45
51
  */
46
52
  static open(_descriptor: string, _timeoutMs?: number, context?: TraceContext): Promise<TransportNodeHidSingleton>;
47
- setAllowAutoDisconnect(allow: boolean): void;
48
53
  /**
49
- * Exchange with the device using APDU protocol.
54
+ * Exchanges with the device using APDU protocol
50
55
  *
51
56
  * @param apdu
52
57
  * @returns a promise of apdu response
53
58
  */
54
59
  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
+ */
55
66
  close(): Promise<void>;
56
67
  }
57
68
  //# 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,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
+ {"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,EAAS,MAAM,gBAAgB,CAAC;AAWlE,MAAM,MAAM,qBAAqB,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,wBAAwB;gBACjE,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,MAAM,CAAC,gCAAgC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,SAAS,CAAC;IACnF,MAAM,CAAC,qCAAqC;IAM5C;;;;OAIG;IACH,MAAM,CAAC,mCAAmC;IAiB1C;;;;;OAKG;IACH,MAAM,CAAC,UAAU;IAsBjB;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CACT,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,yBAAyB,CAAC;IAkErC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvC;;;;;OAKG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAavB"}
@@ -22,15 +22,6 @@ 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
- };
34
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
35
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
27
  };
@@ -43,17 +34,7 @@ const errors_1 = require("@ledgerhq/errors");
43
34
  const listenDevices_1 = require("./listenDevices");
44
35
  const LOG_TYPE = "hid-verbose";
45
36
  let transportInstance = null;
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
- };
37
+ const DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS = 5000;
57
38
  /**
58
39
  * node-hid Transport implementation
59
40
  * @example
@@ -64,36 +45,56 @@ const setDisconnectTimeout = () => {
64
45
  class TransportNodeHidSingleton extends hw_transport_node_hid_noevents_1.default {
65
46
  constructor(device, { context } = {}) {
66
47
  super(device, { context, logType: LOG_TYPE });
67
- this.preventAutoDisconnect = false;
48
+ }
49
+ static clearDisconnectAfterInactivityTimeout() {
50
+ if (TransportNodeHidSingleton.disconnectAfterInactivityTimeout) {
51
+ clearTimeout(TransportNodeHidSingleton.disconnectAfterInactivityTimeout);
52
+ }
68
53
  }
69
54
  /**
70
- * convenience wrapper for auto-disconnect logic
55
+ * Disconnects device from singleton instance after some inactivity (no new `open`).
56
+ *
57
+ * Currently, there is only one transport instance (for only one device connected via USB).
71
58
  */
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");
59
+ static setDisconnectAfterInactivityTimeout() {
60
+ TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
61
+ TransportNodeHidSingleton.disconnectAfterInactivityTimeout = setTimeout(() => {
62
+ (0, logs_1.trace)({
63
+ type: LOG_TYPE,
64
+ message: "Disconnecting after inactivity, if not prevented",
65
+ data: {
66
+ hasInstance: Boolean(transportInstance),
67
+ },
68
+ });
69
+ if (transportInstance) {
76
70
  TransportNodeHidSingleton.disconnect();
77
71
  }
78
- else if (transportInstance) {
79
- // If we have disabled the auto-disconnect, try again in DISCONNECT_TIMEOUT
80
- clearDisconnectTimeout();
81
- setDisconnectTimeout();
82
- }
83
- });
72
+ }, DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS);
84
73
  }
85
74
  /**
86
- * globally disconnect the transport singleton
75
+ * Disconnects from the HID device associated to the transport singleton.
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.
87
79
  */
88
80
  static disconnect() {
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();
81
+ (0, logs_1.trace)({
82
+ type: LOG_TYPE,
83
+ message: "Disconnecting from HID device",
84
+ data: {
85
+ hasInstance: Boolean(transportInstance),
86
+ },
96
87
  });
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
+ }
97
98
  }
98
99
  /**
99
100
  * Connects to the first Ledger device connected via USB
@@ -105,61 +106,79 @@ class TransportNodeHidSingleton extends hw_transport_node_hid_noevents_1.default
105
106
  */
106
107
  static open(_descriptor, _timeoutMs, context) {
107
108
  const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
108
- clearDisconnectTimeout();
109
- return Promise.resolve().then(() => {
109
+ TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
110
+ if (transportInstance) {
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
110
136
  if (transportInstance) {
111
- tracer.trace("Reusing already opened transport instance");
112
- return transportInstance;
137
+ tracer.trace("Listened to on remove device event. Emitting a disconnect");
138
+ transportInstance.emit("disconnect");
113
139
  }
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;
137
140
  });
138
- }
139
- setAllowAutoDisconnect(allow) {
140
- this.preventAutoDisconnect = !allow;
141
+ /**
142
+ * Disconnect event received from the transport instance.
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);
141
160
  }
142
161
  /**
143
- * Exchange with the device using APDU protocol.
162
+ * Exchanges with the device using APDU protocol
144
163
  *
145
164
  * @param apdu
146
165
  * @returns a promise of apdu response
147
166
  */
148
167
  exchange(apdu) {
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
- });
168
+ return super.exchange(apdu);
158
169
  }
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
+ */
159
176
  close() {
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;
177
+ this.tracer.trace("Closing transport instance by triggering a disconnection after some inactivity", {
178
+ hasInstance: !!transportInstance,
179
+ disconnectAfterInactivityTimeoutMs: DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS,
180
+ });
181
+ TransportNodeHidSingleton.setDisconnectAfterInactivityTimeout();
163
182
  return Promise.resolve();
164
183
  }
165
184
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TransportNodeHid.js","sourceRoot":"","sources":["../src/TransportNodeHid.ts"],"names":[],"mappings":"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"}
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<any>;
6
+ export type ListenDescriptorEvent = DescriptorEvent<string>;
7
7
  /**
8
8
  * node-hid Transport implementation
9
9
  * @example
@@ -12,7 +12,6 @@ export type ListenDescriptorEvent = DescriptorEvent<any>;
12
12
  * TransportNodeHid.create().then(transport => ...)
13
13
  */
14
14
  export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
15
- preventAutoDisconnect: boolean;
16
15
  constructor(device: HID.HID, { context }?: {
17
16
  context?: TraceContext;
18
17
  });
@@ -27,14 +26,21 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
27
26
  /**
28
27
  */
29
28
  static listen: (observer: Observer<ListenDescriptorEvent>) => Subscription;
29
+ static disconnectAfterInactivityTimeout: ReturnType<typeof setTimeout> | undefined;
30
+ static clearDisconnectAfterInactivityTimeout(): void;
30
31
  /**
31
- * convenience wrapper for auto-disconnect logic
32
+ * Disconnects device from singleton instance after some inactivity (no new `open`).
33
+ *
34
+ * Currently, there is only one transport instance (for only one device connected via USB).
32
35
  */
33
- static autoDisconnect(): Promise<void>;
36
+ static setDisconnectAfterInactivityTimeout(): void;
34
37
  /**
35
- * globally disconnect the transport singleton
38
+ * Disconnects from the HID device associated to the transport singleton.
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.
36
42
  */
37
- static disconnect(): Promise<void>;
43
+ static disconnect(): void;
38
44
  /**
39
45
  * Connects to the first Ledger device connected via USB
40
46
  *
@@ -44,14 +50,19 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
44
50
  * Legacy: `_descriptor` is needed to follow the Transport definition
45
51
  */
46
52
  static open(_descriptor: string, _timeoutMs?: number, context?: TraceContext): Promise<TransportNodeHidSingleton>;
47
- setAllowAutoDisconnect(allow: boolean): void;
48
53
  /**
49
- * Exchange with the device using APDU protocol.
54
+ * Exchanges with the device using APDU protocol
50
55
  *
51
56
  * @param apdu
52
57
  * @returns a promise of apdu response
53
58
  */
54
59
  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
+ */
55
66
  close(): Promise<void>;
56
67
  }
57
68
  //# 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,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
+ {"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,EAAS,MAAM,gBAAgB,CAAC;AAWlE,MAAM,MAAM,qBAAqB,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,wBAAwB;gBACjE,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,MAAM,CAAC,gCAAgC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,SAAS,CAAC;IACnF,MAAM,CAAC,qCAAqC;IAM5C;;;;OAIG;IACH,MAAM,CAAC,mCAAmC;IAiB1C;;;;;OAKG;IACH,MAAM,CAAC,UAAU;IAsBjB;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CACT,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,yBAAyB,CAAC;IAkErC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvC;;;;;OAKG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAavB"}
@@ -1,31 +1,12 @@
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
- };
10
1
  import HID from "node-hid";
11
2
  import TransportNodeHidNoEvents, { getDevices } from "@ledgerhq/hw-transport-node-hid-noevents";
12
- import { LocalTracer, log } from "@ledgerhq/logs";
3
+ import { LocalTracer, trace } from "@ledgerhq/logs";
13
4
  import { identifyUSBProductId } from "@ledgerhq/devices";
14
5
  import { CantOpenDevice } from "@ledgerhq/errors";
15
6
  import { listenDevices } from "./listenDevices";
16
7
  const LOG_TYPE = "hid-verbose";
17
8
  let transportInstance = null;
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
- };
9
+ const DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS = 5000;
29
10
  /**
30
11
  * node-hid Transport implementation
31
12
  * @example
@@ -36,36 +17,56 @@ const setDisconnectTimeout = () => {
36
17
  class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
37
18
  constructor(device, { context } = {}) {
38
19
  super(device, { context, logType: LOG_TYPE });
39
- this.preventAutoDisconnect = false;
20
+ }
21
+ static clearDisconnectAfterInactivityTimeout() {
22
+ if (TransportNodeHidSingleton.disconnectAfterInactivityTimeout) {
23
+ clearTimeout(TransportNodeHidSingleton.disconnectAfterInactivityTimeout);
24
+ }
40
25
  }
41
26
  /**
42
- * convenience wrapper for auto-disconnect logic
27
+ * Disconnects device from singleton instance after some inactivity (no new `open`).
28
+ *
29
+ * Currently, there is only one transport instance (for only one device connected via USB).
43
30
  */
44
- static autoDisconnect() {
45
- return __awaiter(this, void 0, void 0, function* () {
46
- if (transportInstance && !transportInstance.preventAutoDisconnect) {
47
- log("hid-verbose", "triggering auto disconnect");
31
+ static setDisconnectAfterInactivityTimeout() {
32
+ TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
33
+ TransportNodeHidSingleton.disconnectAfterInactivityTimeout = setTimeout(() => {
34
+ trace({
35
+ type: LOG_TYPE,
36
+ message: "Disconnecting after inactivity, if not prevented",
37
+ data: {
38
+ hasInstance: Boolean(transportInstance),
39
+ },
40
+ });
41
+ if (transportInstance) {
48
42
  TransportNodeHidSingleton.disconnect();
49
43
  }
50
- else if (transportInstance) {
51
- // If we have disabled the auto-disconnect, try again in DISCONNECT_TIMEOUT
52
- clearDisconnectTimeout();
53
- setDisconnectTimeout();
54
- }
55
- });
44
+ }, DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS);
56
45
  }
57
46
  /**
58
- * globally disconnect the transport singleton
47
+ * Disconnects from the HID device associated to the transport singleton.
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.
59
51
  */
60
52
  static disconnect() {
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();
53
+ trace({
54
+ type: LOG_TYPE,
55
+ message: "Disconnecting from HID device",
56
+ data: {
57
+ hasInstance: Boolean(transportInstance),
58
+ },
68
59
  });
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
+ }
69
70
  }
70
71
  /**
71
72
  * Connects to the first Ledger device connected via USB
@@ -77,61 +78,79 @@ class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
77
78
  */
78
79
  static open(_descriptor, _timeoutMs, context) {
79
80
  const tracer = new LocalTracer(LOG_TYPE, context);
80
- clearDisconnectTimeout();
81
- return Promise.resolve().then(() => {
81
+ TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
82
+ if (transportInstance) {
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
82
108
  if (transportInstance) {
83
- tracer.trace("Reusing already opened transport instance");
84
- return transportInstance;
109
+ tracer.trace("Listened to on remove device event. Emitting a disconnect");
110
+ transportInstance.emit("disconnect");
85
111
  }
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;
109
112
  });
110
- }
111
- setAllowAutoDisconnect(allow) {
112
- this.preventAutoDisconnect = !allow;
113
+ /**
114
+ * Disconnect event received from the transport instance.
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);
113
132
  }
114
133
  /**
115
- * Exchange with the device using APDU protocol.
134
+ * Exchanges with the device using APDU protocol
116
135
  *
117
136
  * @param apdu
118
137
  * @returns a promise of apdu response
119
138
  */
120
139
  exchange(apdu) {
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
- });
140
+ return super.exchange(apdu);
130
141
  }
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
+ */
131
148
  close() {
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;
149
+ this.tracer.trace("Closing transport instance by triggering a disconnection after some inactivity", {
150
+ hasInstance: !!transportInstance,
151
+ disconnectAfterInactivityTimeoutMs: DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS,
152
+ });
153
+ TransportNodeHidSingleton.setDisconnectAfterInactivityTimeout();
135
154
  return Promise.resolve();
136
155
  }
137
156
  }
@@ -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,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,QAAQ,GAAG,aAAa,CAAC;AAE/B,IAAI,iBAAiB,GAAqC,IAAI,CAAC;AAE/D,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,IAAI,iBAAiB,CAAC;AACtB,MAAM,sBAAsB,GAAG,GAAG,EAAE;IAClC,IAAI,iBAAiB,EAAE;QACrB,YAAY,CAAC,iBAAiB,CAAC,CAAC;KACjC;AACH,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,GAAG,EAAE;IAChC,sBAAsB,EAAE,CAAC;IACzB,iBAAiB,GAAG,UAAU,CAC5B,GAAG,EAAE,CAAC,yBAAyB,CAAC,cAAc,EAAE,EAChD,kBAAkB,CACnB,CAAC;AACJ,CAAC,CAAC;AAIF;;;;;;GAMG;AACH,MAAqB,yBAA0B,SAAQ,wBAAwB;IAG7E,YAAY,MAAe,EAAE,EAAE,OAAO,KAAiC,EAAE;QACvE,KAAK,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QAHhD,0BAAqB,GAAG,KAAK,CAAC;IAI9B,CAAC;IAqED;;OAEG;IACH,MAAM,CAAO,cAAc;;YACzB,IAAI,iBAAiB,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,EAAE;gBACjE,GAAG,CAAC,aAAa,EAAE,4BAA4B,CAAC,CAAC;gBACjD,yBAAyB,CAAC,UAAU,EAAE,CAAC;aACxC;iBAAM,IAAI,iBAAiB,EAAE;gBAC5B,2EAA2E;gBAC3E,sBAAsB,EAAE,CAAC;gBACzB,oBAAoB,EAAE,CAAC;aACxB;QACH,CAAC;KAAA;IAED;;OAEG;IACH,MAAM,CAAO,UAAU;;YACrB,IAAI,iBAAiB,EAAE;gBACrB,iBAAiB,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjC,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACrC,iBAAiB,GAAG,IAAI,CAAC;aAC1B;YACD,sBAAsB,EAAE,CAAC;QAC3B,CAAC;KAAA;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CACT,WAAmB,EACnB,UAAmB,EACnB,OAAsB;QAEtB,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,sBAAsB,EAAE,CAAC;QAEzB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YACjC,IAAI,iBAAiB,EAAE;gBACrB,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;gBAC1D,OAAO,iBAAiB,CAAC;aAC1B;YAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEzD,MAAM,CAAC,KAAK,CAAC,qDAAqD,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAChF,iBAAiB,GAAG,IAAI,yBAAyB,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAc,CAAC,EAAE;gBACpF,OAAO;aACR,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,aAAa,CAC5B,GAAG,EAAE,GAAE,CAAC,EACR,GAAG,EAAE;gBACH,8DAA8D;gBAC9D,IAAI,iBAAiB,EAAE;oBACrB,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;iBACtC;YACH,CAAC,CACF,CAAC;YAEF,MAAM,YAAY,GAAG,GAAG,EAAE;gBACxB,IAAI,CAAC,iBAAiB;oBAAE,OAAO;gBAC/B,MAAM,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;gBACzE,iBAAiB,CAAC,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;gBAClD,iBAAiB,GAAG,IAAI,CAAC;gBACzB,QAAQ,EAAE,CAAC;YACb,CAAC,CAAC;YAEF,iBAAiB,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;YACjD,OAAO,iBAAiB,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,sBAAsB,CAAC,KAAc;QACnC,IAAI,CAAC,qBAAqB,GAAG,CAAC,KAAK,CAAC;IACtC,CAAC;IAED;;;;;OAKG;IACG,QAAQ,CAAC,IAAY;;;;;YACzB,sBAAsB,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,MAAM,OAAM,QAAQ,YAAC,IAAI,CAAC,CAAC;YAC1C,oBAAoB,EAAE,CAAC;YACvB,OAAO,MAAM,CAAC;QAChB,CAAC;KAAA;IAED,KAAK;QACH,2EAA2E;QAC3E,yDAAyD;QACzD,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC;QACnC,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;;AAxKD;;GAEG;AACI,qCAAW,GAAG,wBAAwB,CAAC,WAAW,AAAvC,CAAwC;AAE1D;;GAEG;AACI,8BAAI,GAAG,wBAAwB,CAAC,IAAI,AAAhC,CAAiC;AAE5C;GACG;AACI,gCAAM,GAAG,CAAC,QAAyC,EAAgB,EAAE;IAC1E,IAAI,YAAqB,CAAC;IAC1B,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;QAC3C,oFAAoF;QACpF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;YAC5B,IAAI,CAAC,YAAY,EAAE;gBACjB,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC3D,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,KAAK;oBACX,UAAU,EAAE,EAAE;oBACd,MAAM,EAAE;wBACN,IAAI,EAAE,MAAM,CAAC,UAAU;qBACxB;oBACD,WAAW;iBACZ,CAAC,CAAC;aACJ;SACF;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE;QACrB,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,KAAK;YACX,UAAU,EAAE,EAAE;YACd,WAAW;YACX,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC,UAAU;aACxB;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE;QACxB,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE;YACd,WAAW;YACX,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC,UAAU;aACxB;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE5C,SAAS,WAAW;QAClB,IAAI,EAAE,CAAC;QACP,YAAY,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,OAAO;QACL,WAAW;KACZ,CAAC;AACJ,CAAC,AArDY,CAqDX;eAxEiB,yBAAyB"}
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.3-next.0",
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",
34
33
  "@ledgerhq/errors": "^6.16.1",
35
- "@ledgerhq/hw-transport-node-hid-noevents": "^6.29.2",
34
+ "@ledgerhq/hw-transport": "^6.30.3-next.0",
35
+ "@ledgerhq/hw-transport-node-hid-noevents": "^6.29.3-next.0",
36
36
  "@ledgerhq/logs": "^6.12.0"
37
37
  },
38
38
  "gitHead": "dd0dea64b58e5a9125c8a422dcffd29e5ef6abec",
@@ -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, log } from "@ledgerhq/logs";
4
+ import { LocalTracer, TraceContext, trace } from "@ledgerhq/logs";
5
5
  import { identifyUSBProductId } from "@ledgerhq/devices";
6
6
  import { CantOpenDevice } from "@ledgerhq/errors";
7
7
  import { listenDevices } from "./listenDevices";
@@ -10,23 +10,9 @@ const LOG_TYPE = "hid-verbose";
10
10
 
11
11
  let transportInstance: TransportNodeHidSingleton | null = null;
12
12
 
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
- };
13
+ const DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS = 5000;
28
14
 
29
- export type ListenDescriptorEvent = DescriptorEvent<any>;
15
+ export type ListenDescriptorEvent = DescriptorEvent<string>;
30
16
 
31
17
  /**
32
18
  * node-hid Transport implementation
@@ -36,8 +22,6 @@ export type ListenDescriptorEvent = DescriptorEvent<any>;
36
22
  * TransportNodeHid.create().then(transport => ...)
37
23
  */
38
24
  export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
39
- preventAutoDisconnect = false;
40
-
41
25
  constructor(device: HID.HID, { context }: { context?: TraceContext } = {}) {
42
26
  super(device, { context, logType: LOG_TYPE });
43
27
  }
@@ -109,30 +93,61 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
109
93
  };
110
94
  };
111
95
 
96
+ static disconnectAfterInactivityTimeout: ReturnType<typeof setTimeout> | undefined;
97
+ static clearDisconnectAfterInactivityTimeout() {
98
+ if (TransportNodeHidSingleton.disconnectAfterInactivityTimeout) {
99
+ clearTimeout(TransportNodeHidSingleton.disconnectAfterInactivityTimeout);
100
+ }
101
+ }
102
+
112
103
  /**
113
- * convenience wrapper for auto-disconnect logic
104
+ * Disconnects device from singleton instance after some inactivity (no new `open`).
105
+ *
106
+ * Currently, there is only one transport instance (for only one device connected via USB).
114
107
  */
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
- }
108
+ static setDisconnectAfterInactivityTimeout() {
109
+ TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
110
+ TransportNodeHidSingleton.disconnectAfterInactivityTimeout = setTimeout(() => {
111
+ trace({
112
+ type: LOG_TYPE,
113
+ message: "Disconnecting after inactivity, if not prevented",
114
+ data: {
115
+ hasInstance: Boolean(transportInstance),
116
+ },
117
+ });
118
+
119
+ if (transportInstance) {
120
+ TransportNodeHidSingleton.disconnect();
121
+ }
122
+ }, DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS);
124
123
  }
125
124
 
126
125
  /**
127
- * globally disconnect the transport singleton
126
+ * Disconnects from the HID device associated to the transport singleton.
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.
128
130
  */
129
- static async disconnect() {
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
+
130
142
  if (transportInstance) {
131
143
  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
+ });
132
148
  transportInstance.emit("disconnect");
133
149
  transportInstance = null;
134
150
  }
135
- clearDisconnectTimeout();
136
151
  }
137
152
 
138
153
  /**
@@ -149,66 +164,97 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
149
164
  context?: TraceContext,
150
165
  ): Promise<TransportNodeHidSingleton> {
151
166
  const tracer = new LocalTracer(LOG_TYPE, context);
152
- clearDisconnectTimeout();
167
+ TransportNodeHidSingleton.clearDisconnectAfterInactivityTimeout();
153
168
 
154
- return Promise.resolve().then(() => {
155
- if (transportInstance) {
156
- tracer.trace("Reusing already opened transport instance");
157
- return transportInstance;
158
- }
169
+ if (transportInstance) {
170
+ tracer.trace("Reusing already opened transport instance");
171
+ return Promise.resolve(transportInstance);
172
+ }
159
173
 
160
- const device = getDevices()[0];
161
- if (!device) throw new CantOpenDevice("no device found");
174
+ const devicesDetectedDuringOpen = getDevices();
175
+ tracer.trace(`Devices detected during open: ${devicesDetectedDuringOpen.length}`, {
176
+ devicesDetectedDuringOpen,
177
+ });
162
178
 
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
- });
179
+ if (devicesDetectedDuringOpen.length === 0) {
180
+ return Promise.reject(new CantOpenDevice("No device found"));
181
+ }
182
+ const device = devicesDetectedDuringOpen[0];
167
183
 
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;
184
+ tracer.trace("Found a device, creating HID transport instance ...", { device });
185
+
186
+ let HIDDevice: HID | undefined;
187
+ try {
188
+ HIDDevice = new HID.HID(device.path as string);
189
+ } catch (error) {
190
+ tracer.trace(`Error while connecting to device: ${error}`, { error });
191
+ return Promise.reject(error);
192
+ }
193
+
194
+ transportInstance = new TransportNodeHidSingleton(HIDDevice, {
195
+ context,
188
196
  });
189
- }
190
197
 
191
- setAllowAutoDisconnect(allow: boolean): void {
192
- this.preventAutoDisconnect = !allow;
198
+ const clearDeviceEventsListener = listenDevices(
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);
193
229
  }
194
230
 
195
231
  /**
196
- * Exchange with the device using APDU protocol.
232
+ * Exchanges with the device using APDU protocol
197
233
  *
198
234
  * @param apdu
199
235
  * @returns a promise of apdu response
200
236
  */
201
- async exchange(apdu: Buffer): Promise<Buffer> {
202
- clearDisconnectTimeout();
203
- const result = await super.exchange(apdu);
204
- setDisconnectTimeout();
205
- return result;
237
+ exchange(apdu: Buffer): Promise<Buffer> {
238
+ return super.exchange(apdu);
206
239
  }
207
240
 
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
+ */
208
247
  close(): Promise<void> {
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;
248
+ this.tracer.trace(
249
+ "Closing transport instance by triggering a disconnection after some inactivity",
250
+ {
251
+ hasInstance: !!transportInstance,
252
+ disconnectAfterInactivityTimeoutMs: DISCONNECT_AFTER_INACTIVITY_TIMEOUT_MS,
253
+ },
254
+ );
255
+
256
+ TransportNodeHidSingleton.setDisconnectAfterInactivityTimeout();
257
+
212
258
  return Promise.resolve();
213
259
  }
214
260
  }