@onekeyfe/hd-transport-web-device 1.2.8-alpha.51 → 1.2.8-alpha.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -7
- package/dist/index.js +10 -86
- package/dist/webusb.d.ts +1 -7
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/webusb.ts +11 -90
- package/__tests__/webusb-identity-probe.test.ts +0 -268
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
|
13
13
|
protocolType?: ProtocolType;
|
|
14
14
|
}
|
|
15
15
|
declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
16
|
-
private readonly identityProbe;
|
|
17
16
|
messages: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
|
|
18
17
|
/** Protobuf schema for Protocol V2 transports. */
|
|
19
18
|
messagesV2: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
|
|
@@ -30,8 +29,6 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
30
29
|
private staleProtocolPaths;
|
|
31
30
|
/** Paths currently acquired (between a successful acquire() and its release()). */
|
|
32
31
|
private acquiredPaths;
|
|
33
|
-
private identityProbeDevices;
|
|
34
|
-
private identityProbeCleanupPaths;
|
|
35
32
|
/**
|
|
36
33
|
* The exact USBDevice object each cached protocol was probed against. The
|
|
37
34
|
* browser returns the same object identity for a device as long as it stays
|
|
@@ -56,7 +53,7 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
56
53
|
configurationId: number;
|
|
57
54
|
endpointId: number;
|
|
58
55
|
interfaceId: number;
|
|
59
|
-
constructor(
|
|
56
|
+
constructor();
|
|
60
57
|
/**
|
|
61
58
|
* Initialize WebUSB transport
|
|
62
59
|
*/
|
|
@@ -132,9 +129,6 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
132
129
|
* Discovers interface/endpoint numbers from USB descriptors on first connection.
|
|
133
130
|
*/
|
|
134
131
|
connectToDevice(path: string, first: boolean): Promise<undefined>;
|
|
135
|
-
private assertHandleAvailable;
|
|
136
|
-
private claimIdentityProbeHandle;
|
|
137
|
-
private releaseIdentityProbeHandle;
|
|
138
132
|
private closeOpenDevice;
|
|
139
133
|
private clearEndpointHalt;
|
|
140
134
|
/**
|
package/dist/index.js
CHANGED
|
@@ -56,8 +56,6 @@ const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
|
|
|
56
56
|
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
57
57
|
let activeWebUsbTransport;
|
|
58
58
|
let usbDisconnectListenerAttached = false;
|
|
59
|
-
const identityProbeHandles = new WeakSet();
|
|
60
|
-
const openingHandles = new WeakSet();
|
|
61
59
|
function registerActiveWebUsbTransport(instance, usb) {
|
|
62
60
|
activeWebUsbTransport = instance;
|
|
63
61
|
if (usbDisconnectListenerAttached)
|
|
@@ -74,20 +72,17 @@ function registerActiveWebUsbTransport(instance, usb) {
|
|
|
74
72
|
});
|
|
75
73
|
}
|
|
76
74
|
class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
77
|
-
constructor(
|
|
75
|
+
constructor() {
|
|
78
76
|
super({
|
|
79
77
|
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
80
78
|
maxFrameBytes: transport.PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
81
79
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
82
80
|
});
|
|
83
|
-
this.identityProbe = identityProbe;
|
|
84
81
|
this.deviceProtocol = new Map();
|
|
85
82
|
this.confirmedDeviceProtocols = new Map();
|
|
86
83
|
this.deviceProtocolHints = new Map();
|
|
87
84
|
this.staleProtocolPaths = new Set();
|
|
88
85
|
this.acquiredPaths = new Set();
|
|
89
|
-
this.identityProbeDevices = new Map();
|
|
90
|
-
this.identityProbeCleanupPaths = new Set();
|
|
91
86
|
this.probedDeviceObjects = new Map();
|
|
92
87
|
this.deviceEndpoints = new Map();
|
|
93
88
|
this.name = 'WebUsbTransport';
|
|
@@ -106,8 +101,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
106
101
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB is not supported by current browsers');
|
|
107
102
|
}
|
|
108
103
|
this.usb = usb;
|
|
109
|
-
|
|
110
|
-
registerActiveWebUsbTransport(this, usb);
|
|
104
|
+
registerActiveWebUsbTransport(this, usb);
|
|
111
105
|
}
|
|
112
106
|
emitDeviceDisconnect(path, device) {
|
|
113
107
|
var _a, _b;
|
|
@@ -156,13 +150,6 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
156
150
|
}
|
|
157
151
|
enumerate() {
|
|
158
152
|
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
-
for (const path of this.identityProbeCleanupPaths) {
|
|
160
|
-
try {
|
|
161
|
-
yield this.release(path);
|
|
162
|
-
}
|
|
163
|
-
catch (_a) {
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
153
|
yield this.getConnectedDevices();
|
|
167
154
|
return this.deviceList;
|
|
168
155
|
});
|
|
@@ -200,10 +187,6 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
200
187
|
return __awaiter(this, void 0, void 0, function* () {
|
|
201
188
|
if (!input.path)
|
|
202
189
|
return;
|
|
203
|
-
if (this.identityProbe) {
|
|
204
|
-
yield this.getConnectedDevices();
|
|
205
|
-
this.claimIdentityProbeHandle(input.path, yield this.findDevice(input.path));
|
|
206
|
-
}
|
|
207
190
|
try {
|
|
208
191
|
yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
|
|
209
192
|
yield this.closeOpenDevice(input.path);
|
|
@@ -265,12 +248,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
265
248
|
catch (e) {
|
|
266
249
|
this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
|
|
267
250
|
this.acquiredPaths.delete(input.path);
|
|
268
|
-
|
|
269
|
-
yield this.closeOpenDevice(input.path);
|
|
270
|
-
}
|
|
271
|
-
finally {
|
|
272
|
-
this.releaseIdentityProbeHandle(input.path);
|
|
273
|
-
}
|
|
251
|
+
yield this.closeOpenDevice(input.path);
|
|
274
252
|
throw e;
|
|
275
253
|
}
|
|
276
254
|
});
|
|
@@ -397,22 +375,12 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
397
375
|
var _a;
|
|
398
376
|
return __awaiter(this, void 0, void 0, function* () {
|
|
399
377
|
let device = yield this.findDevice(path);
|
|
400
|
-
if (this.identityProbe && this.identityProbeDevices.get(path) !== device) {
|
|
401
|
-
yield this.closeOpenDevice(path);
|
|
402
|
-
this.releaseIdentityProbeHandle(path);
|
|
403
|
-
this.claimIdentityProbeHandle(path, device);
|
|
404
|
-
}
|
|
405
|
-
this.assertHandleAvailable(device);
|
|
406
378
|
if (!device.opened) {
|
|
407
|
-
openingHandles.add(device);
|
|
408
379
|
try {
|
|
409
380
|
yield device.open();
|
|
410
381
|
}
|
|
411
382
|
catch (error) {
|
|
412
|
-
return
|
|
413
|
-
}
|
|
414
|
-
finally {
|
|
415
|
-
openingHandles.delete(device);
|
|
383
|
+
return this.throwWebUsbIoError(path, 'open', error);
|
|
416
384
|
}
|
|
417
385
|
}
|
|
418
386
|
if (first || this.deviceProtocol.get(path) !== 'V1') {
|
|
@@ -425,22 +393,12 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
425
393
|
}
|
|
426
394
|
yield this.getConnectedDevices();
|
|
427
395
|
device = yield this.findDevice(path);
|
|
428
|
-
if (this.identityProbe && this.identityProbeDevices.get(path) !== device) {
|
|
429
|
-
yield this.closeOpenDevice(path);
|
|
430
|
-
this.releaseIdentityProbeHandle(path);
|
|
431
|
-
this.claimIdentityProbeHandle(path, device);
|
|
432
|
-
}
|
|
433
|
-
this.assertHandleAvailable(device);
|
|
434
396
|
if (!device.opened) {
|
|
435
|
-
openingHandles.add(device);
|
|
436
397
|
try {
|
|
437
398
|
yield device.open();
|
|
438
399
|
}
|
|
439
400
|
catch (error) {
|
|
440
|
-
return
|
|
441
|
-
}
|
|
442
|
-
finally {
|
|
443
|
-
openingHandles.delete(device);
|
|
401
|
+
return this.throwWebUsbIoError(path, 'open', error);
|
|
444
402
|
}
|
|
445
403
|
}
|
|
446
404
|
if (first ||
|
|
@@ -460,39 +418,12 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
460
418
|
yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
461
419
|
});
|
|
462
420
|
}
|
|
463
|
-
assertHandleAvailable(device) {
|
|
464
|
-
if (identityProbeHandles.has(device) &&
|
|
465
|
-
![...this.identityProbeDevices.values()].includes(device)) {
|
|
466
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'USB device is already in use');
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
claimIdentityProbeHandle(path, device) {
|
|
470
|
-
if (device.opened || openingHandles.has(device) || identityProbeHandles.has(device)) {
|
|
471
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'USB device is already in use');
|
|
472
|
-
}
|
|
473
|
-
identityProbeHandles.add(device);
|
|
474
|
-
this.identityProbeDevices.set(path, device);
|
|
475
|
-
}
|
|
476
|
-
releaseIdentityProbeHandle(path) {
|
|
477
|
-
const device = this.identityProbeDevices.get(path);
|
|
478
|
-
if (device === null || device === void 0 ? void 0 : device.opened) {
|
|
479
|
-
this.identityProbeCleanupPaths.add(path);
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
if (device)
|
|
483
|
-
identityProbeHandles.delete(device);
|
|
484
|
-
this.identityProbeDevices.delete(path);
|
|
485
|
-
this.identityProbeCleanupPaths.delete(path);
|
|
486
|
-
}
|
|
487
421
|
closeOpenDevice(path) {
|
|
488
422
|
var _a, _b, _c, _d;
|
|
489
423
|
return __awaiter(this, void 0, void 0, function* () {
|
|
490
|
-
const current = this.
|
|
491
|
-
? this.identityProbeDevices.get(path)
|
|
492
|
-
: (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
|
|
424
|
+
const current = (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
|
|
493
425
|
if (!(current === null || current === void 0 ? void 0 : current.opened))
|
|
494
426
|
return;
|
|
495
|
-
this.assertHandleAvailable(current);
|
|
496
427
|
const endpoints = this.deviceEndpoints.get(path);
|
|
497
428
|
const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
|
|
498
429
|
try {
|
|
@@ -506,8 +437,6 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
506
437
|
}
|
|
507
438
|
catch (error) {
|
|
508
439
|
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[WebUsbTransport] close before reconnect failed:', error);
|
|
509
|
-
if (this.identityProbe)
|
|
510
|
-
throw error;
|
|
511
440
|
}
|
|
512
441
|
});
|
|
513
442
|
}
|
|
@@ -820,10 +749,10 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
820
749
|
return false;
|
|
821
750
|
}
|
|
822
751
|
try {
|
|
823
|
-
|
|
752
|
+
yield this.callProtocolV1(path, 'Initialize', {}, {
|
|
824
753
|
timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
|
|
825
754
|
});
|
|
826
|
-
return
|
|
755
|
+
return true;
|
|
827
756
|
}
|
|
828
757
|
catch (error) {
|
|
829
758
|
if (this.isWebUsbIoHardwareError(error))
|
|
@@ -940,13 +869,8 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
940
869
|
release(path) {
|
|
941
870
|
return __awaiter(this, void 0, void 0, function* () {
|
|
942
871
|
this.acquiredPaths.delete(path);
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
yield this.closeOpenDevice(path);
|
|
946
|
-
}
|
|
947
|
-
finally {
|
|
948
|
-
this.releaseIdentityProbeHandle(path);
|
|
949
|
-
}
|
|
872
|
+
yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
|
|
873
|
+
yield this.closeOpenDevice(path);
|
|
950
874
|
this.deviceEndpoints.delete(path);
|
|
951
875
|
});
|
|
952
876
|
}
|
package/dist/webusb.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
|
9
9
|
protocolType?: ProtocolType;
|
|
10
10
|
}
|
|
11
11
|
export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
12
|
-
private readonly identityProbe;
|
|
13
12
|
messages: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
14
13
|
messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
15
14
|
private protocolV2SchemaSource;
|
|
@@ -18,8 +17,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
18
17
|
private deviceProtocolHints;
|
|
19
18
|
private staleProtocolPaths;
|
|
20
19
|
private acquiredPaths;
|
|
21
|
-
private identityProbeDevices;
|
|
22
|
-
private identityProbeCleanupPaths;
|
|
23
20
|
private probedDeviceObjects;
|
|
24
21
|
private deviceEndpoints;
|
|
25
22
|
name: string;
|
|
@@ -32,7 +29,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
32
29
|
configurationId: number;
|
|
33
30
|
endpointId: number;
|
|
34
31
|
interfaceId: number;
|
|
35
|
-
constructor(
|
|
32
|
+
constructor();
|
|
36
33
|
init(logger: any, emitter?: EventEmitter): void;
|
|
37
34
|
emitDeviceDisconnect(path: string, device?: USBDevice): void;
|
|
38
35
|
markProtocolStale(path: string): void;
|
|
@@ -50,9 +47,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
50
47
|
connect(path: string, first: boolean): Promise<undefined>;
|
|
51
48
|
private discoverEndpoints;
|
|
52
49
|
connectToDevice(path: string, first: boolean): Promise<undefined>;
|
|
53
|
-
private assertHandleAvailable;
|
|
54
|
-
private claimIdentityProbeHandle;
|
|
55
|
-
private releaseIdentityProbeHandle;
|
|
56
50
|
private closeOpenDevice;
|
|
57
51
|
private clearEndpointHalt;
|
|
58
52
|
private assertAcquired;
|
package/dist/webusb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";;AACA,OAAO,SAAS,EAAE,EAQhB,0BAA0B,EAG3B,MAAM,wBAAwB,CAAC;AAahC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAuBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-web-device",
|
|
3
|
-
"version": "1.2.8-alpha.
|
|
3
|
+
"version": "1.2.8-alpha.52",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"lint:fix": "eslint . --fix"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.8-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.8-alpha.
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.8-alpha.52",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.8-alpha.52"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@onekeyfe/hd-transport-electron": "1.2.8-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.8-alpha.52",
|
|
29
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
30
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "389c588beb19de26451c729bb09bc8e7ce6b0717"
|
|
33
33
|
}
|
package/src/webusb.ts
CHANGED
|
@@ -82,10 +82,6 @@ type WebUsbOperation = 'open' | 'claimInterface' | 'transferIn' | 'transferOut';
|
|
|
82
82
|
*/
|
|
83
83
|
let activeWebUsbTransport: WebUsbTransport | undefined;
|
|
84
84
|
let usbDisconnectListenerAttached = false;
|
|
85
|
-
// Only isolated discovery owns these handles. Normal calls must never close or
|
|
86
|
-
// open them, including while native open() is still pending.
|
|
87
|
-
const identityProbeHandles = new WeakSet<USBDevice>();
|
|
88
|
-
const openingHandles = new WeakSet<USBDevice>();
|
|
89
85
|
|
|
90
86
|
function registerActiveWebUsbTransport(instance: WebUsbTransport, usb: USB) {
|
|
91
87
|
activeWebUsbTransport = instance;
|
|
@@ -128,10 +124,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
128
124
|
/** Paths currently acquired (between a successful acquire() and its release()). */
|
|
129
125
|
private acquiredPaths: Set<string> = new Set();
|
|
130
126
|
|
|
131
|
-
private identityProbeDevices = new Map<string, USBDevice>();
|
|
132
|
-
|
|
133
|
-
private identityProbeCleanupPaths = new Set<string>();
|
|
134
|
-
|
|
135
127
|
/**
|
|
136
128
|
* The exact USBDevice object each cached protocol was probed against. The
|
|
137
129
|
* browser returns the same object identity for a device as long as it stays
|
|
@@ -168,7 +160,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
168
160
|
|
|
169
161
|
interfaceId = INTERFACE_ID;
|
|
170
162
|
|
|
171
|
-
constructor(
|
|
163
|
+
constructor() {
|
|
172
164
|
super({
|
|
173
165
|
router: PROTOCOL_V2_CHANNEL_USB,
|
|
174
166
|
maxFrameBytes: PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
@@ -191,7 +183,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
191
183
|
);
|
|
192
184
|
}
|
|
193
185
|
this.usb = usb;
|
|
194
|
-
|
|
186
|
+
registerActiveWebUsbTransport(this, usb);
|
|
195
187
|
}
|
|
196
188
|
|
|
197
189
|
/**
|
|
@@ -275,14 +267,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
275
267
|
* It does NOT prompt the user to select a device
|
|
276
268
|
*/
|
|
277
269
|
async enumerate() {
|
|
278
|
-
// Retry only our failed cleanup before Core skips already-open USB handles.
|
|
279
|
-
for (const path of this.identityProbeCleanupPaths) {
|
|
280
|
-
try {
|
|
281
|
-
await this.release(path);
|
|
282
|
-
} catch {
|
|
283
|
-
// Keep ownership while native close still fails; retry on the next probe.
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
270
|
await this.getConnectedDevices();
|
|
287
271
|
return this.deviceList;
|
|
288
272
|
}
|
|
@@ -327,10 +311,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
327
311
|
*/
|
|
328
312
|
async acquire(input: AcquireInput) {
|
|
329
313
|
if (!input.path) return;
|
|
330
|
-
if (this.identityProbe) {
|
|
331
|
-
await this.getConnectedDevices();
|
|
332
|
-
this.claimIdentityProbeHandle(input.path, await this.findDevice(input.path));
|
|
333
|
-
}
|
|
334
314
|
try {
|
|
335
315
|
await this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
|
|
336
316
|
await this.closeOpenDevice(input.path);
|
|
@@ -414,11 +394,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
414
394
|
} catch (e) {
|
|
415
395
|
this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
|
|
416
396
|
this.acquiredPaths.delete(input.path);
|
|
417
|
-
|
|
418
|
-
await this.closeOpenDevice(input.path);
|
|
419
|
-
} finally {
|
|
420
|
-
this.releaseIdentityProbeHandle(input.path);
|
|
421
|
-
}
|
|
397
|
+
await this.closeOpenDevice(input.path);
|
|
422
398
|
throw e;
|
|
423
399
|
}
|
|
424
400
|
}
|
|
@@ -593,20 +569,11 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
593
569
|
*/
|
|
594
570
|
async connectToDevice(path: string, first: boolean) {
|
|
595
571
|
let device: USBDevice = await this.findDevice(path);
|
|
596
|
-
if (this.identityProbe && this.identityProbeDevices.get(path) !== device) {
|
|
597
|
-
await this.closeOpenDevice(path);
|
|
598
|
-
this.releaseIdentityProbeHandle(path);
|
|
599
|
-
this.claimIdentityProbeHandle(path, device);
|
|
600
|
-
}
|
|
601
|
-
this.assertHandleAvailable(device);
|
|
602
572
|
if (!device.opened) {
|
|
603
|
-
openingHandles.add(device);
|
|
604
573
|
try {
|
|
605
574
|
await device.open();
|
|
606
575
|
} catch (error) {
|
|
607
|
-
return
|
|
608
|
-
} finally {
|
|
609
|
-
openingHandles.delete(device);
|
|
576
|
+
return this.throwWebUsbIoError(path, 'open', error);
|
|
610
577
|
}
|
|
611
578
|
}
|
|
612
579
|
// A V1 packet retry continues an in-flight exchange. Preserve its endpoint
|
|
@@ -620,20 +587,11 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
620
587
|
}
|
|
621
588
|
await this.getConnectedDevices();
|
|
622
589
|
device = await this.findDevice(path);
|
|
623
|
-
if (this.identityProbe && this.identityProbeDevices.get(path) !== device) {
|
|
624
|
-
await this.closeOpenDevice(path);
|
|
625
|
-
this.releaseIdentityProbeHandle(path);
|
|
626
|
-
this.claimIdentityProbeHandle(path, device);
|
|
627
|
-
}
|
|
628
|
-
this.assertHandleAvailable(device);
|
|
629
590
|
if (!device.opened) {
|
|
630
|
-
openingHandles.add(device);
|
|
631
591
|
try {
|
|
632
592
|
await device.open();
|
|
633
593
|
} catch (error) {
|
|
634
|
-
return
|
|
635
|
-
} finally {
|
|
636
|
-
openingHandles.delete(device);
|
|
594
|
+
return this.throwWebUsbIoError(path, 'open', error);
|
|
637
595
|
}
|
|
638
596
|
}
|
|
639
597
|
|
|
@@ -657,41 +615,9 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
657
615
|
await this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
658
616
|
}
|
|
659
617
|
|
|
660
|
-
private assertHandleAvailable(device: USBDevice) {
|
|
661
|
-
if (
|
|
662
|
-
identityProbeHandles.has(device) &&
|
|
663
|
-
![...this.identityProbeDevices.values()].includes(device)
|
|
664
|
-
) {
|
|
665
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'USB device is already in use');
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
private claimIdentityProbeHandle(path: string, device: USBDevice) {
|
|
670
|
-
if (device.opened || openingHandles.has(device) || identityProbeHandles.has(device)) {
|
|
671
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'USB device is already in use');
|
|
672
|
-
}
|
|
673
|
-
identityProbeHandles.add(device);
|
|
674
|
-
this.identityProbeDevices.set(path, device);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
private releaseIdentityProbeHandle(path: string) {
|
|
678
|
-
const device = this.identityProbeDevices.get(path);
|
|
679
|
-
// A failed native close must not make an open probe handle available to other callers.
|
|
680
|
-
if (device?.opened) {
|
|
681
|
-
this.identityProbeCleanupPaths.add(path);
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
if (device) identityProbeHandles.delete(device);
|
|
685
|
-
this.identityProbeDevices.delete(path);
|
|
686
|
-
this.identityProbeCleanupPaths.delete(path);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
618
|
private async closeOpenDevice(path: string) {
|
|
690
|
-
const current = this.
|
|
691
|
-
? this.identityProbeDevices.get(path)
|
|
692
|
-
: this.deviceList.find(device => device.path === path)?.device;
|
|
619
|
+
const current = this.deviceList.find(device => device.path === path)?.device;
|
|
693
620
|
if (!current?.opened) return;
|
|
694
|
-
this.assertHandleAvailable(current);
|
|
695
621
|
|
|
696
622
|
const endpoints = this.deviceEndpoints.get(path);
|
|
697
623
|
const ifaceNum = endpoints?.interfaceNumber ?? this.interfaceId;
|
|
@@ -704,7 +630,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
704
630
|
await current.close();
|
|
705
631
|
} catch (error) {
|
|
706
632
|
this.Log?.debug('[WebUsbTransport] close before reconnect failed:', error);
|
|
707
|
-
if (this.identityProbe) throw error;
|
|
708
633
|
}
|
|
709
634
|
}
|
|
710
635
|
|
|
@@ -1059,15 +984,15 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
1059
984
|
}
|
|
1060
985
|
|
|
1061
986
|
try {
|
|
1062
|
-
|
|
987
|
+
await this.callProtocolV1(
|
|
1063
988
|
path,
|
|
1064
|
-
|
|
989
|
+
'Initialize',
|
|
1065
990
|
{},
|
|
1066
991
|
{
|
|
1067
992
|
timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
|
|
1068
993
|
}
|
|
1069
994
|
);
|
|
1070
|
-
return
|
|
995
|
+
return true;
|
|
1071
996
|
} catch (error) {
|
|
1072
997
|
if (this.isWebUsbIoHardwareError(error)) throw error;
|
|
1073
998
|
return false;
|
|
@@ -1224,12 +1149,8 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
1224
1149
|
*/
|
|
1225
1150
|
async release(path: string) {
|
|
1226
1151
|
this.acquiredPaths.delete(path);
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
await this.closeOpenDevice(path);
|
|
1230
|
-
} finally {
|
|
1231
|
-
this.releaseIdentityProbeHandle(path);
|
|
1232
|
-
}
|
|
1152
|
+
await this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
|
|
1153
|
+
await this.closeOpenDevice(path);
|
|
1233
1154
|
// Keep deviceProtocol/deviceProtocolHints across release: the probe result is a
|
|
1234
1155
|
// physical-device property, so the next acquire can skip the wire-level probe.
|
|
1235
1156
|
// V2 entries are still dropped by onProtocolV2UsbLinkInvalidated via the link
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import WebUsbTransport from '../src/webusb';
|
|
2
|
-
import { probeUsbDeviceIdentities } from '@onekeyfe/hd-common-connect-sdk/src';
|
|
3
|
-
|
|
4
|
-
import type { MessageFromOneKey, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
5
|
-
|
|
6
|
-
jest.mock('@onekeyfe/hd-core', () => jest.requireActual('../../core/src'));
|
|
7
|
-
jest.mock('../../core/src/data/config', () => ({
|
|
8
|
-
getSDKVersion: () => '1.0.0-test',
|
|
9
|
-
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0-test/',
|
|
10
|
-
}));
|
|
11
|
-
jest.mock('@onekeyfe/hd-transport-web-device', () => ({
|
|
12
|
-
WebUsbTransport: jest.requireActual('../src/webusb').default,
|
|
13
|
-
}));
|
|
14
|
-
jest.mock('@onekeyfe/hd-transport-http', () => ({ __esModule: true, default: jest.fn() }));
|
|
15
|
-
jest.mock('@onekeyfe/hd-transport-lowlevel', () => ({}));
|
|
16
|
-
jest.mock('@onekeyfe/hd-transport-emulator', () => ({}));
|
|
17
|
-
|
|
18
|
-
describe('WebUSB identity probe ownership', () => {
|
|
19
|
-
let originalNavigator: PropertyDescriptor | undefined;
|
|
20
|
-
const device = {
|
|
21
|
-
vendorId: 0x1209,
|
|
22
|
-
productId: 0x53c1,
|
|
23
|
-
serialNumber: 'USB_PATH',
|
|
24
|
-
opened: false,
|
|
25
|
-
close: jest.fn(),
|
|
26
|
-
};
|
|
27
|
-
let disconnectListener: (event: { device: typeof device }) => void;
|
|
28
|
-
const usb = {
|
|
29
|
-
getDevices: jest.fn(),
|
|
30
|
-
addEventListener: jest.fn((_type, callback) => {
|
|
31
|
-
disconnectListener = callback;
|
|
32
|
-
}),
|
|
33
|
-
};
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
originalNavigator = Object.getOwnPropertyDescriptor(globalThis, 'navigator');
|
|
36
|
-
device.opened = false;
|
|
37
|
-
device.close.mockReset().mockImplementation(() => {
|
|
38
|
-
device.opened = false;
|
|
39
|
-
return Promise.resolve();
|
|
40
|
-
});
|
|
41
|
-
usb.getDevices.mockResolvedValue([device]);
|
|
42
|
-
usb.addEventListener.mockClear();
|
|
43
|
-
Object.defineProperty(globalThis, 'navigator', { configurable: true, value: { usb } });
|
|
44
|
-
});
|
|
45
|
-
afterEach(() => {
|
|
46
|
-
if (originalNavigator) Object.defineProperty(globalThis, 'navigator', originalNavigator);
|
|
47
|
-
else delete (globalThis as { navigator?: Navigator }).navigator;
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('does not close a handle already opened by another operation', async () => {
|
|
51
|
-
const probe = new WebUsbTransport(true);
|
|
52
|
-
probe.init({ debug: jest.fn() });
|
|
53
|
-
device.opened = true;
|
|
54
|
-
await expect(probe.acquire({ path: 'USB_PATH' })).rejects.toThrow(
|
|
55
|
-
'USB device is already in use'
|
|
56
|
-
);
|
|
57
|
-
expect(device.close).not.toHaveBeenCalled();
|
|
58
|
-
expect(usb.addEventListener).not.toHaveBeenCalled();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('does not borrow a handle while a normal native open is pending', async () => {
|
|
62
|
-
const active = new WebUsbTransport();
|
|
63
|
-
const probe = new WebUsbTransport(true);
|
|
64
|
-
active.init({ debug: jest.fn() });
|
|
65
|
-
probe.init({ debug: jest.fn() });
|
|
66
|
-
let finishOpen!: () => void;
|
|
67
|
-
let started!: () => void;
|
|
68
|
-
const opening = new Promise<void>(resolve => {
|
|
69
|
-
finishOpen = resolve;
|
|
70
|
-
});
|
|
71
|
-
const entered = new Promise<void>(resolve => {
|
|
72
|
-
started = resolve;
|
|
73
|
-
});
|
|
74
|
-
const native = Object.assign(device, {
|
|
75
|
-
open: jest.fn(async () => {
|
|
76
|
-
started();
|
|
77
|
-
await opening;
|
|
78
|
-
device.opened = true;
|
|
79
|
-
}),
|
|
80
|
-
reset: jest.fn(),
|
|
81
|
-
configurations: [],
|
|
82
|
-
configuration: { configurationValue: 1 },
|
|
83
|
-
selectConfiguration: jest.fn(),
|
|
84
|
-
claimInterface: jest.fn(),
|
|
85
|
-
clearHalt: jest.fn(),
|
|
86
|
-
});
|
|
87
|
-
jest.spyOn(active, 'findDevice').mockResolvedValue(native as unknown as USBDevice);
|
|
88
|
-
const connecting = active.connectToDevice('USB_PATH', true);
|
|
89
|
-
await entered;
|
|
90
|
-
await expect(probe.acquire({ path: 'USB_PATH' })).rejects.toThrow(
|
|
91
|
-
'USB device is already in use'
|
|
92
|
-
);
|
|
93
|
-
expect(device.close).not.toHaveBeenCalled();
|
|
94
|
-
finishOpen();
|
|
95
|
-
await connecting;
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('a normal connection cannot close an isolated probe handle and release closes the captured handle after re-enumeration', async () => {
|
|
99
|
-
const probe = new WebUsbTransport(true);
|
|
100
|
-
probe.init({ debug: jest.fn() });
|
|
101
|
-
const internals = probe as unknown as {
|
|
102
|
-
rotateProtocolV2UsbGeneration: () => Promise<void>;
|
|
103
|
-
detectProtocol: () => Promise<'V1'>;
|
|
104
|
-
};
|
|
105
|
-
jest.spyOn(internals, 'rotateProtocolV2UsbGeneration').mockResolvedValue(undefined);
|
|
106
|
-
jest.spyOn(internals, 'detectProtocol').mockResolvedValue('V1');
|
|
107
|
-
jest.spyOn(probe, 'connect').mockImplementation(() => {
|
|
108
|
-
device.opened = true;
|
|
109
|
-
return Promise.resolve();
|
|
110
|
-
});
|
|
111
|
-
await probe.acquire({ path: 'USB_PATH' });
|
|
112
|
-
const active = new WebUsbTransport();
|
|
113
|
-
active.init({ debug: jest.fn() });
|
|
114
|
-
await expect(active.connectToDevice('USB_PATH', true)).rejects.toThrow(
|
|
115
|
-
'USB device is already in use'
|
|
116
|
-
);
|
|
117
|
-
expect(device.close).not.toHaveBeenCalled();
|
|
118
|
-
const replacement = { ...device, close: jest.fn() };
|
|
119
|
-
usb.getDevices.mockResolvedValue([replacement]);
|
|
120
|
-
await probe.getConnectedDevices();
|
|
121
|
-
device.close.mockRejectedValueOnce(new Error('native close failed'));
|
|
122
|
-
await expect(probe.release('USB_PATH')).rejects.toThrow('native close failed');
|
|
123
|
-
await probe.enumerate();
|
|
124
|
-
expect(device.close).toHaveBeenCalledTimes(2);
|
|
125
|
-
expect(replacement.close).not.toHaveBeenCalled();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('does not replace the active SDK disconnect observer', () => {
|
|
129
|
-
const active = new WebUsbTransport();
|
|
130
|
-
active.init({ debug: jest.fn() });
|
|
131
|
-
const probe = new WebUsbTransport(true);
|
|
132
|
-
probe.init({ debug: jest.fn() });
|
|
133
|
-
const activeDisconnect = jest.spyOn(active, 'markProtocolStale');
|
|
134
|
-
const probeDisconnect = jest.spyOn(probe, 'markProtocolStale');
|
|
135
|
-
disconnectListener({ device });
|
|
136
|
-
expect(activeDisconnect).toHaveBeenCalledWith('USB_PATH');
|
|
137
|
-
expect(probeDisconnect).not.toHaveBeenCalled();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test.each(['release', 'acquire failure'])(
|
|
141
|
-
'keeps a probe handle protected after close fails during %s and recovers after cleanup',
|
|
142
|
-
async phase => {
|
|
143
|
-
Object.assign(device, {
|
|
144
|
-
open: jest.fn(() => {
|
|
145
|
-
device.opened = true;
|
|
146
|
-
return Promise.resolve();
|
|
147
|
-
}),
|
|
148
|
-
releaseInterface: jest.fn().mockResolvedValue(undefined),
|
|
149
|
-
reset: jest.fn(),
|
|
150
|
-
configurations: [],
|
|
151
|
-
configuration: { configurationValue: 1 },
|
|
152
|
-
selectConfiguration: jest.fn(),
|
|
153
|
-
claimInterface: jest.fn(),
|
|
154
|
-
clearHalt: jest.fn(),
|
|
155
|
-
});
|
|
156
|
-
const probe = new WebUsbTransport(true);
|
|
157
|
-
probe.init({ debug: jest.fn() });
|
|
158
|
-
const internals = probe as unknown as {
|
|
159
|
-
rotateProtocolV2UsbGeneration: () => Promise<void>;
|
|
160
|
-
detectProtocol: () => Promise<'V1'>;
|
|
161
|
-
};
|
|
162
|
-
jest.spyOn(internals, 'rotateProtocolV2UsbGeneration').mockResolvedValue(undefined);
|
|
163
|
-
jest.spyOn(internals, 'detectProtocol').mockResolvedValue('V1');
|
|
164
|
-
jest.spyOn(probe, 'connect').mockImplementation(() => {
|
|
165
|
-
device.opened = true;
|
|
166
|
-
if (phase === 'acquire failure') return Promise.reject(new Error('probe failed'));
|
|
167
|
-
return Promise.resolve();
|
|
168
|
-
});
|
|
169
|
-
const closeFailure = new Error('native close failed');
|
|
170
|
-
device.close.mockRejectedValueOnce(closeFailure);
|
|
171
|
-
if (phase === 'release') {
|
|
172
|
-
await probe.acquire({ path: 'USB_PATH' });
|
|
173
|
-
await expect(probe.release('USB_PATH')).rejects.toBe(closeFailure);
|
|
174
|
-
} else {
|
|
175
|
-
await expect(probe.acquire({ path: 'USB_PATH' })).rejects.toBe(closeFailure);
|
|
176
|
-
}
|
|
177
|
-
expect(device.opened).toBe(true);
|
|
178
|
-
const active = new WebUsbTransport();
|
|
179
|
-
active.init({ debug: jest.fn() });
|
|
180
|
-
await expect(active.connectToDevice('USB_PATH', true)).rejects.toThrow(
|
|
181
|
-
'USB device is already in use'
|
|
182
|
-
);
|
|
183
|
-
expect(device.close).toHaveBeenCalledTimes(1);
|
|
184
|
-
|
|
185
|
-
await probe.release('USB_PATH');
|
|
186
|
-
expect(device.opened).toBe(false);
|
|
187
|
-
await expect(active.connectToDevice('USB_PATH', true)).resolves.toBeUndefined();
|
|
188
|
-
expect(device.opened).toBe(true);
|
|
189
|
-
await active.release('USB_PATH');
|
|
190
|
-
}
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
test.each(['release', 'acquire failure'])(
|
|
194
|
-
'the public probe retries failed %s cleanup on its next call',
|
|
195
|
-
async phase => {
|
|
196
|
-
const internals = WebUsbTransport.prototype as unknown as {
|
|
197
|
-
rotateProtocolV2UsbGeneration: () => Promise<void>;
|
|
198
|
-
detectProtocol: () => Promise<'V1'>;
|
|
199
|
-
};
|
|
200
|
-
jest.spyOn(internals, 'rotateProtocolV2UsbGeneration').mockResolvedValue(undefined);
|
|
201
|
-
jest.spyOn(internals, 'detectProtocol').mockResolvedValue('V1');
|
|
202
|
-
jest.spyOn(WebUsbTransport.prototype, 'getProtocolType').mockReturnValue('V1');
|
|
203
|
-
jest.spyOn(WebUsbTransport.prototype, 'configure').mockImplementation(() => undefined);
|
|
204
|
-
jest
|
|
205
|
-
.spyOn(WebUsbTransport.prototype, 'configureProtocolV2')
|
|
206
|
-
.mockImplementation(() => undefined);
|
|
207
|
-
const connect = jest.spyOn(WebUsbTransport.prototype, 'connect').mockImplementation(() => {
|
|
208
|
-
device.opened = true;
|
|
209
|
-
return Promise.resolve();
|
|
210
|
-
});
|
|
211
|
-
if (phase === 'acquire failure') {
|
|
212
|
-
connect.mockImplementationOnce(() => {
|
|
213
|
-
device.opened = true;
|
|
214
|
-
return Promise.reject(new Error('probe failed'));
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
jest.spyOn(WebUsbTransport.prototype, 'call').mockResolvedValue({
|
|
218
|
-
type: 'Features',
|
|
219
|
-
message: { onekey_serial_no: 'PHYSICAL_SERIAL', onekey_device_type: 'CLASSIC1S' },
|
|
220
|
-
});
|
|
221
|
-
device.close.mockRejectedValueOnce(new Error('native close failed'));
|
|
222
|
-
try {
|
|
223
|
-
await expect(probeUsbDeviceIdentities({ commType: 'webusb' })).resolves.toEqual([]);
|
|
224
|
-
expect(device.opened).toBe(true);
|
|
225
|
-
const active = new WebUsbTransport();
|
|
226
|
-
active.init({ debug: jest.fn() });
|
|
227
|
-
await expect(active.connectToDevice('USB_PATH', true)).rejects.toThrow(
|
|
228
|
-
'USB device is already in use'
|
|
229
|
-
);
|
|
230
|
-
device.close.mockRejectedValueOnce(new Error('native close still failing'));
|
|
231
|
-
await expect(probeUsbDeviceIdentities({ commType: 'webusb' })).resolves.toEqual([]);
|
|
232
|
-
expect(device.opened).toBe(true);
|
|
233
|
-
expect(connect).toHaveBeenCalledTimes(1);
|
|
234
|
-
expect(device.close).toHaveBeenCalledTimes(2);
|
|
235
|
-
await expect(probeUsbDeviceIdentities({ commType: 'webusb' })).resolves.toMatchObject([
|
|
236
|
-
{ path: 'USB_PATH', serialNo: 'PHYSICAL_SERIAL' },
|
|
237
|
-
]);
|
|
238
|
-
expect(connect).toHaveBeenCalledTimes(2);
|
|
239
|
-
expect(device.opened).toBe(false);
|
|
240
|
-
} finally {
|
|
241
|
-
jest.restoreAllMocks();
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
test.each([false, true])(
|
|
247
|
-
'uses a read-only V1 probe only for isolated discovery (%s)',
|
|
248
|
-
async identityProbe => {
|
|
249
|
-
const probe = new WebUsbTransport(identityProbe);
|
|
250
|
-
const internals = probe as unknown as {
|
|
251
|
-
messages: object;
|
|
252
|
-
callProtocolV1: (
|
|
253
|
-
path: string,
|
|
254
|
-
name: string,
|
|
255
|
-
data: object,
|
|
256
|
-
options: TransportCallOptions
|
|
257
|
-
) => Promise<MessageFromOneKey>;
|
|
258
|
-
probeProtocolV1: (path: string) => Promise<boolean>;
|
|
259
|
-
};
|
|
260
|
-
internals.messages = {};
|
|
261
|
-
const call = jest
|
|
262
|
-
.spyOn(internals, 'callProtocolV1')
|
|
263
|
-
.mockResolvedValue({ type: 'Features', message: {} });
|
|
264
|
-
await expect(internals.probeProtocolV1('USB_PATH')).resolves.toBe(true);
|
|
265
|
-
expect(call.mock.calls[0][1]).toBe(identityProbe ? 'GetFeatures' : 'Initialize');
|
|
266
|
-
}
|
|
267
|
-
);
|
|
268
|
-
});
|