@onekeyfe/hd-transport-web-device 1.2.0-alpha.11 → 1.2.0-alpha.13
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-protocol-v2-timeout.test.ts +40 -0
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.js +35 -53
- package/dist/transportLog.d.ts +7 -0
- package/dist/transportLog.d.ts.map +1 -0
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +4 -19
- package/src/transportLog.ts +11 -0
- package/src/webusb.ts +34 -53
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import transport, { ProtocolV2FrameAssembler } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
3
|
+
import WebUsbTransport from '../src/webusb';
|
|
4
|
+
|
|
5
|
+
const schema = {
|
|
6
|
+
nested: {
|
|
7
|
+
Ping: { fields: { message: { type: 'string', id: 1 } } },
|
|
8
|
+
Success: { fields: { message: { type: 'string', id: 1 } } },
|
|
9
|
+
MessageType: {
|
|
10
|
+
values: {
|
|
11
|
+
MessageType_Ping: 60206,
|
|
12
|
+
MessageType_Success: 60207,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
19
|
+
test('invalidates and resets the cached connection before another call can start', async () => {
|
|
20
|
+
const webusb = new WebUsbTransport() as any;
|
|
21
|
+
const path = 'pro2-webusb';
|
|
22
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
23
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
24
|
+
webusb.protocolV2Assemblers.set(path, new ProtocolV2FrameAssembler());
|
|
25
|
+
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
26
|
+
webusb.receiveProtocolV2Frame = jest.fn(() => new Promise(() => undefined));
|
|
27
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(async () => {
|
|
28
|
+
webusb.protocolV2Sessions.delete(path);
|
|
29
|
+
webusb.protocolV2ReadTimeouts.delete(path);
|
|
30
|
+
webusb.protocolV2Assemblers.get(path)?.reset();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await expect(
|
|
34
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'timeout' }, { timeoutMs: 10 })
|
|
35
|
+
).rejects.toThrow('timeout');
|
|
36
|
+
|
|
37
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
|
|
38
|
+
expect(webusb.protocolV2Sessions.has(path)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -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":";AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAIvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAqCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IA0B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAO7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IA0F9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAgBxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAqCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA2BlB,cAAc;YAuEd,cAAc;IA6B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,14 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
41
41
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
|
|
45
|
+
function shouldSuppressHighVolumeCallLog(name) {
|
|
46
|
+
return HIGH_VOLUME_CALLS.has(name);
|
|
47
|
+
}
|
|
48
|
+
function createTransportCallLog(name, protocol) {
|
|
49
|
+
return { name, protocol };
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
const { parseConfigure: parseConfigure$1, check: check$1, ProtocolV1: ProtocolV1$1 } = transport__default["default"];
|
|
45
53
|
const CONFIGURATION_ID = 1;
|
|
46
54
|
const INTERFACE_ID = 0;
|
|
@@ -52,19 +60,6 @@ const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
|
52
60
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
53
61
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
54
62
|
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
55
|
-
const WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
56
|
-
function shouldSuppressWebUsbCallLog(name) {
|
|
57
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
58
|
-
return WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
59
|
-
}
|
|
60
|
-
function isLogBlockCommand$1(name) {
|
|
61
|
-
var _a, _b;
|
|
62
|
-
return (_b = (_a = transport.LogBlockCommand === null || transport.LogBlockCommand === void 0 ? void 0 : transport.LogBlockCommand.has) === null || _a === void 0 ? void 0 : _a.call(transport.LogBlockCommand, name)) !== null && _b !== void 0 ? _b : false;
|
|
63
|
-
}
|
|
64
|
-
function shouldBlockWebUsbCallDataLog(name) {
|
|
65
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
66
|
-
return isLogBlockCommand$1(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
67
|
-
}
|
|
68
63
|
function inferProtocolHintFromDeviceName$1(name) {
|
|
69
64
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
70
65
|
}
|
|
@@ -100,11 +95,9 @@ class WebUsbTransport {
|
|
|
100
95
|
this.messages = messages;
|
|
101
96
|
}
|
|
102
97
|
configureProtocolV2(signedData) {
|
|
103
|
-
var _a;
|
|
104
98
|
this.messagesV2 = parseConfigure$1(signedData);
|
|
105
99
|
this.protocolV2Sessions.clear();
|
|
106
100
|
this.protocolV2ReadTimeouts.clear();
|
|
107
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] Protocol V2 schema configured');
|
|
108
101
|
}
|
|
109
102
|
promptDeviceAccess() {
|
|
110
103
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -145,7 +138,10 @@ class WebUsbTransport {
|
|
|
145
138
|
if (!this.usb)
|
|
146
139
|
return [];
|
|
147
140
|
const devices = yield this.usb.getDevices();
|
|
148
|
-
const onekeyDevices = devices.filter(dev =>
|
|
141
|
+
const onekeyDevices = devices.filter(dev => {
|
|
142
|
+
const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
|
|
143
|
+
return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
|
|
144
|
+
});
|
|
149
145
|
this.deviceList = onekeyDevices.map(device => {
|
|
150
146
|
const path = this.getDevicePath(device);
|
|
151
147
|
const protocolHint = inferProtocolHintFromDeviceName$1(device.productName);
|
|
@@ -158,10 +154,6 @@ class WebUsbTransport {
|
|
|
158
154
|
commType: 'webusb',
|
|
159
155
|
};
|
|
160
156
|
});
|
|
161
|
-
for (const dev of onekeyDevices) {
|
|
162
|
-
this.Log.debug(`[WebUSB] Device: name="${dev.productName}" serial="${dev.serialNumber}" ` +
|
|
163
|
-
`VID=0x${dev.vendorId.toString(16)} PID=0x${dev.productId.toString(16)}`);
|
|
164
|
-
}
|
|
165
157
|
return this.deviceList;
|
|
166
158
|
});
|
|
167
159
|
}
|
|
@@ -200,14 +192,12 @@ class WebUsbTransport {
|
|
|
200
192
|
if (expectedProtocol === 'V1') {
|
|
201
193
|
if (yield this.probeProtocolV1(path)) {
|
|
202
194
|
this.deviceProtocol.set(path, 'V1');
|
|
203
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
|
|
204
195
|
return 'V1';
|
|
205
196
|
}
|
|
206
197
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
207
198
|
}
|
|
208
199
|
if (expectedProtocol === 'V2') {
|
|
209
200
|
this.deviceProtocol.set(path, 'V2');
|
|
210
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
|
|
211
201
|
return 'V2';
|
|
212
202
|
}
|
|
213
203
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
@@ -215,7 +205,6 @@ class WebUsbTransport {
|
|
|
215
205
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
|
|
216
206
|
if (detected) {
|
|
217
207
|
this.deviceProtocol.set(path, protocol);
|
|
218
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> ${protocol}`);
|
|
219
208
|
return protocol;
|
|
220
209
|
}
|
|
221
210
|
}
|
|
@@ -287,7 +276,6 @@ class WebUsbTransport {
|
|
|
287
276
|
var _a, _b;
|
|
288
277
|
return __awaiter(this, void 0, void 0, function* () {
|
|
289
278
|
let device = yield this.findDevice(path);
|
|
290
|
-
this.Log.debug('[WebUsbTransport] connecting to device:', device.productName, 'PID:', device.productId);
|
|
291
279
|
if (!device.opened) {
|
|
292
280
|
yield device.open();
|
|
293
281
|
}
|
|
@@ -524,7 +512,7 @@ class WebUsbTransport {
|
|
|
524
512
|
yield this.connect(path, false);
|
|
525
513
|
});
|
|
526
514
|
}
|
|
527
|
-
withProtocolReadTimeout(
|
|
515
|
+
withProtocolReadTimeout(_path, promise, timeoutMs, protocol, onTimeout) {
|
|
528
516
|
return __awaiter(this, void 0, void 0, function* () {
|
|
529
517
|
let timer;
|
|
530
518
|
let timedOut = false;
|
|
@@ -539,11 +527,11 @@ class WebUsbTransport {
|
|
|
539
527
|
return yield Promise.race([
|
|
540
528
|
guardedPromise,
|
|
541
529
|
new Promise((_, reject) => {
|
|
542
|
-
timer = setTimeout(() =>
|
|
530
|
+
timer = setTimeout(() => {
|
|
543
531
|
timedOut = true;
|
|
544
532
|
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
545
533
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
546
|
-
}
|
|
534
|
+
}, timeoutMs);
|
|
547
535
|
}),
|
|
548
536
|
]);
|
|
549
537
|
}
|
|
@@ -562,8 +550,7 @@ class WebUsbTransport {
|
|
|
562
550
|
yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
|
|
563
551
|
return true;
|
|
564
552
|
}
|
|
565
|
-
catch (
|
|
566
|
-
this.Log.debug('[WebUsbTransport] Protocol V1 Initialize probe failed:', error);
|
|
553
|
+
catch (_error) {
|
|
567
554
|
return false;
|
|
568
555
|
}
|
|
569
556
|
});
|
|
@@ -595,12 +582,8 @@ class WebUsbTransport {
|
|
|
595
582
|
if (!protocol) {
|
|
596
583
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
|
|
597
584
|
}
|
|
598
|
-
if (
|
|
599
|
-
|
|
600
|
-
this.Log.debug('call-', ' name: ', name, ' protocol: ', protocol);
|
|
601
|
-
}
|
|
602
|
-
else {
|
|
603
|
-
this.Log.debug('call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
|
|
585
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
586
|
+
this.Log.debug('transport call', createTransportCallLog(name, protocol));
|
|
604
587
|
}
|
|
605
588
|
if (protocol === 'V2') {
|
|
606
589
|
return this.callProtocolV2(path, name, data, options);
|
|
@@ -660,6 +643,18 @@ class WebUsbTransport {
|
|
|
660
643
|
try {
|
|
661
644
|
return yield session.call(name, data, options);
|
|
662
645
|
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
648
|
+
if (message.includes('protocol v2 read timeout') || message.includes('response timeout')) {
|
|
649
|
+
try {
|
|
650
|
+
yield this.resetConnectionAfterProbe(path);
|
|
651
|
+
}
|
|
652
|
+
catch (resetError) {
|
|
653
|
+
this.Log.debug('[WebUsbTransport] Protocol V2 timeout reset failed:', resetError);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
663
658
|
finally {
|
|
664
659
|
this.protocolV2ReadTimeouts.delete(path);
|
|
665
660
|
}
|
|
@@ -742,6 +737,8 @@ class WebUsbTransport {
|
|
|
742
737
|
this.deviceProtocolHints.delete(path);
|
|
743
738
|
(_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
744
739
|
this.protocolV2Assemblers.delete(path);
|
|
740
|
+
this.protocolV2Sessions.delete(path);
|
|
741
|
+
this.protocolV2ReadTimeouts.delete(path);
|
|
745
742
|
this.deviceEndpoints.delete(path);
|
|
746
743
|
});
|
|
747
744
|
}
|
|
@@ -750,15 +747,6 @@ class WebUsbTransport {
|
|
|
750
747
|
}
|
|
751
748
|
}
|
|
752
749
|
|
|
753
|
-
const FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
754
|
-
function shouldSuppressHighVolumeCallLog(name) {
|
|
755
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
756
|
-
return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
757
|
-
}
|
|
758
|
-
function isLogBlockCommand(name) {
|
|
759
|
-
var _a, _b;
|
|
760
|
-
return (_b = (_a = transport.LogBlockCommand === null || transport.LogBlockCommand === void 0 ? void 0 : transport.LogBlockCommand.has) === null || _a === void 0 ? void 0 : _a.call(transport.LogBlockCommand, name)) !== null && _b !== void 0 ? _b : false;
|
|
761
|
-
}
|
|
762
750
|
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
763
751
|
function inferProtocolHintFromDeviceName(name) {
|
|
764
752
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
@@ -871,12 +859,10 @@ class ElectronBleTransport {
|
|
|
871
859
|
this.configured = true;
|
|
872
860
|
}
|
|
873
861
|
configureProtocolV2(signedData) {
|
|
874
|
-
var _a;
|
|
875
862
|
this._messagesV2 = parseConfigure(signedData);
|
|
876
863
|
this.protocolV2Links
|
|
877
864
|
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
878
865
|
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
|
|
879
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V2 schema configured');
|
|
880
866
|
}
|
|
881
867
|
listen() {
|
|
882
868
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1281,7 +1267,7 @@ class ElectronBleTransport {
|
|
|
1281
1267
|
}
|
|
1282
1268
|
}
|
|
1283
1269
|
call(uuid, name, data, options) {
|
|
1284
|
-
var _a
|
|
1270
|
+
var _a;
|
|
1285
1271
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1286
1272
|
if (!this._messages) {
|
|
1287
1273
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
@@ -1293,12 +1279,8 @@ class ElectronBleTransport {
|
|
|
1293
1279
|
if (!protocol) {
|
|
1294
1280
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1295
1281
|
}
|
|
1296
|
-
if (shouldSuppressHighVolumeCallLog(name))
|
|
1297
|
-
|
|
1298
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] call', 'name:', name, 'protocol:', protocol);
|
|
1299
|
-
}
|
|
1300
|
-
else {
|
|
1301
|
-
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] call', 'name:', name, 'data:', data, 'protocol:', protocol);
|
|
1282
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
1283
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', createTransportCallLog(name, protocol));
|
|
1302
1284
|
}
|
|
1303
1285
|
if (protocol === 'V2') {
|
|
1304
1286
|
return this.callProtocolV2(uuid, name, data, options);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
export declare function shouldSuppressHighVolumeCallLog(name: string): boolean;
|
|
3
|
+
export declare function createTransportCallLog(name: string, protocol: ProtocolType): {
|
|
4
|
+
name: string;
|
|
5
|
+
protocol: ProtocolType;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=transportLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transportLog.d.ts","sourceRoot":"","sources":["../src/transportLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI3D,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,WAE3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY;;;EAE1E"}
|
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,
|
|
1
|
+
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAUN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,sBAAsB,CAA8C;IAG5E,OAAO,CAAC,eAAe,CAA2C;IAOlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAW7B,kBAAkB;IAmBlB,SAAS;IASf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA0CtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YAmCpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,yBAAyB;YAyBzB,uBAAuB;YAmCvB,eAAe;YAaf,eAAe;IAiBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA8BlB,cAAc;YAkCd,cAAc;YAuDd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAmB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-web-device",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.13",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
"lint:fix": "eslint . --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
23
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.13",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.13"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
27
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.13",
|
|
28
28
|
"@types/w3c-web-usb": "^1.0.6",
|
|
29
29
|
"@types/web-bluetooth": "^0.0.17"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "a907a7ec140123107f6188b37550a7143f1e0d91"
|
|
32
32
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import transport, {
|
|
2
|
-
LogBlockCommand,
|
|
3
2
|
PROTOCOL_V1_MESSAGE_HEADER_SIZE,
|
|
4
3
|
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
5
4
|
ProtocolV2FrameAssembler,
|
|
@@ -17,22 +16,13 @@ import {
|
|
|
17
16
|
wait,
|
|
18
17
|
} from '@onekeyfe/hd-shared';
|
|
19
18
|
|
|
19
|
+
import { createTransportCallLog, shouldSuppressHighVolumeCallLog } from './transportLog';
|
|
20
|
+
|
|
20
21
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
21
22
|
import type { DesktopAPI } from '@onekeyfe/hd-transport-electron';
|
|
22
23
|
import type { OneKeyDeviceInfo, ProtocolType, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
23
24
|
import type EventEmitter from 'events';
|
|
24
25
|
|
|
25
|
-
const FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
26
|
-
|
|
27
|
-
function shouldSuppressHighVolumeCallLog(name: string) {
|
|
28
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
29
|
-
return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function isLogBlockCommand(name: string) {
|
|
33
|
-
return (LogBlockCommand as Set<string> | undefined)?.has?.(name) ?? false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
26
|
const { parseConfigure, ProtocolV1, check } = transport;
|
|
37
27
|
|
|
38
28
|
declare global {
|
|
@@ -221,7 +211,6 @@ export default class ElectronBleTransport {
|
|
|
221
211
|
this.protocolV2Links
|
|
222
212
|
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
223
213
|
.catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
|
|
224
|
-
this.Log?.debug('[Electron BLE] Protocol V2 schema configured');
|
|
225
214
|
}
|
|
226
215
|
|
|
227
216
|
async listen() {
|
|
@@ -682,12 +671,8 @@ export default class ElectronBleTransport {
|
|
|
682
671
|
`Device protocol has not been detected for ${uuid}`
|
|
683
672
|
);
|
|
684
673
|
}
|
|
685
|
-
if (shouldSuppressHighVolumeCallLog(name)) {
|
|
686
|
-
|
|
687
|
-
} else if (isLogBlockCommand(name)) {
|
|
688
|
-
this.Log?.debug('[Electron BLE] call', 'name:', name, 'protocol:', protocol);
|
|
689
|
-
} else {
|
|
690
|
-
this.Log?.debug('[Electron BLE] call', 'name:', name, 'data:', data, 'protocol:', protocol);
|
|
674
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
675
|
+
this.Log?.debug('transport call', createTransportCallLog(name, protocol));
|
|
691
676
|
}
|
|
692
677
|
|
|
693
678
|
if (protocol === 'V2') {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
3
|
+
const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
|
|
4
|
+
|
|
5
|
+
export function shouldSuppressHighVolumeCallLog(name: string) {
|
|
6
|
+
return HIGH_VOLUME_CALLS.has(name);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createTransportCallLog(name: string, protocol: ProtocolType) {
|
|
10
|
+
return { name, protocol };
|
|
11
|
+
}
|
package/src/webusb.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-undef */
|
|
2
2
|
import transport, {
|
|
3
|
-
LogBlockCommand,
|
|
4
3
|
PROTOCOL_V1_CHUNK_PAYLOAD_SIZE,
|
|
5
4
|
PROTOCOL_V1_MESSAGE_HEADER_SIZE,
|
|
6
5
|
PROTOCOL_V1_REPORT_ID,
|
|
@@ -11,9 +10,17 @@ import transport, {
|
|
|
11
10
|
ProtocolV2Session,
|
|
12
11
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
13
12
|
} from '@onekeyfe/hd-transport';
|
|
14
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ERRORS,
|
|
15
|
+
HardwareErrorCode,
|
|
16
|
+
ONEKEY_WEBUSB_FILTER,
|
|
17
|
+
isKnownTrezorWebUsbDevice,
|
|
18
|
+
wait,
|
|
19
|
+
} from '@onekeyfe/hd-shared';
|
|
15
20
|
import ByteBuffer from 'bytebuffer';
|
|
16
21
|
|
|
22
|
+
import { createTransportCallLog, shouldSuppressHighVolumeCallLog } from './transportLog';
|
|
23
|
+
|
|
17
24
|
import type {
|
|
18
25
|
AcquireInput,
|
|
19
26
|
OneKeyDeviceInfoBase,
|
|
@@ -33,22 +40,6 @@ const HEADER_LENGTH = PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
|
33
40
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
34
41
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
35
42
|
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
36
|
-
const WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
37
|
-
|
|
38
|
-
function shouldSuppressWebUsbCallLog(name: string) {
|
|
39
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
40
|
-
return WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function isLogBlockCommand(name: string) {
|
|
44
|
-
return (LogBlockCommand as Set<string> | undefined)?.has?.(name) ?? false;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function shouldBlockWebUsbCallDataLog(name: string) {
|
|
48
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
49
|
-
return isLogBlockCommand(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
43
|
function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
|
|
53
44
|
return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
|
|
54
45
|
}
|
|
@@ -159,7 +150,6 @@ export default class WebUsbTransport {
|
|
|
159
150
|
this.messagesV2 = parseConfigure(signedData);
|
|
160
151
|
this.protocolV2Sessions.clear();
|
|
161
152
|
this.protocolV2ReadTimeouts.clear();
|
|
162
|
-
this.Log?.debug('[WebUsbTransport] Protocol V2 schema configured');
|
|
163
153
|
}
|
|
164
154
|
|
|
165
155
|
/**
|
|
@@ -218,11 +208,13 @@ export default class WebUsbTransport {
|
|
|
218
208
|
if (!this.usb) return [];
|
|
219
209
|
|
|
220
210
|
const devices = await this.usb.getDevices();
|
|
221
|
-
const onekeyDevices = devices.filter(dev =>
|
|
222
|
-
ONEKEY_WEBUSB_FILTER.some(
|
|
223
|
-
desc
|
|
224
|
-
|
|
225
|
-
|
|
211
|
+
const onekeyDevices = devices.filter(dev => {
|
|
212
|
+
const isOneKey = ONEKEY_WEBUSB_FILTER.some(
|
|
213
|
+
(desc: { vendorId: number; productId: number }) =>
|
|
214
|
+
dev.vendorId === desc.vendorId && dev.productId === desc.productId
|
|
215
|
+
);
|
|
216
|
+
return isOneKey && !isKnownTrezorWebUsbDevice(dev);
|
|
217
|
+
});
|
|
226
218
|
|
|
227
219
|
this.deviceList = onekeyDevices.map(device => {
|
|
228
220
|
const path = this.getDevicePath(device);
|
|
@@ -238,14 +230,6 @@ export default class WebUsbTransport {
|
|
|
238
230
|
};
|
|
239
231
|
});
|
|
240
232
|
|
|
241
|
-
// Debug: log all discovered devices. Protocol is detected after acquire via wire probe.
|
|
242
|
-
for (const dev of onekeyDevices) {
|
|
243
|
-
this.Log.debug(
|
|
244
|
-
`[WebUSB] Device: name="${dev.productName}" serial="${dev.serialNumber}" ` +
|
|
245
|
-
`VID=0x${dev.vendorId.toString(16)} PID=0x${dev.productId.toString(16)}`
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
233
|
return this.deviceList;
|
|
250
234
|
}
|
|
251
235
|
|
|
@@ -300,7 +284,6 @@ export default class WebUsbTransport {
|
|
|
300
284
|
if (expectedProtocol === 'V1') {
|
|
301
285
|
if (await this.probeProtocolV1(path)) {
|
|
302
286
|
this.deviceProtocol.set(path, 'V1');
|
|
303
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
|
|
304
287
|
return 'V1';
|
|
305
288
|
}
|
|
306
289
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
@@ -310,7 +293,6 @@ export default class WebUsbTransport {
|
|
|
310
293
|
// 免探测路径:调用方显式承诺该设备是 V2(例如固件升级重启后的重连场景,
|
|
311
294
|
// 上层已经探测过协议并通过 expectedProtocol 传回),这里不再重复探测。
|
|
312
295
|
this.deviceProtocol.set(path, 'V2');
|
|
313
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
|
|
314
296
|
return 'V2';
|
|
315
297
|
}
|
|
316
298
|
|
|
@@ -325,7 +307,6 @@ export default class WebUsbTransport {
|
|
|
325
307
|
protocol === 'V1' ? await this.probeProtocolV1(path) : await this.probeProtocolV2(path);
|
|
326
308
|
if (detected) {
|
|
327
309
|
this.deviceProtocol.set(path, protocol);
|
|
328
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> ${protocol}`);
|
|
329
310
|
return protocol;
|
|
330
311
|
}
|
|
331
312
|
}
|
|
@@ -414,13 +395,6 @@ export default class WebUsbTransport {
|
|
|
414
395
|
*/
|
|
415
396
|
async connectToDevice(path: string, first: boolean) {
|
|
416
397
|
let device: USBDevice = await this.findDevice(path);
|
|
417
|
-
this.Log.debug(
|
|
418
|
-
'[WebUsbTransport] connecting to device:',
|
|
419
|
-
device.productName,
|
|
420
|
-
'PID:',
|
|
421
|
-
device.productId
|
|
422
|
-
);
|
|
423
|
-
|
|
424
398
|
if (!device.opened) {
|
|
425
399
|
await device.open();
|
|
426
400
|
}
|
|
@@ -676,13 +650,12 @@ export default class WebUsbTransport {
|
|
|
676
650
|
}
|
|
677
651
|
|
|
678
652
|
private async withProtocolReadTimeout<T>(
|
|
679
|
-
|
|
653
|
+
_path: string,
|
|
680
654
|
promise: Promise<T>,
|
|
681
655
|
timeoutMs: number,
|
|
682
656
|
protocol: ProtocolType,
|
|
683
657
|
onTimeout?: () => void
|
|
684
658
|
): Promise<T> {
|
|
685
|
-
void path;
|
|
686
659
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
687
660
|
let timedOut = false;
|
|
688
661
|
const waitForeverAfterTimeout = () => new Promise<never>(() => {});
|
|
@@ -699,7 +672,7 @@ export default class WebUsbTransport {
|
|
|
699
672
|
return await Promise.race([
|
|
700
673
|
guardedPromise,
|
|
701
674
|
new Promise<never>((_, reject) => {
|
|
702
|
-
timer = setTimeout(
|
|
675
|
+
timer = setTimeout(() => {
|
|
703
676
|
timedOut = true;
|
|
704
677
|
onTimeout?.();
|
|
705
678
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
@@ -719,8 +692,7 @@ export default class WebUsbTransport {
|
|
|
719
692
|
try {
|
|
720
693
|
await this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
|
|
721
694
|
return true;
|
|
722
|
-
} catch (
|
|
723
|
-
this.Log.debug('[WebUsbTransport] Protocol V1 Initialize probe failed:', error);
|
|
695
|
+
} catch (_error) {
|
|
724
696
|
return false;
|
|
725
697
|
}
|
|
726
698
|
}
|
|
@@ -765,12 +737,8 @@ export default class WebUsbTransport {
|
|
|
765
737
|
);
|
|
766
738
|
}
|
|
767
739
|
|
|
768
|
-
if (
|
|
769
|
-
|
|
770
|
-
} else if (shouldBlockWebUsbCallDataLog(name)) {
|
|
771
|
-
this.Log.debug('call-', ' name: ', name, ' protocol: ', protocol);
|
|
772
|
-
} else {
|
|
773
|
-
this.Log.debug('call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
|
|
740
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
741
|
+
this.Log.debug('transport call', createTransportCallLog(name, protocol));
|
|
774
742
|
}
|
|
775
743
|
|
|
776
744
|
if (protocol === 'V2') {
|
|
@@ -853,6 +821,17 @@ export default class WebUsbTransport {
|
|
|
853
821
|
this.protocolV2Assemblers.get(path)?.reset();
|
|
854
822
|
try {
|
|
855
823
|
return await session.call(name, data, options);
|
|
824
|
+
} catch (error) {
|
|
825
|
+
const message =
|
|
826
|
+
error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
827
|
+
if (message.includes('protocol v2 read timeout') || message.includes('response timeout')) {
|
|
828
|
+
try {
|
|
829
|
+
await this.resetConnectionAfterProbe(path);
|
|
830
|
+
} catch (resetError) {
|
|
831
|
+
this.Log.debug('[WebUsbTransport] Protocol V2 timeout reset failed:', resetError);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
throw error;
|
|
856
835
|
} finally {
|
|
857
836
|
this.protocolV2ReadTimeouts.delete(path);
|
|
858
837
|
}
|
|
@@ -960,6 +939,8 @@ export default class WebUsbTransport {
|
|
|
960
939
|
this.deviceProtocolHints.delete(path);
|
|
961
940
|
this.protocolV2Assemblers.get(path)?.reset();
|
|
962
941
|
this.protocolV2Assemblers.delete(path);
|
|
942
|
+
this.protocolV2Sessions.delete(path);
|
|
943
|
+
this.protocolV2ReadTimeouts.delete(path);
|
|
963
944
|
this.deviceEndpoints.delete(path);
|
|
964
945
|
}
|
|
965
946
|
|