@ledgerhq/device-transport-kit-node-hid 0.0.0-develop-20260212001325
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/LICENSE.MD +202 -0
- package/README.md +74 -0
- package/lib/cjs/api/data/NodeHidConfig.js +2 -0
- package/lib/cjs/api/data/NodeHidConfig.js.map +7 -0
- package/lib/cjs/api/index.js +2 -0
- package/lib/cjs/api/index.js.map +7 -0
- package/lib/cjs/api/model/Errors.js +2 -0
- package/lib/cjs/api/model/Errors.js.map +7 -0
- package/lib/cjs/api/model/HIDDevice.stub.js +2 -0
- package/lib/cjs/api/model/HIDDevice.stub.js.map +7 -0
- package/lib/cjs/api/transport/NodeHidApduSender.js +2 -0
- package/lib/cjs/api/transport/NodeHidApduSender.js.map +7 -0
- package/lib/cjs/api/transport/NodeHidApduSender.test.js +2 -0
- package/lib/cjs/api/transport/NodeHidApduSender.test.js.map +7 -0
- package/lib/cjs/api/transport/NodeHidTransport.js +2 -0
- package/lib/cjs/api/transport/NodeHidTransport.js.map +7 -0
- package/lib/cjs/api/transport/NodeHidTransport.test.js +2 -0
- package/lib/cjs/api/transport/NodeHidTransport.test.js.map +7 -0
- package/lib/cjs/index.js +2 -0
- package/lib/cjs/index.js.map +7 -0
- package/lib/cjs/package.json +59 -0
- package/lib/types/api/data/NodeHidConfig.d.ts +3 -0
- package/lib/types/api/data/NodeHidConfig.d.ts.map +1 -0
- package/lib/types/api/index.d.ts +3 -0
- package/lib/types/api/index.d.ts.map +1 -0
- package/lib/types/api/model/Errors.d.ts +12 -0
- package/lib/types/api/model/Errors.d.ts.map +1 -0
- package/lib/types/api/model/HIDDevice.stub.d.ts +3 -0
- package/lib/types/api/model/HIDDevice.stub.d.ts.map +1 -0
- package/lib/types/api/transport/NodeHidApduSender.d.ts +31 -0
- package/lib/types/api/transport/NodeHidApduSender.d.ts.map +1 -0
- package/lib/types/api/transport/NodeHidApduSender.test.d.ts +2 -0
- package/lib/types/api/transport/NodeHidApduSender.test.d.ts.map +1 -0
- package/lib/types/api/transport/NodeHidTransport.d.ts +79 -0
- package/lib/types/api/transport/NodeHidTransport.d.ts.map +1 -0
- package/lib/types/api/transport/NodeHidTransport.test.d.ts +2 -0
- package/lib/types/api/transport/NodeHidTransport.test.d.ts.map +1 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -0
- package/package.json +58 -0
package/LICENSE.MD
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2024-present Ledger
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Transport Device Kit Node HID
|
|
2
|
+
|
|
3
|
+
> [!CAUTION]
|
|
4
|
+
> This is still under development and we are free to make new interfaces which may lead to breaking changes.
|
|
5
|
+
|
|
6
|
+
- [Transport Device Kit Node HID Documentation](#transport-device-kit-node-hid)
|
|
7
|
+
- [Description](#description)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Usage](#usage)
|
|
10
|
+
- [Compatibility](#compatibility)
|
|
11
|
+
- [Pre-requisites](#pre-requisites)
|
|
12
|
+
- [Main Features](#main-features)
|
|
13
|
+
- [How To](#how-to)
|
|
14
|
+
|
|
15
|
+
## Description
|
|
16
|
+
|
|
17
|
+
This transport is used to interact with a Ledger device through the Node HID (USB) implementation by the Device Management Kit.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
To install the core package, run the following command:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install @ledgerhq/device-transport-kit-node-hid
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Compatibility
|
|
30
|
+
|
|
31
|
+
This library has been tested successfully on Node v20
|
|
32
|
+
|
|
33
|
+
### Pre-requisites
|
|
34
|
+
|
|
35
|
+
To use this transport, ensure you have the Device Management Kit installed in your project.
|
|
36
|
+
|
|
37
|
+
### Main Features
|
|
38
|
+
|
|
39
|
+
- Exposing a transport factory to be injected into the DeviceManagementKit
|
|
40
|
+
- Exposing the transport directly for a custom configuration
|
|
41
|
+
|
|
42
|
+
### How To
|
|
43
|
+
|
|
44
|
+
To use the transport, you need to inject it in the DeviceManagementKitBuilder before the build. This will allow the Device Management Kit to find and interact with devices with Node HID protocol.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { DeviceManagementKitBuilder } from "@ledgerhq/device-management-kit"
|
|
48
|
+
import { nodeHidTransportFactory, NodeHidTransport } from "@ledgerhq/device-transport-kit-node-hid"
|
|
49
|
+
|
|
50
|
+
// Easy setup with the factory
|
|
51
|
+
const dmk = new DeviceManagementKitBuilder()
|
|
52
|
+
.addTransport(nodeHidTransportFactory)
|
|
53
|
+
.build();
|
|
54
|
+
|
|
55
|
+
// With custom config
|
|
56
|
+
const dmk = new DeviceManagementKitBuilder()
|
|
57
|
+
.addTransport(({
|
|
58
|
+
deviceModelDataSource: DeviceModelDataSource;
|
|
59
|
+
loggerServiceFactory: (tag: string) => LoggerPublisherService;
|
|
60
|
+
apduSenderServiceFactory: ApduSenderServiceFactory;
|
|
61
|
+
apduReceiverServiceFactory: ApduReceiverServiceFactory;
|
|
62
|
+
}) => {
|
|
63
|
+
// custom code
|
|
64
|
+
return new NodeHidTransport(
|
|
65
|
+
deviceModelDataSource,
|
|
66
|
+
loggerServiceFactory,
|
|
67
|
+
apduSenderServiceFactory,
|
|
68
|
+
apduReceiverServiceFactory,
|
|
69
|
+
);
|
|
70
|
+
})
|
|
71
|
+
.build();
|
|
72
|
+
|
|
73
|
+
// You can then make use of the Device Management Kit
|
|
74
|
+
```
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var I=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var e=Object.prototype.hasOwnProperty;var n=(o,E)=>{for(var C in E)I(o,C,{get:E[C],enumerable:!0})},p=(o,E,C,T)=>{if(E&&typeof E=="object"||typeof E=="function")for(let t of c(E))!e.call(o,t)&&t!==C&&I(o,t,{get:()=>E[t],enumerable:!(T=_(E,t))||T.enumerable});return o};var r=o=>p(I({},"__esModule",{value:!0}),o);var M={};n(M,{FRAME_SIZE:()=>s,RECONNECT_DEVICE_TIMEOUT:()=>x});module.exports=r(M);const s=64,x=6e3;0&&(module.exports={FRAME_SIZE,RECONNECT_DEVICE_TIMEOUT});
|
|
2
|
+
//# sourceMappingURL=NodeHidConfig.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/api/data/NodeHidConfig.ts"],
|
|
4
|
+
"sourcesContent": ["export const FRAME_SIZE = 64;\nexport const RECONNECT_DEVICE_TIMEOUT = 6000; // in some cases, when opening/closing an app, it takes up to 6s between the HID \"disconnect\" and \"connect\" events\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gBAAAE,EAAA,6BAAAC,IAAA,eAAAC,EAAAJ,GAAO,MAAME,EAAa,GACbC,EAA2B",
|
|
6
|
+
"names": ["NodeHidConfig_exports", "__export", "FRAME_SIZE", "RECONNECT_DEVICE_TIMEOUT", "__toCommonJS"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var a=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var x=(r,o)=>{for(var d in o)a(r,d,{get:o[d],enumerable:!0})},p=(r,o,d,f)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of m(o))!s.call(r,t)&&t!==d&&a(r,t,{get:()=>o[t],enumerable:!(f=H(o,t))||f.enumerable});return r},n=(r,o,d)=>(p(r,o,"default"),d&&p(d,o,"default"));var T=r=>p(a({},"__esModule",{value:!0}),r);var i={};x(i,{NodeHidTransport:()=>e.NodeHidTransport,nodeHidIdentifier:()=>e.nodeHidIdentifier,nodeHidTransportFactory:()=>e.nodeHidTransportFactory});module.exports=T(i);n(i,require("./model/Errors"),module.exports);var e=require("./transport/NodeHidTransport");0&&(module.exports={NodeHidTransport,nodeHidIdentifier,nodeHidTransportFactory,...require("./model/Errors")});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/api/index.ts"],
|
|
4
|
+
"sourcesContent": ["export * from \"@api/model/Errors\";\nexport {\n nodeHidIdentifier,\n NodeHidTransport,\n nodeHidTransportFactory,\n} from \"@api/transport/NodeHidTransport\";\n"],
|
|
5
|
+
"mappings": "2dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,0JAAAE,EAAAF,GAAAG,EAAAH,EAAc,6BAAd,gBACA,IAAAI,EAIO",
|
|
6
|
+
"names": ["api_exports", "__export", "__toCommonJS", "__reExport", "import_NodeHidTransport"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var n=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var i=(o,r)=>{for(var e in r)n(o,e,{get:r[e],enumerable:!0})},l=(o,r,e,p)=>{if(r&&typeof r=="object"||typeof r=="function")for(let d of a(r))!u.call(o,d)&&d!==e&&n(o,d,{get:()=>r[d],enumerable:!(p=s(r,d))||p.enumerable});return o};var c=o=>l(n({},"__esModule",{value:!0}),o);var x={};i(x,{NodeHidSendReportError:()=>E,NodeHidTransportNotSupportedError:()=>N});module.exports=c(x);var t=require("@ledgerhq/device-management-kit");class N extends t.GeneralDmkError{constructor(e){super(e);this.err=e}_tag="NodeHidTransportNotSupportedError"}class E extends t.GeneralDmkError{constructor(e){super(e);this.err=e}_tag="NodeHidSendReportError"}0&&(module.exports={NodeHidSendReportError,NodeHidTransportNotSupportedError});
|
|
2
|
+
//# sourceMappingURL=Errors.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/api/model/Errors.ts"],
|
|
4
|
+
"sourcesContent": ["import { GeneralDmkError } from \"@ledgerhq/device-management-kit\";\n\nexport class NodeHidTransportNotSupportedError extends GeneralDmkError {\n override readonly _tag = \"NodeHidTransportNotSupportedError\";\n constructor(readonly err?: unknown) {\n super(err);\n }\n}\nexport class NodeHidSendReportError extends GeneralDmkError {\n override readonly _tag = \"NodeHidSendReportError\";\n constructor(readonly err?: unknown) {\n super(err);\n }\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,4BAAAE,EAAA,sCAAAC,IAAA,eAAAC,EAAAJ,GAAA,IAAAK,EAAgC,2CAEzB,MAAMF,UAA0C,iBAAgB,CAErE,YAAqBG,EAAe,CAClC,MAAMA,CAAG,EADU,SAAAA,CAErB,CAHkB,KAAO,mCAI3B,CACO,MAAMJ,UAA+B,iBAAgB,CAE1D,YAAqBI,EAAe,CAClC,MAAMA,CAAG,EADU,SAAAA,CAErB,CAHkB,KAAO,wBAI3B",
|
|
6
|
+
"names": ["Errors_exports", "__export", "NodeHidSendReportError", "NodeHidTransportNotSupportedError", "__toCommonJS", "import_device_management_kit", "err"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var o=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var t=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var D=(e,d)=>{for(var a in d)o(e,a,{get:d[a],enumerable:!0})},n=(e,d,a,i)=>{if(d&&typeof d=="object"||typeof d=="function")for(let r of t(d))!u.call(e,r)&&r!==a&&o(e,r,{get:()=>d[r],enumerable:!(i=c(d,r))||i.enumerable});return e};var v=e=>n(o({},"__esModule",{value:!0}),e);var x={};D(x,{nodeHidDeviceStubBuilder:()=>p});module.exports=v(x);const p=(e={})=>({vendorId:11415,productId:16401,path:"/dev/hidraw0",manufacturer:"Ledger",product:"Ledger Nano X",release:256,interface:0,usagePage:65440,usage:1,...e});0&&(module.exports={nodeHidDeviceStubBuilder});
|
|
2
|
+
//# sourceMappingURL=HIDDevice.stub.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/api/model/HIDDevice.stub.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Device as NodeHIDDevice } from \"node-hid\";\n\nexport const nodeHidDeviceStubBuilder = (\n props: Partial<NodeHIDDevice> = {},\n): NodeHIDDevice => ({\n vendorId: 0x2c97,\n productId: 0x4011,\n path: \"/dev/hidraw0\",\n manufacturer: \"Ledger\",\n product: \"Ledger Nano X\",\n release: 0x0100,\n interface: 0,\n usagePage: 0xffa0,\n usage: 0x0001,\n ...props,\n});\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,8BAAAE,IAAA,eAAAC,EAAAH,GAEO,MAAME,EAA2B,CACtCE,EAAgC,CAAC,KACd,CACnB,SAAU,MACV,UAAW,MACX,KAAM,eACN,aAAc,SACd,QAAS,gBACT,QAAS,IACT,UAAW,EACX,UAAW,MACX,MAAO,EACP,GAAGA,CACL",
|
|
6
|
+
"names": ["HIDDevice_stub_exports", "__export", "nodeHidDeviceStubBuilder", "__toCommonJS", "props"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var a=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var l=(n,e)=>{for(var i in e)a(n,i,{get:e[i],enumerable:!0})},y=(n,e,i,d)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of A(e))!m.call(n,t)&&t!==i&&a(n,t,{get:()=>e[t],enumerable:!(d=g(e,t))||d.enumerable});return n};var S=n=>y(a({},"__esModule",{value:!0}),n);var R={};l(R,{NodeHidApduSender:()=>f});module.exports=S(R);var o=require("@ledgerhq/device-management-kit"),h=require("node-hid"),r=require("purify-ts"),v=require("../data/NodeHidConfig"),u=require("../model/Errors");class f{dependencies;apduSenderFactory;apduSender;apduReceiverFactory;apduReceiver;logger;hidAsync;sendApduPromiseResolver;abortTimeout;constructor({dependencies:e,apduSenderFactory:i,apduReceiverFactory:d,loggerFactory:t}){const s=r.Maybe.of(o.FramerUtils.numberToByteArray(Math.floor(Math.random()*65535),2));this.dependencies=e,this.apduSenderFactory=i,this.apduSender=this.apduSenderFactory({frameSize:v.FRAME_SIZE,channel:s,padding:!0}),this.apduReceiverFactory=d,this.apduReceiver=this.apduReceiverFactory({channel:s}),this.logger=t("NodeHidApduSender"),this.hidAsync=r.Nothing,this.sendApduPromiseResolver=r.Nothing,this.abortTimeout=r.Nothing}async sendApdu(e,i,d){return this.hidAsync.caseOf({Just:async t=>{const s=new Promise(p=>{this.sendApduPromiseResolver=(0,r.Just)(p)});for(const p of this.apduSender.getFrames(e))try{const c=Buffer.from([0].concat([...p.getRawData()]));await t.write(c)}catch(c){return this.logger.info("Error sending frame",{data:{error:c}}),Promise.resolve((0,r.Left)(new u.NodeHidSendReportError(c)))}return this.logger.debug((0,o.formatApduSentLog)(e)),d&&(this.abortTimeout=(0,r.Just)(setTimeout(()=>{this.logger.debug("[sendApdu] Abort timeout",{data:{abortTimeout:d}}),this.resolvePendingApdu((0,r.Left)(new o.SendApduTimeoutError("Abort timeout")))},d))),s},Nothing:()=>Promise.resolve((0,r.Left)(new o.OpeningConnectionError("Device not connected")))})}getDependencies(){return this.dependencies}setDependencies(e){this.dependencies=e}async setupConnection(){if(await this.hidAsync.caseOf({Just:async e=>{try{await e.close(),this.hidAsync=r.Nothing,this.sendApduPromiseResolver=r.Nothing,this.abortTimeout=r.Nothing}catch(i){throw this.logger.error("Error while closing device",{data:{device:this.dependencies.device,error:i}}),i}},Nothing:()=>Promise.resolve()}),this.dependencies.device.path===void 0)throw new Error("Missing device path");return await new Promise(e=>setTimeout(e,300)),this.hidAsync=r.Maybe.of(await h.HIDAsync.open(this.dependencies.device.path,{nonExclusive:!0})),this.hidAsync.caseOf({Just:e=>{e.on("data",i=>this.receiveHidInputReport(i)),e.on("error",i=>{this.logger.error("Error while receiving data",{data:{error:i}}),this.resolvePendingApdu((0,r.Left)(new u.NodeHidSendReportError(i)))}),this.logger.info("\u{1F50C} Connected to device")},Nothing:()=>{const e=new Error("Error while opening device");throw this.logger.error("Error while opening device",{data:{error:e}}),e}})}async closeConnection(){return this.hidAsync.caseOf({Just:async e=>{try{await e.close(),this.hidAsync=r.Nothing,this.sendApduPromiseResolver=r.Nothing,this.abortTimeout=r.Nothing,this.logger.info("\u{1F51A} Disconnect")}catch(i){throw this.logger.error("Error while closing device",{data:{device:this.dependencies.device,error:i}}),i}},Nothing:()=>{}})}receiveHidInputReport(e){const i=new Uint8Array(e);this.apduReceiver.handleFrame(i).map(t=>{t.map(s=>{this.logger.debug((0,o.formatApduReceivedLog)(s)),this.resolvePendingApdu((0,r.Right)(s))})}).mapLeft(t=>{this.resolvePendingApdu((0,r.Left)(t))})}resolvePendingApdu(e){this.abortTimeout.map(i=>{this.abortTimeout=r.Nothing,clearTimeout(i)}),this.sendApduPromiseResolver.map(i=>{this.sendApduPromiseResolver=r.Nothing,i(e)})}}0&&(module.exports={NodeHidApduSender});
|
|
2
|
+
//# sourceMappingURL=NodeHidApduSender.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/api/transport/NodeHidApduSender.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n type ApduReceiverService,\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderService,\n type ApduSenderServiceFactory,\n type DeviceApduSender,\n type DmkError,\n formatApduReceivedLog,\n formatApduSentLog,\n FramerUtils,\n type LoggerPublisherService,\n OpeningConnectionError,\n type SendApduResult,\n SendApduTimeoutError,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Device as NodeHIDDevice, HIDAsync } from \"node-hid\";\nimport { type Either, Just, Left, Maybe, Nothing, Right } from \"purify-ts\";\n\nimport { FRAME_SIZE } from \"@api/data/NodeHidConfig\";\nimport { NodeHidSendReportError } from \"@api/model/Errors\";\n\nexport type NodeHidApduSenderDependencies = {\n device: NodeHIDDevice;\n};\n\nexport type NodeHidApduSenderConstructorArgs = {\n dependencies: NodeHidApduSenderDependencies;\n apduSenderFactory: ApduSenderServiceFactory;\n apduReceiverFactory: ApduReceiverServiceFactory;\n loggerFactory: (tag: string) => LoggerPublisherService;\n};\n\nexport class NodeHidApduSender\n implements DeviceApduSender<NodeHidApduSenderDependencies>\n{\n private dependencies: NodeHidApduSenderDependencies;\n private readonly apduSenderFactory: ApduSenderServiceFactory;\n private apduSender: ApduSenderService;\n private readonly apduReceiverFactory: ApduReceiverServiceFactory;\n private apduReceiver: ApduReceiverService;\n private readonly logger: LoggerPublisherService;\n private hidAsync: Maybe<HIDAsync>;\n private sendApduPromiseResolver: Maybe<\n (value: Either<DmkError, ApduResponse>) => void\n >;\n private abortTimeout: Maybe<ReturnType<typeof setTimeout>>;\n\n constructor({\n dependencies,\n apduSenderFactory,\n apduReceiverFactory,\n loggerFactory,\n }: NodeHidApduSenderConstructorArgs) {\n const channel = Maybe.of(\n FramerUtils.numberToByteArray(Math.floor(Math.random() * 0xffff), 2),\n );\n this.dependencies = dependencies;\n this.apduSenderFactory = apduSenderFactory;\n this.apduSender = this.apduSenderFactory({\n frameSize: FRAME_SIZE,\n channel,\n padding: true,\n });\n this.apduReceiverFactory = apduReceiverFactory;\n this.apduReceiver = this.apduReceiverFactory({ channel });\n this.logger = loggerFactory(\"NodeHidApduSender\");\n this.hidAsync = Nothing;\n this.sendApduPromiseResolver = Nothing;\n this.abortTimeout = Nothing;\n }\n\n public async sendApdu(\n apdu: Uint8Array,\n _triggersDisconnection?: boolean,\n abortTimeout?: number,\n ): Promise<SendApduResult> {\n return this.hidAsync.caseOf({\n Just: async (hidAsync) => {\n const resultPromise = new Promise<Either<DmkError, ApduResponse>>(\n (resolve) => {\n this.sendApduPromiseResolver = Just(resolve);\n },\n );\n\n for (const frame of this.apduSender.getFrames(apdu)) {\n try {\n const report = Buffer.from([0x00].concat([...frame.getRawData()]));\n await hidAsync.write(report);\n } catch (error) {\n this.logger.info(\"Error sending frame\", { data: { error } });\n return Promise.resolve(Left(new NodeHidSendReportError(error)));\n }\n }\n\n this.logger.debug(formatApduSentLog(apdu));\n\n if (abortTimeout) {\n this.abortTimeout = Just(\n setTimeout(() => {\n this.logger.debug(\"[sendApdu] Abort timeout\", {\n data: { abortTimeout },\n });\n this.resolvePendingApdu(\n Left(new SendApduTimeoutError(\"Abort timeout\")),\n );\n }, abortTimeout),\n );\n }\n\n return resultPromise;\n },\n Nothing: () => {\n return Promise.resolve(\n Left(new OpeningConnectionError(\"Device not connected\")),\n );\n },\n });\n }\n\n public getDependencies(): NodeHidApduSenderDependencies {\n return this.dependencies;\n }\n\n public setDependencies(dependencies: NodeHidApduSenderDependencies): void {\n this.dependencies = dependencies;\n }\n\n public async setupConnection(): Promise<void> {\n await this.hidAsync.caseOf({\n Just: async (hidAsync) => {\n try {\n await hidAsync.close();\n this.hidAsync = Nothing;\n this.sendApduPromiseResolver = Nothing;\n this.abortTimeout = Nothing;\n } catch (error) {\n this.logger.error(\"Error while closing device\", {\n data: { device: this.dependencies.device, error },\n });\n throw error;\n }\n },\n Nothing: () => Promise.resolve(),\n });\n\n if (undefined === this.dependencies.device.path) {\n throw new Error(\"Missing device path\");\n }\n\n await new Promise((resolve) => setTimeout(resolve, 300)); //make sure the device is opened on the OS level\n\n this.hidAsync = Maybe.of(\n await HIDAsync.open(this.dependencies.device.path, {\n nonExclusive: true,\n }),\n );\n\n return this.hidAsync.caseOf({\n Just: (hidAsync) => {\n hidAsync.on(\"data\", (data: Buffer) => this.receiveHidInputReport(data));\n hidAsync.on(\"error\", (error) => {\n this.logger.error(\"Error while receiving data\", { data: { error } });\n this.resolvePendingApdu(Left(new NodeHidSendReportError(error)));\n });\n this.logger.info(\"\uD83D\uDD0C Connected to device\");\n },\n Nothing: () => {\n const error = new Error(\"Error while opening device\");\n this.logger.error(`Error while opening device`, { data: { error } });\n throw error;\n },\n });\n }\n\n public async closeConnection(): Promise<void> {\n return this.hidAsync.caseOf({\n Just: async (hidAsync) => {\n try {\n await hidAsync.close();\n this.hidAsync = Nothing;\n this.sendApduPromiseResolver = Nothing;\n this.abortTimeout = Nothing;\n this.logger.info(\"\uD83D\uDD1A Disconnect\");\n } catch (error) {\n this.logger.error(\"Error while closing device\", {\n data: { device: this.dependencies.device, error },\n });\n throw error;\n }\n },\n Nothing: () => {\n return;\n },\n });\n }\n\n private receiveHidInputReport(buffer: Buffer) {\n const data = new Uint8Array(buffer);\n const maybeApduResponse = this.apduReceiver.handleFrame(data);\n\n maybeApduResponse\n .map((response) => {\n response.map((apduResponse) => {\n this.logger.debug(formatApduReceivedLog(apduResponse));\n this.resolvePendingApdu(Right(apduResponse));\n });\n })\n .mapLeft((error) => {\n this.resolvePendingApdu(Left(error));\n });\n }\n\n private resolvePendingApdu(result: Either<DmkError, ApduResponse>): void {\n this.abortTimeout.map((timeout) => {\n this.abortTimeout = Nothing;\n clearTimeout(timeout);\n });\n this.sendApduPromiseResolver.map((resolve) => {\n this.sendApduPromiseResolver = Nothing;\n resolve(result);\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uBAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAeO,2CACPC,EAAuD,oBACvDC,EAA+D,qBAE/DC,EAA2B,mCAC3BC,EAAuC,6BAahC,MAAMN,CAEb,CACU,aACS,kBACT,WACS,oBACT,aACS,OACT,SACA,wBAGA,aAER,YAAY,CACV,aAAAO,EACA,kBAAAC,EACA,oBAAAC,EACA,cAAAC,CACF,EAAqC,CACnC,MAAMC,EAAU,QAAM,GACpB,cAAY,kBAAkB,KAAK,MAAM,KAAK,OAAO,EAAI,KAAM,EAAG,CAAC,CACrE,EACA,KAAK,aAAeJ,EACpB,KAAK,kBAAoBC,EACzB,KAAK,WAAa,KAAK,kBAAkB,CACvC,UAAW,aACX,QAAAG,EACA,QAAS,EACX,CAAC,EACD,KAAK,oBAAsBF,EAC3B,KAAK,aAAe,KAAK,oBAAoB,CAAE,QAAAE,CAAQ,CAAC,EACxD,KAAK,OAASD,EAAc,mBAAmB,EAC/C,KAAK,SAAW,UAChB,KAAK,wBAA0B,UAC/B,KAAK,aAAe,SACtB,CAEA,MAAa,SACXE,EACAC,EACAC,EACyB,CACzB,OAAO,KAAK,SAAS,OAAO,CAC1B,KAAM,MAAOC,GAAa,CACxB,MAAMC,EAAgB,IAAI,QACvBC,GAAY,CACX,KAAK,2BAA0B,QAAKA,CAAO,CAC7C,CACF,EAEA,UAAWC,KAAS,KAAK,WAAW,UAAUN,CAAI,EAChD,GAAI,CACF,MAAMO,EAAS,OAAO,KAAK,CAAC,CAAI,EAAE,OAAO,CAAC,GAAGD,EAAM,WAAW,CAAC,CAAC,CAAC,EACjE,MAAMH,EAAS,MAAMI,CAAM,CAC7B,OAASC,EAAO,CACd,YAAK,OAAO,KAAK,sBAAuB,CAAE,KAAM,CAAE,MAAAA,CAAM,CAAE,CAAC,EACpD,QAAQ,WAAQ,QAAK,IAAI,yBAAuBA,CAAK,CAAC,CAAC,CAChE,CAGF,YAAK,OAAO,SAAM,qBAAkBR,CAAI,CAAC,EAErCE,IACF,KAAK,gBAAe,QAClB,WAAW,IAAM,CACf,KAAK,OAAO,MAAM,2BAA4B,CAC5C,KAAM,CAAE,aAAAA,CAAa,CACvB,CAAC,EACD,KAAK,sBACH,QAAK,IAAI,uBAAqB,eAAe,CAAC,CAChD,CACF,EAAGA,CAAY,CACjB,GAGKE,CACT,EACA,QAAS,IACA,QAAQ,WACb,QAAK,IAAI,yBAAuB,sBAAsB,CAAC,CACzD,CAEJ,CAAC,CACH,CAEO,iBAAiD,CACtD,OAAO,KAAK,YACd,CAEO,gBAAgBT,EAAmD,CACxE,KAAK,aAAeA,CACtB,CAEA,MAAa,iBAAiC,CAkB5C,GAjBA,MAAM,KAAK,SAAS,OAAO,CACzB,KAAM,MAAOQ,GAAa,CACxB,GAAI,CACF,MAAMA,EAAS,MAAM,EACrB,KAAK,SAAW,UAChB,KAAK,wBAA0B,UAC/B,KAAK,aAAe,SACtB,OAASK,EAAO,CACd,WAAK,OAAO,MAAM,6BAA8B,CAC9C,KAAM,CAAE,OAAQ,KAAK,aAAa,OAAQ,MAAAA,CAAM,CAClD,CAAC,EACKA,CACR,CACF,EACA,QAAS,IAAM,QAAQ,QAAQ,CACjC,CAAC,EAEiB,KAAK,aAAa,OAAO,OAAvC,OACF,MAAM,IAAI,MAAM,qBAAqB,EAGvC,aAAM,IAAI,QAASH,GAAY,WAAWA,EAAS,GAAG,CAAC,EAEvD,KAAK,SAAW,QAAM,GACpB,MAAM,WAAS,KAAK,KAAK,aAAa,OAAO,KAAM,CACjD,aAAc,EAChB,CAAC,CACH,EAEO,KAAK,SAAS,OAAO,CAC1B,KAAOF,GAAa,CAClBA,EAAS,GAAG,OAASM,GAAiB,KAAK,sBAAsBA,CAAI,CAAC,EACtEN,EAAS,GAAG,QAAUK,GAAU,CAC9B,KAAK,OAAO,MAAM,6BAA8B,CAAE,KAAM,CAAE,MAAAA,CAAM,CAAE,CAAC,EACnE,KAAK,sBAAmB,QAAK,IAAI,yBAAuBA,CAAK,CAAC,CAAC,CACjE,CAAC,EACD,KAAK,OAAO,KAAK,+BAAwB,CAC3C,EACA,QAAS,IAAM,CACb,MAAMA,EAAQ,IAAI,MAAM,4BAA4B,EACpD,WAAK,OAAO,MAAM,6BAA8B,CAAE,KAAM,CAAE,MAAAA,CAAM,CAAE,CAAC,EAC7DA,CACR,CACF,CAAC,CACH,CAEA,MAAa,iBAAiC,CAC5C,OAAO,KAAK,SAAS,OAAO,CAC1B,KAAM,MAAOL,GAAa,CACxB,GAAI,CACF,MAAMA,EAAS,MAAM,EACrB,KAAK,SAAW,UAChB,KAAK,wBAA0B,UAC/B,KAAK,aAAe,UACpB,KAAK,OAAO,KAAK,sBAAe,CAClC,OAASK,EAAO,CACd,WAAK,OAAO,MAAM,6BAA8B,CAC9C,KAAM,CAAE,OAAQ,KAAK,aAAa,OAAQ,MAAAA,CAAM,CAClD,CAAC,EACKA,CACR,CACF,EACA,QAAS,IAAM,CAEf,CACF,CAAC,CACH,CAEQ,sBAAsBE,EAAgB,CAC5C,MAAMD,EAAO,IAAI,WAAWC,CAAM,EACR,KAAK,aAAa,YAAYD,CAAI,EAGzD,IAAKE,GAAa,CACjBA,EAAS,IAAKC,GAAiB,CAC7B,KAAK,OAAO,SAAM,yBAAsBA,CAAY,CAAC,EACrD,KAAK,sBAAmB,SAAMA,CAAY,CAAC,CAC7C,CAAC,CACH,CAAC,EACA,QAASJ,GAAU,CAClB,KAAK,sBAAmB,QAAKA,CAAK,CAAC,CACrC,CAAC,CACL,CAEQ,mBAAmBK,EAA8C,CACvE,KAAK,aAAa,IAAKC,GAAY,CACjC,KAAK,aAAe,UACpB,aAAaA,CAAO,CACtB,CAAC,EACD,KAAK,wBAAwB,IAAKT,GAAY,CAC5C,KAAK,wBAA0B,UAC/BA,EAAQQ,CAAM,CAChB,CAAC,CACH,CACF",
|
|
6
|
+
"names": ["NodeHidApduSender_exports", "__export", "NodeHidApduSender", "__toCommonJS", "import_device_management_kit", "import_node_hid", "import_purify_ts", "import_NodeHidConfig", "import_Errors", "dependencies", "apduSenderFactory", "apduReceiverFactory", "loggerFactory", "channel", "apdu", "_triggersDisconnection", "abortTimeout", "hidAsync", "resultPromise", "resolve", "frame", "report", "error", "data", "buffer", "response", "apduResponse", "result", "timeout"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var A=Object.create;var y=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var g=Object.getPrototypeOf,R=Object.prototype.hasOwnProperty;var B=(i,d,u,p)=>{if(d&&typeof d=="object"||typeof d=="function")for(let o of k(d))!R.call(i,o)&&o!==u&&y(i,o,{get:()=>d[o],enumerable:!(p=h(d,o))||p.enumerable});return i};var m=(i,d,u)=>(u=i!=null?A(g(i)):{},B(d||!i||!i.__esModule?y(u,"default",{value:i,enumerable:!0}):u,i));var w=require("@ledgerhq/device-management-kit"),l=require("purify-ts"),x=require("../model/Errors"),v=require("../model/HIDDevice.stub"),f=require("./NodeHidApduSender");vi.mock("node-hid",()=>({HIDAsync:{open:vi.fn()}}));describe("NodeHidApduSender",()=>{const i={getFrames:vi.fn()},d={handleFrame:vi.fn()},u={info:vi.fn(),debug:vi.fn(),error:vi.fn(),warn:vi.fn()},p=(0,v.nodeHidDeviceStubBuilder)();let o,t;beforeEach(async()=>{vi.clearAllMocks(),vi.useFakeTimers(),o={on:vi.fn((r,s)=>{r==="data"?o.dataCallback=s:r==="error"&&(o.errorCallback=s)}),write:vi.fn().mockResolvedValue(void 0),close:vi.fn().mockResolvedValue(void 0)};const{HIDAsync:e}=await import("node-hid");vi.mocked(e.open).mockResolvedValue(o);const n=vi.fn().mockReturnValue(u),a=vi.fn().mockReturnValue(i),c=vi.fn().mockReturnValue(d);t=new f.NodeHidApduSender({dependencies:{device:p},apduSenderFactory:a,apduReceiverFactory:c,loggerFactory:n})}),afterEach(()=>{vi.restoreAllMocks(),vi.useRealTimers()}),it("should get dependencies",()=>{const e=t.getDependencies();expect(e).toEqual({device:p})}),it("should set dependencies",()=>{const e=(0,v.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",product:"Ledger Nano S Plus"});t.setDependencies({device:e});const n=t.getDependencies();expect(n).toEqual({device:e})}),it("should setup connection",async()=>{const{HIDAsync:e}=await import("node-hid"),n=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await n,expect(e.open).toHaveBeenCalledWith(p.path,{nonExclusive:!0}),expect(o.on).toHaveBeenCalledWith("data",expect.any(Function)),expect(o.on).toHaveBeenCalledWith("error",expect.any(Function)),expect(u.info).toHaveBeenCalledWith("\u{1F50C} Connected to device")}),it("should throw error if device path is missing",async()=>{const e=(0,v.nodeHidDeviceStubBuilder)({path:void 0});t.setDependencies({device:e}),await expect(t.setupConnection()).rejects.toThrow("Missing device path")}),it("should handle setup connection error",async()=>{const e=new Error("Failed to open device"),{HIDAsync:n}=await import("node-hid");vi.mocked(n.open).mockRejectedValue(e);const c=t.setupConnection().catch(s=>s);await vi.advanceTimersByTimeAsync(300);const r=await c;expect(r).toBe(e)}),it("should close existing connection before setting up new one",async()=>{const e=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await e;const n={on:vi.fn(),write:vi.fn().mockResolvedValue(void 0),close:vi.fn().mockResolvedValue(void 0)},{HIDAsync:a}=await import("node-hid");vi.mocked(a.open).mockResolvedValue(n);const c=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await c,expect(o.close).toHaveBeenCalled()}),it("should close connection",async()=>{const e=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await e,await t.closeConnection(),expect(o.close).toHaveBeenCalled(),expect(u.info).toHaveBeenCalledWith("\u{1F51A} Disconnect")}),it("should handle close connection error",async()=>{const e=new Error("Failed to close device");o.close=vi.fn().mockRejectedValue(e);const n=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await n,await expect(t.closeConnection()).rejects.toThrow(e),expect(u.error).toHaveBeenCalledWith("Error while closing device",{data:{device:p,error:e}})}),it("should do nothing when closing without connection",async()=>{await t.closeConnection(),expect(o.close).not.toHaveBeenCalled()}),it("should return error when sending APDU without connection",async()=>{const e=new Uint8Array([0,1,2,3]),n=await t.sendApdu(e);expect(n.isLeft()).toBe(!0),expect(n.extract()).toBeInstanceOf(w.OpeningConnectionError)}),it("should send APDU successfully",async()=>{const e=new Uint8Array([0,1,2,3]),n=[{getRawData:()=>new Uint8Array([0,1])},{getRawData:()=>new Uint8Array([2,3])}],a={data:new Uint8Array([1,2]),statusCode:new Uint8Array([144,0])};i.getFrames.mockReturnValue(n),d.handleFrame.mockReturnValue((0,l.Right)(l.Maybe.of(a)));const c=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await c;const r=t.sendApdu(e);o.dataCallback?.(Buffer.from([1,2]));const s=await r;expect(o.write).toHaveBeenCalledTimes(2),expect(s.isRight()).toBe(!0),expect(s.extract()).toEqual(a)}),it("should handle send APDU error",async()=>{const e=new Uint8Array([0,1,2,3]),n=[{getRawData:()=>new Uint8Array([0,1])},{getRawData:()=>new Uint8Array([2,3])}],a=new Error("Failed to write");i.getFrames.mockReturnValue(n),o.write=vi.fn().mockRejectedValue(a);const c=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await c;const r=await t.sendApdu(e);expect(r.isLeft()).toBe(!0),expect(r.extract()).toBeInstanceOf(x.NodeHidSendReportError)}),it("should handle received response error",async()=>{const e=new Uint8Array([0,1,2,3]),n=[{getRawData:()=>new Uint8Array([0,1])},{getRawData:()=>new Uint8Array([2,3])}],a=new Error("Error while receiving APDU");i.getFrames.mockReturnValue(n),d.handleFrame.mockReturnValue((0,l.Left)(a));const c=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await c;const r=t.sendApdu(e);o.dataCallback?.(Buffer.from([]));const s=await r;expect(o.write).toHaveBeenCalled(),expect(s.isLeft()).toBe(!0),expect(s.extract()).toStrictEqual(a)}),it("should handle HIDAsync error event",async()=>{const e=new Uint8Array([0,1,2,3]),n=[{getRawData:()=>new Uint8Array([0,1])}],a=new Error("HID error");i.getFrames.mockReturnValue(n);const c=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await c;const r=t.sendApdu(e);o.errorCallback?.(a);const s=await r;expect(s.isLeft()).toBe(!0),expect(s.extract()).toBeInstanceOf(x.NodeHidSendReportError),expect(u.error).toHaveBeenCalledWith("Error while receiving data",{data:{error:a}})}),it("should handle send APDU timeout",async()=>{const e=new Uint8Array([0,1,2,3]),n=[{getRawData:()=>new Uint8Array([0,1])},{getRawData:()=>new Uint8Array([2,3])}];i.getFrames.mockReturnValue(n);const a=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await a;const c=t.sendApdu(e,!1,100);await vi.advanceTimersByTimeAsync(100);const r=await c;expect(r.isLeft()).toBe(!0),expect(r.extract()).toBeInstanceOf(w.SendApduTimeoutError)}),it("should clear timeout when response is received before timeout",async()=>{const e=new Uint8Array([0,1,2,3]),n=[{getRawData:()=>new Uint8Array([0,1])}],a={data:new Uint8Array([1,2]),statusCode:new Uint8Array([144,0])};i.getFrames.mockReturnValue(n),d.handleFrame.mockReturnValue((0,l.Right)(l.Maybe.of(a)));const c=t.setupConnection();await vi.advanceTimersByTimeAsync(300),await c;const r=t.sendApdu(e,!1,1e3);o.dataCallback?.(Buffer.from([1,2]));const s=await r;expect(s.isRight()).toBe(!0),expect(s.extract()).toEqual(a)})});
|
|
2
|
+
//# sourceMappingURL=NodeHidApduSender.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/api/transport/NodeHidApduSender.test.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n type ApduResponse,\n type LoggerPublisherService,\n OpeningConnectionError,\n SendApduTimeoutError,\n} from \"@ledgerhq/device-management-kit\";\nimport type { Device as NodeHIDDevice, HIDAsync } from \"node-hid\";\nimport { Left, Maybe, Right } from \"purify-ts\";\n\nimport { NodeHidSendReportError } from \"@api/model/Errors\";\nimport { nodeHidDeviceStubBuilder } from \"@api/model/HIDDevice.stub\";\n\nimport { NodeHidApduSender } from \"./NodeHidApduSender\";\n\n// Mock HIDAsync.open static method\nvi.mock(\"node-hid\", () => ({\n HIDAsync: {\n open: vi.fn(),\n },\n}));\n\ndescribe(\"NodeHidApduSender\", () => {\n const mockApduSender = {\n getFrames: vi.fn(),\n };\n\n const mockApduReceiver = {\n handleFrame: vi.fn(),\n };\n\n const mockLogger = {\n info: vi.fn(),\n debug: vi.fn(),\n error: vi.fn(),\n warn: vi.fn(),\n };\n\n const mockDevice: NodeHIDDevice = nodeHidDeviceStubBuilder();\n\n // Mock HIDAsync instance\n let mockHidAsync: {\n on: ReturnType<typeof vi.fn>;\n write: ReturnType<typeof vi.fn>;\n close: ReturnType<typeof vi.fn>;\n dataCallback?: (data: Buffer) => void;\n errorCallback?: (error: Error) => void;\n };\n\n let nodeHidApduSender: NodeHidApduSender;\n\n beforeEach(async () => {\n vi.clearAllMocks();\n vi.useFakeTimers();\n\n // Create a fresh mock HIDAsync instance for each test\n mockHidAsync = {\n on: vi.fn((event: string, callback: (data: unknown) => void) => {\n if (event === \"data\") {\n mockHidAsync.dataCallback = callback as (data: Buffer) => void;\n } else if (event === \"error\") {\n mockHidAsync.errorCallback = callback as (error: Error) => void;\n }\n }),\n write: vi.fn().mockResolvedValue(undefined),\n close: vi.fn().mockResolvedValue(undefined),\n };\n\n // Mock HIDAsync.open to return our mock instance\n const { HIDAsync } = await import(\"node-hid\");\n vi.mocked(HIDAsync.open).mockResolvedValue(\n mockHidAsync as unknown as HIDAsync,\n );\n\n const mockLoggerFactory = vi\n .fn()\n .mockReturnValue(mockLogger as unknown as LoggerPublisherService);\n const mockApduSenderFactory = vi.fn().mockReturnValue(mockApduSender);\n const mockApduReceiverFactory = vi.fn().mockReturnValue(mockApduReceiver);\n\n nodeHidApduSender = new NodeHidApduSender({\n dependencies: { device: mockDevice },\n apduSenderFactory: mockApduSenderFactory,\n apduReceiverFactory: mockApduReceiverFactory,\n loggerFactory: mockLoggerFactory,\n });\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n vi.useRealTimers();\n });\n\n it(\"should get dependencies\", () => {\n const dependencies = nodeHidApduSender.getDependencies();\n expect(dependencies).toEqual({\n device: mockDevice,\n });\n });\n\n it(\"should set dependencies\", () => {\n const newDevice: NodeHIDDevice = nodeHidDeviceStubBuilder({\n path: \"/dev/hidraw1\",\n product: \"Ledger Nano S Plus\",\n });\n nodeHidApduSender.setDependencies({ device: newDevice });\n const dependencies = nodeHidApduSender.getDependencies();\n expect(dependencies).toEqual({ device: newDevice });\n });\n\n it(\"should setup connection\", async () => {\n const { HIDAsync } = await import(\"node-hid\");\n\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n expect(HIDAsync.open).toHaveBeenCalledWith(mockDevice.path, {\n nonExclusive: true,\n });\n expect(mockHidAsync.on).toHaveBeenCalledWith(\"data\", expect.any(Function));\n expect(mockHidAsync.on).toHaveBeenCalledWith(\"error\", expect.any(Function));\n expect(mockLogger.info).toHaveBeenCalledWith(\"\uD83D\uDD0C Connected to device\");\n });\n\n it(\"should throw error if device path is missing\", async () => {\n const deviceWithoutPath: NodeHIDDevice = nodeHidDeviceStubBuilder({\n path: undefined,\n });\n nodeHidApduSender.setDependencies({ device: deviceWithoutPath });\n\n await expect(nodeHidApduSender.setupConnection()).rejects.toThrow(\n \"Missing device path\",\n );\n });\n\n it(\"should handle setup connection error\", async () => {\n const error = new Error(\"Failed to open device\");\n const { HIDAsync } = await import(\"node-hid\");\n vi.mocked(HIDAsync.open).mockRejectedValue(error);\n\n // Capture the promise and its rejection handler immediately\n const setupPromise = nodeHidApduSender.setupConnection();\n // Attach rejection handler before advancing timers to avoid unhandled rejection warning\n const resultPromise = setupPromise.catch((e) => e);\n\n await vi.advanceTimersByTimeAsync(300);\n const result = await resultPromise;\n expect(result).toBe(error);\n });\n\n it(\"should close existing connection before setting up new one\", async () => {\n // Setup first connection\n const firstSetupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await firstSetupPromise;\n\n // Create a new mock for the second connection\n const secondMockHidAsync = {\n on: vi.fn(),\n write: vi.fn().mockResolvedValue(undefined),\n close: vi.fn().mockResolvedValue(undefined),\n };\n\n const { HIDAsync } = await import(\"node-hid\");\n vi.mocked(HIDAsync.open).mockResolvedValue(\n secondMockHidAsync as unknown as HIDAsync,\n );\n\n // Setup second connection - should close the first\n const secondSetupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await secondSetupPromise;\n\n expect(mockHidAsync.close).toHaveBeenCalled();\n });\n\n it(\"should close connection\", async () => {\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n await nodeHidApduSender.closeConnection();\n\n expect(mockHidAsync.close).toHaveBeenCalled();\n expect(mockLogger.info).toHaveBeenCalledWith(\"\uD83D\uDD1A Disconnect\");\n });\n\n it(\"should handle close connection error\", async () => {\n const error = new Error(\"Failed to close device\");\n mockHidAsync.close = vi.fn().mockRejectedValue(error);\n\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n await expect(nodeHidApduSender.closeConnection()).rejects.toThrow(error);\n expect(mockLogger.error).toHaveBeenCalledWith(\n \"Error while closing device\",\n {\n data: { device: mockDevice, error },\n },\n );\n });\n\n it(\"should do nothing when closing without connection\", async () => {\n // Don't setup connection, just try to close\n await nodeHidApduSender.closeConnection();\n\n expect(mockHidAsync.close).not.toHaveBeenCalled();\n });\n\n it(\"should return error when sending APDU without connection\", async () => {\n const apdu = new Uint8Array([0x00, 0x01, 0x02, 0x03]);\n\n const result = await nodeHidApduSender.sendApdu(apdu);\n\n expect(result.isLeft()).toBe(true);\n expect(result.extract()).toBeInstanceOf(OpeningConnectionError);\n });\n\n it(\"should send APDU successfully\", async () => {\n const apdu = new Uint8Array([0x00, 0x01, 0x02, 0x03]);\n const frames = [\n { getRawData: () => new Uint8Array([0x00, 0x01]) },\n { getRawData: () => new Uint8Array([0x02, 0x03]) },\n ];\n const apduResponse = {\n data: new Uint8Array([0x01, 0x02]),\n statusCode: new Uint8Array([0x90, 0x00]),\n } as ApduResponse;\n\n mockApduSender.getFrames.mockReturnValue(frames);\n mockApduReceiver.handleFrame.mockReturnValue(Right(Maybe.of(apduResponse)));\n\n // Setup connection\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n // Send APDU\n const promise = nodeHidApduSender.sendApdu(apdu);\n\n // Simulate receiving response\n mockHidAsync.dataCallback?.(Buffer.from([0x01, 0x02]));\n\n // Await response\n const result = await promise;\n\n expect(mockHidAsync.write).toHaveBeenCalledTimes(2);\n expect(result.isRight()).toBe(true);\n expect(result.extract()).toEqual(apduResponse);\n });\n\n it(\"should handle send APDU error\", async () => {\n const apdu = new Uint8Array([0x00, 0x01, 0x02, 0x03]);\n const frames = [\n { getRawData: () => new Uint8Array([0x00, 0x01]) },\n { getRawData: () => new Uint8Array([0x02, 0x03]) },\n ];\n const error = new Error(\"Failed to write\");\n\n mockApduSender.getFrames.mockReturnValue(frames);\n mockHidAsync.write = vi.fn().mockRejectedValue(error);\n\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n const result = await nodeHidApduSender.sendApdu(apdu);\n\n expect(result.isLeft()).toBe(true);\n expect(result.extract()).toBeInstanceOf(NodeHidSendReportError);\n });\n\n it(\"should handle received response error\", async () => {\n const apdu = new Uint8Array([0x00, 0x01, 0x02, 0x03]);\n const frames = [\n { getRawData: () => new Uint8Array([0x00, 0x01]) },\n { getRawData: () => new Uint8Array([0x02, 0x03]) },\n ];\n const apduError = new Error(\"Error while receiving APDU\");\n\n mockApduSender.getFrames.mockReturnValue(frames);\n mockApduReceiver.handleFrame.mockReturnValue(Left(apduError));\n\n // Setup connection\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n // Send APDU\n const promise = nodeHidApduSender.sendApdu(apdu);\n\n // Simulate receiving response with error\n mockHidAsync.dataCallback?.(Buffer.from([]));\n\n // Await response\n const result = await promise;\n\n expect(mockHidAsync.write).toHaveBeenCalled();\n expect(result.isLeft()).toBe(true);\n expect(result.extract()).toStrictEqual(apduError);\n });\n\n it(\"should handle HIDAsync error event\", async () => {\n const apdu = new Uint8Array([0x00, 0x01, 0x02, 0x03]);\n const frames = [{ getRawData: () => new Uint8Array([0x00, 0x01]) }];\n const hidError = new Error(\"HID error\");\n\n mockApduSender.getFrames.mockReturnValue(frames);\n\n // Setup connection\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n // Send APDU\n const promise = nodeHidApduSender.sendApdu(apdu);\n\n // Simulate HID error\n mockHidAsync.errorCallback?.(hidError);\n\n // Await response\n const result = await promise;\n\n expect(result.isLeft()).toBe(true);\n expect(result.extract()).toBeInstanceOf(NodeHidSendReportError);\n expect(mockLogger.error).toHaveBeenCalledWith(\n \"Error while receiving data\",\n { data: { error: hidError } },\n );\n });\n\n it(\"should handle send APDU timeout\", async () => {\n const apdu = new Uint8Array([0x00, 0x01, 0x02, 0x03]);\n const frames = [\n { getRawData: () => new Uint8Array([0x00, 0x01]) },\n { getRawData: () => new Uint8Array([0x02, 0x03]) },\n ];\n\n mockApduSender.getFrames.mockReturnValue(frames);\n\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n const promise = nodeHidApduSender.sendApdu(apdu, false, 100);\n\n // Advance timers to trigger timeout - need to run pending timers\n await vi.advanceTimersByTimeAsync(100);\n\n const result = await promise;\n expect(result.isLeft()).toBe(true);\n expect(result.extract()).toBeInstanceOf(SendApduTimeoutError);\n });\n\n it(\"should clear timeout when response is received before timeout\", async () => {\n const apdu = new Uint8Array([0x00, 0x01, 0x02, 0x03]);\n const frames = [{ getRawData: () => new Uint8Array([0x00, 0x01]) }];\n const apduResponse = {\n data: new Uint8Array([0x01, 0x02]),\n statusCode: new Uint8Array([0x90, 0x00]),\n } as ApduResponse;\n\n mockApduSender.getFrames.mockReturnValue(frames);\n mockApduReceiver.handleFrame.mockReturnValue(Right(Maybe.of(apduResponse)));\n\n const setupPromise = nodeHidApduSender.setupConnection();\n await vi.advanceTimersByTimeAsync(300);\n await setupPromise;\n\n const promise = nodeHidApduSender.sendApdu(apdu, false, 1000);\n\n // Respond before timeout\n mockHidAsync.dataCallback?.(Buffer.from([0x01, 0x02]));\n\n const result = await promise;\n expect(result.isRight()).toBe(true);\n expect(result.extract()).toEqual(apduResponse);\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_device_management_kit", "import_purify_ts", "import_Errors", "import_HIDDevice", "import_NodeHidApduSender", "mockApduSender", "mockApduReceiver", "mockLogger", "mockDevice", "mockHidAsync", "nodeHidApduSender", "event", "callback", "HIDAsync", "mockLoggerFactory", "mockApduSenderFactory", "mockApduReceiverFactory", "dependencies", "newDevice", "setupPromise", "deviceWithoutPath", "error", "resultPromise", "e", "result", "firstSetupPromise", "secondMockHidAsync", "secondSetupPromise", "apdu", "frames", "apduResponse", "promise", "apduError", "hidError"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var u=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var N=(v,e)=>{for(var i in e)u(v,i,{get:e[i],enumerable:!0})},S=(v,e,i,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of C(e))!A.call(v,o)&&o!==i&&u(v,o,{get:()=>e[o],enumerable:!(t=H(e,o))||t.enumerable});return v};var T=v=>S(u({},"__esModule",{value:!0}),v);var w={};N(w,{NodeHidTransport:()=>_,nodeHidIdentifier:()=>m,nodeHidTransportFactory:()=>b});module.exports=T(w);var c=require("@ledgerhq/device-management-kit"),h=require("node-hid"),d=require("purify-ts"),p=require("rxjs"),D=require("usb"),l=require("uuid"),y=require("../data/NodeHidConfig"),f=require("../model/Errors"),I=require("../transport/NodeHidApduSender");const E={devicesAsync:h.devicesAsync,HIDAsync:h.HIDAsync},m="NODE-HID";class _{constructor(e,i,t,o,r=a=>new c.DeviceConnectionStateMachine(a),s=a=>new I.NodeHidApduSender(a)){this._deviceModelDataSource=e;this._loggerServiceFactory=i;this._apduSenderFactory=t;this._apduReceiverFactory=o;this._deviceConnectionStateMachineFactory=r;this._deviceApduSenderFactory=s;this._logger=i("NodeHidTransport"),this.startListeningToConnectionEvents()}_transportDiscoveredDevices=new p.BehaviorSubject([]);_deviceConnectionsByHidDevice=new Map;_deviceConnectionsPendingReconnection=new Set;_connectionListenersAbortController=new AbortController;_logger;connectionType="USB";identifier=m;get hidApi(){return this.isSupported()?(0,d.Right)(E):(0,d.Left)(new f.NodeHidTransportNotSupportedError("NodeHID not supported"))}isSupported(){try{return this._logger.debug("isSupported: true"),!0}catch(e){return this._logger.error("isSupported: error",{data:{error:e}}),!1}}getIdentifier(){return this.identifier}async getDevices(){return d.EitherAsync.liftEither(this.hidApi).map(async e=>{try{const t=(await e.devicesAsync()).filter(r=>r.vendorId===c.LEDGER_VENDOR_ID);return Array.from(new Map(t.map(r=>[`${r.vendorId}:${r.productId}`,r])).values())}catch(i){const t=new c.NoAccessibleDeviceError(i);throw this._logger.error("getDevices: error getting devices",{data:{error:i}}),t}})}mapHIDDeviceToTransportDiscoveredDevice(e){const i=this._transportDiscoveredDevices.getValue().find(r=>r.hidDevice.vendorId===e.vendorId&&r.hidDevice.productId===e.productId);if(i)return i;const t=this._deviceConnectionsByHidDevice.get(e);return this.getDeviceModel(e).caseOf({Just:r=>{const s=t?.getDeviceId()??(0,l.v4)(),a={id:s,deviceModel:r,hidDevice:e,transport:this.identifier};return this._logger.debug(`Discovered device ${s} ${a.deviceModel.productName}`),a},Nothing:()=>{throw this._logger.warn(`Device not recognized: hidDevice.productId: 0x${e.productId.toString(16)}`),new c.DeviceNotRecognizedError(`Device not recognized: hidDevice.productId: 0x${e.productId.toString(16)}`)}})}listenToAvailableDevices(){return this.updateTransportDiscoveredDevices(),this._transportDiscoveredDevices.pipe((0,p.map)(e=>e.map(({hidDevice:i,...t})=>t)))}async updateTransportDiscoveredDevices(){(await this.getDevices()).caseOf({Left:i=>{this._logger.error("Error while getting accessible device",{data:{error:i}})},Right:i=>{this._transportDiscoveredDevices.next(i.map(t=>this.mapHIDDeviceToTransportDiscoveredDevice(t)))}})}async promptDeviceAccess(){return d.EitherAsync.liftEither(this.hidApi).map(async e=>{let i=[];try{i=(await e.devicesAsync()).filter(r=>r.vendorId===c.LEDGER_VENDOR_ID),await this.updateTransportDiscoveredDevices()}catch(o){const r=new c.NoAccessibleDeviceError(o);throw this._logger.error("promptDeviceAccess: error requesting device",{data:{error:o}}),r}if(this._logger.debug(`promptDeviceAccess: hidDevices len ${i.length}`),i.length===0)throw this._logger.warn("No device was selected"),new c.NoAccessibleDeviceError("No selected device");const t=[];for(const o of i)t.push(o),this._logger.debug("promptDeviceAccess: selected device",{data:{hidDevice:o}});return t}).run()}startDiscovering(){return this._logger.debug("startDiscovering"),(0,p.from)(this.promptDeviceAccess()).pipe((0,p.switchMap)(e=>e.caseOf({Left:i=>{throw this._logger.error("Error while getting accessible device",{data:{error:i}}),i},Right:i=>{this._logger.info(`Got access to ${i.length} HID devices`);const t=i.map(o=>this.mapHIDDeviceToTransportDiscoveredDevice(o));return(0,p.from)(t)}})))}stopDiscovering(){}startListeningToConnectionEvents(){this._logger.debug("startListeningToConnectionEvents"),D.usb.on("attach",e=>{this.handleDeviceConnection(e)}),D.usb.on("detach",e=>{this.handleDeviceDisconnection(e)}),process.on("exit",()=>{D.usb.unrefHotplugEvents(),D.usb.removeAllListeners()})}stopListeningToConnectionEvents(){this._logger.debug("stopListeningToConnectionEvents"),this._connectionListenersAbortController.abort(),D.usb.removeAllListeners()}async connect({deviceId:e,onDisconnect:i}){this._logger.debug("connect",{data:{deviceId:e}});const t=this._transportDiscoveredDevices.getValue().find(n=>n.id===e);if(!t)return this._logger.error(`Unknown device ${e}`),(0,d.Left)(new c.UnknownDeviceError(`Unknown device ${e}`));const o=this._deviceConnectionsByHidDevice.get(t.hidDevice);if(o)return(0,d.Right)(new c.TransportConnectedDevice({id:e,deviceModel:t.deviceModel,type:this.connectionType,sendApdu:(...n)=>o.sendApdu(...n),transport:this.identifier}));const r=this._deviceApduSenderFactory({dependencies:{device:t.hidDevice},apduSenderFactory:this._apduSenderFactory,apduReceiverFactory:this._apduReceiverFactory,loggerFactory:this._loggerServiceFactory}),s=this._deviceConnectionStateMachineFactory({deviceId:e,deviceApduSender:r,timeoutDuration:y.RECONNECT_DEVICE_TIMEOUT,tryToReconnect:()=>{this._deviceConnectionsByHidDevice.forEach((n,g)=>{n.getDeviceId()===e&&(this._deviceConnectionsPendingReconnection.add(n),this._deviceConnectionsByHidDevice.delete(g))})},onTerminated:()=>{this._deviceConnectionsPendingReconnection.forEach(n=>{n.getDeviceId()===e&&(this._deviceConnectionsPendingReconnection.delete(n),i(n.getDeviceId()))}),this._deviceConnectionsByHidDevice.forEach((n,g)=>{n.getDeviceId()===e&&(this._deviceConnectionsByHidDevice.delete(g),i(n.getDeviceId()))})}});try{await s.setupConnection()}catch(n){return this._logger.error("Error while setting up device connection",{data:{error:n}}),(0,d.Left)(new c.OpeningConnectionError(n))}this._deviceConnectionsByHidDevice.set(t.hidDevice,s);const a=new c.TransportConnectedDevice({sendApdu:(...n)=>s.sendApdu(...n),deviceModel:t.deviceModel,id:e,type:this.connectionType,transport:this.identifier});return(0,d.Right)(a)}getDeviceModel(e){const{productId:i}=e,t=this._deviceModelDataSource.getAllDeviceModels().find(o=>o.usbProductId===i>>8||o.bootloaderUsbProductId===i);return t?d.Maybe.of(t):d.Maybe.zero()}getHidUsbProductId(e){return this.getDeviceModel(e).caseOf({Just:i=>i.usbProductId,Nothing:()=>e.productId>>8})}async disconnect(e){this._logger.debug("disconnect",{data:{connectedDevice:e}});const i=Array.from(this._deviceConnectionsByHidDevice.values()).find(t=>t.getDeviceId()===e.connectedDevice.id);return i?(i.closeConnection(),Promise.resolve((0,d.Right)(void 0))):(this._logger.error("No matching device connection found",{data:{connectedDevice:e}}),Promise.resolve((0,d.Left)(new c.UnknownDeviceError(`Unknown device ${e.connectedDevice.id}`))))}async handleDeviceDisconnection(e){const{idVendor:i,idProduct:t}=e.deviceDescriptor;if(i!==c.LEDGER_VENDOR_ID)return;this._logger.info("[handleDeviceDisconnectionEvent] Device disconnected",{data:{vendorId:i,productId:t}}),this.updateTransportDiscoveredDevices(),(()=>{for(const[r,s]of this._deviceConnectionsByHidDevice.entries())if(r.vendorId===i&&(r.productId>>8===t||r.productId===t))return(0,d.Just)(s);return d.Nothing})().caseOf({Just:r=>{try{r.eventDeviceDisconnected()}catch(s){this._logger.error("Error while handling device disconnection",{data:{error:s}})}},Nothing:()=>{this._logger.error("No matching device connection found",{data:{vendorId:i,productId:t}})}})}async handleDeviceReconnection(e,i){try{this._deviceConnectionsPendingReconnection.delete(e),e.setDependencies({device:i}),await e.setupConnection(),this._deviceConnectionsByHidDevice.set(i,e),e.eventDeviceConnected()}catch(t){this._logger.error("Error while reconnecting to device",{data:{error:t}}),e.closeConnection()}}async handleDeviceConnection(e){const{idVendor:i,idProduct:t}=e.deviceDescriptor;if(i!==c.LEDGER_VENDOR_ID)return;this._logger.info("[handleDeviceConnection] New device connected",{data:{vendorId:i,productId:t}}),(await this.getDevices()).caseOf({Left:r=>{this._logger.error("Error while getting HID devices for reconnection",{data:{error:r}})},Right:async r=>{const s=r.find(n=>n.vendorId===i&&(n.productId>>8===t||n.productId===t));if(!s){this._logger.debug("[handleDeviceConnection] No matching HID device found",{data:{vendorId:i,productId:t}});return}const a=Array.from(this._deviceConnectionsPendingReconnection).find(n=>this.getHidUsbProductId(n.getDependencies().device)===this.getHidUsbProductId(s));a&&await this.handleDeviceReconnection(a,s),await this.updateTransportDiscoveredDevices()}})}destroy(){this.stopListeningToConnectionEvents(),this._deviceConnectionsByHidDevice.forEach(e=>{e.closeConnection()}),this._deviceConnectionsPendingReconnection.clear()}}const b=({deviceModelDataSource:v,loggerServiceFactory:e,apduSenderServiceFactory:i,apduReceiverServiceFactory:t})=>new _(v,e,i,t);0&&(module.exports={NodeHidTransport,nodeHidIdentifier,nodeHidTransportFactory});
|
|
2
|
+
//# sourceMappingURL=NodeHidTransport.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/api/transport/NodeHidTransport.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n type ApduReceiverServiceFactory,\n type ApduSenderServiceFactory,\n type ConnectError,\n type ConnectionType,\n DeviceConnectionStateMachine,\n type DeviceConnectionStateMachineParams,\n type DeviceId,\n type DeviceModelDataSource,\n DeviceNotRecognizedError,\n type DisconnectHandler,\n type DmkError,\n LEDGER_VENDOR_ID,\n type LoggerPublisherService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n type Transport,\n TransportConnectedDevice,\n type TransportDeviceModel,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Device as NodeHIDDevice, devicesAsync, HIDAsync } from \"node-hid\";\nimport {\n type Either,\n EitherAsync,\n Just,\n Left,\n Maybe,\n Nothing,\n Right,\n} from \"purify-ts\";\nimport { BehaviorSubject, from, map, type Observable, switchMap } from \"rxjs\";\nimport { type Device, usb } from \"usb\";\nimport { v4 as uuid } from \"uuid\";\n\nimport { RECONNECT_DEVICE_TIMEOUT } from \"@api/data/NodeHidConfig\";\nimport { NodeHidTransportNotSupportedError } from \"@api/model/Errors\";\nimport {\n NodeHidApduSender,\n type NodeHidApduSenderConstructorArgs,\n type NodeHidApduSenderDependencies,\n} from \"@api/transport/NodeHidApduSender\";\n\ntype NodeHIDAPI = typeof NodeHIDAPI;\nconst NodeHIDAPI = { devicesAsync, HIDAsync } as const;\n\ntype PromptDeviceAccessError =\n | NoAccessibleDeviceError\n | NodeHidTransportNotSupportedError;\n\ntype NodeHidTransportDiscoveredDevice = TransportDiscoveredDevice & {\n hidDevice: NodeHIDDevice;\n};\n\nexport const nodeHidIdentifier: TransportIdentifier = \"NODE-HID\";\n\nexport class NodeHidTransport implements Transport {\n /** List of HID devices that have been discovered */\n private _transportDiscoveredDevices: BehaviorSubject<\n Array<NodeHidTransportDiscoveredDevice>\n > = new BehaviorSubject<Array<NodeHidTransportDiscoveredDevice>>([]);\n\n /** Map of *connected* HIDDevice to their NodeHidDeviceConnection */\n private _deviceConnectionsByHidDevice: Map<\n NodeHIDDevice,\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > = new Map();\n\n /**\n * Set of NodeHidDeviceConnection for which the HIDDevice has been\n * disconnected, so they are waiting for a reconnection\n */\n private _deviceConnectionsPendingReconnection: Set<\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > = new Set();\n\n /** AbortController to stop listening to HID connection events */\n private _connectionListenersAbortController: AbortController =\n new AbortController();\n private _logger: LoggerPublisherService;\n private readonly connectionType: ConnectionType = \"USB\";\n private readonly identifier: TransportIdentifier = nodeHidIdentifier;\n\n constructor(\n private readonly _deviceModelDataSource: DeviceModelDataSource,\n private readonly _loggerServiceFactory: (\n tag: string,\n ) => LoggerPublisherService,\n private readonly _apduSenderFactory: ApduSenderServiceFactory,\n private readonly _apduReceiverFactory: ApduReceiverServiceFactory,\n private readonly _deviceConnectionStateMachineFactory: (\n args: DeviceConnectionStateMachineParams<NodeHidApduSenderDependencies>,\n ) => DeviceConnectionStateMachine<NodeHidApduSenderDependencies> = (args) =>\n new DeviceConnectionStateMachine(args),\n private readonly _deviceApduSenderFactory: (\n args: NodeHidApduSenderConstructorArgs,\n ) => NodeHidApduSender = (args) => new NodeHidApduSender(args),\n ) {\n this._logger = _loggerServiceFactory(\"NodeHidTransport\");\n\n this.startListeningToConnectionEvents();\n }\n\n /**\n * Get the NodeHID API if supported or error\n * @returns `Either<NodeHidTransportNotSupportedError, HID>`\n */\n private get hidApi(): Either<NodeHidTransportNotSupportedError, NodeHIDAPI> {\n if (this.isSupported()) {\n return Right(NodeHIDAPI);\n }\n\n return Left(new NodeHidTransportNotSupportedError(\"NodeHID not supported\"));\n }\n\n isSupported() {\n try {\n const result = true; // Node hid should be supported !\n this._logger.debug(`isSupported: ${result}`);\n return result;\n } catch (error) {\n this._logger.error(`isSupported: error`, { data: { error } });\n return false;\n }\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n private async getDevices(): Promise<Either<DmkError, NodeHIDDevice[]>> {\n return EitherAsync.liftEither(this.hidApi).map(async (hidApi) => {\n try {\n const allDevices = await hidApi.devicesAsync();\n\n const ledgerDevices = allDevices.filter(\n (hidDevice) => hidDevice.vendorId === LEDGER_VENDOR_ID,\n );\n\n // Remove duplicates from same device with different interfaces by keeping only one device per vendorId:productId combination\n const uniqueDevices = Array.from(\n new Map(\n ledgerDevices.map((device) => [\n `${device.vendorId}:${device.productId}`,\n device,\n ]),\n ).values(),\n );\n\n return uniqueDevices;\n } catch (error) {\n const deviceError = new NoAccessibleDeviceError(error);\n this._logger.error(`getDevices: error getting devices`, {\n data: { error },\n });\n throw deviceError;\n }\n });\n }\n\n /**\n * Map a HIDDevice to an TransportDiscoveredDevice, either by creating a new one or returning an existing one\n */\n private mapHIDDeviceToTransportDiscoveredDevice(\n hidDevice: NodeHIDDevice,\n ): NodeHidTransportDiscoveredDevice {\n const existingDiscoveredDevice = this._transportDiscoveredDevices\n .getValue()\n .find(\n (internalDevice) =>\n internalDevice.hidDevice.vendorId === hidDevice.vendorId &&\n internalDevice.hidDevice.productId === hidDevice.productId,\n );\n\n if (existingDiscoveredDevice) {\n return existingDiscoveredDevice;\n }\n\n const existingDeviceConnection =\n this._deviceConnectionsByHidDevice.get(hidDevice);\n\n const maybeDeviceModel = this.getDeviceModel(hidDevice);\n return maybeDeviceModel.caseOf({\n Just: (deviceModel) => {\n const id = existingDeviceConnection?.getDeviceId() ?? uuid();\n\n const discoveredDevice = {\n id,\n deviceModel,\n hidDevice,\n transport: this.identifier,\n };\n\n this._logger.debug(\n `Discovered device ${id} ${discoveredDevice.deviceModel.productName}`,\n );\n\n return discoveredDevice;\n },\n Nothing: () => {\n // [ASK] Or we just ignore the not recognized device ? And log them\n this._logger.warn(\n `Device not recognized: hidDevice.productId: 0x${hidDevice.productId.toString(16)}`,\n );\n throw new DeviceNotRecognizedError(\n `Device not recognized: hidDevice.productId: 0x${hidDevice.productId.toString(16)}`,\n );\n },\n });\n }\n\n /**\n * Listen to known devices (devices to which the user has granted access)\n */\n public listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n this.updateTransportDiscoveredDevices();\n return this._transportDiscoveredDevices.pipe(\n map((devices) => devices.map(({ hidDevice, ...device }) => device)),\n );\n }\n\n private async updateTransportDiscoveredDevices(): Promise<void> {\n const eitherDevices = await this.getDevices();\n\n eitherDevices.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting accessible device\", {\n data: { error },\n });\n },\n Right: (hidDevices) => {\n this._transportDiscoveredDevices.next(\n hidDevices.map((hidDevice) =>\n this.mapHIDDeviceToTransportDiscoveredDevice(hidDevice),\n ),\n );\n },\n });\n }\n\n private async promptDeviceAccess(): Promise<\n Either<PromptDeviceAccessError, NodeHIDDevice[]>\n > {\n return EitherAsync.liftEither(this.hidApi)\n .map(async (hidApi) => {\n let hidDevices: NodeHIDDevice[] = [];\n\n try {\n const allDevices = await hidApi.devicesAsync();\n\n hidDevices = allDevices.filter(\n (d) => d.vendorId === LEDGER_VENDOR_ID,\n );\n await this.updateTransportDiscoveredDevices();\n } catch (error) {\n const deviceError = new NoAccessibleDeviceError(error);\n this._logger.error(`promptDeviceAccess: error requesting device`, {\n data: { error },\n });\n throw deviceError;\n }\n\n this._logger.debug(\n `promptDeviceAccess: hidDevices len ${hidDevices.length}`,\n );\n\n // Granted access to 0 device (by clicking on cancel for ex) results in an error\n if (hidDevices.length === 0) {\n this._logger.warn(\"No device was selected\");\n throw new NoAccessibleDeviceError(\"No selected device\");\n }\n\n const discoveredHidDevices: NodeHIDDevice[] = [];\n\n for (const hidDevice of hidDevices) {\n discoveredHidDevices.push(hidDevice);\n\n this._logger.debug(`promptDeviceAccess: selected device`, {\n data: { hidDevice },\n });\n }\n\n return discoveredHidDevices;\n })\n .run();\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this._logger.debug(\"startDiscovering\");\n\n return from(this.promptDeviceAccess()).pipe(\n switchMap((either) => {\n return either.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting accessible device\", {\n data: { error },\n });\n throw error;\n },\n Right: (hidDevices) => {\n this._logger.info(`Got access to ${hidDevices.length} HID devices`);\n\n const discoveredDevices = hidDevices.map((hidDevice) => {\n return this.mapHIDDeviceToTransportDiscoveredDevice(hidDevice);\n });\n\n return from(discoveredDevices);\n },\n });\n }),\n );\n }\n\n stopDiscovering(): void {\n /**\n * This does nothing because the startDiscovering method is just a\n * promise wrapped into an observable. So there is no need to stop it.\n */\n }\n\n private startListeningToConnectionEvents(): void {\n this._logger.debug(\"startListeningToConnectionEvents\");\n\n usb.on(\"attach\", (device) => {\n this.handleDeviceConnection(device);\n });\n\n usb.on(\"detach\", (device) => {\n this.handleDeviceDisconnection(device);\n });\n\n process.on(\"exit\", () => {\n usb.unrefHotplugEvents();\n usb.removeAllListeners();\n });\n }\n\n private stopListeningToConnectionEvents(): void {\n this._logger.debug(\"stopListeningToConnectionEvents\");\n this._connectionListenersAbortController.abort();\n usb.removeAllListeners();\n }\n\n /**\n * Connect to a HID USB device and update the internal state of the associated device\n */\n async connect({\n deviceId,\n onDisconnect,\n }: {\n deviceId: DeviceId;\n onDisconnect: DisconnectHandler;\n }): Promise<Either<ConnectError, TransportConnectedDevice>> {\n this._logger.debug(\"connect\", { data: { deviceId } });\n\n const matchingInternalDevice = this._transportDiscoveredDevices\n .getValue()\n .find((internalDevice) => internalDevice.id === deviceId);\n\n if (!matchingInternalDevice) {\n this._logger.error(`Unknown device ${deviceId}`);\n return Left(new UnknownDeviceError(`Unknown device ${deviceId}`));\n }\n\n const alreadyExistingDeviceConnection =\n this._deviceConnectionsByHidDevice.get(matchingInternalDevice.hidDevice);\n if (alreadyExistingDeviceConnection) {\n return Right(\n new TransportConnectedDevice({\n id: deviceId,\n deviceModel: matchingInternalDevice.deviceModel,\n type: this.connectionType,\n sendApdu: (...args) =>\n alreadyExistingDeviceConnection.sendApdu(...args),\n transport: this.identifier,\n }),\n );\n }\n\n const nodeHidApduSender = this._deviceApduSenderFactory({\n dependencies: { device: matchingInternalDevice.hidDevice },\n apduSenderFactory: this._apduSenderFactory,\n apduReceiverFactory: this._apduReceiverFactory,\n loggerFactory: this._loggerServiceFactory,\n });\n\n const deviceConnection = this._deviceConnectionStateMachineFactory({\n deviceId,\n deviceApduSender: nodeHidApduSender,\n timeoutDuration: RECONNECT_DEVICE_TIMEOUT,\n tryToReconnect: () => {\n this._deviceConnectionsByHidDevice.forEach(\n (deviceConnection, hidDevice) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsPendingReconnection.add(deviceConnection);\n this._deviceConnectionsByHidDevice.delete(hidDevice);\n }\n },\n );\n },\n onTerminated: () => {\n this._deviceConnectionsPendingReconnection.forEach(\n (deviceConnection) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsPendingReconnection.delete(\n deviceConnection,\n );\n onDisconnect(deviceConnection.getDeviceId());\n }\n },\n );\n this._deviceConnectionsByHidDevice.forEach(\n (deviceConnection, hidDevice) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsByHidDevice.delete(hidDevice);\n onDisconnect(deviceConnection.getDeviceId());\n }\n },\n );\n },\n });\n\n try {\n await deviceConnection.setupConnection();\n } catch (error) {\n this._logger.error(\"Error while setting up device connection\", {\n data: { error },\n });\n\n return Left(new OpeningConnectionError(error));\n }\n\n this._deviceConnectionsByHidDevice.set(\n matchingInternalDevice.hidDevice,\n deviceConnection,\n );\n\n const connectedDevice = new TransportConnectedDevice({\n sendApdu: (...args) => deviceConnection.sendApdu(...args),\n deviceModel: matchingInternalDevice.deviceModel,\n id: deviceId,\n type: this.connectionType,\n transport: this.identifier,\n });\n\n return Right(connectedDevice);\n }\n\n private getDeviceModel(\n hidDevice: NodeHIDDevice,\n ): Maybe<TransportDeviceModel> {\n const { productId } = hidDevice;\n const matchingModel = this._deviceModelDataSource.getAllDeviceModels().find(\n (deviceModel) =>\n // outside of bootloader mode, the value that we need to identify a device model is the first byte of the actual hidDevice.productId\n deviceModel.usbProductId === productId >> 8 ||\n deviceModel.bootloaderUsbProductId === productId,\n );\n return matchingModel ? Maybe.of(matchingModel) : Maybe.zero();\n }\n\n private getHidUsbProductId(hidDevice: NodeHIDDevice): number {\n return this.getDeviceModel(hidDevice).caseOf({\n Just: (deviceModel) => deviceModel.usbProductId,\n Nothing: () => hidDevice.productId >> 8,\n });\n }\n\n /**\n * Disconnect from a HID USB device\n */\n async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }): Promise<Either<DmkError, void>> {\n this._logger.debug(\"disconnect\", { data: { connectedDevice: params } });\n\n const matchingDeviceConnection = Array.from(\n this._deviceConnectionsByHidDevice.values(),\n ).find(\n (deviceConnection) =>\n deviceConnection.getDeviceId() === params.connectedDevice.id,\n );\n\n if (!matchingDeviceConnection) {\n this._logger.error(\"No matching device connection found\", {\n data: { connectedDevice: params },\n });\n return Promise.resolve(\n Left(\n new UnknownDeviceError(`Unknown device ${params.connectedDevice.id}`),\n ),\n );\n }\n\n matchingDeviceConnection.closeConnection();\n return Promise.resolve(Right(undefined));\n }\n\n /**\n * Handle the disconnection event of a HID device\n * @param device USB device that was detached\n */\n private async handleDeviceDisconnection(device: Device): Promise<void> {\n const { idVendor, idProduct } = device.deviceDescriptor;\n\n if (idVendor !== LEDGER_VENDOR_ID) {\n return;\n }\n\n this._logger.info(\"[handleDeviceDisconnectionEvent] Device disconnected\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n\n this.updateTransportDiscoveredDevices();\n\n const matchingDeviceConnection = (): Maybe<\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > => {\n for (const [\n hidDevice,\n deviceConnection,\n ] of this._deviceConnectionsByHidDevice.entries()) {\n if (\n hidDevice.vendorId === idVendor &&\n (hidDevice.productId >> 8 === idProduct ||\n hidDevice.productId === idProduct)\n ) {\n return Just(deviceConnection);\n }\n }\n return Nothing;\n };\n\n matchingDeviceConnection().caseOf({\n Just: (deviceConnection) => {\n try {\n deviceConnection.eventDeviceDisconnected();\n } catch (error) {\n this._logger.error(\"Error while handling device disconnection\", {\n data: { error },\n });\n }\n },\n Nothing: () => {\n this._logger.error(\"No matching device connection found\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n },\n });\n }\n\n private async handleDeviceReconnection(\n deviceConnection: DeviceConnectionStateMachine<NodeHidApduSenderDependencies>,\n hidDevice: NodeHIDDevice,\n ) {\n try {\n this._deviceConnectionsPendingReconnection.delete(deviceConnection);\n deviceConnection.setDependencies({ device: hidDevice });\n await deviceConnection.setupConnection();\n this._deviceConnectionsByHidDevice.set(hidDevice, deviceConnection);\n deviceConnection.eventDeviceConnected();\n } catch (error) {\n this._logger.error(\"Error while reconnecting to device\", {\n data: { error },\n });\n deviceConnection.closeConnection();\n }\n }\n\n /**\n * Handle the connection event of a HID device\n * @param device USB device that was attached\n */\n private async handleDeviceConnection(device: Device): Promise<void> {\n const { idVendor, idProduct } = device.deviceDescriptor;\n\n if (idVendor !== LEDGER_VENDOR_ID) {\n return;\n }\n\n this._logger.info(\"[handleDeviceConnection] New device connected\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n\n // Find the corresponding HID device by matching vendor and product IDs\n const eitherDevices = await this.getDevices();\n eitherDevices.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting HID devices for reconnection\", {\n data: { error },\n });\n },\n Right: async (hidDevices) => {\n const matchingHidDevice = hidDevices.find(\n (hidDevice) =>\n hidDevice.vendorId === idVendor &&\n (hidDevice.productId >> 8 === idProduct ||\n hidDevice.productId === idProduct),\n );\n\n if (!matchingHidDevice) {\n this._logger.debug(\n \"[handleDeviceConnection] No matching HID device found\",\n {\n data: { vendorId: idVendor, productId: idProduct },\n },\n );\n\n return;\n }\n\n // Check if there's a pending reconnection for a device with matching product ID\n const matchingDeviceConnection = Array.from(\n this._deviceConnectionsPendingReconnection,\n ).find(\n (deviceConnection) =>\n this.getHidUsbProductId(\n deviceConnection.getDependencies().device,\n ) === this.getHidUsbProductId(matchingHidDevice),\n );\n\n if (matchingDeviceConnection) {\n await this.handleDeviceReconnection(\n matchingDeviceConnection,\n matchingHidDevice,\n );\n }\n\n /**\n * Note: we do this after handling the reconnection to allow the newly\n * discovered device to keep the same DeviceId as the previous one in case\n * of a reconnection.\n */\n await this.updateTransportDiscoveredDevices();\n },\n });\n }\n\n public destroy() {\n this.stopListeningToConnectionEvents();\n this._deviceConnectionsByHidDevice.forEach((connection) => {\n connection.closeConnection();\n });\n this._deviceConnectionsPendingReconnection.clear();\n }\n}\n\nexport const nodeHidTransportFactory: TransportFactory = ({\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n}) =>\n new NodeHidTransport(\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n );\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["NodeHidTransport_exports", "__export", "NodeHidTransport", "nodeHidIdentifier", "nodeHidTransportFactory", "__toCommonJS", "import_device_management_kit", "import_node_hid", "import_purify_ts", "import_rxjs", "import_usb", "import_uuid", "import_NodeHidConfig", "import_Errors", "import_NodeHidApduSender", "NodeHIDAPI", "_deviceModelDataSource", "_loggerServiceFactory", "_apduSenderFactory", "_apduReceiverFactory", "_deviceConnectionStateMachineFactory", "args", "_deviceApduSenderFactory", "error", "hidApi", "ledgerDevices", "hidDevice", "device", "deviceError", "existingDiscoveredDevice", "internalDevice", "existingDeviceConnection", "deviceModel", "id", "uuid", "discoveredDevice", "devices", "hidDevices", "d", "discoveredHidDevices", "either", "discoveredDevices", "deviceId", "onDisconnect", "matchingInternalDevice", "alreadyExistingDeviceConnection", "nodeHidApduSender", "deviceConnection", "connectedDevice", "productId", "matchingModel", "params", "matchingDeviceConnection", "idVendor", "idProduct", "matchingHidDevice", "connection", "deviceModelDataSource", "loggerServiceFactory", "apduSenderServiceFactory", "apduReceiverServiceFactory"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var s=require("@ledgerhq/device-management-kit"),w=require("purify-ts"),m=require("rxjs"),b=require("../data/NodeHidConfig"),a=require("../model/HIDDevice.stub"),V=require("./NodeHidTransport");const d=vi.fn();vi.mock("node-hid",()=>({devicesAsync:(...n)=>d(...n),HIDAsync:{open:vi.fn()}}));const I=vi.fn(),T=[],N=[];vi.mock("usb",()=>({usb:{on:(n,l)=>{I(n,l),n==="attach"?T.push(l):n==="detach"&&N.push(l)},removeAllListeners:vi.fn(),unrefHotplugEvents:vi.fn()},Device:class{}}));class M{constructor(l,f){this.subscribers=l,this.tag=f}subscribers=[];tag="";error=vi.fn();warn=vi.fn();info=vi.fn();debug=vi.fn()}const R=new s.StaticDeviceModelDataSource,P=new M([],"node-hid"),D=(0,a.nodeHidDeviceStubBuilder)(),v=async()=>{const n=await vi.importActual("timers");return new Promise(n.setImmediate)},H=(n,l)=>({deviceDescriptor:{idVendor:n,idProduct:l}}),k=(n,l)=>{const f=H(n,l);T.forEach(h=>h(f))},y=(n,l)=>{const f=H(n,l);N.forEach(h=>h(f))};describe("NodeHidTransport",()=>{let n,l,f,h=vi.fn();const C=vi.fn(),E=vi.fn(),O={sendApdu:vi.fn().mockResolvedValue((0,w.Right)({data:new Uint8Array,statusCode:new Uint8Array([144,0])})),getDependencies:vi.fn().mockReturnValue({device:D}),setDependencies:vi.fn(),closeConnection:vi.fn(),setupConnection:vi.fn()},g={getDependencies:vi.fn().mockReturnValue({device:D}),setDependencies:vi.fn(),getDeviceId:vi.fn(),sendApdu:vi.fn().mockResolvedValue((0,w.Right)({data:new Uint8Array,statusCode:new Uint8Array([144,0])})),setupConnection:vi.fn(),eventDeviceConnected:C,eventDeviceDisconnected:E,closeConnection:vi.fn()};function B(){T.length=0,N.length=0,l=vi.fn(),f=vi.fn(),h=vi.fn(e=>({...g,getDeviceId:vi.fn().mockReturnValue(e.deviceId),getDependencies:e.deviceApduSender.getDependencies,setDependencies:e.deviceApduSender.setDependencies}));const c=vi.fn(e=>({...O,getDependencies:()=>e.dependencies,setDependencies:i=>e.dependencies=i}));n=new V.NodeHidTransport(R,()=>P,f,l,h,c)}beforeEach(()=>{B(),vi.useFakeTimers()}),afterEach(()=>{vi.restoreAllMocks(),vi.clearAllMocks(),vi.useRealTimers()});const x=(c,e)=>{n.startDiscovering().subscribe({next:c,error:e})};describe("isSupported",()=>{it("should always support the transport",()=>{expect(n.isSupported()).toBe(!0)})}),describe("getIdentifier",()=>{it("should return NODE-HID identifier",()=>{expect(n.getIdentifier()).toBe("NODE-HID")})}),describe("startDiscovering",()=>{R.getAllDeviceModels().flatMap(e=>[{testTitle:`should emit device when discovered (${e.productName})`,hidDevice:(0,a.nodeHidDeviceStubBuilder)({productId:e.usbProductId<<8,product:e.productName}),expectedDeviceModel:e},{testTitle:`should emit device when discovered (${e.productName}, bootloader)`,hidDevice:(0,a.nodeHidDeviceStubBuilder)({productId:e.bootloaderUsbProductId,product:e.productName}),expectedDeviceModel:e}]).forEach(e=>{it(e.testTitle,()=>new Promise((i,t)=>{d.mockResolvedValueOnce([e.hidDevice]),x(o=>{try{expect(o).toEqual(expect.objectContaining({deviceModel:e.expectedDeviceModel})),i()}catch(r){t(r)}},o=>{t(o)})}))}),it("should emit multiple devices if several are connected",()=>new Promise((e,i)=>{d.mockResolvedValueOnce([D,(0,a.nodeHidDeviceStubBuilder)({productId:20497,product:"Ledger Nano S Plus",path:"/dev/hidraw1"})]);let t=0;x(o=>{try{switch(t){case 0:expect(o).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X,productName:"Ledger Nano X",usbProductId:64})}));break;case 1:expect(o).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_SP,productName:"Ledger Nano S Plus",usbProductId:80})})),e();break}t++}catch(r){i(r)}},o=>{i(o)})})),it("should throw DeviceNotRecognizedError if the device is not recognized",()=>new Promise((e,i)=>{d.mockResolvedValueOnce([(0,a.nodeHidDeviceStubBuilder)({productId:16962})]),x(()=>{i("should not return a device")},t=>{expect(t).toBeInstanceOf(s.DeviceNotRecognizedError),e()})})),it("should emit an error if devicesAsync throws",()=>new Promise((e,i)=>{const t="devices async error";d.mockImplementationOnce(()=>{throw new Error(t)}),x(()=>{i("should not return a device")},o=>{expect(o).toBeInstanceOf(s.NoAccessibleDeviceError),expect(o).toStrictEqual(new s.NoAccessibleDeviceError(new Error(t))),e()})})),it("should emit an error if no devices are found",()=>new Promise((e,i)=>{d.mockResolvedValueOnce([]),x(t=>{i(`Should not emit any value, but emitted ${JSON.stringify(t)}`)},t=>{try{expect(t).toBeInstanceOf(s.NoAccessibleDeviceError),e()}catch(o){i(o)}})})),it("should emit the same discoveredDevice object if discovered twice in a row",async()=>{d.mockResolvedValue([D]);const e=await new Promise((t,o)=>{x(()=>t(),r=>o(r))}),i=await new Promise((t,o)=>{x(()=>t(),r=>o(r))});expect(i).toBe(e)})}),describe("destroy",()=>{it("should stop monitoring connections when destroyed",()=>{const c=vi.spyOn(AbortController.prototype,"abort");n.destroy(),expect(c).toHaveBeenCalled()})}),describe("connect",()=>{it("should throw UnknownDeviceError if no internal device",async()=>{const c={deviceId:"fake",onDisconnect:vi.fn()},e=await n.connect(c);expect(e).toStrictEqual((0,w.Left)(new s.UnknownDeviceError("Unknown device fake")))}),it("should throw OpeningConnectionError if the device cannot be opened",async()=>{const c="cannot be opened";g.setupConnection.mockRejectedValueOnce(new Error(c)),d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const e=await(0,m.lastValueFrom)(n.startDiscovering()),i=await n.connect({deviceId:e.id,onDisconnect:vi.fn()});expect(i.isLeft()).toBe(!0),expect(i.extract()).toBeInstanceOf(s.OpeningConnectionError)}),it("should return a device if available",async()=>{d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const c=await(0,m.lastValueFrom)(n.startDiscovering()),e=await n.connect({deviceId:c.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0),expect(e.extract()).toEqual(expect.objectContaining({id:c.id}))}),it("should return an existing connected device",async()=>{d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const c=await(0,m.lastValueFrom)(n.startDiscovering());await n.connect({deviceId:c.id,onDisconnect:vi.fn()});const e=await n.connect({deviceId:c.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0),expect(e.extract()).toEqual(expect.objectContaining({id:c.id}))})}),describe("disconnect",()=>{it("should throw an error if the device is not connected",async()=>{const c=(0,s.connectedDeviceStubBuilder)(),e=await n.disconnect({connectedDevice:c});expect(e).toStrictEqual((0,w.Left)(new s.UnknownDeviceError(`Unknown device ${c.id}`)))}),it("should disconnect if the device is connected",async()=>{d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const c=await(0,m.lastValueFrom)(n.startDiscovering()),e=await n.connect({deviceId:c.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0);const i=await n.disconnect({connectedDevice:e.unsafeCoerce()});expect(i).toStrictEqual((0,w.Right)(void 0))}),it("should call disconnect handler if a connected device is unplugged",async()=>{let c=vi.fn();h.mockImplementationOnce(S=>(c=S.onTerminated,{...g,getDeviceId:vi.fn().mockReturnValue(S.deviceId)}));const e=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),i=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",productId:20497});d.mockResolvedValueOnce([e,i]),d.mockResolvedValue([e,i]);const t=await(0,m.lastValueFrom)(n.startDiscovering().pipe((0,m.toArray)()));expect(t.length).toStrictEqual(2);const o=vi.fn(),r=await n.connect({deviceId:t[0].id,onDisconnect:o}),u=vi.fn(),p=await n.connect({deviceId:t[1].id,onDisconnect:u});expect(r.isRight()).toStrictEqual(!0),expect(p.isRight()).toStrictEqual(!0),c(),expect(o).toHaveBeenCalled(),expect(u).not.toHaveBeenCalled()}),it("should call disconnect handler if a connected device is unplugged while reconnecting",async()=>{let c=vi.fn(),e=vi.fn();h.mockImplementationOnce(A=>(c=A.onTerminated,e=A.tryToReconnect,{...g,getDeviceId:vi.fn().mockReturnValue(A.deviceId)}));const i=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),t=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",productId:20497});d.mockResolvedValueOnce([i,t]),d.mockResolvedValue([i,t]);const o=await(0,m.lastValueFrom)(n.startDiscovering().pipe((0,m.toArray)()));expect(o.length).toStrictEqual(2);const r=vi.fn(),u=await n.connect({deviceId:o[0].id,onDisconnect:r}),p=vi.fn(),S=await n.connect({deviceId:o[1].id,onDisconnect:p});expect(u.isRight()).toStrictEqual(!0),expect(S.isRight()).toStrictEqual(!0),e(),c(),expect(r).toHaveBeenCalled(),expect(p).not.toHaveBeenCalled()})}),describe("reconnect",()=>{it("should stop disconnection if reconnection happens",()=>new Promise((c,e)=>{const i=vi.fn();let t=vi.fn();const o=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),r=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1"});d.mockResolvedValueOnce([o]),d.mockResolvedValue([o,r]),h.mockImplementationOnce(u=>(t=u.tryToReconnect,{...g,getDeviceId:vi.fn().mockReturnValue(u.deviceId),getDependencies:u.deviceApduSender.getDependencies,setDependencies:u.deviceApduSender.setDependencies})),x(async u=>{try{await n.connect({deviceId:u.id,onDisconnect:i}),y(11415,64),await v(),expect(E).toHaveBeenCalled(),t(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT/3),k(11415,64),await v(),expect(C).toHaveBeenCalled(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT),expect(i).not.toHaveBeenCalled(),c()}catch(p){e(p)}})})),it("should be able to reconnect twice in a row if the device is unplugged and replugged twice",()=>new Promise((c,e)=>{const i=vi.fn();let t=vi.fn();const o=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),r=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1"}),u=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw2"});d.mockResolvedValueOnce([o]),d.mockResolvedValue([o,r,u]),h.mockImplementationOnce(p=>(t=p.tryToReconnect,{...g,getDeviceId:vi.fn().mockReturnValue(p.deviceId),getDependencies:p.deviceApduSender.getDependencies,setDependencies:p.deviceApduSender.setDependencies})),x(async p=>{await n.connect({deviceId:p.id,onDisconnect:i});try{y(11415,64),await v(),expect(E).toHaveBeenCalled(),t(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT/3),k(11415,64),await v(),expect(C).toHaveBeenCalled(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT),expect(i).not.toHaveBeenCalled(),y(11415,64),await v(),expect(E).toHaveBeenCalled(),t(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT/3),k(11415,64),await v(),expect(C).toHaveBeenCalled(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT),expect(i).not.toHaveBeenCalled(),c()}catch(S){e(S)}})}))}),describe("listenToAvailableDevices",()=>{it("should emit the devices already connected before listening",async()=>{const c=(0,a.nodeHidDeviceStubBuilder)();d.mockResolvedValue([c]);const e=vi.fn(),i=vi.fn();let t=[];n.listenToAvailableDevices().subscribe({next:o=>{t=o},complete:e,error:i}),await v(),expect(t).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),expect(e).not.toHaveBeenCalled(),expect(i).not.toHaveBeenCalled()}),it("should emit the new list of devices after connection and disconnection events",async()=>{B();const c=(0,a.nodeHidDeviceStubBuilder)({productId:R.getDeviceModel({id:s.DeviceModelId.NANO_X}).usbProductId<<8,path:"/dev/hidraw0"}),e=(0,a.nodeHidDeviceStubBuilder)({productId:R.getDeviceModel({id:s.DeviceModelId.STAX}).usbProductId<<8,path:"/dev/hidraw1"});d.mockResolvedValue([c]);const i=vi.fn(),t=vi.fn();let o=[];n.listenToAvailableDevices().subscribe({next:r=>{o=r},complete:i,error:t}),await v(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),d.mockResolvedValue([c,e]),k(11415,96),await v(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})}),expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.STAX})})]),d.mockResolvedValue([e]),y(11415,64),await v(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.STAX})})]),expect(i).not.toHaveBeenCalled(),expect(t).not.toHaveBeenCalled()}),it("should preserve DeviceId in case the device has been disconnected and reconnected before the timeout",async()=>{const c=(0,a.nodeHidDeviceStubBuilder)();d.mockResolvedValue([c]);const e=vi.fn(),i=vi.fn();let t=[];n.listenToAvailableDevices().subscribe({next:r=>{t=r},complete:e,error:i}),await v();const o=t[0]?.id;expect(o).toBeTruthy(),expect(t[0]?.deviceModel?.id).toBe(s.DeviceModelId.NANO_X),await n.connect({deviceId:t[0].id,onDisconnect:vi.fn()}),await v(),d.mockResolvedValue([]),y(11415,64),await v(),expect(t).toEqual([]),d.mockResolvedValue([c]),k(11415,64),await v(),expect(t).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),expect(t[0]?.id).toBeTruthy(),expect(t[0]?.id).toBe(o)})}),describe("USB connection events",()=>{it("should register usb attach and detach listeners on construction",()=>{expect(I).toHaveBeenCalledWith("attach",expect.any(Function)),expect(I).toHaveBeenCalledWith("detach",expect.any(Function))}),it("should ignore non-Ledger USB devices",async()=>{d.mockResolvedValue([D]);let c=[];n.listenToAvailableDevices().subscribe({next:e=>{c=e}}),await v(),expect(c.length).toBe(1),k(4660,22136),await v(),expect(c.length).toBe(1)})})});
|
|
2
|
+
//# sourceMappingURL=NodeHidTransport.test.js.map
|