@onekeyfe/hd-transport-web-device 1.2.3-alpha.4 → 1.2.3-alpha.5
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.
|
@@ -64,6 +64,54 @@ describe('WebUsbTransport protocol probe cache', () => {
|
|
|
64
64
|
}
|
|
65
65
|
);
|
|
66
66
|
|
|
67
|
+
test('reports claimInterface failures as WebUSB device access errors', async () => {
|
|
68
|
+
const webusb = new WebUsbTransport();
|
|
69
|
+
const path = 'connected-usb-device';
|
|
70
|
+
const claimError = Object.assign(new Error('Unable to claim interface'), {
|
|
71
|
+
name: 'NetworkError',
|
|
72
|
+
});
|
|
73
|
+
const device = {
|
|
74
|
+
opened: true,
|
|
75
|
+
configuration: { configurationValue: 1 },
|
|
76
|
+
configurations: [],
|
|
77
|
+
claimInterface: jest.fn().mockRejectedValue(claimError),
|
|
78
|
+
};
|
|
79
|
+
jest.spyOn(webusb, 'findDevice').mockResolvedValue(device as unknown as USBDevice);
|
|
80
|
+
jest.spyOn(webusb, 'getConnectedDevices').mockResolvedValue([]);
|
|
81
|
+
const state = webusb as unknown as { deviceProtocol: Map<string, 'V1' | 'V2'> };
|
|
82
|
+
state.deviceProtocol.set(path, 'V1');
|
|
83
|
+
|
|
84
|
+
await expect(webusb.connectToDevice(path, false)).rejects.toMatchObject({
|
|
85
|
+
errorCode: HardwareErrorCode.WebUsbDeviceAccessError,
|
|
86
|
+
params: {
|
|
87
|
+
operation: 'claimInterface',
|
|
88
|
+
nativeErrorName: 'NetworkError',
|
|
89
|
+
nativeErrorMessage: 'Unable to claim interface',
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('reports transferIn failures as WebUSB device access errors', async () => {
|
|
95
|
+
const webusb = new WebUsbTransport() as any;
|
|
96
|
+
const path = 'connected-usb-device';
|
|
97
|
+
const transferError = Object.assign(new Error('Transfer endpoint is unavailable'), {
|
|
98
|
+
name: 'NetworkError',
|
|
99
|
+
});
|
|
100
|
+
webusb.findDevice = jest.fn().mockResolvedValue({
|
|
101
|
+
opened: true,
|
|
102
|
+
transferIn: jest.fn().mockRejectedValue(transferError),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
await expect(webusb.transferInWithRetry(path, 64)).rejects.toMatchObject({
|
|
106
|
+
errorCode: HardwareErrorCode.WebUsbDeviceAccessError,
|
|
107
|
+
params: {
|
|
108
|
+
operation: 'transferIn',
|
|
109
|
+
nativeErrorName: 'NetworkError',
|
|
110
|
+
nativeErrorMessage: 'Transfer endpoint is unavailable',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
67
115
|
test('acquire skips the wire probe when the protocol is already cached', async () => {
|
|
68
116
|
const webusb = buildAcquirableTransport();
|
|
69
117
|
const path = 'pro-webusb';
|
|
@@ -238,9 +238,15 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
238
238
|
webusb.resetConnectionAfterProbe = jest.fn();
|
|
239
239
|
await webusb.rotateProtocolV2UsbGeneration(path, 'test connection');
|
|
240
240
|
|
|
241
|
-
await expect(
|
|
242
|
-
'
|
|
243
|
-
)
|
|
241
|
+
await expect(
|
|
242
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'read-error' })
|
|
243
|
+
).rejects.toMatchObject({
|
|
244
|
+
errorCode: HardwareErrorCode.BridgeDeviceDisconnected,
|
|
245
|
+
params: {
|
|
246
|
+
operation: 'transferIn',
|
|
247
|
+
nativeErrorMessage: 'NetworkError: transferIn device disconnected',
|
|
248
|
+
},
|
|
249
|
+
});
|
|
244
250
|
|
|
245
251
|
expect(webusb.readProtocolV2UsbPacket).toHaveBeenCalledTimes(1);
|
|
246
252
|
expect(webusb.resetProtocolV2UsbNativeLink).toHaveBeenCalledWith(
|
package/dist/index.d.ts
CHANGED
|
@@ -118,7 +118,7 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
118
118
|
/**
|
|
119
119
|
* Connect to device with retry mechanism
|
|
120
120
|
*/
|
|
121
|
-
connect(path: string, first: boolean): Promise<
|
|
121
|
+
connect(path: string, first: boolean): Promise<undefined>;
|
|
122
122
|
/**
|
|
123
123
|
* Discover vendor-class (0xFF) interface and its IN/OUT endpoint numbers from USB descriptors.
|
|
124
124
|
* Falls back to legacy hardcoded values if no vendor interface is found.
|
|
@@ -128,7 +128,7 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
128
128
|
* Connect to specific device.
|
|
129
129
|
* Discovers interface/endpoint numbers from USB descriptors on first connection.
|
|
130
130
|
*/
|
|
131
|
-
connectToDevice(path: string, first: boolean): Promise<
|
|
131
|
+
connectToDevice(path: string, first: boolean): Promise<undefined>;
|
|
132
132
|
private closeOpenDevice;
|
|
133
133
|
private clearEndpointHalt;
|
|
134
134
|
/**
|
|
@@ -141,6 +141,10 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
141
141
|
private assertAcquired;
|
|
142
142
|
post(session: string, name: string, data: Record<string, unknown>): Promise<void>;
|
|
143
143
|
private getErrorMessage;
|
|
144
|
+
private getErrorName;
|
|
145
|
+
private isWebUsbIoHardwareError;
|
|
146
|
+
private isDeviceStillEnumerated;
|
|
147
|
+
private throwWebUsbIoError;
|
|
144
148
|
private isRetryablePacketIoError;
|
|
145
149
|
private reconnectForPacketIoRetry;
|
|
146
150
|
private getTransferInData;
|
package/dist/index.js
CHANGED
|
@@ -395,7 +395,12 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
395
395
|
}
|
|
396
396
|
const endpoints = this.discoverEndpoints(device);
|
|
397
397
|
this.deviceEndpoints.set(path, endpoints);
|
|
398
|
-
|
|
398
|
+
try {
|
|
399
|
+
yield device.claimInterface(endpoints.interfaceNumber);
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
return this.throwWebUsbIoError(path, 'claimInterface', error);
|
|
403
|
+
}
|
|
399
404
|
yield this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
|
|
400
405
|
yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
401
406
|
});
|
|
@@ -459,6 +464,59 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
459
464
|
}
|
|
460
465
|
return String(error);
|
|
461
466
|
}
|
|
467
|
+
getErrorName(error) {
|
|
468
|
+
if (typeof error === 'object' && error && 'name' in error) {
|
|
469
|
+
const { name } = error;
|
|
470
|
+
return typeof name === 'string' ? name : String(name !== null && name !== void 0 ? name : '');
|
|
471
|
+
}
|
|
472
|
+
return '';
|
|
473
|
+
}
|
|
474
|
+
isWebUsbIoHardwareError(error) {
|
|
475
|
+
return (error instanceof hdShared.HardwareError &&
|
|
476
|
+
(error.errorCode === hdShared.HardwareErrorCode.BridgeDeviceDisconnected ||
|
|
477
|
+
error.errorCode === hdShared.HardwareErrorCode.WebUsbDeviceAccessError));
|
|
478
|
+
}
|
|
479
|
+
isDeviceStillEnumerated(path) {
|
|
480
|
+
var _a;
|
|
481
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
482
|
+
if (!this.usb)
|
|
483
|
+
return undefined;
|
|
484
|
+
try {
|
|
485
|
+
const devices = yield this.usb.getDevices();
|
|
486
|
+
return devices.some(device => hdShared.resolveOneKeyUsbDevicePath(device) === path);
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] failed to verify WebUSB device presence:', error);
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
throwWebUsbIoError(path, operation, error) {
|
|
495
|
+
var _a;
|
|
496
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
497
|
+
if (this.isWebUsbIoHardwareError(error))
|
|
498
|
+
throw error;
|
|
499
|
+
if (error instanceof transport.ProtocolV2LinkError && error.code !== 'io')
|
|
500
|
+
throw error;
|
|
501
|
+
const nativeError = error instanceof transport.ProtocolV2LinkError ? (_a = error.cause) !== null && _a !== void 0 ? _a : error : error;
|
|
502
|
+
if (this.isWebUsbIoHardwareError(nativeError))
|
|
503
|
+
throw nativeError;
|
|
504
|
+
const nativeErrorName = this.getErrorName(nativeError);
|
|
505
|
+
const nativeErrorMessage = this.getErrorMessage(nativeError);
|
|
506
|
+
const disconnectedByMessage = /(?:device )?disconnected|device not found|action was interrupted/i.test(`${nativeErrorName}: ${nativeErrorMessage}`);
|
|
507
|
+
const stillEnumerated = disconnectedByMessage
|
|
508
|
+
? false
|
|
509
|
+
: yield this.isDeviceStillEnumerated(path);
|
|
510
|
+
const errorCode = disconnectedByMessage || stillEnumerated === false
|
|
511
|
+
? hdShared.HardwareErrorCode.BridgeDeviceDisconnected
|
|
512
|
+
: hdShared.HardwareErrorCode.WebUsbDeviceAccessError;
|
|
513
|
+
throw hdShared.ERRORS.TypedError(errorCode, nativeErrorMessage || undefined, {
|
|
514
|
+
operation,
|
|
515
|
+
nativeErrorName: nativeErrorName || undefined,
|
|
516
|
+
nativeErrorMessage: nativeErrorMessage || undefined,
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
}
|
|
462
520
|
isRetryablePacketIoError(error) {
|
|
463
521
|
const message = this.getErrorMessage(error).toLowerCase();
|
|
464
522
|
return (message.includes('transferout') ||
|
|
@@ -525,7 +583,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
525
583
|
lastError = error;
|
|
526
584
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
527
585
|
if (!shouldRetry) {
|
|
528
|
-
|
|
586
|
+
return this.throwWebUsbIoError(path, 'transferOut', error);
|
|
529
587
|
}
|
|
530
588
|
try {
|
|
531
589
|
yield this.reconnectForPacketIoRetry(path, 'out', attempt, error);
|
|
@@ -536,7 +594,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
536
594
|
}
|
|
537
595
|
}
|
|
538
596
|
}
|
|
539
|
-
|
|
597
|
+
return this.throwWebUsbIoError(path, 'transferOut', lastError);
|
|
540
598
|
});
|
|
541
599
|
}
|
|
542
600
|
transferOutOnce(path, packet) {
|
|
@@ -577,7 +635,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
577
635
|
}
|
|
578
636
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
579
637
|
if (!shouldRetry) {
|
|
580
|
-
|
|
638
|
+
return this.throwWebUsbIoError(path, 'transferIn', error);
|
|
581
639
|
}
|
|
582
640
|
try {
|
|
583
641
|
yield this.reconnectForPacketIoRetry(path, 'in', attempt, error);
|
|
@@ -588,7 +646,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
588
646
|
}
|
|
589
647
|
}
|
|
590
648
|
}
|
|
591
|
-
|
|
649
|
+
return this.throwWebUsbIoError(path, 'transferIn', lastError);
|
|
592
650
|
});
|
|
593
651
|
}
|
|
594
652
|
transferInOnce(path, length) {
|
|
@@ -665,7 +723,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
665
723
|
});
|
|
666
724
|
return true;
|
|
667
725
|
}
|
|
668
|
-
catch (
|
|
726
|
+
catch (error) {
|
|
727
|
+
if (this.isWebUsbIoHardwareError(error))
|
|
728
|
+
throw error;
|
|
669
729
|
return false;
|
|
670
730
|
}
|
|
671
731
|
});
|
|
@@ -680,6 +740,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
680
740
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
|
|
681
741
|
logger: this.Log,
|
|
682
742
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
743
|
+
shouldRethrow: error => this.isWebUsbIoHardwareError(error),
|
|
683
744
|
});
|
|
684
745
|
});
|
|
685
746
|
}
|
|
@@ -726,7 +787,15 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
726
787
|
}
|
|
727
788
|
callProtocolV2(path, name, data, options) {
|
|
728
789
|
return __awaiter(this, void 0, void 0, function* () {
|
|
729
|
-
|
|
790
|
+
try {
|
|
791
|
+
return yield this.callProtocolV2Usb(path, name, data, options);
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
if (!(error instanceof transport.ProtocolV2LinkError) || error.code !== 'io')
|
|
795
|
+
throw error;
|
|
796
|
+
const operation = error.message.includes(' USB write failed:') ? 'transferOut' : 'transferIn';
|
|
797
|
+
return this.throwWebUsbIoError(path, operation, error);
|
|
798
|
+
}
|
|
730
799
|
});
|
|
731
800
|
}
|
|
732
801
|
receiveData(path, timeoutMs) {
|
package/dist/webusb.d.ts
CHANGED
|
@@ -44,14 +44,18 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
44
44
|
private createProtocolDetectionError;
|
|
45
45
|
private detectProtocol;
|
|
46
46
|
findDevice(path: string): Promise<USBDevice>;
|
|
47
|
-
connect(path: string, first: boolean): Promise<
|
|
47
|
+
connect(path: string, first: boolean): Promise<undefined>;
|
|
48
48
|
private discoverEndpoints;
|
|
49
|
-
connectToDevice(path: string, first: boolean): Promise<
|
|
49
|
+
connectToDevice(path: string, first: boolean): Promise<undefined>;
|
|
50
50
|
private closeOpenDevice;
|
|
51
51
|
private clearEndpointHalt;
|
|
52
52
|
private assertAcquired;
|
|
53
53
|
post(session: string, name: string, data: Record<string, unknown>): Promise<void>;
|
|
54
54
|
private getErrorMessage;
|
|
55
|
+
private getErrorName;
|
|
56
|
+
private isWebUsbIoHardwareError;
|
|
57
|
+
private isDeviceStillEnumerated;
|
|
58
|
+
private throwWebUsbIoError;
|
|
55
59
|
private isRetryablePacketIoError;
|
|
56
60
|
private reconnectForPacketIoRetry;
|
|
57
61
|
private getTransferInData;
|
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;
|
|
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.3-alpha.
|
|
3
|
+
"version": "1.2.3-alpha.5",
|
|
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.3-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.3-alpha.
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.3-alpha.5",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.3-alpha.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@onekeyfe/hd-transport-electron": "1.2.3-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.3-alpha.5",
|
|
29
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
30
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "ee493c3ad41a6bc7fb249fba71d4b9bf8efcced8"
|
|
33
33
|
}
|
package/src/webusb.ts
CHANGED
|
@@ -13,6 +13,7 @@ import transport, {
|
|
|
13
13
|
} from '@onekeyfe/hd-transport';
|
|
14
14
|
import {
|
|
15
15
|
ERRORS,
|
|
16
|
+
HardwareError,
|
|
16
17
|
HardwareErrorCode,
|
|
17
18
|
ONEKEY_WEBUSB_FILTER,
|
|
18
19
|
inferProtocolHintFromUsbId,
|
|
@@ -70,6 +71,8 @@ interface TransferCancelToken {
|
|
|
70
71
|
cancelled: boolean;
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
type WebUsbOperation = 'claimInterface' | 'transferIn' | 'transferOut';
|
|
75
|
+
|
|
73
76
|
/**
|
|
74
77
|
* The navigator.usb disconnect listener is module-scoped and attached at most
|
|
75
78
|
* once: listeners on navigator.usb are global and never garbage collected, so a
|
|
@@ -592,7 +595,11 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
592
595
|
// Discover endpoints from USB descriptors; descriptors are not used for protocol selection.
|
|
593
596
|
const endpoints = this.discoverEndpoints(device);
|
|
594
597
|
this.deviceEndpoints.set(path, endpoints);
|
|
595
|
-
|
|
598
|
+
try {
|
|
599
|
+
await device.claimInterface(endpoints.interfaceNumber);
|
|
600
|
+
} catch (error) {
|
|
601
|
+
return this.throwWebUsbIoError(path, 'claimInterface', error);
|
|
602
|
+
}
|
|
596
603
|
await this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
|
|
597
604
|
await this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
598
605
|
}
|
|
@@ -662,6 +669,65 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
662
669
|
return String(error);
|
|
663
670
|
}
|
|
664
671
|
|
|
672
|
+
private getErrorName(error: unknown) {
|
|
673
|
+
if (typeof error === 'object' && error && 'name' in error) {
|
|
674
|
+
const { name } = error as { name?: unknown };
|
|
675
|
+
return typeof name === 'string' ? name : String(name ?? '');
|
|
676
|
+
}
|
|
677
|
+
return '';
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private isWebUsbIoHardwareError(error: unknown): error is HardwareError {
|
|
681
|
+
return (
|
|
682
|
+
error instanceof HardwareError &&
|
|
683
|
+
(error.errorCode === HardwareErrorCode.BridgeDeviceDisconnected ||
|
|
684
|
+
error.errorCode === HardwareErrorCode.WebUsbDeviceAccessError)
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
private async isDeviceStillEnumerated(path: string): Promise<boolean | undefined> {
|
|
689
|
+
if (!this.usb) return undefined;
|
|
690
|
+
try {
|
|
691
|
+
const devices = await this.usb.getDevices();
|
|
692
|
+
return devices.some(device => resolveOneKeyUsbDevicePath(device) === path);
|
|
693
|
+
} catch (error) {
|
|
694
|
+
this.Log?.debug('[WebUsbTransport] failed to verify WebUSB device presence:', error);
|
|
695
|
+
return undefined;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
private async throwWebUsbIoError(
|
|
700
|
+
path: string,
|
|
701
|
+
operation: WebUsbOperation,
|
|
702
|
+
error: unknown
|
|
703
|
+
): Promise<never> {
|
|
704
|
+
if (this.isWebUsbIoHardwareError(error)) throw error;
|
|
705
|
+
if (error instanceof ProtocolV2LinkError && error.code !== 'io') throw error;
|
|
706
|
+
|
|
707
|
+
const nativeError = error instanceof ProtocolV2LinkError ? error.cause ?? error : error;
|
|
708
|
+
if (this.isWebUsbIoHardwareError(nativeError)) throw nativeError;
|
|
709
|
+
|
|
710
|
+
const nativeErrorName = this.getErrorName(nativeError);
|
|
711
|
+
const nativeErrorMessage = this.getErrorMessage(nativeError);
|
|
712
|
+
const disconnectedByMessage =
|
|
713
|
+
/(?:device )?disconnected|device not found|action was interrupted/i.test(
|
|
714
|
+
`${nativeErrorName}: ${nativeErrorMessage}`
|
|
715
|
+
);
|
|
716
|
+
const stillEnumerated = disconnectedByMessage
|
|
717
|
+
? false
|
|
718
|
+
: await this.isDeviceStillEnumerated(path);
|
|
719
|
+
const errorCode =
|
|
720
|
+
disconnectedByMessage || stillEnumerated === false
|
|
721
|
+
? HardwareErrorCode.BridgeDeviceDisconnected
|
|
722
|
+
: HardwareErrorCode.WebUsbDeviceAccessError;
|
|
723
|
+
|
|
724
|
+
throw ERRORS.TypedError(errorCode, nativeErrorMessage || undefined, {
|
|
725
|
+
operation,
|
|
726
|
+
nativeErrorName: nativeErrorName || undefined,
|
|
727
|
+
nativeErrorMessage: nativeErrorMessage || undefined,
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
|
|
665
731
|
private isRetryablePacketIoError(error: unknown) {
|
|
666
732
|
const message = this.getErrorMessage(error).toLowerCase();
|
|
667
733
|
return (
|
|
@@ -742,7 +808,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
742
808
|
lastError = error;
|
|
743
809
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
744
810
|
if (!shouldRetry) {
|
|
745
|
-
|
|
811
|
+
return this.throwWebUsbIoError(path, 'transferOut', error);
|
|
746
812
|
}
|
|
747
813
|
try {
|
|
748
814
|
await this.reconnectForPacketIoRetry(path, 'out', attempt, error);
|
|
@@ -756,7 +822,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
756
822
|
}
|
|
757
823
|
}
|
|
758
824
|
}
|
|
759
|
-
|
|
825
|
+
return this.throwWebUsbIoError(path, 'transferOut', lastError);
|
|
760
826
|
}
|
|
761
827
|
|
|
762
828
|
private async transferOutOnce(path: string, packet: Uint8Array) {
|
|
@@ -798,7 +864,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
798
864
|
}
|
|
799
865
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
800
866
|
if (!shouldRetry) {
|
|
801
|
-
|
|
867
|
+
return this.throwWebUsbIoError(path, 'transferIn', error);
|
|
802
868
|
}
|
|
803
869
|
try {
|
|
804
870
|
await this.reconnectForPacketIoRetry(path, 'in', attempt, error);
|
|
@@ -812,7 +878,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
812
878
|
}
|
|
813
879
|
}
|
|
814
880
|
}
|
|
815
|
-
|
|
881
|
+
return this.throwWebUsbIoError(path, 'transferIn', lastError);
|
|
816
882
|
}
|
|
817
883
|
|
|
818
884
|
private async transferInOnce(path: string, length: number): Promise<DataView> {
|
|
@@ -894,7 +960,8 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
894
960
|
}
|
|
895
961
|
);
|
|
896
962
|
return true;
|
|
897
|
-
} catch (
|
|
963
|
+
} catch (error) {
|
|
964
|
+
if (this.isWebUsbIoHardwareError(error)) throw error;
|
|
898
965
|
return false;
|
|
899
966
|
}
|
|
900
967
|
}
|
|
@@ -909,6 +976,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
909
976
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
|
|
910
977
|
logger: this.Log,
|
|
911
978
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
979
|
+
shouldRethrow: error => this.isWebUsbIoHardwareError(error),
|
|
912
980
|
});
|
|
913
981
|
}
|
|
914
982
|
|
|
@@ -986,7 +1054,13 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
986
1054
|
data: Record<string, unknown>,
|
|
987
1055
|
options?: TransportCallOptions
|
|
988
1056
|
) {
|
|
989
|
-
|
|
1057
|
+
try {
|
|
1058
|
+
return await this.callProtocolV2Usb(path, name, data, options);
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
if (!(error instanceof ProtocolV2LinkError) || error.code !== 'io') throw error;
|
|
1061
|
+
const operation = error.message.includes(' USB write failed:') ? 'transferOut' : 'transferIn';
|
|
1062
|
+
return this.throwWebUsbIoError(path, operation, error);
|
|
1063
|
+
}
|
|
990
1064
|
}
|
|
991
1065
|
|
|
992
1066
|
/**
|