@onekeyfe/hd-transport-web-device 1.2.0-alpha.21 → 1.2.0-alpha.22
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 +19 -17
- package/__tests__/webusb-protocol-v2-timeout.test.ts +103 -2
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +30 -33
- package/dist/webusb.d.ts +0 -1
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +7 -10
- package/src/webusb.ts +24 -30
|
@@ -141,7 +141,6 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
141
141
|
{ message: 'ok' },
|
|
142
142
|
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
143
143
|
);
|
|
144
|
-
|
|
145
144
|
nobleBle.onNotification.mockImplementation(handler => {
|
|
146
145
|
notificationHandler = handler;
|
|
147
146
|
return jest.fn();
|
|
@@ -220,19 +219,21 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
220
219
|
const device = { id: 'named-pro2-id', name: 'OneKey Pro 2' };
|
|
221
220
|
const nobleBle = createNobleBle(device);
|
|
222
221
|
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
223
|
-
const probeResponse = ProtocolV2.encodeFrame(
|
|
224
|
-
schemas,
|
|
225
|
-
'Success',
|
|
226
|
-
{ message: 'ok' },
|
|
227
|
-
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
228
|
-
);
|
|
229
222
|
|
|
230
223
|
nobleBle.onNotification.mockImplementation(handler => {
|
|
231
224
|
notificationHandler = handler;
|
|
232
225
|
return jest.fn();
|
|
233
226
|
});
|
|
227
|
+
let responseSeq = 0;
|
|
234
228
|
nobleBle.write.mockImplementation(() => {
|
|
235
|
-
|
|
229
|
+
responseSeq += 1;
|
|
230
|
+
const response = ProtocolV2.encodeFrame(
|
|
231
|
+
schemas,
|
|
232
|
+
'Success',
|
|
233
|
+
{ message: 'ok' },
|
|
234
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
235
|
+
);
|
|
236
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
|
|
236
237
|
return Promise.resolve();
|
|
237
238
|
});
|
|
238
239
|
const transport = configureTransport(nobleBle);
|
|
@@ -304,19 +305,20 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
304
305
|
const device = { id: 'repeated-acquire-pro2-id', name: 'OneKey Pro 2' };
|
|
305
306
|
const nobleBle = createNobleBle(device);
|
|
306
307
|
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
307
|
-
const response = ProtocolV2.encodeFrame(
|
|
308
|
-
schemas,
|
|
309
|
-
'Success',
|
|
310
|
-
{ message: 'ok' },
|
|
311
|
-
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
312
|
-
);
|
|
313
|
-
|
|
314
308
|
nobleBle.onNotification.mockImplementation(handler => {
|
|
315
309
|
notificationHandler = handler;
|
|
316
310
|
return jest.fn();
|
|
317
311
|
});
|
|
312
|
+
let responseSeq = 0;
|
|
318
313
|
nobleBle.write.mockImplementation(() => {
|
|
319
|
-
|
|
314
|
+
responseSeq += 1;
|
|
315
|
+
const sequencedResponse = ProtocolV2.encodeFrame(
|
|
316
|
+
schemas,
|
|
317
|
+
'Success',
|
|
318
|
+
{ message: 'ok' },
|
|
319
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
320
|
+
);
|
|
321
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(sequencedResponse)), 0);
|
|
320
322
|
return Promise.resolve();
|
|
321
323
|
});
|
|
322
324
|
const transport = configureTransport(nobleBle);
|
|
@@ -334,7 +336,7 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
334
336
|
const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
|
|
335
337
|
Number.parseInt(hex.slice(12, 14), 16)
|
|
336
338
|
);
|
|
337
|
-
expect(sentSeqs).toEqual([1, 2]);
|
|
339
|
+
expect(sentSeqs).toEqual([1, 2, 3]);
|
|
338
340
|
} finally {
|
|
339
341
|
await transport.release(device.id);
|
|
340
342
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import transport, {
|
|
1
|
+
import transport, {
|
|
2
|
+
ProtocolV2,
|
|
3
|
+
ProtocolV2FrameAssembler,
|
|
4
|
+
ProtocolV2LinkError,
|
|
5
|
+
} from '@onekeyfe/hd-transport';
|
|
2
6
|
|
|
3
7
|
import WebUsbTransport from '../src/webusb';
|
|
4
8
|
|
|
@@ -49,7 +53,6 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
49
53
|
webusb.receiveProtocolV2Frame = jest.fn(() => new Promise<void>(() => {}));
|
|
50
54
|
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
51
55
|
webusb.protocolV2Sessions.delete(path);
|
|
52
|
-
webusb.protocolV2ReadTimeouts.delete(path);
|
|
53
56
|
webusb.protocolV2Assemblers.get(path)?.reset();
|
|
54
57
|
});
|
|
55
58
|
|
|
@@ -60,4 +63,102 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
60
63
|
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
|
|
61
64
|
expect(webusb.protocolV2Sessions.has(path)).toBe(false);
|
|
62
65
|
});
|
|
66
|
+
|
|
67
|
+
test.each(['router', 'packet-source', 'ack-sequence', 'response-sequence', 'frame'] as const)(
|
|
68
|
+
'invalidates cached state for typed Protocol V2 %s errors',
|
|
69
|
+
async code => {
|
|
70
|
+
const webusb = new WebUsbTransport() as any;
|
|
71
|
+
const path = 'pro2-webusb';
|
|
72
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
73
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
74
|
+
webusb.protocolV2Assemblers.set(path, new ProtocolV2FrameAssembler());
|
|
75
|
+
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
76
|
+
const recoveredResponse = ProtocolV2.encodeFrame(
|
|
77
|
+
{ protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
|
|
78
|
+
'Success',
|
|
79
|
+
{ message: 'recovered' },
|
|
80
|
+
{ seq: 1 }
|
|
81
|
+
);
|
|
82
|
+
webusb.receiveProtocolV2Frame = jest
|
|
83
|
+
.fn()
|
|
84
|
+
.mockRejectedValueOnce(
|
|
85
|
+
new ProtocolV2LinkError(code, `Protocol V2 ${code} validation failed`)
|
|
86
|
+
)
|
|
87
|
+
.mockResolvedValue(recoveredResponse);
|
|
88
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
89
|
+
webusb.protocolV2Sessions.delete(path);
|
|
90
|
+
webusb.protocolV2Assemblers.get(path)?.reset();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await expect(webusb.callProtocolV2(path, 'Ping', { message: 'mismatch' })).rejects.toThrow(
|
|
94
|
+
`${code} validation failed`
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
|
|
98
|
+
expect(webusb.protocolV2Sessions.has(path)).toBe(false);
|
|
99
|
+
|
|
100
|
+
await expect(
|
|
101
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'after-reset' })
|
|
102
|
+
).resolves.toMatchObject({
|
|
103
|
+
type: 'Success',
|
|
104
|
+
message: { message: 'recovered' },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
test('does not discard buffered Protocol V2 frames before each call', async () => {
|
|
110
|
+
const webusb = new WebUsbTransport() as any;
|
|
111
|
+
const path = 'pro2-webusb';
|
|
112
|
+
const assembler = new ProtocolV2FrameAssembler();
|
|
113
|
+
const reset = jest.spyOn(assembler, 'reset');
|
|
114
|
+
let responseSequence = 0;
|
|
115
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
116
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
117
|
+
webusb.protocolV2Assemblers.set(path, assembler);
|
|
118
|
+
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
119
|
+
webusb.receiveProtocolV2Frame = jest.fn().mockImplementation(() => {
|
|
120
|
+
responseSequence += 1;
|
|
121
|
+
const response = ProtocolV2.encodeFrame(
|
|
122
|
+
{ protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
|
|
123
|
+
'Success',
|
|
124
|
+
{ message: 'ok' },
|
|
125
|
+
{ seq: responseSequence }
|
|
126
|
+
);
|
|
127
|
+
return Promise.resolve(response);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await webusb.callProtocolV2(path, 'Ping', { message: 'first' });
|
|
131
|
+
await webusb.callProtocolV2(path, 'Ping', { message: 'second' });
|
|
132
|
+
|
|
133
|
+
expect(reset).not.toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('keeps queued Protocol V2 read timeouts scoped to each call', async () => {
|
|
137
|
+
const webusb = new WebUsbTransport() as any;
|
|
138
|
+
const path = 'pro2-webusb';
|
|
139
|
+
let responseSequence = 0;
|
|
140
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
141
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
142
|
+
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
143
|
+
webusb.receiveProtocolV2Frame = jest.fn().mockImplementation(() => {
|
|
144
|
+
responseSequence += 1;
|
|
145
|
+
return Promise.resolve(
|
|
146
|
+
ProtocolV2.encodeFrame(
|
|
147
|
+
{ protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
|
|
148
|
+
'Success',
|
|
149
|
+
{ message: 'ok' },
|
|
150
|
+
{ seq: responseSequence }
|
|
151
|
+
)
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await Promise.all([
|
|
156
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'long' }, { timeoutMs: 1_000 }),
|
|
157
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'short' }, { timeoutMs: 25 }),
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
expect(
|
|
161
|
+
webusb.receiveProtocolV2Frame.mock.calls.map(([, timeoutMs]: unknown[]) => timeoutMs)
|
|
162
|
+
).toEqual([1_000, 25]);
|
|
163
|
+
});
|
|
63
164
|
});
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAoCF,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;IA0B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -25,8 +25,6 @@ declare class WebUsbTransport {
|
|
|
25
25
|
private protocolV2Sessions;
|
|
26
26
|
/** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
|
|
27
27
|
private protocolV2Sequences;
|
|
28
|
-
/** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
|
|
29
|
-
private protocolV2ReadTimeouts;
|
|
30
28
|
/** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
|
|
31
29
|
private deviceEndpoints;
|
|
32
30
|
/**
|
package/dist/index.js
CHANGED
|
@@ -70,7 +70,6 @@ class WebUsbTransport {
|
|
|
70
70
|
this.protocolV2Assemblers = new Map();
|
|
71
71
|
this.protocolV2Sessions = new Map();
|
|
72
72
|
this.protocolV2Sequences = new Map();
|
|
73
|
-
this.protocolV2ReadTimeouts = new Map();
|
|
74
73
|
this.deviceEndpoints = new Map();
|
|
75
74
|
this.mockSerialPaths = new WeakMap();
|
|
76
75
|
this.mockSerialCounter = 0;
|
|
@@ -98,7 +97,6 @@ class WebUsbTransport {
|
|
|
98
97
|
configureProtocolV2(signedData) {
|
|
99
98
|
this.messagesV2 = parseConfigure$1(signedData);
|
|
100
99
|
this.protocolV2Sessions.clear();
|
|
101
|
-
this.protocolV2ReadTimeouts.clear();
|
|
102
100
|
}
|
|
103
101
|
promptDeviceAccess() {
|
|
104
102
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -199,8 +197,11 @@ class WebUsbTransport {
|
|
|
199
197
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
200
198
|
}
|
|
201
199
|
if (expectedProtocol === 'V2') {
|
|
202
|
-
this.
|
|
203
|
-
|
|
200
|
+
if (yield this.probeProtocolV2(path)) {
|
|
201
|
+
this.deviceProtocol.set(path, 'V2');
|
|
202
|
+
return 'V2';
|
|
203
|
+
}
|
|
204
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
204
205
|
}
|
|
205
206
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
206
207
|
for (const protocol of probeOrder) {
|
|
@@ -495,7 +496,6 @@ class WebUsbTransport {
|
|
|
495
496
|
return __awaiter(this, void 0, void 0, function* () {
|
|
496
497
|
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
497
498
|
this.protocolV2Sessions.delete(path);
|
|
498
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
499
499
|
try {
|
|
500
500
|
const device = yield this.findDevice(path);
|
|
501
501
|
if (device.opened) {
|
|
@@ -517,7 +517,7 @@ class WebUsbTransport {
|
|
|
517
517
|
yield this.connect(path, false);
|
|
518
518
|
});
|
|
519
519
|
}
|
|
520
|
-
withProtocolReadTimeout(
|
|
520
|
+
withProtocolReadTimeout(path, promise, timeoutMs, protocol, onTimeout) {
|
|
521
521
|
return __awaiter(this, void 0, void 0, function* () {
|
|
522
522
|
let timer;
|
|
523
523
|
let timedOut = false;
|
|
@@ -532,11 +532,19 @@ class WebUsbTransport {
|
|
|
532
532
|
return yield Promise.race([
|
|
533
533
|
guardedPromise,
|
|
534
534
|
new Promise((_, reject) => {
|
|
535
|
-
timer = setTimeout(() => {
|
|
535
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
536
536
|
timedOut = true;
|
|
537
537
|
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
538
|
+
if (protocol === 'V1') {
|
|
539
|
+
try {
|
|
540
|
+
yield this.resetConnectionAfterProbe(path);
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
538
546
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
539
|
-
}, timeoutMs);
|
|
547
|
+
}), timeoutMs);
|
|
540
548
|
}),
|
|
541
549
|
]);
|
|
542
550
|
}
|
|
@@ -617,7 +625,6 @@ class WebUsbTransport {
|
|
|
617
625
|
});
|
|
618
626
|
}
|
|
619
627
|
callProtocolV2(path, name, data, options) {
|
|
620
|
-
var _a;
|
|
621
628
|
return __awaiter(this, void 0, void 0, function* () {
|
|
622
629
|
const protocolV1Messages = this.messages;
|
|
623
630
|
if (!this.messagesV2) {
|
|
@@ -641,33 +648,27 @@ class WebUsbTransport {
|
|
|
641
648
|
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
642
649
|
sequenceCursor,
|
|
643
650
|
writeFrame: (frame) => this.transferOutOnce(path, frame),
|
|
644
|
-
readFrame:
|
|
651
|
+
readFrame: context => this.receiveProtocolV2Frame(path, context.timeoutMs),
|
|
645
652
|
logger: this.Log,
|
|
646
653
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
647
|
-
createTimeoutError: (messageName, timeoutMs) => new
|
|
654
|
+
createTimeoutError: (messageName, timeoutMs) => new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
|
|
648
655
|
});
|
|
649
656
|
this.protocolV2Sessions.set(path, session);
|
|
650
657
|
}
|
|
651
|
-
this.protocolV2ReadTimeouts.set(path, options === null || options === void 0 ? void 0 : options.timeoutMs);
|
|
652
|
-
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
653
658
|
try {
|
|
654
659
|
return yield session.call(name, data, options);
|
|
655
660
|
}
|
|
656
661
|
catch (error) {
|
|
657
|
-
|
|
658
|
-
if (message.includes('protocol v2 read timeout') || message.includes('response timeout')) {
|
|
662
|
+
if (transport.isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
|
|
659
663
|
try {
|
|
660
664
|
yield this.resetConnectionAfterProbe(path);
|
|
661
665
|
}
|
|
662
666
|
catch (resetError) {
|
|
663
|
-
this.Log.debug('[WebUsbTransport] Protocol V2
|
|
667
|
+
this.Log.debug('[WebUsbTransport] Protocol V2 link reset failed:', resetError);
|
|
664
668
|
}
|
|
665
669
|
}
|
|
666
670
|
throw error;
|
|
667
671
|
}
|
|
668
|
-
finally {
|
|
669
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
670
|
-
}
|
|
671
672
|
});
|
|
672
673
|
}
|
|
673
674
|
receiveProtocolV2Frame(path, timeoutMs) {
|
|
@@ -688,12 +689,7 @@ class WebUsbTransport {
|
|
|
688
689
|
})
|
|
689
690
|
: yield transferIn;
|
|
690
691
|
const bytes = new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
|
|
691
|
-
|
|
692
|
-
frame = assembler.push(bytes);
|
|
693
|
-
}
|
|
694
|
-
catch (error) {
|
|
695
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
|
|
696
|
-
}
|
|
692
|
+
frame = assembler.push(bytes);
|
|
697
693
|
}
|
|
698
694
|
return frame;
|
|
699
695
|
});
|
|
@@ -748,7 +744,6 @@ class WebUsbTransport {
|
|
|
748
744
|
(_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
749
745
|
this.protocolV2Assemblers.delete(path);
|
|
750
746
|
this.protocolV2Sessions.delete(path);
|
|
751
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
752
747
|
this.deviceEndpoints.delete(path);
|
|
753
748
|
});
|
|
754
749
|
}
|
|
@@ -764,7 +759,6 @@ function inferProtocolHintFromDeviceName(name) {
|
|
|
764
759
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
765
760
|
const BLE_PACKET_SIZE = 192;
|
|
766
761
|
const BLE_WRITE_DELAY_MS = 5;
|
|
767
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
768
762
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
769
763
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
770
764
|
class ElectronBleTransport {
|
|
@@ -1023,9 +1017,12 @@ class ElectronBleTransport {
|
|
|
1023
1017
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1024
1018
|
}
|
|
1025
1019
|
if (expectedProtocol === 'V2') {
|
|
1026
|
-
this.
|
|
1027
|
-
|
|
1028
|
-
|
|
1020
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1021
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1022
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1023
|
+
return 'V2';
|
|
1024
|
+
}
|
|
1025
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1029
1026
|
}
|
|
1030
1027
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1031
1028
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
@@ -1361,17 +1358,17 @@ class ElectronBleTransport {
|
|
|
1361
1358
|
});
|
|
1362
1359
|
}
|
|
1363
1360
|
callProtocolV2(uuid, name, data, options) {
|
|
1364
|
-
var _a
|
|
1361
|
+
var _a;
|
|
1365
1362
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1366
1363
|
if (!this._messages || !this._messagesV2) {
|
|
1367
1364
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1368
1365
|
}
|
|
1369
|
-
const callOptions =
|
|
1366
|
+
const callOptions = options;
|
|
1370
1367
|
try {
|
|
1371
1368
|
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
1372
1369
|
}
|
|
1373
1370
|
catch (e) {
|
|
1374
|
-
(
|
|
1371
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1375
1372
|
throw e;
|
|
1376
1373
|
}
|
|
1377
1374
|
});
|
package/dist/webusb.d.ts
CHANGED
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,SAaN,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,mBAAmB,CAAoD;IAG/E,OAAO,CAAC,eAAe,CAA2C;IAMlE,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;IAU7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IAiDtB,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;YAwBzB,uBAAuB;YA0CvB,eAAe;YAaf,eAAe;IAgBvB,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;YA0Dd,sBAAsB;IAqC9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAkB1B,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.22",
|
|
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.22",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.22"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
27
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.22",
|
|
28
28
|
"@types/w3c-web-usb": "^1.0.6",
|
|
29
29
|
"@types/web-bluetooth": "^0.0.17"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "ce94a5ebbe141b0c15db7c68d08085ca1ddb0166"
|
|
32
32
|
}
|
|
@@ -62,7 +62,6 @@ const toBleDescriptor = (
|
|
|
62
62
|
|
|
63
63
|
const BLE_PACKET_SIZE = 192;
|
|
64
64
|
const BLE_WRITE_DELAY_MS = 5;
|
|
65
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30_000;
|
|
66
65
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
67
66
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
68
67
|
|
|
@@ -379,11 +378,12 @@ export default class ElectronBleTransport {
|
|
|
379
378
|
}
|
|
380
379
|
|
|
381
380
|
if (expectedProtocol === 'V2') {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
381
|
+
if (await this.probeProtocolV2(uuid)) {
|
|
382
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
383
|
+
this.Log?.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
384
|
+
return 'V2';
|
|
385
|
+
}
|
|
386
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -761,10 +761,7 @@ export default class ElectronBleTransport {
|
|
|
761
761
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
-
const callOptions =
|
|
765
|
-
...options,
|
|
766
|
-
timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
|
|
767
|
-
};
|
|
764
|
+
const callOptions = options;
|
|
768
765
|
|
|
769
766
|
try {
|
|
770
767
|
return await this.protocolV2Links.call(
|
package/src/webusb.ts
CHANGED
|
@@ -7,8 +7,10 @@ import transport, {
|
|
|
7
7
|
PROTOCOL_V2_CHANNEL_USB,
|
|
8
8
|
PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
9
9
|
ProtocolV2FrameAssembler,
|
|
10
|
+
ProtocolV2LinkError,
|
|
10
11
|
ProtocolV2SequenceCursor,
|
|
11
12
|
ProtocolV2Session,
|
|
13
|
+
isProtocolV2LinkError,
|
|
12
14
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
13
15
|
} from '@onekeyfe/hd-transport';
|
|
14
16
|
import {
|
|
@@ -85,9 +87,6 @@ export default class WebUsbTransport {
|
|
|
85
87
|
/** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
|
|
86
88
|
private protocolV2Sequences: Map<string, ProtocolV2SequenceCursor> = new Map();
|
|
87
89
|
|
|
88
|
-
/** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
|
|
89
|
-
private protocolV2ReadTimeouts: Map<string, number | undefined> = new Map();
|
|
90
|
-
|
|
91
90
|
/** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
|
|
92
91
|
private deviceEndpoints: Map<string, DeviceEndpoints> = new Map();
|
|
93
92
|
|
|
@@ -152,7 +151,6 @@ export default class WebUsbTransport {
|
|
|
152
151
|
configureProtocolV2(signedData: any) {
|
|
153
152
|
this.messagesV2 = parseConfigure(signedData);
|
|
154
153
|
this.protocolV2Sessions.clear();
|
|
155
|
-
this.protocolV2ReadTimeouts.clear();
|
|
156
154
|
}
|
|
157
155
|
|
|
158
156
|
/**
|
|
@@ -293,10 +291,11 @@ export default class WebUsbTransport {
|
|
|
293
291
|
}
|
|
294
292
|
|
|
295
293
|
if (expectedProtocol === 'V2') {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
if (await this.probeProtocolV2(path)) {
|
|
295
|
+
this.deviceProtocol.set(path, 'V2');
|
|
296
|
+
return 'V2';
|
|
297
|
+
}
|
|
298
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
300
299
|
}
|
|
301
300
|
|
|
302
301
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -635,7 +634,6 @@ export default class WebUsbTransport {
|
|
|
635
634
|
private async resetConnectionAfterProbe(path: string) {
|
|
636
635
|
this.protocolV2Assemblers.get(path)?.reset();
|
|
637
636
|
this.protocolV2Sessions.delete(path);
|
|
638
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
639
637
|
|
|
640
638
|
try {
|
|
641
639
|
const device = await this.findDevice(path);
|
|
@@ -658,7 +656,7 @@ export default class WebUsbTransport {
|
|
|
658
656
|
}
|
|
659
657
|
|
|
660
658
|
private async withProtocolReadTimeout<T>(
|
|
661
|
-
|
|
659
|
+
path: string,
|
|
662
660
|
promise: Promise<T>,
|
|
663
661
|
timeoutMs: number,
|
|
664
662
|
protocol: ProtocolType,
|
|
@@ -680,9 +678,16 @@ export default class WebUsbTransport {
|
|
|
680
678
|
return await Promise.race([
|
|
681
679
|
guardedPromise,
|
|
682
680
|
new Promise<never>((_, reject) => {
|
|
683
|
-
timer = setTimeout(() => {
|
|
681
|
+
timer = setTimeout(async () => {
|
|
684
682
|
timedOut = true;
|
|
685
683
|
onTimeout?.();
|
|
684
|
+
if (protocol === 'V1') {
|
|
685
|
+
try {
|
|
686
|
+
await this.resetConnectionAfterProbe(path);
|
|
687
|
+
} catch (error) {
|
|
688
|
+
this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
686
691
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
687
692
|
}, timeoutMs);
|
|
688
693
|
}),
|
|
@@ -821,32 +826,29 @@ export default class WebUsbTransport {
|
|
|
821
826
|
router: PROTOCOL_V2_CHANNEL_USB,
|
|
822
827
|
sequenceCursor,
|
|
823
828
|
writeFrame: (frame: Uint8Array) => this.transferOutOnce(path, frame),
|
|
824
|
-
readFrame:
|
|
829
|
+
readFrame: context => this.receiveProtocolV2Frame(path, context.timeoutMs),
|
|
825
830
|
logger: this.Log,
|
|
826
831
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
827
832
|
createTimeoutError: (messageName: string, timeoutMs: number) =>
|
|
828
|
-
new
|
|
833
|
+
new ProtocolV2LinkError(
|
|
834
|
+
'response-timeout',
|
|
835
|
+
`Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`
|
|
836
|
+
),
|
|
829
837
|
});
|
|
830
838
|
this.protocolV2Sessions.set(path, session);
|
|
831
839
|
}
|
|
832
840
|
|
|
833
|
-
this.protocolV2ReadTimeouts.set(path, options?.timeoutMs);
|
|
834
|
-
this.protocolV2Assemblers.get(path)?.reset();
|
|
835
841
|
try {
|
|
836
842
|
return await session.call(name, data, options);
|
|
837
843
|
} catch (error) {
|
|
838
|
-
|
|
839
|
-
error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
840
|
-
if (message.includes('protocol v2 read timeout') || message.includes('response timeout')) {
|
|
844
|
+
if (isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
|
|
841
845
|
try {
|
|
842
846
|
await this.resetConnectionAfterProbe(path);
|
|
843
847
|
} catch (resetError) {
|
|
844
|
-
this.Log.debug('[WebUsbTransport] Protocol V2
|
|
848
|
+
this.Log.debug('[WebUsbTransport] Protocol V2 link reset failed:', resetError);
|
|
845
849
|
}
|
|
846
850
|
}
|
|
847
851
|
throw error;
|
|
848
|
-
} finally {
|
|
849
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
850
852
|
}
|
|
851
853
|
}
|
|
852
854
|
|
|
@@ -879,14 +881,7 @@ export default class WebUsbTransport {
|
|
|
879
881
|
dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)
|
|
880
882
|
)
|
|
881
883
|
);
|
|
882
|
-
|
|
883
|
-
frame = assembler.push(bytes);
|
|
884
|
-
} catch (error) {
|
|
885
|
-
throw ERRORS.TypedError(
|
|
886
|
-
HardwareErrorCode.NetworkError,
|
|
887
|
-
error instanceof Error ? error.message : String(error)
|
|
888
|
-
);
|
|
889
|
-
}
|
|
884
|
+
frame = assembler.push(bytes);
|
|
890
885
|
}
|
|
891
886
|
return frame;
|
|
892
887
|
}
|
|
@@ -953,7 +948,6 @@ export default class WebUsbTransport {
|
|
|
953
948
|
this.protocolV2Assemblers.get(path)?.reset();
|
|
954
949
|
this.protocolV2Assemblers.delete(path);
|
|
955
950
|
this.protocolV2Sessions.delete(path);
|
|
956
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
957
951
|
this.deviceEndpoints.delete(path);
|
|
958
952
|
}
|
|
959
953
|
|