@onekeyfe/hd-transport-web-device 1.2.8-alpha.104 → 1.2.8-alpha.106
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/__tests__/webusb-identity-probe.test.ts +50 -2
- package/__tests__/webusb-protocol-cache.test.ts +241 -1
- package/dist/index.d.ts +20 -2
- package/dist/index.js +376 -65
- package/dist/webusb.d.ts +21 -2
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/webusb.ts +499 -57
|
@@ -120,6 +120,53 @@ describe('WebUSB identity probe ownership', () => {
|
|
|
120
120
|
}
|
|
121
121
|
);
|
|
122
122
|
|
|
123
|
+
test('clears the Core session while retaining ownership after normal native close fails', async () => {
|
|
124
|
+
const previous = TransportManager.transport;
|
|
125
|
+
const { settings } = DataManager;
|
|
126
|
+
const active = createAcquiredTransport();
|
|
127
|
+
TransportManager.transport = active as unknown as Transport;
|
|
128
|
+
const selected = new Device({
|
|
129
|
+
path: 'USB_PATH',
|
|
130
|
+
id: 'USB_PATH',
|
|
131
|
+
name: 'Mock device',
|
|
132
|
+
commType: 'webusb',
|
|
133
|
+
session: 'USB_PATH',
|
|
134
|
+
debug: false,
|
|
135
|
+
});
|
|
136
|
+
selected.mainId = 'USB_PATH';
|
|
137
|
+
selected.deviceConnector = {
|
|
138
|
+
release: (path: string) => active.release(path),
|
|
139
|
+
} as never;
|
|
140
|
+
(selected as unknown as { deviceAcquired: boolean }).deviceAcquired = true;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
await active.acquire({ path: 'USB_PATH' });
|
|
144
|
+
device.close.mockRejectedValueOnce(new Error('native close failed'));
|
|
145
|
+
|
|
146
|
+
await selected.release();
|
|
147
|
+
|
|
148
|
+
expect(selected.originalDescriptor.session).toBeNull();
|
|
149
|
+
expect(selected.hasDeviceAcquire()).toBe(false);
|
|
150
|
+
expect(device.opened).toBe(true);
|
|
151
|
+
|
|
152
|
+
const competing = new WebUsbTransport();
|
|
153
|
+
competing.init({ debug: jest.fn() });
|
|
154
|
+
await expect(competing.connectToDevice('USB_PATH', true)).rejects.toThrow(
|
|
155
|
+
'USB device is already in use'
|
|
156
|
+
);
|
|
157
|
+
} finally {
|
|
158
|
+
device.close.mockImplementation(() => {
|
|
159
|
+
device.opened = false;
|
|
160
|
+
return Promise.resolve();
|
|
161
|
+
});
|
|
162
|
+
await active.release('USB_PATH');
|
|
163
|
+
await active.stop();
|
|
164
|
+
TransportManager.transport = previous;
|
|
165
|
+
DataManager.settings = settings;
|
|
166
|
+
jest.restoreAllMocks();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
123
170
|
test('does not replace the transport until USB cleanup completes, or after it fails', async () => {
|
|
124
171
|
const previous = TransportManager.transport;
|
|
125
172
|
const { settings } = DataManager;
|
|
@@ -547,9 +594,10 @@ describe('WebUSB identity probe ownership', () => {
|
|
|
547
594
|
};
|
|
548
595
|
jest.spyOn(internals, 'rotateProtocolV2UsbGeneration').mockResolvedValue(undefined);
|
|
549
596
|
jest.spyOn(internals, 'detectProtocol').mockResolvedValue('V1');
|
|
597
|
+
const acquireFailure = new Error('probe failed');
|
|
550
598
|
jest.spyOn(probe, 'connect').mockImplementation(() => {
|
|
551
599
|
device.opened = true;
|
|
552
|
-
if (phase === 'acquire failure') return Promise.reject(
|
|
600
|
+
if (phase === 'acquire failure') return Promise.reject(acquireFailure);
|
|
553
601
|
return Promise.resolve();
|
|
554
602
|
});
|
|
555
603
|
const closeFailure = new Error('native close failed');
|
|
@@ -558,7 +606,7 @@ describe('WebUSB identity probe ownership', () => {
|
|
|
558
606
|
await probe.acquire({ path: 'USB_PATH' });
|
|
559
607
|
await expect(probe.release('USB_PATH')).rejects.toBe(closeFailure);
|
|
560
608
|
} else {
|
|
561
|
-
await expect(probe.acquire({ path: 'USB_PATH' })).rejects.toBe(
|
|
609
|
+
await expect(probe.acquire({ path: 'USB_PATH' })).rejects.toBe(acquireFailure);
|
|
562
610
|
}
|
|
563
611
|
expect(device.opened).toBe(true);
|
|
564
612
|
const active = new WebUsbTransport();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import transport from '@onekeyfe/hd-transport';
|
|
2
|
-
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
3
|
|
|
4
4
|
import WebUsbTransport from '../src/webusb';
|
|
5
5
|
|
|
@@ -64,6 +64,97 @@ describe('WebUsbTransport protocol probe cache', () => {
|
|
|
64
64
|
}
|
|
65
65
|
);
|
|
66
66
|
|
|
67
|
+
test('uses the SDK 1.1.31 WebUSB sequence with descriptor endpoints for Protocol V1', async () => {
|
|
68
|
+
const webusb = new WebUsbTransport() as any;
|
|
69
|
+
const logger = { debug: jest.fn() };
|
|
70
|
+
const path = 'legacy-v1-device';
|
|
71
|
+
const device = {
|
|
72
|
+
opened: false,
|
|
73
|
+
configuration: { configurationValue: 1 },
|
|
74
|
+
configurations: [
|
|
75
|
+
{
|
|
76
|
+
interfaces: [
|
|
77
|
+
{
|
|
78
|
+
interfaceNumber: 7,
|
|
79
|
+
alternates: [
|
|
80
|
+
{
|
|
81
|
+
interfaceClass: 0xff,
|
|
82
|
+
endpoints: [
|
|
83
|
+
{ direction: 'in', endpointNumber: 3 },
|
|
84
|
+
{ direction: 'out', endpointNumber: 4 },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
open: jest.fn().mockImplementation(() => {
|
|
93
|
+
device.opened = true;
|
|
94
|
+
return Promise.resolve();
|
|
95
|
+
}),
|
|
96
|
+
reset: jest.fn().mockResolvedValue(undefined),
|
|
97
|
+
selectConfiguration: jest.fn().mockResolvedValue(undefined),
|
|
98
|
+
claimInterface: jest.fn().mockResolvedValue(undefined),
|
|
99
|
+
clearHalt: jest.fn().mockResolvedValue(undefined),
|
|
100
|
+
};
|
|
101
|
+
webusb.Log = logger;
|
|
102
|
+
webusb.findDevice = jest.fn().mockResolvedValue(device);
|
|
103
|
+
webusb.getConnectedDevices = jest.fn().mockResolvedValue([]);
|
|
104
|
+
|
|
105
|
+
await webusb.connect(path, true, { expectedProtocol: 'V1' });
|
|
106
|
+
|
|
107
|
+
expect(device.open).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(device.selectConfiguration).toHaveBeenCalledWith(1);
|
|
109
|
+
expect(device.reset).toHaveBeenCalledTimes(1);
|
|
110
|
+
expect(device.claimInterface).toHaveBeenCalledWith(7);
|
|
111
|
+
expect(webusb.deviceEndpoints.get(path)).toEqual({
|
|
112
|
+
interfaceNumber: 7,
|
|
113
|
+
endpointIn: 3,
|
|
114
|
+
endpointOut: 4,
|
|
115
|
+
});
|
|
116
|
+
expect(device.selectConfiguration.mock.invocationCallOrder[0]).toBeLessThan(
|
|
117
|
+
device.reset.mock.invocationCallOrder[0]
|
|
118
|
+
);
|
|
119
|
+
expect(device.reset.mock.invocationCallOrder[0]).toBeLessThan(
|
|
120
|
+
device.claimInterface.mock.invocationCallOrder[0]
|
|
121
|
+
);
|
|
122
|
+
expect(webusb.getConnectedDevices).not.toHaveBeenCalled();
|
|
123
|
+
expect(device.clearHalt).not.toHaveBeenCalled();
|
|
124
|
+
expect(logger.debug).toHaveBeenCalledWith(
|
|
125
|
+
'[WebUsbTransport][lifecycle]',
|
|
126
|
+
expect.objectContaining({
|
|
127
|
+
event: 'connect-attempt',
|
|
128
|
+
phase: 'start',
|
|
129
|
+
connectionStrategy: 'protocol-v1-legacy-1.1.31',
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('keeps the current WebUSB sequence when a cached V1 protocol is force-reprobed', async () => {
|
|
135
|
+
const webusb = new WebUsbTransport() as any;
|
|
136
|
+
const path = 'force-reprobe-device';
|
|
137
|
+
const device = {
|
|
138
|
+
opened: true,
|
|
139
|
+
configuration: { configurationValue: 1 },
|
|
140
|
+
configurations: [],
|
|
141
|
+
reset: jest.fn().mockResolvedValue(undefined),
|
|
142
|
+
selectConfiguration: jest.fn().mockResolvedValue(undefined),
|
|
143
|
+
claimInterface: jest.fn().mockResolvedValue(undefined),
|
|
144
|
+
clearHalt: jest.fn().mockResolvedValue(undefined),
|
|
145
|
+
};
|
|
146
|
+
webusb.Log = { debug: jest.fn() };
|
|
147
|
+
webusb.deviceProtocol.set(path, 'V1');
|
|
148
|
+
webusb.findDevice = jest.fn().mockResolvedValue(device);
|
|
149
|
+
webusb.getConnectedDevices = jest.fn().mockResolvedValue([]);
|
|
150
|
+
|
|
151
|
+
await webusb.connectToDevice(path, true, { forceProtocolDetection: true });
|
|
152
|
+
|
|
153
|
+
expect(device.reset).toHaveBeenCalledTimes(1);
|
|
154
|
+
expect(webusb.getConnectedDevices).toHaveBeenCalledTimes(1);
|
|
155
|
+
expect(device.clearHalt).toHaveBeenCalledTimes(2);
|
|
156
|
+
});
|
|
157
|
+
|
|
67
158
|
test('reports claimInterface failures as WebUSB device access errors', async () => {
|
|
68
159
|
const webusb = new WebUsbTransport();
|
|
69
160
|
const path = 'connected-usb-device';
|
|
@@ -91,6 +182,151 @@ describe('WebUsbTransport protocol probe cache', () => {
|
|
|
91
182
|
});
|
|
92
183
|
});
|
|
93
184
|
|
|
185
|
+
test('does not retry a typed WebUSB handle error', async () => {
|
|
186
|
+
const webusb = new WebUsbTransport() as any;
|
|
187
|
+
const logger = { debug: jest.fn() };
|
|
188
|
+
const path = 'sensitive-device-path';
|
|
189
|
+
const accessError = ERRORS.TypedError(
|
|
190
|
+
HardwareErrorCode.WebUsbDeviceAccessError,
|
|
191
|
+
'Unable to claim interface',
|
|
192
|
+
{ operation: 'claimInterface' }
|
|
193
|
+
);
|
|
194
|
+
webusb.Log = logger;
|
|
195
|
+
webusb.connectToDevice = jest.fn().mockRejectedValue(accessError);
|
|
196
|
+
|
|
197
|
+
await expect(webusb.connect(path, true)).rejects.toBe(accessError);
|
|
198
|
+
|
|
199
|
+
expect(webusb.connectToDevice).toHaveBeenCalledTimes(1);
|
|
200
|
+
expect(logger.debug).toHaveBeenCalledWith(
|
|
201
|
+
'[WebUsbTransport][lifecycle]',
|
|
202
|
+
expect.objectContaining({
|
|
203
|
+
event: 'connect-attempt',
|
|
204
|
+
phase: 'error',
|
|
205
|
+
attempt: 1,
|
|
206
|
+
maxAttempts: 5,
|
|
207
|
+
willRetry: false,
|
|
208
|
+
errorCode: HardwareErrorCode.WebUsbDeviceAccessError,
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
expect(JSON.stringify(logger.debug.mock.calls)).not.toContain(path);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('preserves the existing reconnect retry for a transient disconnect', async () => {
|
|
215
|
+
const webusb = new WebUsbTransport() as any;
|
|
216
|
+
const disconnected = ERRORS.TypedError(HardwareErrorCode.BridgeDeviceDisconnected);
|
|
217
|
+
webusb.Log = { debug: jest.fn() };
|
|
218
|
+
webusb.connectToDevice = jest
|
|
219
|
+
.fn()
|
|
220
|
+
.mockRejectedValueOnce(disconnected)
|
|
221
|
+
.mockResolvedValueOnce(undefined);
|
|
222
|
+
|
|
223
|
+
await expect(webusb.connect('device-path', true)).resolves.toBeUndefined();
|
|
224
|
+
|
|
225
|
+
expect(webusb.connectToDevice).toHaveBeenCalledTimes(2);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test.each([false, true])('retains claim error when cleanup fails=%s', async cleanupFails => {
|
|
229
|
+
const webusb = new WebUsbTransport() as any;
|
|
230
|
+
const logger = { debug: jest.fn() };
|
|
231
|
+
const path = 'sensitive-device-path';
|
|
232
|
+
const claimError = Object.assign(new Error('Unable to claim interface'), {
|
|
233
|
+
name: 'NetworkError',
|
|
234
|
+
});
|
|
235
|
+
const usbInterface = {
|
|
236
|
+
interfaceNumber: 0,
|
|
237
|
+
claimed: false,
|
|
238
|
+
alternates: [],
|
|
239
|
+
};
|
|
240
|
+
const originalDevice = {
|
|
241
|
+
vendorId: 0x1209,
|
|
242
|
+
productId: 0x53c1,
|
|
243
|
+
serialNumber: path,
|
|
244
|
+
opened: false,
|
|
245
|
+
configuration: { configurationValue: 1, interfaces: [usbInterface] },
|
|
246
|
+
configurations: [],
|
|
247
|
+
open: jest.fn().mockImplementation(() => {
|
|
248
|
+
originalDevice.opened = true;
|
|
249
|
+
return Promise.resolve();
|
|
250
|
+
}),
|
|
251
|
+
reset: jest.fn().mockResolvedValue(undefined),
|
|
252
|
+
selectConfiguration: jest.fn().mockResolvedValue(undefined),
|
|
253
|
+
claimInterface: jest.fn().mockResolvedValue(undefined),
|
|
254
|
+
releaseInterface: jest.fn().mockResolvedValue(undefined),
|
|
255
|
+
close: jest.fn().mockImplementation(() => {
|
|
256
|
+
originalDevice.opened = false;
|
|
257
|
+
return Promise.resolve();
|
|
258
|
+
}),
|
|
259
|
+
clearHalt: jest.fn().mockResolvedValue(undefined),
|
|
260
|
+
};
|
|
261
|
+
const replacementDevice = {
|
|
262
|
+
...originalDevice,
|
|
263
|
+
opened: false,
|
|
264
|
+
open: jest.fn().mockImplementation(() => {
|
|
265
|
+
replacementDevice.opened = true;
|
|
266
|
+
return Promise.resolve();
|
|
267
|
+
}),
|
|
268
|
+
reset: jest.fn().mockResolvedValue(undefined),
|
|
269
|
+
selectConfiguration: jest.fn().mockResolvedValue(undefined),
|
|
270
|
+
claimInterface: jest.fn().mockRejectedValue(claimError),
|
|
271
|
+
releaseInterface: jest.fn().mockResolvedValue(undefined),
|
|
272
|
+
close: jest.fn().mockImplementation(() => {
|
|
273
|
+
replacementDevice.opened = false;
|
|
274
|
+
return Promise.resolve();
|
|
275
|
+
}),
|
|
276
|
+
clearHalt: jest.fn().mockResolvedValue(undefined),
|
|
277
|
+
};
|
|
278
|
+
if (cleanupFails) {
|
|
279
|
+
replacementDevice.close.mockRejectedValueOnce(new Error('native close failed'));
|
|
280
|
+
}
|
|
281
|
+
webusb.Log = logger;
|
|
282
|
+
webusb.usb = { getDevices: jest.fn().mockResolvedValue([replacementDevice]) };
|
|
283
|
+
webusb.deviceList = [{ path, device: originalDevice, commType: 'webusb' }];
|
|
284
|
+
webusb.rotateProtocolV2UsbGeneration = jest.fn().mockResolvedValue(undefined);
|
|
285
|
+
|
|
286
|
+
await expect(webusb.acquire({ path })).rejects.toMatchObject({
|
|
287
|
+
errorCode: HardwareErrorCode.WebUsbDeviceAccessError,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
expect(originalDevice.claimInterface).not.toHaveBeenCalled();
|
|
291
|
+
expect(replacementDevice.claimInterface).toHaveBeenCalledTimes(1);
|
|
292
|
+
expect(originalDevice.releaseInterface).not.toHaveBeenCalled();
|
|
293
|
+
expect(replacementDevice.releaseInterface).not.toHaveBeenCalled();
|
|
294
|
+
expect(originalDevice.close).toHaveBeenCalledTimes(1);
|
|
295
|
+
expect(replacementDevice.close).toHaveBeenCalledTimes(1);
|
|
296
|
+
expect(originalDevice.opened).toBe(false);
|
|
297
|
+
expect(replacementDevice.opened).toBe(cleanupFails);
|
|
298
|
+
if (cleanupFails) {
|
|
299
|
+
expect(logger.debug).toHaveBeenCalledWith(
|
|
300
|
+
'[WebUsbTransport][lifecycle]',
|
|
301
|
+
expect.objectContaining({
|
|
302
|
+
event: 'acquire-cleanup',
|
|
303
|
+
phase: 'error',
|
|
304
|
+
errorMessage: 'native close failed',
|
|
305
|
+
})
|
|
306
|
+
);
|
|
307
|
+
const competing = new WebUsbTransport();
|
|
308
|
+
jest
|
|
309
|
+
.spyOn(competing, 'findDevice')
|
|
310
|
+
.mockResolvedValue(replacementDevice as unknown as USBDevice);
|
|
311
|
+
await expect(competing.connectToDevice(path, true)).rejects.toThrow(
|
|
312
|
+
'USB device is already in use'
|
|
313
|
+
);
|
|
314
|
+
await webusb.release(path);
|
|
315
|
+
expect(replacementDevice.opened).toBe(false);
|
|
316
|
+
}
|
|
317
|
+
expect(logger.debug).toHaveBeenCalledWith(
|
|
318
|
+
'[WebUsbTransport][lifecycle]',
|
|
319
|
+
expect.objectContaining({
|
|
320
|
+
event: 'handle-operation',
|
|
321
|
+
operation: 'releaseInterface',
|
|
322
|
+
phase: 'skipped',
|
|
323
|
+
reason: 'interface-not-claimed',
|
|
324
|
+
deviceRefId: expect.any(Number),
|
|
325
|
+
})
|
|
326
|
+
);
|
|
327
|
+
expect(JSON.stringify(logger.debug.mock.calls)).not.toContain(path);
|
|
328
|
+
});
|
|
329
|
+
|
|
94
330
|
test('reports transferIn failures as WebUSB device access errors', async () => {
|
|
95
331
|
const webusb = new WebUsbTransport() as any;
|
|
96
332
|
const path = 'connected-usb-device';
|
|
@@ -193,6 +429,10 @@ describe('WebUsbTransport protocol probe cache', () => {
|
|
|
193
429
|
|
|
194
430
|
await expect(webusb.acquire({ path, expectedProtocol: 'V1' })).resolves.toBe(path);
|
|
195
431
|
|
|
432
|
+
expect(webusb.connect).toHaveBeenCalledWith(path, true, {
|
|
433
|
+
expectedProtocol: 'V1',
|
|
434
|
+
forceProtocolDetection: undefined,
|
|
435
|
+
});
|
|
196
436
|
expect(webusb.detectProtocol).not.toHaveBeenCalled();
|
|
197
437
|
expect(webusb.deviceProtocol.get(path)).toBe('V1');
|
|
198
438
|
expect(webusb.acquiredPaths.has(path)).toBe(true);
|
package/dist/index.d.ts
CHANGED
|
@@ -12,8 +12,16 @@ interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
|
12
12
|
device: USBDevice;
|
|
13
13
|
protocolType?: ProtocolType;
|
|
14
14
|
}
|
|
15
|
+
interface WebUsbConnectOptions {
|
|
16
|
+
expectedProtocol?: ProtocolType;
|
|
17
|
+
forceProtocolDetection?: boolean;
|
|
18
|
+
}
|
|
15
19
|
declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
16
20
|
private readonly identityProbe;
|
|
21
|
+
private readonly transportInstanceId;
|
|
22
|
+
private handleOperationSequence;
|
|
23
|
+
private deviceLogSequence;
|
|
24
|
+
private deviceLogIds;
|
|
17
25
|
messages: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
|
|
18
26
|
/** Protobuf schema for Protocol V2 transports. */
|
|
19
27
|
messagesV2: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
|
|
@@ -68,6 +76,13 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
68
76
|
private stopResources;
|
|
69
77
|
private assertRunning;
|
|
70
78
|
private runHandleOperation;
|
|
79
|
+
private getInterfaceClaimed;
|
|
80
|
+
private getDeviceLogId;
|
|
81
|
+
private getHandleLogState;
|
|
82
|
+
private getDiagnosticError;
|
|
83
|
+
private logLifecycle;
|
|
84
|
+
private logSkippedHandleOperation;
|
|
85
|
+
private runLoggedHandleOperation;
|
|
71
86
|
/**
|
|
72
87
|
* Announce that a USB device left. Called from the module-scoped disconnect
|
|
73
88
|
* listener, which is why it is public rather than inlined.
|
|
@@ -128,7 +143,8 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
128
143
|
/**
|
|
129
144
|
* Connect to device with retry mechanism
|
|
130
145
|
*/
|
|
131
|
-
connect(path: string, first: boolean): Promise<undefined>;
|
|
146
|
+
connect(path: string, first: boolean, options?: WebUsbConnectOptions): Promise<undefined>;
|
|
147
|
+
private shouldUseLegacyV1Connection;
|
|
132
148
|
/**
|
|
133
149
|
* Discover vendor-class (0xFF) interface and its IN/OUT endpoint numbers from USB descriptors.
|
|
134
150
|
* Falls back to legacy hardcoded values if no vendor interface is found.
|
|
@@ -138,7 +154,8 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
138
154
|
* Connect to specific device.
|
|
139
155
|
* Discovers interface/endpoint numbers from USB descriptors on first connection.
|
|
140
156
|
*/
|
|
141
|
-
connectToDevice(path: string, first: boolean): Promise<undefined>;
|
|
157
|
+
connectToDevice(path: string, first: boolean, options?: WebUsbConnectOptions): Promise<undefined>;
|
|
158
|
+
private claimDeviceInterface;
|
|
142
159
|
private assertHandleAvailable;
|
|
143
160
|
private claimNormalHandle;
|
|
144
161
|
private releaseNormalHandles;
|
|
@@ -146,6 +163,7 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
146
163
|
private claimIdentityProbeHandle;
|
|
147
164
|
private releaseIdentityProbeHandle;
|
|
148
165
|
private closeOpenDevice;
|
|
166
|
+
private closeDeviceHandle;
|
|
149
167
|
private clearEndpointHalt;
|
|
150
168
|
/**
|
|
151
169
|
* With the protocol cache surviving release(), deviceProtocol presence no
|