@onekeyfe/hd-transport-web-device 1.2.8-alpha.3 → 1.2.8-alpha.4
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__/electron-ble-transport.test.ts +23 -34
- package/__tests__/webusb-protocol-cache.test.ts +0 -121
- package/__tests__/webusb-protocol-v2-timeout.test.ts +3 -9
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +2 -7
- package/dist/index.js +15 -116
- package/dist/webusb.d.ts +2 -7
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +7 -6
- package/src/webusb.ts +10 -117
|
@@ -182,30 +182,16 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
182
182
|
jest.clearAllMocks();
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
-
test.each([
|
|
186
|
-
{ errorCode: HardwareErrorCode.BleLocationError, message: 'Permission denied' },
|
|
187
|
-
{ code: HardwareErrorCode.BleLocationError },
|
|
188
|
-
{ message: 'Bluetooth unauthorized' },
|
|
189
|
-
])('preserves Bluetooth permission failures from the native bridge: %j', async error => {
|
|
190
|
-
const nobleBle = createNobleBle();
|
|
191
|
-
nobleBle.enumerate.mockRejectedValueOnce(error);
|
|
192
|
-
const bleTransport = configureTransport(nobleBle);
|
|
193
|
-
|
|
194
|
-
await expect(bleTransport.enumerate()).rejects.toMatchObject({
|
|
195
|
-
errorCode: HardwareErrorCode.BleLocationError,
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
185
|
test('does not treat an error envelope from a legacy preload as a successful connect', async () => {
|
|
200
|
-
const device = { id: '
|
|
186
|
+
const device = { id: 'stale-bond-id', name: 'OneKey Pro 2' };
|
|
201
187
|
const nobleBle = createNobleBle(device);
|
|
202
188
|
nobleBle.connect.mockResolvedValue({
|
|
203
189
|
type: 'NobleBleIpcError',
|
|
204
190
|
success: false,
|
|
205
191
|
error: {
|
|
206
192
|
name: 'HardwareError',
|
|
207
|
-
message: 'Bluetooth
|
|
208
|
-
errorCode: HardwareErrorCode.
|
|
193
|
+
message: 'Bluetooth pairing information is no longer valid',
|
|
194
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
209
195
|
},
|
|
210
196
|
} as never);
|
|
211
197
|
const bleTransport = configureTransport(nobleBle);
|
|
@@ -213,7 +199,7 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
213
199
|
await expect(
|
|
214
200
|
bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
215
201
|
).rejects.toMatchObject({
|
|
216
|
-
errorCode: HardwareErrorCode.
|
|
202
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
217
203
|
});
|
|
218
204
|
expect(nobleBle.subscribe).not.toHaveBeenCalled();
|
|
219
205
|
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
@@ -656,13 +642,14 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
656
642
|
expect(transport.getProtocolType(device.id)).toBeUndefined();
|
|
657
643
|
});
|
|
658
644
|
|
|
659
|
-
test('fails Protocol V2 acquire immediately when subscribe reports
|
|
660
|
-
const device = { id: '
|
|
645
|
+
test('fails Protocol V2 acquire immediately when subscribe reports insufficient encryption', async () => {
|
|
646
|
+
const device = { id: 'stale-bond-pro2-id', name: 'OneKey Pro 2' };
|
|
661
647
|
const nobleBle = createNobleBle(device);
|
|
662
648
|
nobleBle.subscribe.mockRejectedValue({
|
|
663
649
|
name: 'HardwareError',
|
|
664
|
-
message: 'Bluetooth
|
|
665
|
-
errorCode: HardwareErrorCode.
|
|
650
|
+
message: 'Bluetooth pairing information is no longer valid',
|
|
651
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
652
|
+
params: { nativeErrorMessage: 'Encryption is insufficient' },
|
|
666
653
|
});
|
|
667
654
|
const transport = configureTransport(nobleBle);
|
|
668
655
|
const probe = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
@@ -670,7 +657,7 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
670
657
|
await expect(
|
|
671
658
|
transport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
672
659
|
).rejects.toMatchObject({
|
|
673
|
-
errorCode: HardwareErrorCode.
|
|
660
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
674
661
|
});
|
|
675
662
|
|
|
676
663
|
expect(probe).not.toHaveBeenCalled();
|
|
@@ -678,19 +665,21 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
678
665
|
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
679
666
|
});
|
|
680
667
|
|
|
681
|
-
test('rehydrates a structured
|
|
668
|
+
test('rehydrates a structured stale-bond error before the protocol is known', async () => {
|
|
682
669
|
const device = { id: 'reset-unknown-protocol-id', name: 'OneKey Pro 2' };
|
|
683
670
|
const nobleBle = createNobleBle(device);
|
|
684
671
|
nobleBle.connect.mockRejectedValue({
|
|
685
672
|
name: 'HardwareError',
|
|
686
|
-
message: 'Bluetooth
|
|
687
|
-
errorCode: HardwareErrorCode.
|
|
673
|
+
message: 'Bluetooth pairing information is no longer valid',
|
|
674
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
675
|
+
params: { nativeErrorMessage: 'CBErrorDomain:14 native message' },
|
|
688
676
|
});
|
|
689
677
|
const transport = configureTransport(nobleBle);
|
|
690
678
|
|
|
691
679
|
await expect(transport.acquire({ uuid: device.id })).rejects.toMatchObject({
|
|
692
680
|
name: 'HardwareError',
|
|
693
|
-
errorCode: HardwareErrorCode.
|
|
681
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
682
|
+
params: { nativeErrorMessage: 'CBErrorDomain:14 native message' },
|
|
694
683
|
});
|
|
695
684
|
});
|
|
696
685
|
|
|
@@ -710,7 +699,7 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
710
699
|
}
|
|
711
700
|
});
|
|
712
701
|
|
|
713
|
-
test('
|
|
702
|
+
test('does not classify a generic macOS connection failure as a stale bond', async () => {
|
|
714
703
|
const device = { id: 'offline-pro2-macos-id', name: 'OneKey Pro 2' };
|
|
715
704
|
const nobleBle = createNobleBle(device);
|
|
716
705
|
nobleBle.connect.mockRejectedValue(new Error('connection failed'));
|
|
@@ -726,7 +715,7 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
726
715
|
}
|
|
727
716
|
});
|
|
728
717
|
|
|
729
|
-
test('
|
|
718
|
+
test('keeps stale-bond subscribe mapping out of Protocol V1 acquire', async () => {
|
|
730
719
|
const device = { id: 'classic-v1-id', name: 'OneKey Classic' };
|
|
731
720
|
const nobleBle = createNobleBle(device);
|
|
732
721
|
nobleBle.subscribe.mockRejectedValue(new Error('Encryption is insufficient'));
|
|
@@ -791,19 +780,19 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
791
780
|
}
|
|
792
781
|
);
|
|
793
782
|
|
|
794
|
-
test('preserves a native
|
|
795
|
-
const device = { id: '
|
|
783
|
+
test('preserves a native stale-bond error during an expected Protocol V2 probe', async () => {
|
|
784
|
+
const device = { id: 'stale-bond-probe-id', name: 'OneKey Pro 2' };
|
|
796
785
|
const nobleBle = createNobleBle(device);
|
|
797
786
|
nobleBle.write.mockRejectedValue({
|
|
798
787
|
name: 'HardwareError',
|
|
799
|
-
message: 'Bluetooth
|
|
800
|
-
errorCode: HardwareErrorCode.
|
|
788
|
+
message: 'Bluetooth pairing information is no longer valid',
|
|
789
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
801
790
|
});
|
|
802
791
|
const transport = configureTransport(nobleBle);
|
|
803
792
|
|
|
804
793
|
await expect(
|
|
805
794
|
transport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
806
|
-
).rejects.toMatchObject({ errorCode: HardwareErrorCode.
|
|
795
|
+
).rejects.toMatchObject({ errorCode: HardwareErrorCode.BleBondInvalid });
|
|
807
796
|
|
|
808
797
|
expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
|
|
809
798
|
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
@@ -64,127 +64,6 @@ 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
|
-
|
|
115
|
-
test.each(['LIBUSB_ERROR_ACCESS', "Failed to execute 'open' on 'USBDevice': Access denied."])(
|
|
116
|
-
'reports USB open permission failure: %s',
|
|
117
|
-
async message => {
|
|
118
|
-
const webusb = new WebUsbTransport();
|
|
119
|
-
const device = {
|
|
120
|
-
opened: false,
|
|
121
|
-
open: jest
|
|
122
|
-
.fn()
|
|
123
|
-
.mockRejectedValue(Object.assign(new Error(message), { name: 'SecurityError' })),
|
|
124
|
-
};
|
|
125
|
-
jest.spyOn(webusb, 'findDevice').mockResolvedValue(device as unknown as USBDevice);
|
|
126
|
-
await expect(webusb.connect('mock-usb', true)).rejects.toMatchObject({
|
|
127
|
-
errorCode: HardwareErrorCode.BridgeNeedsPermission,
|
|
128
|
-
params: {
|
|
129
|
-
operation: 'open',
|
|
130
|
-
nativeErrorName: 'SecurityError',
|
|
131
|
-
nativeErrorMessage: message,
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
expect(device.open).toHaveBeenCalledTimes(1);
|
|
135
|
-
}
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
test('does not classify protected-interface SecurityError as missing OS permission', async () => {
|
|
139
|
-
const webusb = new WebUsbTransport();
|
|
140
|
-
const device = {
|
|
141
|
-
opened: false,
|
|
142
|
-
open: jest
|
|
143
|
-
.fn()
|
|
144
|
-
.mockRejectedValue(
|
|
145
|
-
Object.assign(new Error('Protected interface'), { name: 'SecurityError' })
|
|
146
|
-
),
|
|
147
|
-
};
|
|
148
|
-
jest.spyOn(webusb, 'findDevice').mockResolvedValue(device as unknown as USBDevice);
|
|
149
|
-
await expect(webusb.connectToDevice('mock-usb', true)).rejects.toMatchObject({
|
|
150
|
-
errorCode: HardwareErrorCode.WebUsbDeviceAccessError,
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test.each(['claimInterface', 'transferIn', 'transferOut'] as const)(
|
|
155
|
-
'preserves USB permission failures during %s',
|
|
156
|
-
async operation => {
|
|
157
|
-
const webusb = new WebUsbTransport() as unknown as {
|
|
158
|
-
throwWebUsbIoError(path: string, operation: string, error: unknown): Promise<never>;
|
|
159
|
-
};
|
|
160
|
-
await expect(
|
|
161
|
-
webusb.throwWebUsbIoError('mock-usb', operation, new Error('LIBUSB_ERROR_ACCESS'))
|
|
162
|
-
).rejects.toMatchObject({
|
|
163
|
-
errorCode: HardwareErrorCode.BridgeNeedsPermission,
|
|
164
|
-
params: { operation, nativeErrorMessage: 'LIBUSB_ERROR_ACCESS' },
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
test('fails a stalled USB write without replaying the packet', async () => {
|
|
170
|
-
const webusb = new WebUsbTransport();
|
|
171
|
-
const device = {
|
|
172
|
-
opened: true,
|
|
173
|
-
transferOut: jest.fn().mockResolvedValue({ status: 'stall', bytesWritten: 0 }),
|
|
174
|
-
};
|
|
175
|
-
jest.spyOn(webusb, 'findDevice').mockResolvedValue(device as unknown as USBDevice);
|
|
176
|
-
const state = webusb as unknown as {
|
|
177
|
-
transferOutWithRetry(path: string, packet: Uint8Array): Promise<void>;
|
|
178
|
-
};
|
|
179
|
-
await expect(state.transferOutWithRetry('mock-usb', new Uint8Array([1]))).rejects.toMatchObject(
|
|
180
|
-
{
|
|
181
|
-
errorCode: HardwareErrorCode.WebUsbDeviceAccessError,
|
|
182
|
-
params: { operation: 'transferOut', nativeErrorMessage: 'transferOut status: stall' },
|
|
183
|
-
}
|
|
184
|
-
);
|
|
185
|
-
expect(device.transferOut).toHaveBeenCalledTimes(1);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
67
|
test('acquire skips the wire probe when the protocol is already cached', async () => {
|
|
189
68
|
const webusb = buildAcquirableTransport();
|
|
190
69
|
const path = 'pro-webusb';
|
|
@@ -238,15 +238,9 @@ 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
|
-
)
|
|
244
|
-
errorCode: HardwareErrorCode.BridgeDeviceDisconnected,
|
|
245
|
-
params: {
|
|
246
|
-
operation: 'transferIn',
|
|
247
|
-
nativeErrorMessage: 'NetworkError: transferIn device disconnected',
|
|
248
|
-
},
|
|
249
|
-
});
|
|
241
|
+
await expect(webusb.callProtocolV2(path, 'Ping', { message: 'read-error' })).rejects.toThrow(
|
|
242
|
+
'NetworkError'
|
|
243
|
+
);
|
|
250
244
|
|
|
251
245
|
expect(webusb.readProtocolV2UsbPacket).toHaveBeenCalledTimes(1);
|
|
252
246
|
expect(webusb.resetProtocolV2UsbNativeLink).toHaveBeenCalledWith(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":"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"}
|
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<void>;
|
|
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<void>;
|
|
132
132
|
private closeOpenDevice;
|
|
133
133
|
private clearEndpointHalt;
|
|
134
134
|
/**
|
|
@@ -141,11 +141,6 @@ 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;
|
|
148
|
-
private isWebUsbPermissionDeniedMessage;
|
|
149
144
|
private isRetryablePacketIoError;
|
|
150
145
|
private reconnectForPacketIoRetry;
|
|
151
146
|
private getTransferInData;
|
package/dist/index.js
CHANGED
|
@@ -332,9 +332,6 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
332
332
|
return yield this.connectToDevice(path, first);
|
|
333
333
|
}
|
|
334
334
|
catch (e) {
|
|
335
|
-
if (e instanceof hdShared.HardwareError && e.errorCode === hdShared.HardwareErrorCode.BridgeNeedsPermission) {
|
|
336
|
-
throw e;
|
|
337
|
-
}
|
|
338
335
|
if (i === maxRetries - 1) {
|
|
339
336
|
throw e;
|
|
340
337
|
}
|
|
@@ -376,12 +373,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
376
373
|
return __awaiter(this, void 0, void 0, function* () {
|
|
377
374
|
let device = yield this.findDevice(path);
|
|
378
375
|
if (!device.opened) {
|
|
379
|
-
|
|
380
|
-
yield device.open();
|
|
381
|
-
}
|
|
382
|
-
catch (error) {
|
|
383
|
-
return this.throwWebUsbIoError(path, 'open', error);
|
|
384
|
-
}
|
|
376
|
+
yield device.open();
|
|
385
377
|
}
|
|
386
378
|
if (first || this.deviceProtocol.get(path) !== 'V1') {
|
|
387
379
|
try {
|
|
@@ -394,12 +386,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
394
386
|
yield this.getConnectedDevices();
|
|
395
387
|
device = yield this.findDevice(path);
|
|
396
388
|
if (!device.opened) {
|
|
397
|
-
|
|
398
|
-
yield device.open();
|
|
399
|
-
}
|
|
400
|
-
catch (error) {
|
|
401
|
-
return this.throwWebUsbIoError(path, 'open', error);
|
|
402
|
-
}
|
|
389
|
+
yield device.open();
|
|
403
390
|
}
|
|
404
391
|
if (first ||
|
|
405
392
|
!device.configuration ||
|
|
@@ -408,12 +395,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
408
395
|
}
|
|
409
396
|
const endpoints = this.discoverEndpoints(device);
|
|
410
397
|
this.deviceEndpoints.set(path, endpoints);
|
|
411
|
-
|
|
412
|
-
yield device.claimInterface(endpoints.interfaceNumber);
|
|
413
|
-
}
|
|
414
|
-
catch (error) {
|
|
415
|
-
return this.throwWebUsbIoError(path, 'claimInterface', error);
|
|
416
|
-
}
|
|
398
|
+
yield device.claimInterface(endpoints.interfaceNumber);
|
|
417
399
|
yield this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
|
|
418
400
|
yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
419
401
|
});
|
|
@@ -477,76 +459,8 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
477
459
|
}
|
|
478
460
|
return String(error);
|
|
479
461
|
}
|
|
480
|
-
getErrorName(error) {
|
|
481
|
-
if (typeof error === 'object' && error && 'name' in error) {
|
|
482
|
-
const { name } = error;
|
|
483
|
-
return typeof name === 'string' ? name : String(name !== null && name !== void 0 ? name : '');
|
|
484
|
-
}
|
|
485
|
-
return '';
|
|
486
|
-
}
|
|
487
|
-
isWebUsbIoHardwareError(error) {
|
|
488
|
-
return (error instanceof hdShared.HardwareError &&
|
|
489
|
-
(error.errorCode === hdShared.HardwareErrorCode.BridgeDeviceDisconnected ||
|
|
490
|
-
error.errorCode === hdShared.HardwareErrorCode.BridgeNeedsPermission ||
|
|
491
|
-
error.errorCode === hdShared.HardwareErrorCode.WebUsbDeviceAccessError));
|
|
492
|
-
}
|
|
493
|
-
isDeviceStillEnumerated(path) {
|
|
494
|
-
var _a;
|
|
495
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
496
|
-
if (!this.usb)
|
|
497
|
-
return undefined;
|
|
498
|
-
try {
|
|
499
|
-
const devices = yield this.usb.getDevices();
|
|
500
|
-
return devices.some(device => hdShared.resolveOneKeyUsbDevicePath(device) === path);
|
|
501
|
-
}
|
|
502
|
-
catch (error) {
|
|
503
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] failed to verify WebUSB device presence:', error);
|
|
504
|
-
return undefined;
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
throwWebUsbIoError(path, operation, error) {
|
|
509
|
-
var _a;
|
|
510
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
511
|
-
if (this.isWebUsbIoHardwareError(error))
|
|
512
|
-
throw error;
|
|
513
|
-
if (error instanceof transport.ProtocolV2LinkError && error.code !== 'io')
|
|
514
|
-
throw error;
|
|
515
|
-
const nativeError = error instanceof transport.ProtocolV2LinkError ? (_a = error.cause) !== null && _a !== void 0 ? _a : error : error;
|
|
516
|
-
if (this.isWebUsbIoHardwareError(nativeError))
|
|
517
|
-
throw nativeError;
|
|
518
|
-
const nativeErrorName = this.getErrorName(nativeError);
|
|
519
|
-
const nativeErrorMessage = this.getErrorMessage(nativeError);
|
|
520
|
-
if (this.isWebUsbPermissionDeniedMessage(nativeErrorMessage)) {
|
|
521
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BridgeNeedsPermission, nativeErrorMessage, {
|
|
522
|
-
operation,
|
|
523
|
-
nativeErrorName: nativeErrorName || undefined,
|
|
524
|
-
nativeErrorMessage,
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
const disconnectedByMessage = /(?:device )?disconnected|device not found|action was interrupted/i.test(`${nativeErrorName}: ${nativeErrorMessage}`);
|
|
528
|
-
const stillEnumerated = disconnectedByMessage
|
|
529
|
-
? false
|
|
530
|
-
: yield this.isDeviceStillEnumerated(path);
|
|
531
|
-
const errorCode = disconnectedByMessage || stillEnumerated === false
|
|
532
|
-
? hdShared.HardwareErrorCode.BridgeDeviceDisconnected
|
|
533
|
-
: hdShared.HardwareErrorCode.WebUsbDeviceAccessError;
|
|
534
|
-
throw hdShared.ERRORS.TypedError(errorCode, nativeErrorMessage || undefined, {
|
|
535
|
-
operation,
|
|
536
|
-
nativeErrorName: nativeErrorName || undefined,
|
|
537
|
-
nativeErrorMessage: nativeErrorMessage || undefined,
|
|
538
|
-
});
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
isWebUsbPermissionDeniedMessage(message) {
|
|
542
|
-
return /LIBUSB_ERROR_ACCESS|\b(?:access|permission) denied\b/i.test(message);
|
|
543
|
-
}
|
|
544
462
|
isRetryablePacketIoError(error) {
|
|
545
|
-
if (this.isWebUsbIoHardwareError(error))
|
|
546
|
-
return false;
|
|
547
463
|
const message = this.getErrorMessage(error).toLowerCase();
|
|
548
|
-
if (this.isWebUsbPermissionDeniedMessage(message))
|
|
549
|
-
return false;
|
|
550
464
|
return (message.includes('transferout') ||
|
|
551
465
|
message.includes('transferin') ||
|
|
552
466
|
message.includes('usbdevice') ||
|
|
@@ -611,7 +525,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
611
525
|
lastError = error;
|
|
612
526
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
613
527
|
if (!shouldRetry) {
|
|
614
|
-
|
|
528
|
+
throw error;
|
|
615
529
|
}
|
|
616
530
|
try {
|
|
617
531
|
yield this.reconnectForPacketIoRetry(path, 'out', attempt, error);
|
|
@@ -622,7 +536,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
622
536
|
}
|
|
623
537
|
}
|
|
624
538
|
}
|
|
625
|
-
|
|
539
|
+
throw lastError;
|
|
626
540
|
});
|
|
627
541
|
}
|
|
628
542
|
transferOutOnce(path, packet) {
|
|
@@ -635,10 +549,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
635
549
|
const endpoints = this.deviceEndpoints.get(path);
|
|
636
550
|
const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
637
551
|
const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
638
|
-
|
|
639
|
-
if (result.status !== 'ok') {
|
|
640
|
-
return this.throwWebUsbIoError(path, 'transferOut', new Error(`transferOut status: ${String(result.status)}`));
|
|
641
|
-
}
|
|
552
|
+
yield device.transferOut(endpointOut, transferBuffer);
|
|
642
553
|
});
|
|
643
554
|
}
|
|
644
555
|
transferInWithRetry(path, length, cancelToken) {
|
|
@@ -666,7 +577,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
666
577
|
}
|
|
667
578
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
668
579
|
if (!shouldRetry) {
|
|
669
|
-
|
|
580
|
+
throw error;
|
|
670
581
|
}
|
|
671
582
|
try {
|
|
672
583
|
yield this.reconnectForPacketIoRetry(path, 'in', attempt, error);
|
|
@@ -677,7 +588,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
677
588
|
}
|
|
678
589
|
}
|
|
679
590
|
}
|
|
680
|
-
|
|
591
|
+
throw lastError;
|
|
681
592
|
});
|
|
682
593
|
}
|
|
683
594
|
transferInOnce(path, length) {
|
|
@@ -754,9 +665,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
754
665
|
});
|
|
755
666
|
return true;
|
|
756
667
|
}
|
|
757
|
-
catch (
|
|
758
|
-
if (this.isWebUsbIoHardwareError(error))
|
|
759
|
-
throw error;
|
|
668
|
+
catch (_error) {
|
|
760
669
|
return false;
|
|
761
670
|
}
|
|
762
671
|
});
|
|
@@ -771,7 +680,6 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
771
680
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
|
|
772
681
|
logger: this.Log,
|
|
773
682
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
774
|
-
shouldRethrow: error => this.isWebUsbIoHardwareError(error),
|
|
775
683
|
});
|
|
776
684
|
});
|
|
777
685
|
}
|
|
@@ -818,15 +726,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
818
726
|
}
|
|
819
727
|
callProtocolV2(path, name, data, options) {
|
|
820
728
|
return __awaiter(this, void 0, void 0, function* () {
|
|
821
|
-
|
|
822
|
-
return yield this.callProtocolV2Usb(path, name, data, options);
|
|
823
|
-
}
|
|
824
|
-
catch (error) {
|
|
825
|
-
if (!(error instanceof transport.ProtocolV2LinkError) || error.code !== 'io')
|
|
826
|
-
throw error;
|
|
827
|
-
const operation = error.message.includes(' USB write failed:') ? 'transferOut' : 'transferIn';
|
|
828
|
-
return this.throwWebUsbIoError(path, operation, error);
|
|
829
|
-
}
|
|
729
|
+
return this.callProtocolV2Usb(path, name, data, options);
|
|
830
730
|
});
|
|
831
731
|
}
|
|
832
732
|
receiveData(path, timeoutMs) {
|
|
@@ -1001,14 +901,11 @@ class ElectronBleTransport {
|
|
|
1001
901
|
if (error.code === hdShared.HardwareErrorCode.BlePermissionError) {
|
|
1002
902
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
|
|
1003
903
|
}
|
|
1004
|
-
if (error.code === hdShared.HardwareErrorCode.BleLocationError) {
|
|
1005
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError);
|
|
1006
|
-
}
|
|
1007
904
|
}
|
|
1008
905
|
const errorMessage = error.message || String(error);
|
|
1009
906
|
const poweredOffMessage = hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BlePoweredOff];
|
|
1010
907
|
const unsupportedMessage = hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleUnsupported];
|
|
1011
|
-
const permissionMessage = hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.
|
|
908
|
+
const permissionMessage = hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BlePermissionError];
|
|
1012
909
|
if (errorMessage.includes(poweredOffMessage) || errorMessage.includes('poweredOff')) {
|
|
1013
910
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePoweredOff);
|
|
1014
911
|
}
|
|
@@ -1016,7 +913,7 @@ class ElectronBleTransport {
|
|
|
1016
913
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleUnsupported);
|
|
1017
914
|
}
|
|
1018
915
|
if (errorMessage.includes(permissionMessage) || errorMessage.includes('unauthorized')) {
|
|
1019
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.
|
|
916
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
|
|
1020
917
|
}
|
|
1021
918
|
}
|
|
1022
919
|
return error;
|
|
@@ -1392,7 +1289,9 @@ class ElectronBleTransport {
|
|
|
1392
1289
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1393
1290
|
this.resetProtocolV2Frames(uuid);
|
|
1394
1291
|
},
|
|
1395
|
-
shouldRethrow: error => expectedProtocol === 'V2' ||
|
|
1292
|
+
shouldRethrow: error => expectedProtocol === 'V2' ||
|
|
1293
|
+
hdShared.isBleStaleBondHardwareError(error) ||
|
|
1294
|
+
transport.isProtocolV2LinkDisabledError(error),
|
|
1396
1295
|
});
|
|
1397
1296
|
if (!detected) {
|
|
1398
1297
|
this.clearProbeProtocol(uuid, 'V2');
|
package/dist/webusb.d.ts
CHANGED
|
@@ -44,19 +44,14 @@ 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<void>;
|
|
48
48
|
private discoverEndpoints;
|
|
49
|
-
connectToDevice(path: string, first: boolean): Promise<
|
|
49
|
+
connectToDevice(path: string, first: boolean): Promise<void>;
|
|
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;
|
|
59
|
-
private isWebUsbPermissionDeniedMessage;
|
|
60
55
|
private isRetryablePacketIoError;
|
|
61
56
|
private reconnectForPacketIoRetry;
|
|
62
57
|
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.8-alpha.
|
|
3
|
+
"version": "1.2.8-alpha.4",
|
|
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.4",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.8-alpha.4"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@onekeyfe/hd-transport-electron": "1.2.8-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.8-alpha.4",
|
|
29
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
30
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "82fcab32fa1a37d1b82f73da825e39360624e0c9"
|
|
33
33
|
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
HardwareErrorCode,
|
|
19
19
|
HardwareErrorCodeMessage,
|
|
20
20
|
createDeferred,
|
|
21
|
+
isBleStaleBondHardwareError,
|
|
21
22
|
isHeaderChunk,
|
|
22
23
|
} from '@onekeyfe/hd-shared';
|
|
23
24
|
|
|
@@ -202,14 +203,11 @@ export default class ElectronBleTransport {
|
|
|
202
203
|
if (error.code === HardwareErrorCode.BlePermissionError) {
|
|
203
204
|
return ERRORS.TypedError(HardwareErrorCode.BlePermissionError);
|
|
204
205
|
}
|
|
205
|
-
if (error.code === HardwareErrorCode.BleLocationError) {
|
|
206
|
-
return ERRORS.TypedError(HardwareErrorCode.BleLocationError);
|
|
207
|
-
}
|
|
208
206
|
}
|
|
209
207
|
const errorMessage = error.message || String(error);
|
|
210
208
|
const poweredOffMessage = HardwareErrorCodeMessage[HardwareErrorCode.BlePoweredOff];
|
|
211
209
|
const unsupportedMessage = HardwareErrorCodeMessage[HardwareErrorCode.BleUnsupported];
|
|
212
|
-
const permissionMessage = HardwareErrorCodeMessage[HardwareErrorCode.
|
|
210
|
+
const permissionMessage = HardwareErrorCodeMessage[HardwareErrorCode.BlePermissionError];
|
|
213
211
|
|
|
214
212
|
if (errorMessage.includes(poweredOffMessage) || errorMessage.includes('poweredOff')) {
|
|
215
213
|
return ERRORS.TypedError(HardwareErrorCode.BlePoweredOff);
|
|
@@ -218,7 +216,7 @@ export default class ElectronBleTransport {
|
|
|
218
216
|
return ERRORS.TypedError(HardwareErrorCode.BleUnsupported);
|
|
219
217
|
}
|
|
220
218
|
if (errorMessage.includes(permissionMessage) || errorMessage.includes('unauthorized')) {
|
|
221
|
-
return ERRORS.TypedError(HardwareErrorCode.
|
|
219
|
+
return ERRORS.TypedError(HardwareErrorCode.BlePermissionError);
|
|
222
220
|
}
|
|
223
221
|
}
|
|
224
222
|
return error;
|
|
@@ -678,7 +676,10 @@ export default class ElectronBleTransport {
|
|
|
678
676
|
this.resetProtocolV2Frames(uuid);
|
|
679
677
|
},
|
|
680
678
|
// A declared V2 protocol needs no fallback; preserve the actual link failure.
|
|
681
|
-
shouldRethrow: error =>
|
|
679
|
+
shouldRethrow: error =>
|
|
680
|
+
expectedProtocol === 'V2' ||
|
|
681
|
+
isBleStaleBondHardwareError(error) ||
|
|
682
|
+
isProtocolV2LinkDisabledError(error),
|
|
682
683
|
});
|
|
683
684
|
if (!detected) {
|
|
684
685
|
this.clearProbeProtocol(uuid, 'V2');
|
package/src/webusb.ts
CHANGED
|
@@ -13,7 +13,6 @@ import transport, {
|
|
|
13
13
|
} from '@onekeyfe/hd-transport';
|
|
14
14
|
import {
|
|
15
15
|
ERRORS,
|
|
16
|
-
HardwareError,
|
|
17
16
|
HardwareErrorCode,
|
|
18
17
|
ONEKEY_WEBUSB_FILTER,
|
|
19
18
|
inferProtocolHintFromUsbId,
|
|
@@ -71,8 +70,6 @@ interface TransferCancelToken {
|
|
|
71
70
|
cancelled: boolean;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
type WebUsbOperation = 'open' | 'claimInterface' | 'transferIn' | 'transferOut';
|
|
75
|
-
|
|
76
73
|
/**
|
|
77
74
|
* The navigator.usb disconnect listener is module-scoped and attached at most
|
|
78
75
|
* once: listeners on navigator.usb are global and never garbage collected, so a
|
|
@@ -519,9 +516,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
519
516
|
try {
|
|
520
517
|
return await this.connectToDevice(path, first);
|
|
521
518
|
} catch (e) {
|
|
522
|
-
if (e instanceof HardwareError && e.errorCode === HardwareErrorCode.BridgeNeedsPermission) {
|
|
523
|
-
throw e;
|
|
524
|
-
}
|
|
525
519
|
if (i === maxRetries - 1) {
|
|
526
520
|
throw e;
|
|
527
521
|
}
|
|
@@ -570,11 +564,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
570
564
|
async connectToDevice(path: string, first: boolean) {
|
|
571
565
|
let device: USBDevice = await this.findDevice(path);
|
|
572
566
|
if (!device.opened) {
|
|
573
|
-
|
|
574
|
-
await device.open();
|
|
575
|
-
} catch (error) {
|
|
576
|
-
return this.throwWebUsbIoError(path, 'open', error);
|
|
577
|
-
}
|
|
567
|
+
await device.open();
|
|
578
568
|
}
|
|
579
569
|
// A V1 packet retry continues an in-flight exchange. Preserve its endpoint
|
|
580
570
|
// state as in the legacy transport; probing and V2 recovery still reset.
|
|
@@ -588,11 +578,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
588
578
|
await this.getConnectedDevices();
|
|
589
579
|
device = await this.findDevice(path);
|
|
590
580
|
if (!device.opened) {
|
|
591
|
-
|
|
592
|
-
await device.open();
|
|
593
|
-
} catch (error) {
|
|
594
|
-
return this.throwWebUsbIoError(path, 'open', error);
|
|
595
|
-
}
|
|
581
|
+
await device.open();
|
|
596
582
|
}
|
|
597
583
|
|
|
598
584
|
if (
|
|
@@ -606,11 +592,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
606
592
|
// Discover endpoints from USB descriptors; descriptors are not used for protocol selection.
|
|
607
593
|
const endpoints = this.discoverEndpoints(device);
|
|
608
594
|
this.deviceEndpoints.set(path, endpoints);
|
|
609
|
-
|
|
610
|
-
await device.claimInterface(endpoints.interfaceNumber);
|
|
611
|
-
} catch (error) {
|
|
612
|
-
return this.throwWebUsbIoError(path, 'claimInterface', error);
|
|
613
|
-
}
|
|
595
|
+
await device.claimInterface(endpoints.interfaceNumber);
|
|
614
596
|
await this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
|
|
615
597
|
await this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
616
598
|
}
|
|
@@ -680,82 +662,8 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
680
662
|
return String(error);
|
|
681
663
|
}
|
|
682
664
|
|
|
683
|
-
private getErrorName(error: unknown) {
|
|
684
|
-
if (typeof error === 'object' && error && 'name' in error) {
|
|
685
|
-
const { name } = error as { name?: unknown };
|
|
686
|
-
return typeof name === 'string' ? name : String(name ?? '');
|
|
687
|
-
}
|
|
688
|
-
return '';
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
private isWebUsbIoHardwareError(error: unknown): error is HardwareError {
|
|
692
|
-
return (
|
|
693
|
-
error instanceof HardwareError &&
|
|
694
|
-
(error.errorCode === HardwareErrorCode.BridgeDeviceDisconnected ||
|
|
695
|
-
error.errorCode === HardwareErrorCode.BridgeNeedsPermission ||
|
|
696
|
-
error.errorCode === HardwareErrorCode.WebUsbDeviceAccessError)
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
private async isDeviceStillEnumerated(path: string): Promise<boolean | undefined> {
|
|
701
|
-
if (!this.usb) return undefined;
|
|
702
|
-
try {
|
|
703
|
-
const devices = await this.usb.getDevices();
|
|
704
|
-
return devices.some(device => resolveOneKeyUsbDevicePath(device) === path);
|
|
705
|
-
} catch (error) {
|
|
706
|
-
this.Log?.debug('[WebUsbTransport] failed to verify WebUSB device presence:', error);
|
|
707
|
-
return undefined;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
private async throwWebUsbIoError(
|
|
712
|
-
path: string,
|
|
713
|
-
operation: WebUsbOperation,
|
|
714
|
-
error: unknown
|
|
715
|
-
): Promise<never> {
|
|
716
|
-
if (this.isWebUsbIoHardwareError(error)) throw error;
|
|
717
|
-
if (error instanceof ProtocolV2LinkError && error.code !== 'io') throw error;
|
|
718
|
-
|
|
719
|
-
const nativeError = error instanceof ProtocolV2LinkError ? error.cause ?? error : error;
|
|
720
|
-
if (this.isWebUsbIoHardwareError(nativeError)) throw nativeError;
|
|
721
|
-
|
|
722
|
-
const nativeErrorName = this.getErrorName(nativeError);
|
|
723
|
-
const nativeErrorMessage = this.getErrorMessage(nativeError);
|
|
724
|
-
// SecurityError alone may indicate a protected interface, not OS permissions.
|
|
725
|
-
if (this.isWebUsbPermissionDeniedMessage(nativeErrorMessage)) {
|
|
726
|
-
throw ERRORS.TypedError(HardwareErrorCode.BridgeNeedsPermission, nativeErrorMessage, {
|
|
727
|
-
operation,
|
|
728
|
-
nativeErrorName: nativeErrorName || undefined,
|
|
729
|
-
nativeErrorMessage,
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
const disconnectedByMessage =
|
|
733
|
-
/(?:device )?disconnected|device not found|action was interrupted/i.test(
|
|
734
|
-
`${nativeErrorName}: ${nativeErrorMessage}`
|
|
735
|
-
);
|
|
736
|
-
const stillEnumerated = disconnectedByMessage
|
|
737
|
-
? false
|
|
738
|
-
: await this.isDeviceStillEnumerated(path);
|
|
739
|
-
const errorCode =
|
|
740
|
-
disconnectedByMessage || stillEnumerated === false
|
|
741
|
-
? HardwareErrorCode.BridgeDeviceDisconnected
|
|
742
|
-
: HardwareErrorCode.WebUsbDeviceAccessError;
|
|
743
|
-
|
|
744
|
-
throw ERRORS.TypedError(errorCode, nativeErrorMessage || undefined, {
|
|
745
|
-
operation,
|
|
746
|
-
nativeErrorName: nativeErrorName || undefined,
|
|
747
|
-
nativeErrorMessage: nativeErrorMessage || undefined,
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
private isWebUsbPermissionDeniedMessage(message: string) {
|
|
752
|
-
return /LIBUSB_ERROR_ACCESS|\b(?:access|permission) denied\b/i.test(message);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
665
|
private isRetryablePacketIoError(error: unknown) {
|
|
756
|
-
if (this.isWebUsbIoHardwareError(error)) return false;
|
|
757
666
|
const message = this.getErrorMessage(error).toLowerCase();
|
|
758
|
-
if (this.isWebUsbPermissionDeniedMessage(message)) return false;
|
|
759
667
|
return (
|
|
760
668
|
message.includes('transferout') ||
|
|
761
669
|
message.includes('transferin') ||
|
|
@@ -834,7 +742,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
834
742
|
lastError = error;
|
|
835
743
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
836
744
|
if (!shouldRetry) {
|
|
837
|
-
|
|
745
|
+
throw error;
|
|
838
746
|
}
|
|
839
747
|
try {
|
|
840
748
|
await this.reconnectForPacketIoRetry(path, 'out', attempt, error);
|
|
@@ -848,7 +756,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
848
756
|
}
|
|
849
757
|
}
|
|
850
758
|
}
|
|
851
|
-
|
|
759
|
+
throw lastError;
|
|
852
760
|
}
|
|
853
761
|
|
|
854
762
|
private async transferOutOnce(path: string, packet: Uint8Array) {
|
|
@@ -861,14 +769,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
861
769
|
const transferBuffer = this.toArrayBuffer(
|
|
862
770
|
packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength)
|
|
863
771
|
);
|
|
864
|
-
|
|
865
|
-
if (result.status !== 'ok') {
|
|
866
|
-
return this.throwWebUsbIoError(
|
|
867
|
-
path,
|
|
868
|
-
'transferOut',
|
|
869
|
-
new Error(`transferOut status: ${String(result.status)}`)
|
|
870
|
-
);
|
|
871
|
-
}
|
|
772
|
+
await device.transferOut(endpointOut, transferBuffer);
|
|
872
773
|
}
|
|
873
774
|
|
|
874
775
|
private async transferInWithRetry(
|
|
@@ -897,7 +798,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
897
798
|
}
|
|
898
799
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
899
800
|
if (!shouldRetry) {
|
|
900
|
-
|
|
801
|
+
throw error;
|
|
901
802
|
}
|
|
902
803
|
try {
|
|
903
804
|
await this.reconnectForPacketIoRetry(path, 'in', attempt, error);
|
|
@@ -911,7 +812,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
911
812
|
}
|
|
912
813
|
}
|
|
913
814
|
}
|
|
914
|
-
|
|
815
|
+
throw lastError;
|
|
915
816
|
}
|
|
916
817
|
|
|
917
818
|
private async transferInOnce(path: string, length: number): Promise<DataView> {
|
|
@@ -993,8 +894,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
993
894
|
}
|
|
994
895
|
);
|
|
995
896
|
return true;
|
|
996
|
-
} catch (
|
|
997
|
-
if (this.isWebUsbIoHardwareError(error)) throw error;
|
|
897
|
+
} catch (_error) {
|
|
998
898
|
return false;
|
|
999
899
|
}
|
|
1000
900
|
}
|
|
@@ -1009,7 +909,6 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
1009
909
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
|
|
1010
910
|
logger: this.Log,
|
|
1011
911
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
1012
|
-
shouldRethrow: error => this.isWebUsbIoHardwareError(error),
|
|
1013
912
|
});
|
|
1014
913
|
}
|
|
1015
914
|
|
|
@@ -1087,13 +986,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
1087
986
|
data: Record<string, unknown>,
|
|
1088
987
|
options?: TransportCallOptions
|
|
1089
988
|
) {
|
|
1090
|
-
|
|
1091
|
-
return await this.callProtocolV2Usb(path, name, data, options);
|
|
1092
|
-
} catch (error) {
|
|
1093
|
-
if (!(error instanceof ProtocolV2LinkError) || error.code !== 'io') throw error;
|
|
1094
|
-
const operation = error.message.includes(' USB write failed:') ? 'transferOut' : 'transferIn';
|
|
1095
|
-
return this.throwWebUsbIoError(path, operation, error);
|
|
1096
|
-
}
|
|
989
|
+
return this.callProtocolV2Usb(path, name, data, options);
|
|
1097
990
|
}
|
|
1098
991
|
|
|
1099
992
|
/**
|